diff options
Diffstat (limited to 'source4/torture/rpc')
47 files changed, 33799 insertions, 0 deletions
diff --git a/source4/torture/rpc/alter_context.c b/source4/torture/rpc/alter_context.c new file mode 100644 index 0000000000..7843713074 --- /dev/null +++ b/source4/torture/rpc/alter_context.c @@ -0,0 +1,86 @@ +/* + Unix SMB/CIFS implementation. + + test suite for dcerpc alter_context operations + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "librpc/gen_ndr/ndr_dssetup.h" +#include "librpc/rpc/dcerpc.h" +#include "torture/rpc/rpc.h" + +bool torture_rpc_alter_context(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p, *p2, *p3; + struct policy_handle *handle; + struct ndr_interface_table tmptbl; + struct ndr_syntax_id syntax; + struct ndr_syntax_id transfer_syntax; + bool ret = true; + + torture_comment(torture, "opening LSA connection\n"); + status = torture_rpc_connection(torture, &p, &ndr_table_lsarpc); + torture_assert_ntstatus_ok(torture, status, "connecting"); + + if (!test_lsa_OpenPolicy2(p, torture, &handle)) { + ret = false; + } + + torture_comment(torture, "Opening secondary DSSETUP context\n"); + status = dcerpc_secondary_context(p, &p2, &ndr_table_dssetup); + torture_assert_ntstatus_ok(torture, status, "dcerpc_alter_context failed"); + + tmptbl = ndr_table_dssetup; + tmptbl.syntax_id.if_version += 100; + torture_comment(torture, "Opening bad secondary connection\n"); + status = dcerpc_secondary_context(p, &p3, &tmptbl); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX, + "dcerpc_alter_context with wrong version should fail"); + + torture_comment(torture, "testing DSSETUP pipe operations\n"); + ret &= test_DsRoleGetPrimaryDomainInformation(torture, p2); + + if (handle) { + ret &= test_lsa_Close(p, torture, handle); + } + + syntax = p->syntax; + transfer_syntax = p->transfer_syntax; + + torture_comment(torture, "Testing change of primary context\n"); + status = dcerpc_alter_context(p, torture, &p2->syntax, &p2->transfer_syntax); + torture_assert_ntstatus_ok(torture, status, "dcerpc_alter_context failed"); + + torture_comment(torture, "testing DSSETUP pipe operations - should fault\n"); + ret &= test_DsRoleGetPrimaryDomainInformation_ext(torture, p, NT_STATUS_NET_WRITE_FAULT); + + ret &= test_lsa_OpenPolicy2(p, torture, &handle); + + if (handle) { + ret &= test_lsa_Close(p, torture, handle); + } + + torture_comment(torture, "testing DSSETUP pipe operations\n"); + + ret &= test_DsRoleGetPrimaryDomainInformation(torture, p2); + + return ret; +} diff --git a/source4/torture/rpc/async_bind.c b/source4/torture/rpc/async_bind.c new file mode 100644 index 0000000000..0ebbef1ce6 --- /dev/null +++ b/source4/torture/rpc/async_bind.c @@ -0,0 +1,90 @@ +/* + Unix SMB/CIFS implementation. + + dcerpc torture tests + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Rafal Szczesniak 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "lib/events/events.h" +#include "libcli/composite/composite.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "lib/cmdline/popt_common.h" +#include "librpc/rpc/dcerpc.h" +#include "torture/rpc/rpc.h" + +/* + This test initiates multiple rpc bind requests and verifies + whether all of them are served. +*/ + + +bool torture_async_bind(struct torture_context *torture) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx; + int i; + const char *binding_string; + struct cli_credentials *creds; + extern int torture_numasync; + + struct composite_context **bind_req; + struct dcerpc_pipe **pipe; + const struct ndr_interface_table **table; + + if (!torture_setting_bool(torture, "async", false)) { + printf("async bind test disabled - enable async tests to use\n"); + return true; + } + + binding_string = torture_setting_string(torture, "binding", NULL); + + /* talloc context */ + mem_ctx = talloc_init("torture_async_bind"); + if (mem_ctx == NULL) return false; + + bind_req = talloc_array(torture, struct composite_context*, torture_numasync); + if (bind_req == NULL) return false; + pipe = talloc_array(torture, struct dcerpc_pipe*, torture_numasync); + if (pipe == NULL) return false; + table = talloc_array(torture, const struct ndr_interface_table*, torture_numasync); + if (table == NULL) return false; + + /* credentials */ + creds = cmdline_credentials; + + /* send bind requests */ + for (i = 0; i < torture_numasync; i++) { + table[i] = &ndr_table_lsarpc; + bind_req[i] = dcerpc_pipe_connect_send(mem_ctx, binding_string, + table[i], creds, torture->ev, torture->lp_ctx); + } + + /* recv bind requests */ + for (i = 0; i < torture_numasync; i++) { + status = dcerpc_pipe_connect_recv(bind_req[i], mem_ctx, &pipe[i]); + if (!NT_STATUS_IS_OK(status)) { + printf("async rpc connection failed: %s\n", nt_errstr(status)); + return false; + } + } + + talloc_free(mem_ctx); + return true; +} diff --git a/source4/torture/rpc/atsvc.c b/source4/torture/rpc/atsvc.c new file mode 100644 index 0000000000..23d76ae502 --- /dev/null +++ b/source4/torture/rpc/atsvc.c @@ -0,0 +1,139 @@ +/* + Unix SMB/CIFS implementation. + test suite for atsvc rpc operations + + Copyright (C) Tim Potter 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_atsvc_c.h" +#include "torture/rpc/rpc.h" + +static bool test_JobGetInfo(struct dcerpc_pipe *p, struct torture_context *tctx, uint32_t job_id) +{ + NTSTATUS status; + struct atsvc_JobGetInfo r; + struct atsvc_JobInfo *info = talloc(tctx, struct atsvc_JobInfo); + if (!info) { + return false; + } + + r.in.servername = dcerpc_server_name(p); + r.in.job_id = job_id; + r.out.job_info = &info; + + status = dcerpc_atsvc_JobGetInfo(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "JobGetInfo failed"); + + return true; +} + +static bool test_JobDel(struct dcerpc_pipe *p, struct torture_context *tctx, uint32_t min_job_id, + uint32_t max_job_id) +{ + NTSTATUS status; + struct atsvc_JobDel r; + + r.in.servername = dcerpc_server_name(p); + r.in.min_job_id = min_job_id; + r.in.max_job_id = max_job_id; + + status = dcerpc_atsvc_JobDel(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "JobDel failed"); + + return true; +} + +static bool test_JobEnum(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct atsvc_JobEnum r; + struct atsvc_enum_ctr ctr; + uint32_t resume_handle = 0, i, total_entries = 0; + + bool ret = true; + + r.in.servername = dcerpc_server_name(p); + ctr.entries_read = 0; + ctr.first_entry = NULL; + r.in.ctr = r.out.ctr = &ctr; + r.in.preferred_max_len = 0xffffffff; + r.in.resume_handle = r.out.resume_handle = &resume_handle; + r.out.total_entries = &total_entries; + + status = dcerpc_atsvc_JobEnum(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "JobEnum failed"); + + for (i = 0; i < r.out.ctr->entries_read; i++) { + if (!test_JobGetInfo(p, tctx, r.out.ctr->first_entry[i].job_id)) { + ret = false; + } + } + + return ret; +} + +static bool test_JobAdd(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct atsvc_JobAdd r; + struct atsvc_JobInfo info; + + r.in.servername = dcerpc_server_name(p); + info.job_time = 0x050ae4c0; /* 11:30pm */ + info.days_of_month = 0; /* n/a */ + info.days_of_week = 0x02; /* Tuesday */ + info.flags = 0x11; /* periodic, non-interactive */ + info.command = "foo.exe"; + r.in.job_info = &info; + + status = dcerpc_atsvc_JobAdd(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "JobAdd failed"); + + /* Run EnumJobs again in case there were no jobs to begin with */ + + if (!test_JobEnum(tctx, p)) { + return false; + } + + if (!test_JobGetInfo(p, tctx, *r.out.job_id)) { + return false; + } + + if (!test_JobDel(p, tctx, *r.out.job_id, *r.out.job_id)) { + return false; + } + + return true; +} + +struct torture_suite *torture_rpc_atsvc(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "ATSVC"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "atsvc", &ndr_table_atsvc); + + torture_rpc_tcase_add_test(tcase, "JobEnum", test_JobEnum); + torture_rpc_tcase_add_test(tcase, "JobAdd", test_JobAdd); + + return suite; +} diff --git a/source4/torture/rpc/autoidl.c b/source4/torture/rpc/autoidl.c new file mode 100644 index 0000000000..5ae0201855 --- /dev/null +++ b/source4/torture/rpc/autoidl.c @@ -0,0 +1,277 @@ +/* + Unix SMB/CIFS implementation. + + auto-idl scanner + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_drsuapi_c.h" +#include "librpc/gen_ndr/ndr_misc.h" +#include "librpc/ndr/ndr_table.h" +#include "torture/rpc/rpc.h" +#include "librpc/rpc/dcerpc_proto.h" + + +#if 1 +/* + get a DRSUAPI policy handle +*/ +static bool get_policy_handle(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct drsuapi_DsBind r; + + ZERO_STRUCT(r); + r.out.bind_handle = handle; + + status = dcerpc_drsuapi_DsBind(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("drsuapi_DsBind failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} +#else +/* + get a SAMR handle +*/ +static bool get_policy_handle(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_Connect r; + + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.connect_handle = handle; + + status = dcerpc_samr_Connect(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("samr_Connect failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} +#endif + +static void fill_blob_handle(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + DATA_BLOB b2; + + if (blob->length < 20) { + return; + } + + ndr_push_struct_blob(&b2, mem_ctx, NULL, handle, (ndr_push_flags_fn_t)ndr_push_policy_handle); + + memcpy(blob->data, b2.data, 20); +} + +static void reopen(struct torture_context *tctx, + struct dcerpc_pipe **p, + const struct ndr_interface_table *iface) +{ + NTSTATUS status; + + talloc_free(*p); + + status = torture_rpc_connection(tctx, p, iface); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to reopen '%s' - %s\n", iface->name, nt_errstr(status)); + exit(1); + } +} + +static void print_depth(int depth) +{ + int i; + for (i=0;i<depth;i++) { + printf(" "); + } +} + +static void test_ptr_scan(struct torture_context *tctx, const struct ndr_interface_table *iface, + int opnum, DATA_BLOB *base_in, int min_ofs, int max_ofs, int depth); + +static void try_expand(struct torture_context *tctx, const struct ndr_interface_table *iface, + int opnum, DATA_BLOB *base_in, int insert_ofs, int depth) +{ + DATA_BLOB stub_in, stub_out; + int n; + NTSTATUS status; + struct dcerpc_pipe *p = NULL; + + reopen(tctx, &p, iface); + + /* work out how much to expand to get a non fault */ + for (n=0;n<2000;n++) { + stub_in = data_blob(NULL, base_in->length + n); + data_blob_clear(&stub_in); + memcpy(stub_in.data, base_in->data, insert_ofs); + memcpy(stub_in.data+insert_ofs+n, base_in->data+insert_ofs, base_in->length-insert_ofs); + + status = dcerpc_request(p, NULL, opnum, false, tctx, &stub_in, &stub_out); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + print_depth(depth); + printf("expand by %d gives %s\n", n, nt_errstr(status)); + if (n >= 4) { + test_ptr_scan(tctx, iface, opnum, &stub_in, + insert_ofs, insert_ofs+n, depth+1); + } + return; + } else { +#if 0 + print_depth(depth); + printf("expand by %d gives fault %s\n", n, dcerpc_errstr(tctx, p->last_fault_code)); +#endif + } + if (p->last_fault_code == 5) { + reopen(tctx, &p, iface); + } + } + + talloc_free(p); +} + + +static void test_ptr_scan(struct torture_context *tctx, const struct ndr_interface_table *iface, + int opnum, DATA_BLOB *base_in, int min_ofs, int max_ofs, int depth) +{ + DATA_BLOB stub_in, stub_out; + int ofs; + NTSTATUS status; + struct dcerpc_pipe *p = NULL; + + reopen(tctx, &p, iface); + + stub_in = data_blob(NULL, base_in->length); + memcpy(stub_in.data, base_in->data, base_in->length); + + /* work out which elements are pointers */ + for (ofs=min_ofs;ofs<=max_ofs-4;ofs+=4) { + SIVAL(stub_in.data, ofs, 1); + status = dcerpc_request(p, NULL, opnum, false, tctx, &stub_in, &stub_out); + + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + print_depth(depth); + printf("possible ptr at ofs %d - fault %s\n", + ofs-min_ofs, dcerpc_errstr(tctx, p->last_fault_code)); + if (p->last_fault_code == 5) { + reopen(tctx, &p, iface); + } + if (depth == 0) { + try_expand(tctx, iface, opnum, &stub_in, ofs+4, depth+1); + } else { + try_expand(tctx, iface, opnum, &stub_in, max_ofs, depth+1); + } + SIVAL(stub_in.data, ofs, 0); + continue; + } + SIVAL(stub_in.data, ofs, 0); + } + + talloc_free(p); +} + + +static void test_scan_call(struct torture_context *tctx, const struct ndr_interface_table *iface, int opnum) +{ + DATA_BLOB stub_in, stub_out; + int i; + NTSTATUS status; + struct dcerpc_pipe *p = NULL; + struct policy_handle handle; + + reopen(tctx, &p, iface); + + get_policy_handle(p, tctx, &handle); + + /* work out the minimum amount of input data */ + for (i=0;i<2000;i++) { + stub_in = data_blob(NULL, i); + data_blob_clear(&stub_in); + + + status = dcerpc_request(p, NULL, opnum, false, tctx, &stub_in, &stub_out); + + if (NT_STATUS_IS_OK(status)) { + printf("opnum %d min_input %d - output %d\n", + opnum, (int)stub_in.length, (int)stub_out.length); + dump_data(0, stub_out.data, stub_out.length); + talloc_free(p); + test_ptr_scan(tctx, iface, opnum, &stub_in, 0, stub_in.length, 0); + return; + } + + fill_blob_handle(&stub_in, tctx, &handle); + + status = dcerpc_request(p, NULL, opnum, false, tctx, &stub_in, &stub_out); + + if (NT_STATUS_IS_OK(status)) { + printf("opnum %d min_input %d - output %d (with handle)\n", + opnum, (int)stub_in.length, (int)stub_out.length); + dump_data(0, stub_out.data, stub_out.length); + talloc_free(p); + test_ptr_scan(tctx, iface, opnum, &stub_in, 0, stub_in.length, 0); + return; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + printf("opnum %d size %d fault %s\n", opnum, i, dcerpc_errstr(tctx, p->last_fault_code)); + if (p->last_fault_code == 5) { + reopen(tctx, &p, iface); + } + continue; + } + + printf("opnum %d size %d error %s\n", opnum, i, nt_errstr(status)); + } + + printf("opnum %d minimum not found!?\n", opnum); + talloc_free(p); +} + + +static void test_auto_scan(struct torture_context *tctx, const struct ndr_interface_table *iface) +{ + test_scan_call(tctx, iface, 2); +} + +bool torture_rpc_autoidl(struct torture_context *torture) +{ + const struct ndr_interface_table *iface; + + iface = ndr_table_by_name("drsuapi"); + if (!iface) { + printf("Unknown interface!\n"); + return false; + } + + printf("\nProbing pipe '%s'\n", iface->name); + + test_auto_scan(torture, iface); + + return true; +} diff --git a/source4/torture/rpc/bench.c b/source4/torture/rpc/bench.c new file mode 100644 index 0000000000..2ae92332f8 --- /dev/null +++ b/source4/torture/rpc/bench.c @@ -0,0 +1,122 @@ +/* + Unix SMB/CIFS implementation. + + simple RPC benchmark + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_srvsvc_c.h" +#include "torture/rpc/rpc.h" +#include "param/param.h" + +/**************************/ +/* srvsvc_NetShare */ +/**************************/ +static bool test_NetShareEnumAll(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + struct srvsvc_NetShareEnumAll r; + struct srvsvc_NetShareCtr0 c0; + uint32_t levels[] = {0, 1, 2, 501, 502}; + int i; + bool ret = true; + uint32_t resume_handle; + + ZERO_STRUCT(c0); + + r.in.server_unc = talloc_asprintf(mem_ctx,"\\\\%s",dcerpc_server_name(p)); + r.in.ctr.ctr0 = &c0; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = &resume_handle; + r.out.resume_handle = &resume_handle; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + ZERO_STRUCT(r.out); + resume_handle = 0; + r.in.level = levels[i]; + status = dcerpc_srvsvc_NetShareEnumAll(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("NetShareEnumAll level %u failed - %s\n", r.in.level, nt_errstr(status)); + ret = false; + continue; + } + if (!W_ERROR_IS_OK(r.out.result)) { + printf("NetShareEnumAll level %u failed - %s\n", r.in.level, win_errstr(r.out.result)); + continue; + } + } + + return ret; +} + +/* + benchmark srvsvc netshareenumall queries +*/ +static bool bench_NetShareEnumAll(struct torture_context *tctx, struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx) +{ + struct timeval tv = timeval_current(); + bool ret = true; + int timelimit = torture_setting_int(tctx, "timelimit", 10); + int count=0; + + printf("Running for %d seconds\n", timelimit); + while (timeval_elapsed(&tv) < timelimit) { + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + if (!test_NetShareEnumAll(p, tmp_ctx)) break; + talloc_free(tmp_ctx); + count++; + if (count % 50 == 0) { + if (torture_setting_bool(tctx, "progress", true)) { + printf("%.1f queries per second \r", + count / timeval_elapsed(&tv)); + } + } + } + + printf("%.1f queries per second \n", count / timeval_elapsed(&tv)); + + return ret; +} + + +bool torture_bench_rpc(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *mem_ctx; + bool ret = true; + + mem_ctx = talloc_init("torture_rpc_srvsvc"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_srvsvc); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return false; + } + + if (!bench_NetShareEnumAll(torture, p, mem_ctx)) { + ret = false; + } + + talloc_free(mem_ctx); + + return ret; +} diff --git a/source4/torture/rpc/bind.c b/source4/torture/rpc/bind.c new file mode 100644 index 0000000000..55c9d3686d --- /dev/null +++ b/source4/torture/rpc/bind.c @@ -0,0 +1,82 @@ +/* + Unix SMB/CIFS implementation. + + dcerpc torture tests + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett <abartlet@samba.org 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "lib/cmdline/popt_common.h" +#include "librpc/rpc/dcerpc.h" +#include "torture/rpc/rpc.h" +#include "libcli/libcli.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" + +/* + This test is 'bogus' in that it doesn't actually perform to the + spec. We need to deal with other things inside the DCERPC layer, + before we could have multiple binds. + + We should never pass this test, until such details are fixed in our + client, and it looks like multible binds are never used anyway. + +*/ + +bool torture_multi_bind(struct torture_context *torture) +{ + struct dcerpc_pipe *p; + struct dcerpc_binding *binding; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret; + + mem_ctx = talloc_init("torture_multi_bind"); + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return false; + } + + status = torture_rpc_connection(torture, &p, &ndr_table_lsarpc); + + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return false; + } + + status = dcerpc_pipe_auth(mem_ctx, &p, binding, &ndr_table_lsarpc, cmdline_credentials, + torture->lp_ctx); + + if (NT_STATUS_IS_OK(status)) { + printf("(incorrectly) allowed re-bind to uuid %s - %s\n", + GUID_string(mem_ctx, &ndr_table_lsarpc.syntax_id.uuid), nt_errstr(status)); + ret = false; + } else { + printf("\n"); + ret = true; + } + + talloc_free(mem_ctx); + + return ret; +} diff --git a/source4/torture/rpc/countcalls.c b/source4/torture/rpc/countcalls.c new file mode 100644 index 0000000000..205ee1ec86 --- /dev/null +++ b/source4/torture/rpc/countcalls.c @@ -0,0 +1,129 @@ +/* + Unix SMB/CIFS implementation. + + count number of calls on an interface + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/ndr/libndr.h" +#include "librpc/ndr/ndr_table.h" +#include "torture/rpc/rpc.h" +#include "param/param.h" +#include "librpc/rpc/dcerpc_proto.h" + + + +bool count_calls(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + const struct ndr_interface_table *iface, + bool all) +{ + struct dcerpc_pipe *p; + DATA_BLOB stub_in, stub_out; + int i; + NTSTATUS status = torture_rpc_connection(tctx, &p, iface); + if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status) + || NT_STATUS_EQUAL(NT_STATUS_NET_WRITE_FAULT, status) + || NT_STATUS_EQUAL(NT_STATUS_PORT_UNREACHABLE, status) + || NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) { + if (all) { + /* Not fatal if looking for all pipes */ + return true; + } else { + printf("Failed to open '%s' to count calls - %s\n", iface->name, nt_errstr(status)); + return false; + } + } else if (!NT_STATUS_IS_OK(status)) { + printf("Failed to open '%s' to count calls - %s\n", iface->name, nt_errstr(status)); + return false; + } + + stub_in = data_blob_talloc(p, mem_ctx, 0); + + printf("\nScanning pipe '%s'\n", iface->name); + + for (i=0;i<500;i++) { + status = dcerpc_request(p, NULL, i, false, p, &stub_in, &stub_out); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT) && + p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) { + i--; + break; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT) && + p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) { + i--; + break; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) { + i--; + break; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) { + i--; + break; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + i--; + break; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)) { + i--; + break; + } + } + + if (i==500) { + talloc_free(p); + printf("no limit on calls: %s!?\n", nt_errstr(status)); + return false; + } + + printf("Found %d calls\n", i); + + talloc_free(p); + + return true; + +} + +bool torture_rpc_countcalls(struct torture_context *torture) +{ + const struct ndr_interface_table *iface; + const char *iface_name; + bool ret = true; + const struct ndr_interface_list *l; + iface_name = lp_parm_string(torture->lp_ctx, NULL, "countcalls", "interface"); + if (iface_name != NULL) { + iface = ndr_table_by_name(iface_name); + if (!iface) { + printf("Unknown interface '%s'\n", iface_name); + return false; + } + return count_calls(torture, torture, iface, false); + } + + for (l=ndr_table_list();l;l=l->next) { + TALLOC_CTX *loop_ctx; + loop_ctx = talloc_named(torture, 0, "torture_rpc_councalls loop context"); + ret &= count_calls(torture, loop_ctx, l->table, true); + talloc_free(loop_ctx); + } + return ret; +} diff --git a/source4/torture/rpc/dfs.c b/source4/torture/rpc/dfs.c new file mode 100644 index 0000000000..1c81766ebe --- /dev/null +++ b/source4/torture/rpc/dfs.c @@ -0,0 +1,665 @@ +/* + Unix SMB/CIFS implementation. + test suite for rpc dfs operations + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/rpc/rpc.h" +#include "librpc/gen_ndr/ndr_dfs_c.h" +#include "librpc/gen_ndr/ndr_srvsvc_c.h" +#include "libnet/libnet.h" +#include "libcli/raw/libcliraw.h" +#include "torture/util.h" +#include "libcli/libcli.h" +#include "lib/cmdline/popt_common.h" + +#define SMBTORTURE_DFS_SHARENAME "smbtorture_dfs_share" +#define SMBTORTURE_DFS_DIRNAME "\\smbtorture_dfs_dir" +#define SMBTORTURE_DFS_PATHNAME "C:"SMBTORTURE_DFS_DIRNAME + +#define IS_DFS_VERSION_UNSUPPORTED_CALL_W2K3(x,y)\ + if (x == DFS_MANAGER_VERSION_W2K3) {\ + if (!W_ERROR_EQUAL(y,WERR_NOT_SUPPORTED)) {\ + printf("expected WERR_NOT_SUPPORTED\n");\ + return false;\ + }\ + return true;\ + }\ + +static bool test_NetShareAdd(TALLOC_CTX *mem_ctx, + struct torture_context *tctx, + const char *host, + const char *sharename, + const char *dir) +{ + NTSTATUS status; + struct srvsvc_NetShareInfo2 i; + struct libnet_context* libnetctx; + struct libnet_AddShare r; + + printf("Creating share %s\n", sharename); + + if (!(libnetctx = libnet_context_init(tctx->ev, tctx->lp_ctx))) { + return false; + } + + libnetctx->cred = cmdline_credentials; + + i.name = sharename; + i.type = STYPE_DISKTREE; + i.path = dir; + i.max_users = (uint32_t) -1; + i.comment = "created by smbtorture"; + i.password = NULL; + i.permissions = 0x0; + i.current_users = 0x0; + + r.level = 2; + r.in.server_name = host; + r.in.share = i; + + status = libnet_AddShare(libnetctx, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Failed to add new share: %s (%s)\n", + nt_errstr(status), r.out.error_string); + return false; + } + + return true; +} + +static bool test_NetShareDel(TALLOC_CTX *mem_ctx, + struct torture_context *tctx, + const char *host, + const char *sharename) +{ + NTSTATUS status; + struct libnet_context* libnetctx; + struct libnet_DelShare r; + + printf("Deleting share %s\n", sharename); + + if (!(libnetctx = libnet_context_init(tctx->ev, tctx->lp_ctx))) { + return false; + } + + libnetctx->cred = cmdline_credentials; + + r.in.share_name = sharename; + r.in.server_name = host; + + status = libnet_DelShare(libnetctx, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Failed to delete share: %s (%s)\n", + nt_errstr(status), r.out.error_string); + return false; + } + + return true; +} + +static bool test_CreateDir(TALLOC_CTX *mem_ctx, + struct smbcli_state **cli, + struct torture_context *tctx, + const char *host, + const char *share, + const char *dir) +{ + printf("Creating directory %s\n", dir); + + if (!torture_open_connection_share(mem_ctx, cli, tctx, host, share, tctx->ev)) { + return false; + } + + if (!torture_setup_dir(*cli, dir)) { + return false; + } + + return true; +} + +static bool test_DeleteDir(struct smbcli_state *cli, + const char *dir) +{ + printf("Deleting directory %s\n", dir); + + if (smbcli_deltree(cli->tree, dir) == -1) { + printf("Unable to delete dir %s - %s\n", dir, + smbcli_errstr(cli->tree)); + return false; + } + + return true; +} + +static bool test_GetManagerVersion(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + enum dfs_ManagerVersion *version) +{ + NTSTATUS status; + struct dfs_GetManagerVersion r; + + r.out.version = version; + + status = dcerpc_dfs_GetManagerVersion(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("GetManagerVersion failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + +static bool test_ManagerInitialize(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + const char *host) +{ + NTSTATUS status; + enum dfs_ManagerVersion version; + struct dfs_ManagerInitialize r; + + printf("Testing ManagerInitialize\n"); + + if (!test_GetManagerVersion(p, mem_ctx, &version)) { + return false; + } + + r.in.servername = host; + r.in.flags = 0; + + status = dcerpc_dfs_ManagerInitialize(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("ManagerInitialize failed - %s\n", nt_errstr(status)); + return false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("dfs_ManagerInitialize failed - %s\n", + win_errstr(r.out.result)); + IS_DFS_VERSION_UNSUPPORTED_CALL_W2K3(version, r.out.result); + return false; + } + + return true; +} + +static bool test_GetInfoLevel(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + uint16_t level, + const char *root) +{ + NTSTATUS status; + struct dfs_GetInfo r; + + printf("Testing GetInfo level %u on '%s'\n", level, root); + + r.in.dfs_entry_path = talloc_strdup(mem_ctx, root); + r.in.servername = NULL; + r.in.sharename = NULL; + r.in.level = level; + + status = dcerpc_dfs_GetInfo(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("GetInfo failed - %s\n", nt_errstr(status)); + return false; + } else if (!W_ERROR_IS_OK(r.out.result) && + !W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, r.out.result)) { + printf("dfs_GetInfo failed - %s\n", win_errstr(r.out.result)); + return false; + } + + return true; +} + +static bool test_GetInfo(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + const char *root) +{ + bool ret = true; + /* 103, 104, 105, 106 is only available on Set */ + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 100, 101, 102, 103, 104, 105, 106}; + int i; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + if (!test_GetInfoLevel(p, mem_ctx, levels[i], root)) { + ret = false; + } + } + return ret; +} + +static bool test_EnumLevelEx(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + uint16_t level, + const char *dfs_name) +{ + NTSTATUS status; + struct dfs_EnumEx rex; + uint32_t total=0; + struct dfs_EnumStruct e; + struct dfs_Info1 s; + struct dfs_EnumArray1 e1; + bool ret = true; + + rex.in.level = level; + rex.in.bufsize = (uint32_t)-1; + rex.in.total = &total; + rex.in.info = &e; + rex.in.dfs_name = dfs_name; + + e.level = rex.in.level; + e.e.info1 = &e1; + e.e.info1->count = 0; + e.e.info1->s = &s; + s.path = NULL; + + printf("Testing EnumEx level %u on '%s'\n", level, dfs_name); + + status = dcerpc_dfs_EnumEx(p, mem_ctx, &rex); + if (!NT_STATUS_IS_OK(status)) { + printf("EnumEx failed - %s\n", nt_errstr(status)); + return false; + } + + if (level == 1 && rex.out.total) { + int i; + for (i=0;i<*rex.out.total;i++) { + const char *root = talloc_strdup(mem_ctx, + rex.out.info->e.info1->s[i].path); + if (!test_GetInfo(p, mem_ctx, root)) { + ret = false; + } + } + } + + if (level == 300 && rex.out.total) { + int i,k; + for (i=0;i<*rex.out.total;i++) { + uint16_t levels[] = {1, 2, 3, 4, 200}; /* 300 */ + const char *root = talloc_strdup(mem_ctx, + rex.out.info->e.info300->s[i].dom_root); + for (k=0;k<ARRAY_SIZE(levels);k++) { + if (!test_EnumLevelEx(p, mem_ctx, + levels[k], root)) + { + ret = false; + } + } + if (!test_GetInfo(p, mem_ctx, root)) { + ret = false; + } + } + } + + return ret; +} + + +static bool test_EnumLevel(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + uint16_t level) +{ + NTSTATUS status; + struct dfs_Enum r; + uint32_t total=0; + struct dfs_EnumStruct e; + struct dfs_Info1 s; + struct dfs_EnumArray1 e1; + bool ret = true; + + r.in.level = level; + r.in.bufsize = (uint32_t)-1; + r.in.total = &total; + r.in.info = &e; + + e.level = r.in.level; + e.e.info1 = &e1; + e.e.info1->count = 0; + e.e.info1->s = &s; + s.path = NULL; + + printf("Testing Enum level %u\n", level); + + status = dcerpc_dfs_Enum(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("Enum failed - %s\n", nt_errstr(status)); + return false; + } else if (!W_ERROR_IS_OK(r.out.result) && + !W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, r.out.result)) { + printf("dfs_Enum failed - %s\n", win_errstr(r.out.result)); + return false; + } + + if (level == 1 && r.out.total) { + int i; + for (i=0;i<*r.out.total;i++) { + const char *root = r.out.info->e.info1->s[i].path; + if (!test_GetInfo(p, mem_ctx, root)) { + ret = false; + } + } + + } + + return ret; +} + + +static bool test_Enum(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx) +{ + bool ret = true; + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 200, 300}; + int i; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + if (!test_EnumLevel(p, mem_ctx, levels[i])) { + ret = false; + } + } + + return ret; +} + +static bool test_EnumEx(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + const char *host) +{ + bool ret = true; + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 200, 300}; + int i; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + if (!test_EnumLevelEx(p, mem_ctx, levels[i], host)) { + ret = false; + } + } + + return ret; +} + +static bool test_RemoveStdRoot(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + const char *host, + const char *sharename) +{ + struct dfs_RemoveStdRoot r; + NTSTATUS status; + + printf("Testing RemoveStdRoot\n"); + + r.in.servername = host; + r.in.rootshare = sharename; + r.in.flags = 0; + + status = dcerpc_dfs_RemoveStdRoot(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("RemoveStdRoot failed - %s\n", nt_errstr(status)); + return false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("dfs_RemoveStdRoot failed - %s\n", + win_errstr(r.out.result)); + return false; + } + + return true; +} + +static bool test_AddStdRoot(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + const char *host, + const char *sharename) +{ + NTSTATUS status; + struct dfs_AddStdRoot r; + + printf("Testing AddStdRoot\n"); + + r.in.servername = host; + r.in.rootshare = sharename; + r.in.comment = "standard dfs standalone DFS root created by smbtorture (dfs_AddStdRoot)"; + r.in.flags = 0; + + status = dcerpc_dfs_AddStdRoot(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("AddStdRoot failed - %s\n", nt_errstr(status)); + return false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("dfs_AddStdRoot failed - %s\n", + win_errstr(r.out.result)); + return false; + } + + return true; +} + +static bool test_AddStdRootForced(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + const char *host, + const char *sharename) +{ + NTSTATUS status; + struct dfs_AddStdRootForced r; + enum dfs_ManagerVersion version; + + printf("Testing AddStdRootForced\n"); + + if (!test_GetManagerVersion(p, mem_ctx, &version)) { + return false; + } + + r.in.servername = host; + r.in.rootshare = sharename; + r.in.comment = "standard dfs forced standalone DFS root created by smbtorture (dfs_AddStdRootForced)"; + r.in.store = SMBTORTURE_DFS_PATHNAME; + + status = dcerpc_dfs_AddStdRootForced(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("AddStdRootForced failed - %s\n", nt_errstr(status)); + return false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("dfs_AddStdRootForced failed - %s\n", + win_errstr(r.out.result)); + IS_DFS_VERSION_UNSUPPORTED_CALL_W2K3(version, r.out.result); + return false; + } + + return test_RemoveStdRoot(p, mem_ctx, host, sharename); +} + +static void test_cleanup_stdroot(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct torture_context *tctx, + const char *host, + const char *sharename, + const char *dir) +{ + struct smbcli_state *cli; + + printf("Cleaning up StdRoot\n"); + + test_RemoveStdRoot(p, mem_ctx, host, sharename); + test_NetShareDel(mem_ctx, tctx, host, sharename); + torture_open_connection_share(mem_ctx, &cli, tctx, host, "C$", tctx->ev); + test_DeleteDir(cli, dir); + torture_close_connection(cli); +} + +static bool test_StdRoot(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct torture_context *tctx, + const char *host) +{ + const char *sharename = SMBTORTURE_DFS_SHARENAME; + const char *dir = SMBTORTURE_DFS_DIRNAME; + const char *path = SMBTORTURE_DFS_PATHNAME; + struct smbcli_state *cli; + bool ret = true; + + printf("Testing StdRoot\n"); + + test_cleanup_stdroot(p, mem_ctx, tctx, host, sharename, dir); + + ret &= test_CreateDir(mem_ctx, &cli, tctx, host, "C$", dir); + ret &= test_NetShareAdd(mem_ctx, tctx, host, sharename, path); + ret &= test_AddStdRoot(p, mem_ctx, host, sharename); + ret &= test_RemoveStdRoot(p, mem_ctx, host, sharename); + ret &= test_AddStdRootForced(p, mem_ctx, host, sharename); + ret &= test_NetShareDel(mem_ctx, tctx, host, sharename); + ret &= test_DeleteDir(cli, dir); + + torture_close_connection(cli); + + return ret; +} + +static bool test_GetDcAddress(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + const char *host) +{ + NTSTATUS status; + struct dfs_GetDcAddress r; + uint8_t is_root = 0; + uint32_t ttl = 0; + const char *ptr; + + printf("Testing GetDcAddress\n"); + + ptr = host; + + r.in.servername = host; + r.in.server_fullname = r.out.server_fullname = &ptr; + r.in.is_root = r.out.is_root = &is_root; + r.in.ttl = r.out.ttl = &ttl; + + status = dcerpc_dfs_GetDcAddress(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("GetDcAddress failed - %s\n", nt_errstr(status)); + return false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("dfs_GetDcAddress failed - %s\n", + win_errstr(r.out.result)); + return false; + } + + return true; +} + +static bool test_SetDcAddress(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + const char *host) +{ + NTSTATUS status; + struct dfs_SetDcAddress r; + + printf("Testing SetDcAddress\n"); + + r.in.servername = host; + r.in.server_fullname = host; + r.in.flags = 0; + r.in.ttl = 1000; + + status = dcerpc_dfs_SetDcAddress(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("SetDcAddress failed - %s\n", nt_errstr(status)); + return false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("dfs_SetDcAddress failed - %s\n", + win_errstr(r.out.result)); + return false; + } + + return true; +} + +static bool test_DcAddress(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + const char *host) +{ + if (!test_GetDcAddress(p, mem_ctx, host)) { + return false; + } + + if (!test_SetDcAddress(p, mem_ctx, host)) { + return false; + } + + return true; +} + +static bool test_FlushFtTable(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + const char *host, + const char *sharename) +{ + NTSTATUS status; + struct dfs_FlushFtTable r; + enum dfs_ManagerVersion version; + + printf("Testing FlushFtTable\n"); + + if (!test_GetManagerVersion(p, mem_ctx, &version)) { + return false; + } + + r.in.servername = host; + r.in.rootshare = sharename; + + status = dcerpc_dfs_FlushFtTable(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("FlushFtTable failed - %s\n", nt_errstr(status)); + return false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("dfs_FlushFtTable failed - %s\n", + win_errstr(r.out.result)); + IS_DFS_VERSION_UNSUPPORTED_CALL_W2K3(version, r.out.result); + return false; + } + + return true; +} + +static bool test_FtRoot(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + const char *host) +{ + const char *sharename = SMBTORTURE_DFS_SHARENAME; + + return test_FlushFtTable(p, mem_ctx, host, sharename); +} + +bool torture_rpc_dfs(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + enum dfs_ManagerVersion version; + const char *host = torture_setting_string(torture, "host", NULL); + + status = torture_rpc_connection(torture, &p, &ndr_table_netdfs); + torture_assert_ntstatus_ok(torture, status, "Unable to connect"); + + ret &= test_GetManagerVersion(p, torture, &version); + ret &= test_ManagerInitialize(p, torture, host); + ret &= test_Enum(p, torture); + ret &= test_EnumEx(p, torture, host); + ret &= test_StdRoot(p, torture, torture, host); + ret &= test_FtRoot(p, torture, host); + ret &= test_DcAddress(p, torture, host); + + return ret; +} diff --git a/source4/torture/rpc/drsuapi.c b/source4/torture/rpc/drsuapi.c new file mode 100644 index 0000000000..e5cb3d7ddf --- /dev/null +++ b/source4/torture/rpc/drsuapi.c @@ -0,0 +1,783 @@ +/* + Unix SMB/CIFS implementation. + + DRSUapi tests + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 2004 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_drsuapi_c.h" +#include "torture/rpc/rpc.h" +#include "param/param.h" + +#define TEST_MACHINE_NAME "torturetest" + +bool test_DsBind(struct dcerpc_pipe *p, struct torture_context *tctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct drsuapi_DsBind r; + + GUID_from_string(DRSUAPI_DS_BIND_GUID, &priv->bind_guid); + + r.in.bind_guid = &priv->bind_guid; + r.in.bind_info = NULL; + r.out.bind_handle = &priv->bind_handle; + + torture_comment(tctx, "testing DsBind\n"); + + status = dcerpc_drsuapi_DsBind(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(tctx, p->last_fault_code); + } + torture_fail(tctx, "dcerpc_drsuapi_DsBind failed"); + } else if (!W_ERROR_IS_OK(r.out.result)) { + torture_fail(tctx, "DsBind failed"); + } + + return true; +} + +static bool test_DsGetDomainControllerInfo(struct dcerpc_pipe *p, struct torture_context *torture, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct drsuapi_DsGetDomainControllerInfo r; + bool found = false; + int i, j, k; + + struct { + const char *name; + WERROR expected; + } names[] = { + { + .name = torture_join_dom_netbios_name(priv->join), + .expected = WERR_OK + }, + { + .name = torture_join_dom_dns_name(priv->join), + .expected = WERR_OK + }, + { + .name = "__UNKNOWN_DOMAIN__", + .expected = WERR_DS_OBJ_NOT_FOUND + }, + { + .name = "unknown.domain.samba.example.com", + .expected = WERR_DS_OBJ_NOT_FOUND + }, + }; + int levels[] = {1, 2}; + int level; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + for (j=0; j < ARRAY_SIZE(names); j++) { + level = levels[i]; + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; + + r.in.req.req1.domain_name = names[j].name; + r.in.req.req1.level = level; + + torture_comment(torture, + "testing DsGetDomainControllerInfo level %d on domainname '%s'\n", + r.in.req.req1.level, r.in.req.req1.domain_name); + + status = dcerpc_drsuapi_DsGetDomainControllerInfo(p, torture, &r); + torture_assert_ntstatus_ok(torture, status, + "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed"); + torture_assert_werr_equal(torture, + r.out.result, names[j].expected, + "DsGetDomainControllerInfo level with dns domain failed"); + + if (!W_ERROR_IS_OK(r.out.result)) { + /* If this was an error, we can't read the result structure */ + continue; + } + + torture_assert_int_equal(torture, + r.in.req.req1.level, r.out.level_out, + "dcerpc_drsuapi_DsGetDomainControllerInfo level"); + + switch (level) { + case 1: + for (k=0; k < r.out.ctr.ctr1.count; k++) { + if (strcasecmp_m(r.out.ctr.ctr1.array[k].netbios_name, + torture_join_netbios_name(priv->join)) == 0) { + found = true; + break; + } + } + break; + case 2: + for (k=0; k < r.out.ctr.ctr2.count; k++) { + if (strcasecmp_m(r.out.ctr.ctr2.array[k].netbios_name, + torture_join_netbios_name(priv->join)) == 0) { + found = true; + priv->dcinfo = r.out.ctr.ctr2.array[k]; + break; + } + } + break; + } + torture_assert(torture, found, + "dcerpc_drsuapi_DsGetDomainControllerInfo: Failed to find the domain controller we just created during the join"); + } + } + + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; + + r.in.req.req1.domain_name = "__UNKNOWN_DOMAIN__"; /* This is clearly ignored for this level */ + r.in.req.req1.level = -1; + + printf("testing DsGetDomainControllerInfo level %d on domainname '%s'\n", + r.in.req.req1.level, r.in.req.req1.domain_name); + + status = dcerpc_drsuapi_DsGetDomainControllerInfo(p, torture, &r); + + torture_assert_ntstatus_ok(torture, status, + "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed"); + torture_assert_werr_ok(torture, r.out.result, + "DsGetDomainControllerInfo with dns domain failed"); + + { + const char *dc_account = talloc_asprintf(torture, "%s\\%s$", + torture_join_dom_netbios_name(priv->join), + priv->dcinfo.netbios_name); + for (k=0; k < r.out.ctr.ctr01.count; k++) { + if (strcasecmp_m(r.out.ctr.ctr01.array[k].client_account, + dc_account)) { + found = true; + break; + } + } + torture_assert(torture, found, + "dcerpc_drsuapi_DsGetDomainControllerInfo level: Failed to find the domain controller in last logon records"); + } + + + return true; +} + +static bool test_DsWriteAccountSpn(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct drsuapi_DsWriteAccountSpn r; + struct drsuapi_DsNameString names[2]; + bool ret = true; + + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; + + printf("testing DsWriteAccountSpn\n"); + + r.in.req.req1.operation = DRSUAPI_DS_SPN_OPERATION_ADD; + r.in.req.req1.unknown1 = 0; + r.in.req.req1.object_dn = priv->dcinfo.computer_dn; + r.in.req.req1.count = 2; + r.in.req.req1.spn_names = names; + names[0].str = talloc_asprintf(mem_ctx, "smbtortureSPN/%s",priv->dcinfo.netbios_name); + names[1].str = talloc_asprintf(mem_ctx, "smbtortureSPN/%s",priv->dcinfo.dns_name); + + status = dcerpc_drsuapi_DsWriteAccountSpn(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); + } + printf("dcerpc_drsuapi_DsWriteAccountSpn failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsWriteAccountSpn failed - %s\n", win_errstr(r.out.result)); + ret = false; + } + + r.in.req.req1.operation = DRSUAPI_DS_SPN_OPERATION_DELETE; + r.in.req.req1.unknown1 = 0; + + status = dcerpc_drsuapi_DsWriteAccountSpn(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); + } + printf("dcerpc_drsuapi_DsWriteAccountSpn failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsWriteAccountSpn failed - %s\n", win_errstr(r.out.result)); + ret = false; + } + + return ret; +} + +static bool test_DsReplicaGetInfo(struct dcerpc_pipe *p, struct torture_context *tctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct drsuapi_DsReplicaGetInfo r; + bool ret = true; + int i; + struct { + int32_t level; + int32_t infotype; + const char *obj_dn; + } array[] = { + { + DRSUAPI_DS_REPLICA_GET_INFO, + DRSUAPI_DS_REPLICA_INFO_NEIGHBORS, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO, + DRSUAPI_DS_REPLICA_INFO_CURSORS, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO, + DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO, + DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO, + DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO, + DRSUAPI_DS_REPLICA_INFO_PENDING_OPS, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_CURSORS2, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_CURSORS3, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA2, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_NEIGHBORS02, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_CONNECTIONS04, + "__IGNORED__" + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_CURSORS05, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_06, + NULL + } + }; + + if (torture_setting_bool(tctx, "samba4", false)) { + printf("skipping DsReplicaGetInfo test against Samba4\n"); + return true; + } + + r.in.bind_handle = &priv->bind_handle; + + for (i=0; i < ARRAY_SIZE(array); i++) { + const char *object_dn; + + printf("testing DsReplicaGetInfo level %d infotype %d\n", + array[i].level, array[i].infotype); + + object_dn = (array[i].obj_dn ? array[i].obj_dn : priv->domain_obj_dn); + + r.in.level = array[i].level; + switch(r.in.level) { + case DRSUAPI_DS_REPLICA_GET_INFO: + r.in.req.req1.info_type = array[i].infotype; + r.in.req.req1.object_dn = object_dn; + ZERO_STRUCT(r.in.req.req1.guid1); + break; + case DRSUAPI_DS_REPLICA_GET_INFO2: + r.in.req.req2.info_type = array[i].infotype; + r.in.req.req2.object_dn = object_dn; + ZERO_STRUCT(r.in.req.req1.guid1); + r.in.req.req2.unknown1 = 0; + r.in.req.req2.string1 = NULL; + r.in.req.req2.string2 = NULL; + r.in.req.req2.unknown2 = 0; + break; + } + + status = dcerpc_drsuapi_DsReplicaGetInfo(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(tctx, p->last_fault_code); + } + if (p->last_fault_code != DCERPC_FAULT_INVALID_TAG) { + printf("dcerpc_drsuapi_DsReplicaGetInfo failed - %s\n", errstr); + ret = false; + } else { + printf("DsReplicaGetInfo level %d and/or infotype %d not supported by server\n", + array[i].level, array[i].infotype); + } + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsReplicaGetInfo failed - %s\n", win_errstr(r.out.result)); + ret = false; + } + } + + return ret; +} + +static bool test_DsReplicaSync(struct dcerpc_pipe *p, struct torture_context *tctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + bool ret = true; + int i; + struct drsuapi_DsReplicaSync r; + struct drsuapi_DsReplicaObjectIdentifier nc; + struct GUID null_guid; + struct dom_sid null_sid; + struct { + int32_t level; + } array[] = { + { + 1 + } + }; + + if (!torture_setting_bool(tctx, "dangerous", false)) { + printf("DsReplicaSync disabled - enable dangerous tests to use\n"); + return true; + } + + if (torture_setting_bool(tctx, "samba4", false)) { + printf("skipping DsReplicaSync test against Samba4\n"); + return true; + } + + ZERO_STRUCT(null_guid); + ZERO_STRUCT(null_sid); + + r.in.bind_handle = &priv->bind_handle; + + for (i=0; i < ARRAY_SIZE(array); i++) { + printf("testing DsReplicaSync level %d\n", + array[i].level); + + r.in.level = array[i].level; + switch(r.in.level) { + case 1: + nc.guid = null_guid; + nc.sid = null_sid; + nc.dn = priv->domain_obj_dn?priv->domain_obj_dn:""; + + r.in.req.req1.naming_context = &nc; + r.in.req.req1.source_dsa_guid = priv->dcinfo.ntds_guid; + r.in.req.req1.other_info = NULL; + r.in.req.req1.options = 16; + break; + } + + status = dcerpc_drsuapi_DsReplicaSync(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(tctx, p->last_fault_code); + } + printf("dcerpc_drsuapi_DsReplicaSync failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsReplicaSync failed - %s\n", win_errstr(r.out.result)); + ret = false; + } + } + + return ret; +} + +static bool test_DsReplicaUpdateRefs(struct dcerpc_pipe *p, struct torture_context *tctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + bool ret = true; + int i; + struct drsuapi_DsReplicaUpdateRefs r; + struct drsuapi_DsReplicaObjectIdentifier nc; + struct GUID null_guid; + struct dom_sid null_sid; + struct { + int32_t level; + } array[] = { + { + 1 + } + }; + + if (torture_setting_bool(tctx, "samba4", false)) { + printf("skipping DsReplicaUpdateRefs test against Samba4\n"); + return true; + } + + ZERO_STRUCT(null_guid); + ZERO_STRUCT(null_sid); + + r.in.bind_handle = &priv->bind_handle; + + for (i=0; i < ARRAY_SIZE(array); i++) { + printf("testing DsReplicaUpdateRefs level %d\n", + array[i].level); + + r.in.level = array[i].level; + switch(r.in.level) { + case 1: + nc.guid = null_guid; + nc.sid = null_sid; + nc.dn = priv->domain_obj_dn?priv->domain_obj_dn:""; + + r.in.req.req1.naming_context = &nc; + r.in.req.req1.dest_dsa_dns_name = talloc_asprintf(tctx, "__some_dest_dsa_guid_string._msdn.%s", + priv->domain_dns_name); + r.in.req.req1.dest_dsa_guid = null_guid; + r.in.req.req1.options = 0; + break; + } + + status = dcerpc_drsuapi_DsReplicaUpdateRefs(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(tctx, p->last_fault_code); + } + printf("dcerpc_drsuapi_DsReplicaUpdateRefs failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsReplicaUpdateRefs failed - %s\n", win_errstr(r.out.result)); + ret = false; + } + } + + return ret; +} + +static bool test_DsGetNCChanges(struct dcerpc_pipe *p, struct torture_context *tctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + bool ret = true; + int i; + struct drsuapi_DsGetNCChanges r; + struct drsuapi_DsReplicaObjectIdentifier nc; + struct GUID null_guid; + struct dom_sid null_sid; + struct { + int32_t level; + } array[] = { + { + 5 + }, + { + 8 + } + }; + + if (torture_setting_bool(tctx, "samba4", false)) { + printf("skipping DsGetNCChanges test against Samba4\n"); + return true; + } + + ZERO_STRUCT(null_guid); + ZERO_STRUCT(null_sid); + + for (i=0; i < ARRAY_SIZE(array); i++) { + printf("testing DsGetNCChanges level %d\n", + array[i].level); + + r.in.bind_handle = &priv->bind_handle; + r.in.level = &array[i].level; + + switch (*r.in.level) { + case 5: + nc.guid = null_guid; + nc.sid = null_sid; + nc.dn = priv->domain_obj_dn?priv->domain_obj_dn:""; + + r.in.req.req5.destination_dsa_guid = GUID_random(); + r.in.req.req5.source_dsa_invocation_id = null_guid; + r.in.req.req5.naming_context = &nc; + r.in.req.req5.highwatermark.tmp_highest_usn = 0; + r.in.req.req5.highwatermark.reserved_usn = 0; + r.in.req.req5.highwatermark.highest_usn = 0; + r.in.req.req5.uptodateness_vector = NULL; + r.in.req.req5.replica_flags = 0; + if (lp_parm_bool(tctx->lp_ctx, NULL, "drsuapi","compression", false)) { + r.in.req.req5.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES; + } + r.in.req.req5.max_object_count = 0; + r.in.req.req5.max_ndr_size = 0; + r.in.req.req5.extended_op = DRSUAPI_EXOP_NONE; + r.in.req.req5.fsmo_info = 0; + + break; + case 8: + nc.guid = null_guid; + nc.sid = null_sid; + nc.dn = priv->domain_obj_dn?priv->domain_obj_dn:""; + + r.in.req.req8.destination_dsa_guid = GUID_random(); + r.in.req.req8.source_dsa_invocation_id = null_guid; + r.in.req.req8.naming_context = &nc; + r.in.req.req8.highwatermark.tmp_highest_usn = 0; + r.in.req.req8.highwatermark.reserved_usn = 0; + r.in.req.req8.highwatermark.highest_usn = 0; + r.in.req.req8.uptodateness_vector = NULL; + r.in.req.req8.replica_flags = 0; + if (lp_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "compression", false)) { + r.in.req.req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES; + } + if (lp_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "neighbour_writeable", true)) { + r.in.req.req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE; + } + r.in.req.req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP + | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS + | DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS + | DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED + ; + r.in.req.req8.max_object_count = 402; + r.in.req.req8.max_ndr_size = 402116; + r.in.req.req8.extended_op = DRSUAPI_EXOP_NONE; + r.in.req.req8.fsmo_info = 0; + r.in.req.req8.partial_attribute_set = NULL; + r.in.req.req8.partial_attribute_set_ex = NULL; + r.in.req.req8.mapping_ctr.num_mappings = 0; + r.in.req.req8.mapping_ctr.mappings = NULL; + + break; + } + + status = dcerpc_drsuapi_DsGetNCChanges(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(tctx, p->last_fault_code); + } + printf("dcerpc_drsuapi_DsGetNCChanges failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsGetNCChanges failed - %s\n", win_errstr(r.out.result)); + ret = false; + } + } + + return ret; +} + +bool test_QuerySitesByCost(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct drsuapi_QuerySitesByCost r; + bool ret = true; + + const char *my_site = "Default-First-Site-Name"; + const char *remote_site1 = "smbtorture-nonexisting-site1"; + const char *remote_site2 = "smbtorture-nonexisting-site2"; + + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; + r.in.req.req1.site_from = talloc_strdup(mem_ctx, my_site); + r.in.req.req1.num_req = 2; + r.in.req.req1.site_to = talloc_zero_array(mem_ctx, const char *, r.in.req.req1.num_req); + r.in.req.req1.site_to[0] = talloc_strdup(mem_ctx, remote_site1); + r.in.req.req1.site_to[1] = talloc_strdup(mem_ctx, remote_site2); + r.in.req.req1.flags = 0; + + status = dcerpc_drsuapi_QuerySitesByCost(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); + } + printf("drsuapi_QuerySitesByCost - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("QuerySitesByCost failed - %s\n", win_errstr(r.out.result)); + ret = false; + } + + if (W_ERROR_IS_OK(r.out.result)) { + + if (!W_ERROR_EQUAL(r.out.ctr.ctr1.info[0].error_code, WERR_DS_OBJ_NOT_FOUND) || + !W_ERROR_EQUAL(r.out.ctr.ctr1.info[1].error_code, WERR_DS_OBJ_NOT_FOUND)) { + printf("expected error_code WERR_DS_OBJ_NOT_FOUND, got %s\n", + win_errstr(r.out.ctr.ctr1.info[0].error_code)); + ret = false; + } + + if ((r.out.ctr.ctr1.info[0].site_cost != (uint32_t) -1) || + (r.out.ctr.ctr1.info[1].site_cost != (uint32_t) -1)) { + printf("expected site_cost %d, got %d\n", + (uint32_t) -1, r.out.ctr.ctr1.info[0].site_cost); + ret = false; + } + } + + return ret; + + +} + +bool test_DsUnbind(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct drsuapi_DsUnbind r; + bool ret = true; + + r.in.bind_handle = &priv->bind_handle; + r.out.bind_handle = &priv->bind_handle; + + printf("testing DsUnbind\n"); + + status = dcerpc_drsuapi_DsUnbind(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); + } + printf("dcerpc_drsuapi_DsUnbind failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsBind failed - %s\n", win_errstr(r.out.result)); + ret = false; + } + + return ret; +} + +bool torture_rpc_drsuapi(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct DsPrivate priv; + struct cli_credentials *machine_credentials; + + ZERO_STRUCT(priv); + + priv.join = torture_join_domain(torture, TEST_MACHINE_NAME, ACB_SVRTRUST, + &machine_credentials); + if (!priv.join) { + torture_fail(torture, "Failed to join as BDC"); + } + + status = torture_rpc_connection(torture, + &p, + &ndr_table_drsuapi); + if (!NT_STATUS_IS_OK(status)) { + torture_leave_domain(torture, priv.join); + torture_fail(torture, "Unable to connect to DRSUAPI pipe"); + } + + ret &= test_DsBind(p, torture, &priv); +#if 0 + ret &= test_QuerySitesByCost(p, torture, &priv); +#endif + ret &= test_DsGetDomainControllerInfo(p, torture, &priv); + + ret &= test_DsCrackNames(torture, p, torture, &priv); + + ret &= test_DsWriteAccountSpn(p, torture, &priv); + + ret &= test_DsReplicaGetInfo(p, torture, &priv); + + ret &= test_DsReplicaSync(p, torture, &priv); + + ret &= test_DsReplicaUpdateRefs(p, torture, &priv); + + ret &= test_DsGetNCChanges(p, torture, &priv); + + ret &= test_DsUnbind(p, torture, &priv); + + torture_leave_domain(torture, priv.join); + + return ret; +} + + +bool torture_rpc_drsuapi_cracknames(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct DsPrivate priv; + struct cli_credentials *machine_credentials; + + torture_comment(torture, "Connected to DRSUAPI pipe\n"); + + ZERO_STRUCT(priv); + + priv.join = torture_join_domain(torture, TEST_MACHINE_NAME, ACB_SVRTRUST, + &machine_credentials); + if (!priv.join) { + torture_fail(torture, "Failed to join as BDC\n"); + } + + status = torture_rpc_connection(torture, + &p, + &ndr_table_drsuapi); + if (!NT_STATUS_IS_OK(status)) { + torture_leave_domain(torture, priv.join); + torture_fail(torture, "Unable to connect to DRSUAPI pipe"); + } + + ret &= test_DsBind(p, torture, &priv); + + if (ret) { + /* We don't care if this fails, we just need some info from it */ + test_DsGetDomainControllerInfo(p, torture, &priv); + + ret &= test_DsCrackNames(torture, p, torture, &priv); + + ret &= test_DsUnbind(p, torture, &priv); + } + + torture_leave_domain(torture, priv.join); + + return ret; +} + diff --git a/source4/torture/rpc/drsuapi.h b/source4/torture/rpc/drsuapi.h new file mode 100644 index 0000000000..98438cc9b7 --- /dev/null +++ b/source4/torture/rpc/drsuapi.h @@ -0,0 +1,36 @@ +/* + Unix SMB/CIFS implementation. + + DRSUapi tests + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 2004 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "librpc/gen_ndr/drsuapi.h" + +struct DsPrivate { + struct policy_handle bind_handle; + struct GUID bind_guid; + const char *domain_obj_dn; + const char *domain_guid_str; + const char *domain_dns_name; + struct GUID domain_guid; + struct drsuapi_DsGetDCInfo2 dcinfo; + struct test_join *join; +}; + diff --git a/source4/torture/rpc/drsuapi_cracknames.c b/source4/torture/rpc/drsuapi_cracknames.c new file mode 100644 index 0000000000..fbda69df57 --- /dev/null +++ b/source4/torture/rpc/drsuapi_cracknames.c @@ -0,0 +1,997 @@ +/* + Unix SMB/CIFS implementation. + + DRSUapi tests + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 2004 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_drsuapi_c.h" +#include "torture/rpc/rpc.h" +#include "ldb/include/ldb.h" +#include "libcli/security/security.h" +#include "param/param.h" + +static bool test_DsCrackNamesMatrix(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct DsPrivate *priv, const char *dn, + const char *user_principal_name, const char *service_principal_name) +{ + + + NTSTATUS status; + bool ret = true; + struct drsuapi_DsCrackNames r; + enum drsuapi_DsNameFormat formats[] = { + DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + DRSUAPI_DS_NAME_FORMAT_DISPLAY, + DRSUAPI_DS_NAME_FORMAT_GUID, + DRSUAPI_DS_NAME_FORMAT_CANONICAL, + DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX, + DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN + }; + struct drsuapi_DsNameString names[ARRAY_SIZE(formats)]; + int i, j; + + const char *n_matrix[ARRAY_SIZE(formats)][ARRAY_SIZE(formats)]; + const char *n_from[ARRAY_SIZE(formats)]; + + ZERO_STRUCT(r); + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; + r.in.req.req1.codepage = 1252; /* german */ + r.in.req.req1.language = 0x00000407; /* german */ + r.in.req.req1.count = 1; + r.in.req.req1.names = names; + r.in.req.req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS; + + n_matrix[0][0] = dn; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + r.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; + r.in.req.req1.format_desired = formats[i]; + names[0].str = dn; + status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); + } + printf("testing DsCrackNames (matrix prep) with name '%s' from format: %d desired format:%d ", + names[0].str, r.in.req.req1.format_offered, r.in.req.req1.format_desired); + + printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("testing DsCrackNames (matrix prep) with name '%s' from format: %d desired format:%d ", + names[0].str, r.in.req.req1.format_offered, r.in.req.req1.format_desired); + + printf("DsCrackNames failed - %s\n", win_errstr(r.out.result)); + ret = false; + } + + if (!ret) { + return ret; + } + switch (formats[i]) { + case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: + if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE) { + printf(__location__ ": Unexpected error (%d): This name lookup should fail\n", + r.out.ctr.ctr1->array[0].status); + return false; + } + printf ("(expected) error\n"); + break; + case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: + if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_NO_MAPPING) { + printf(__location__ ": Unexpected error (%d): This name lookup should fail\n", + r.out.ctr.ctr1->array[0].status); + return false; + } + printf ("(expected) error\n"); + break; + case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN: + case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: + if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR) { + printf(__location__ ": Unexpected error (%d): This name lookup should fail\n", + r.out.ctr.ctr1->array[0].status); + return false; + } + printf ("(expected) error\n"); + break; + default: + if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + printf("Error: %d\n", r.out.ctr.ctr1->array[0].status); + return false; + } + } + + switch (formats[i]) { + case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: + n_from[i] = user_principal_name; + break; + case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: + n_from[i] = service_principal_name; + break; + case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: + case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN: + n_from[i] = NULL; + break; + default: + n_from[i] = r.out.ctr.ctr1->array[0].result_name; + printf("%s\n", n_from[i]); + } + } + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + for (j = 0; j < ARRAY_SIZE(formats); j++) { + r.in.req.req1.format_offered = formats[i]; + r.in.req.req1.format_desired = formats[j]; + if (!n_from[i]) { + n_matrix[i][j] = NULL; + continue; + } + names[0].str = n_from[i]; + status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); + } + printf("testing DsCrackNames (matrix) with name '%s' from format: %d desired format:%d failed - %s", + names[0].str, r.in.req.req1.format_offered, r.in.req.req1.format_desired, errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("testing DsCrackNames (matrix) with name '%s' from format: %d desired format:%d failed - %s", + names[0].str, r.in.req.req1.format_offered, r.in.req.req1.format_desired, + win_errstr(r.out.result)); + ret = false; + } + + if (!ret) { + return ret; + } + if (r.out.ctr.ctr1->array[0].status == DRSUAPI_DS_NAME_STATUS_OK) { + n_matrix[i][j] = r.out.ctr.ctr1->array[0].result_name; + } else { + n_matrix[i][j] = NULL; + } + } + } + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + for (j = 0; j < ARRAY_SIZE(formats); j++) { + if (n_matrix[i][j] == n_from[j]) { + + /* We don't have a from name for these yet (and we can't map to them to find it out) */ + } else if (n_matrix[i][j] == NULL && n_from[i] == NULL) { + + /* we can't map to these two */ + } else if (n_matrix[i][j] == NULL && formats[j] == DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL) { + } else if (n_matrix[i][j] == NULL && formats[j] == DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL) { + } else if (n_matrix[i][j] == NULL && n_from[j] != NULL) { + printf("dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: %s should be %s\n", formats[i], formats[j], n_matrix[i][j], n_from[j]); + ret = false; + } else if (n_matrix[i][j] != NULL && n_from[j] == NULL) { + printf("dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: %s should be %s\n", formats[i], formats[j], n_matrix[i][j], n_from[j]); + ret = false; + } else if (strcmp(n_matrix[i][j], n_from[j]) != 0) { + printf("dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: %s should be %s\n", formats[i], formats[j], n_matrix[i][j], n_from[j]); + ret = false; + } + } + } + return ret; +} + +bool test_DsCrackNames(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct drsuapi_DsCrackNames r; + struct drsuapi_DsNameString names[1]; + bool ret = true; + const char *dns_domain; + const char *nt4_domain; + const char *FQDN_1779_name; + struct ldb_context *ldb; + struct ldb_dn *FQDN_1779_dn; + struct ldb_dn *realm_dn; + const char *realm_dn_str; + const char *realm_canonical; + const char *realm_canonical_ex; + const char *user_principal_name; + char *user_principal_name_short; + const char *service_principal_name; + const char *canonical_name; + const char *canonical_ex_name; + const char *dom_sid; + const char *test_dc = torture_join_netbios_name(priv->join); + + ZERO_STRUCT(r); + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; + r.in.req.req1.codepage = 1252; /* german */ + r.in.req.req1.language = 0x00000407; /* german */ + r.in.req.req1.count = 1; + r.in.req.req1.names = names; + r.in.req.req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS; + + r.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY; + r.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT; + + dom_sid = dom_sid_string(mem_ctx, torture_join_sid(priv->join)); + + names[0].str = dom_sid; + + printf("testing DsCrackNames with name '%s' desired format:%d\n", + names[0].str, r.in.req.req1.format_desired); + + status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); + } + printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsCrackNames failed - %s\n", win_errstr(r.out.result)); + ret = false; + } else if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + printf("DsCrackNames failed on name - %d\n", r.out.ctr.ctr1->array[0].status); + ret = false; + } + + if (!ret) { + return ret; + } + + dns_domain = r.out.ctr.ctr1->array[0].dns_domain_name; + nt4_domain = r.out.ctr.ctr1->array[0].result_name; + + r.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_GUID; + + printf("testing DsCrackNames with name '%s' desired format:%d\n", + names[0].str, r.in.req.req1.format_desired); + + status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); + } + printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsCrackNames failed - %s\n", win_errstr(r.out.result)); + ret = false; + } else if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + printf("DsCrackNames failed on name - %d\n", r.out.ctr.ctr1->array[0].status); + ret = false; + } + + if (!ret) { + return ret; + } + + priv->domain_dns_name = r.out.ctr.ctr1->array[0].dns_domain_name; + priv->domain_guid_str = r.out.ctr.ctr1->array[0].result_name; + GUID_from_string(priv->domain_guid_str, &priv->domain_guid); + + r.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; + + printf("testing DsCrackNames with name '%s' desired format:%d\n", + names[0].str, r.in.req.req1.format_desired); + + status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); + } + printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsCrackNames failed - %s\n", win_errstr(r.out.result)); + ret = false; + } else if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + printf("DsCrackNames failed on name - %d\n", r.out.ctr.ctr1->array[0].status); + ret = false; + } + + if (!ret) { + return ret; + } + + ldb = ldb_init(mem_ctx, tctx->ev); + + realm_dn_str = r.out.ctr.ctr1->array[0].result_name; + realm_dn = ldb_dn_new(mem_ctx, ldb, realm_dn_str); + realm_canonical = ldb_dn_canonical_string(mem_ctx, realm_dn); + + if (strcmp(realm_canonical, + talloc_asprintf(mem_ctx, "%s/", dns_domain))!= 0) { + printf("local Round trip on canonical name failed: %s != %s!\n", + realm_canonical, + talloc_asprintf(mem_ctx, "%s/", dns_domain)); + return false; + }; + + realm_canonical_ex = ldb_dn_canonical_ex_string(mem_ctx, realm_dn); + + if (strcmp(realm_canonical_ex, + talloc_asprintf(mem_ctx, "%s\n", dns_domain))!= 0) { + printf("local Round trip on canonical ex name failed: %s != %s!\n", + realm_canonical, + talloc_asprintf(mem_ctx, "%s\n", dns_domain)); + return false; + }; + + r.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT; + r.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; + names[0].str = nt4_domain; + + printf("testing DsCrackNames with name '%s' desired format:%d\n", + names[0].str, r.in.req.req1.format_desired); + + status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); + } + printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsCrackNames failed - %s\n", win_errstr(r.out.result)); + ret = false; + } else if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + printf("DsCrackNames failed on name - %d\n", r.out.ctr.ctr1->array[0].status); + ret = false; + } + + if (!ret) { + return ret; + } + + priv->domain_obj_dn = r.out.ctr.ctr1->array[0].result_name; + + r.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT; + r.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; + names[0].str = talloc_asprintf(mem_ctx, "%s%s$", nt4_domain, test_dc); + + printf("testing DsCrackNames with name '%s' desired format:%d\n", + names[0].str, r.in.req.req1.format_desired); + + status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); + } + printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsCrackNames failed - %s\n", win_errstr(r.out.result)); + ret = false; + } else if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + printf("DsCrackNames failed on name - %d\n", r.out.ctr.ctr1->array[0].status); + ret = false; + } + + if (!ret) { + return ret; + } + + FQDN_1779_name = r.out.ctr.ctr1->array[0].result_name; + + r.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_GUID; + r.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; + names[0].str = priv->domain_guid_str; + + printf("testing DsCrackNames with name '%s' desired format:%d\n", + names[0].str, r.in.req.req1.format_desired); + + status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); + } + printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsCrackNames failed - %s\n", win_errstr(r.out.result)); + ret = false; + } else if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + printf("DsCrackNames failed on name - %d\n", r.out.ctr.ctr1->array[0].status); + ret = false; + } + + if (!ret) { + return ret; + } + + if (strcmp(priv->domain_dns_name, r.out.ctr.ctr1->array[0].dns_domain_name) != 0) { + printf("DsCrackNames failed to return same DNS name - expected %s got %s\n", priv->domain_dns_name, r.out.ctr.ctr1->array[0].dns_domain_name); + return false; + } + + FQDN_1779_dn = ldb_dn_new(mem_ctx, ldb, FQDN_1779_name); + + canonical_name = ldb_dn_canonical_string(mem_ctx, FQDN_1779_dn); + canonical_ex_name = ldb_dn_canonical_ex_string(mem_ctx, FQDN_1779_dn); + + user_principal_name = talloc_asprintf(mem_ctx, "%s$@%s", test_dc, dns_domain); + + /* form up a user@DOMAIN */ + user_principal_name_short = talloc_asprintf(mem_ctx, "%s$@%s", test_dc, nt4_domain); + /* variable nt4_domain includs a trailing \ */ + user_principal_name_short[strlen(user_principal_name_short) - 1] = '\0'; + + service_principal_name = talloc_asprintf(mem_ctx, "HOST/%s", test_dc); + { + + struct { + enum drsuapi_DsNameFormat format_offered; + enum drsuapi_DsNameFormat format_desired; + const char *comment; + const char *str; + const char *expected_str; + const char *expected_dns; + enum drsuapi_DsNameStatus status; + enum drsuapi_DsNameStatus alternate_status; + enum drsuapi_DsNameFlags flags; + bool skip; + } crack[] = { + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = user_principal_name, + .expected_str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = user_principal_name_short, + .expected_str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + .str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = service_principal_name, + .expected_str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "cifs/%s.%s", test_dc, dns_domain), + .comment = "ServicePrincipal Name", + .expected_str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL, + .str = FQDN_1779_name, + .expected_str = canonical_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = canonical_name, + .expected_str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX, + .str = FQDN_1779_name, + .expected_str = canonical_ex_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = canonical_ex_name, + .expected_str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL, + .str = FQDN_1779_name, + .comment = "DN to cannoical syntactial only", + .status = DRSUAPI_DS_NAME_STATUS_OK, + .expected_str = canonical_name, + .flags = DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX, + .str = FQDN_1779_name, + .comment = "DN to cannoical EX syntactial only", + .status = DRSUAPI_DS_NAME_STATUS_OK, + .expected_str = canonical_ex_name, + .flags = DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_DISPLAY, + .str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_GUID, + .str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .str = priv->domain_guid_str, + .comment = "Domain GUID to NT4 ACCOUNT", + .expected_str = nt4_domain, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL, + .str = priv->domain_guid_str, + .comment = "Domain GUID to Canonical", + .expected_str = talloc_asprintf(mem_ctx, "%s/", dns_domain), + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX, + .str = priv->domain_guid_str, + .comment = "Domain GUID to Canonical EX", + .expected_str = talloc_asprintf(mem_ctx, "%s\n", dns_domain), + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_DISPLAY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = "CN=Microsoft Corporation,L=Redmond,S=Washington,C=US", + .comment = "display name for Microsoft Support Account", + .status = DRSUAPI_DS_NAME_STATUS_OK, + .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE, + .skip = torture_setting_bool(tctx, "samba4", false) + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = GUID_string2(mem_ctx, torture_join_user_guid(priv->join)), + .comment = "Account GUID -> DN", + .expected_str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .str = GUID_string2(mem_ctx, torture_join_user_guid(priv->join)), + .comment = "Account GUID -> NT4 Account", + .expected_str = talloc_asprintf(mem_ctx, "%s%s$", nt4_domain, test_dc), + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = GUID_string2(mem_ctx, &priv->dcinfo.site_guid), + .comment = "Site GUID", + .expected_str = priv->dcinfo.site_dn, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = GUID_string2(mem_ctx, &priv->dcinfo.computer_guid), + .comment = "Computer GUID", + .expected_str = priv->dcinfo.computer_dn, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .str = GUID_string2(mem_ctx, &priv->dcinfo.computer_guid), + .comment = "Computer GUID -> NT4 Account", + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = GUID_string2(mem_ctx, &priv->dcinfo.server_guid), + .comment = "Server GUID", + .expected_str = priv->dcinfo.server_dn, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = GUID_string2(mem_ctx, &priv->dcinfo.ntds_guid), + .comment = "NTDS GUID", + .expected_str = priv->dcinfo.ntds_dn, + .status = DRSUAPI_DS_NAME_STATUS_OK, + .skip = GUID_all_zero(&priv->dcinfo.ntds_guid) + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_DISPLAY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = test_dc, + .comment = "DISLPAY NAME search for DC short name", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "krbtgt/%s", dns_domain), + .comment = "Looking for KRBTGT as a serivce principal", + .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY, + .expected_dns = dns_domain + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "bogus/%s", dns_domain), + .comment = "Looking for bogus serivce principal", + .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY, + .expected_dns = dns_domain + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "bogus/%s.%s", test_dc, dns_domain), + .comment = "Looking for bogus serivce on test DC", + .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY, + .expected_dns = talloc_asprintf(mem_ctx, "%s.%s", test_dc, dns_domain) + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "krbtgt"), + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Looking for the kadmin/changepw service as a serivce principal", + .str = talloc_asprintf(mem_ctx, "kadmin/changepw"), + .status = DRSUAPI_DS_NAME_STATUS_OK, + .expected_str = talloc_asprintf(mem_ctx, "CN=krbtgt,CN=Users,%s", realm_dn_str), + .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "cifs/%s.%s@%s", + test_dc, dns_domain, + dns_domain), + .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "cifs/%s.%s@%s", + test_dc, dns_domain, + "BOGUS"), + .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY, + .expected_dns = "BOGUS" + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "cifs/%s.%s@%s", + test_dc, "REALLY", + "BOGUS"), + .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY, + .expected_dns = "BOGUS" + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "cifs/%s.%s", + test_dc, dns_domain), + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "cifs/%s", + test_dc), + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = "NOT A GUID", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = "NOT A SID", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = "NOT AN NT4 NAME", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_GUID, + .comment = "Unparsable DN", + .str = "NOT A DN", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Unparsable user principal", + .str = "NOT A PRINCIPAL", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Unparsable service principal", + .str = "NOT A SERVICE PRINCIPAL", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "BIND GUID (ie, not in the directory)", + .str = GUID_string2(mem_ctx, &priv->bind_guid), + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Unqualified Machine account as user principal", + .str = talloc_asprintf(mem_ctx, "%s$", test_dc), + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Machine account as service principal", + .str = talloc_asprintf(mem_ctx, "%s$", test_dc), + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Full Machine account as service principal", + .str = user_principal_name, + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Realm as an NT4 domain lookup", + .str = talloc_asprintf(mem_ctx, "%s\\", dns_domain), + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "BUILTIN\\ -> DN", + .str = "BUILTIN\\", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "NT AUTHORITY\\ -> DN", + .str = "NT AUTHORITY\\", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "NT AUTHORITY\\ANONYMOUS LOGON -> DN", + .str = "NT AUTHORITY\\ANONYMOUS LOGON", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "NT AUTHORITY\\SYSTEM -> DN", + .str = "NT AUTHORITY\\SYSTEM", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .comment = "BUITIN SID -> NT4 account", + .str = SID_BUILTIN, + .status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING, + .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = SID_BUILTIN, + .comment = "Builtin Domain SID -> DN", + .status = DRSUAPI_DS_NAME_STATUS_OK, + .expected_str = talloc_asprintf(mem_ctx, "CN=Builtin,%s", realm_dn_str), + .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = SID_BUILTIN_ADMINISTRATORS, + .comment = "Builtin Administrors SID -> DN", + .status = DRSUAPI_DS_NAME_STATUS_OK, + .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .str = SID_BUILTIN_ADMINISTRATORS, + .comment = "Builtin Administrors SID -> NT4 Account", + .status = DRSUAPI_DS_NAME_STATUS_OK, + .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .str = SID_NT_ANONYMOUS, + .comment = "NT Anonymous SID -> NT4 Account", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .str = SID_NT_SYSTEM, + .comment = "NT SYSTEM SID -> NT4 Account", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Domain SID -> DN", + .str = dom_sid, + .expected_str = realm_dn_str, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .comment = "Domain SID -> NT4 account", + .str = dom_sid, + .expected_str = nt4_domain, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "invalid user principal name", + .str = "foo@bar", + .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY, + .expected_dns = "bar" + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "invalid user principal name in valid domain", + .str = talloc_asprintf(mem_ctx, "invalidusername@%s", dns_domain), + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + } + }; + int i; + + for (i=0; i < ARRAY_SIZE(crack); i++) { + const char *comment; + r.in.req.req1.format_flags = crack[i].flags; + r.in.req.req1.format_offered = crack[i].format_offered; + r.in.req.req1.format_desired = crack[i].format_desired; + names[0].str = crack[i].str; + + if (crack[i].comment) { + comment = talloc_asprintf(mem_ctx, "'%s' with name '%s' desired format:%d\n", + crack[i].comment, names[0].str, r.in.req.req1.format_desired); + } else { + comment = talloc_asprintf(mem_ctx, "'%s' desired format:%d\n", + names[0].str, r.in.req.req1.format_desired); + } + if (crack[i].skip) { + printf("skipping: %s", comment); + continue; + } + status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); + } + printf("dcerpc_drsuapi_DsCrackNames failed on %s - %s\n", comment, errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsCrackNames failed - %s\n", win_errstr(r.out.result)); + ret = false; + } else if (r.out.ctr.ctr1->array[0].status != crack[i].status) { + if (crack[i].alternate_status) { + if (r.out.ctr.ctr1->array[0].status != crack[i].alternate_status) { + printf("DsCrackNames unexpected status %d, wanted %d or %d on: %s\n", + r.out.ctr.ctr1->array[0].status, + crack[i].status, + crack[i].alternate_status, + comment); + ret = false; + } + } else { + printf("DsCrackNames unexpected status %d, wanted %d on: %s\n", + r.out.ctr.ctr1->array[0].status, + crack[i].status, + comment); + ret = false; + } + } else if (crack[i].expected_str + && (strcmp(r.out.ctr.ctr1->array[0].result_name, + crack[i].expected_str) != 0)) { + if (strcasecmp(r.out.ctr.ctr1->array[0].result_name, + crack[i].expected_str) != 0) { + printf("DsCrackNames failed - got %s, expected %s on %s\n", + r.out.ctr.ctr1->array[0].result_name, + crack[i].expected_str, comment); + ret = false; + } else { + printf("(warning) DsCrackNames returned different case - got %s, expected %s on %s\n", + r.out.ctr.ctr1->array[0].result_name, + crack[i].expected_str, comment); + } + } else if (crack[i].expected_dns + && (strcmp(r.out.ctr.ctr1->array[0].dns_domain_name, + crack[i].expected_dns) != 0)) { + printf("DsCrackNames failed - got DNS name %s, expected %s on %s\n", + r.out.ctr.ctr1->array[0].result_name, + crack[i].expected_str, comment); + ret = false; + } + } + } + + if (!test_DsCrackNamesMatrix(p, mem_ctx, priv, FQDN_1779_name, + user_principal_name, service_principal_name)) { + ret = false; + } + + return ret; +} diff --git a/source4/torture/rpc/dssetup.c b/source4/torture/rpc/dssetup.c new file mode 100644 index 0000000000..aa5f12eb92 --- /dev/null +++ b/source4/torture/rpc/dssetup.c @@ -0,0 +1,64 @@ +/* + Unix SMB/CIFS implementation. + + test suite for dssetup rpc operations + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_dssetup_c.h" +#include "torture/rpc/rpc.h" + + +bool test_DsRoleGetPrimaryDomainInformation_ext(struct torture_context *tctx, + struct dcerpc_pipe *p, + NTSTATUS ext_status) +{ + struct dssetup_DsRoleGetPrimaryDomainInformation r; + NTSTATUS status; + int i; + + for (i=DS_ROLE_BASIC_INFORMATION; i <= DS_ROLE_OP_STATUS; i++) { + r.in.level = i; + torture_comment(tctx, "dcerpc_dssetup_DsRoleGetPrimaryDomainInformation level %d\n", i); + + status = dcerpc_dssetup_DsRoleGetPrimaryDomainInformation(p, tctx, &r); + torture_assert_ntstatus_equal(tctx, ext_status, status, "DsRoleGetPrimaryDomainInformation failed"); + if (NT_STATUS_IS_OK(ext_status)) { + torture_assert_werr_ok(tctx, r.out.result, "DsRoleGetPrimaryDomainInformation failed"); + } + } + + return true; +} + +bool test_DsRoleGetPrimaryDomainInformation(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_DsRoleGetPrimaryDomainInformation_ext(tctx, p, NT_STATUS_OK); +} + +struct torture_suite *torture_rpc_dssetup(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "DSSETUP"); + struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite, "dssetup", &ndr_table_dssetup); + + torture_rpc_tcase_add_test(tcase, "DsRoleGetPrimaryDomainInformation", test_DsRoleGetPrimaryDomainInformation); + + return suite; +} diff --git a/source4/torture/rpc/dssync.c b/source4/torture/rpc/dssync.c new file mode 100644 index 0000000000..e9346f9605 --- /dev/null +++ b/source4/torture/rpc/dssync.c @@ -0,0 +1,893 @@ +/* + Unix SMB/CIFS implementation. + + DsGetNCChanges replication test + + Copyright (C) Stefan (metze) Metzmacher 2005 + Copyright (C) Brad Henry 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/cmdline/popt_common.h" +#include "librpc/gen_ndr/ndr_drsuapi_c.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "libcli/cldap/cldap.h" +#include "libcli/ldap/ldap_client.h" +#include "torture/torture.h" +#include "torture/ldap/proto.h" +#include "libcli/auth/libcli_auth.h" +#include "lib/crypto/crypto.h" +#include "auth/credentials/credentials.h" +#include "libcli/auth/libcli_auth.h" +#include "auth/gensec/gensec.h" +#include "param/param.h" +#include "dsdb/samdb/samdb.h" + +struct DsSyncBindInfo { + struct dcerpc_pipe *pipe; + struct drsuapi_DsBind req; + struct GUID bind_guid; + struct drsuapi_DsBindInfoCtr our_bind_info_ctr; + struct drsuapi_DsBindInfo28 our_bind_info28; + struct drsuapi_DsBindInfo28 peer_bind_info28; + struct policy_handle bind_handle; +}; + +struct DsSyncLDAPInfo { + struct ldap_connection *conn; +}; + +struct DsSyncTest { + struct dcerpc_binding *drsuapi_binding; + + const char *ldap_url; + const char *site_name; + + const char *domain_dn; + + /* what we need to do as 'Administrator' */ + struct { + struct cli_credentials *credentials; + struct DsSyncBindInfo drsuapi; + struct DsSyncLDAPInfo ldap; + } admin; + + /* what we need to do as the new dc machine account */ + struct { + struct cli_credentials *credentials; + struct DsSyncBindInfo drsuapi; + struct drsuapi_DsGetDCInfo2 dc_info2; + struct GUID invocation_id; + struct GUID object_guid; + } new_dc; + + /* info about the old dc */ + struct { + struct drsuapi_DsGetDomainControllerInfo dc_info; + } old_dc; +}; + +static struct DsSyncTest *test_create_context(struct torture_context *tctx) +{ + NTSTATUS status; + struct DsSyncTest *ctx; + struct drsuapi_DsBindInfo28 *our_bind_info28; + struct drsuapi_DsBindInfoCtr *our_bind_info_ctr; + const char *binding = torture_setting_string(tctx, "binding", NULL); + ctx = talloc_zero(tctx, struct DsSyncTest); + if (!ctx) return NULL; + + status = dcerpc_parse_binding(ctx, binding, &ctx->drsuapi_binding); + if (!NT_STATUS_IS_OK(status)) { + printf("Bad binding string %s\n", binding); + return NULL; + } + ctx->drsuapi_binding->flags |= DCERPC_SIGN | DCERPC_SEAL; + + ctx->ldap_url = talloc_asprintf(ctx, "ldap://%s/", ctx->drsuapi_binding->host); + + /* ctx->admin ...*/ + ctx->admin.credentials = cmdline_credentials; + + our_bind_info28 = &ctx->admin.drsuapi.our_bind_info28; + our_bind_info28->supported_extensions = 0xFFFFFFFF; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3; + our_bind_info28->site_guid = GUID_zero(); + our_bind_info28->pid = 0; + our_bind_info28->repl_epoch = 1; + + our_bind_info_ctr = &ctx->admin.drsuapi.our_bind_info_ctr; + our_bind_info_ctr->length = 28; + our_bind_info_ctr->info.info28 = *our_bind_info28; + + GUID_from_string(DRSUAPI_DS_BIND_GUID, &ctx->admin.drsuapi.bind_guid); + + ctx->admin.drsuapi.req.in.bind_guid = &ctx->admin.drsuapi.bind_guid; + ctx->admin.drsuapi.req.in.bind_info = our_bind_info_ctr; + ctx->admin.drsuapi.req.out.bind_handle = &ctx->admin.drsuapi.bind_handle; + + /* ctx->new_dc ...*/ + ctx->new_dc.credentials = cmdline_credentials; + + our_bind_info28 = &ctx->new_dc.drsuapi.our_bind_info28; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT; + if (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "xpress", false)) { + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_XPRESS_COMPRESS; + } + our_bind_info28->site_guid = GUID_zero(); + our_bind_info28->pid = 0; + our_bind_info28->repl_epoch = 0; + + our_bind_info_ctr = &ctx->new_dc.drsuapi.our_bind_info_ctr; + our_bind_info_ctr->length = 28; + our_bind_info_ctr->info.info28 = *our_bind_info28; + + GUID_from_string(DRSUAPI_DS_BIND_GUID_W2K3, &ctx->new_dc.drsuapi.bind_guid); + + ctx->new_dc.drsuapi.req.in.bind_guid = &ctx->new_dc.drsuapi.bind_guid; + ctx->new_dc.drsuapi.req.in.bind_info = our_bind_info_ctr; + ctx->new_dc.drsuapi.req.out.bind_handle = &ctx->new_dc.drsuapi.bind_handle; + + ctx->new_dc.invocation_id = ctx->new_dc.drsuapi.bind_guid; + + /* ctx->old_dc ...*/ + + return ctx; +} + +static bool _test_DsBind(struct torture_context *tctx, + struct DsSyncTest *ctx, struct cli_credentials *credentials, struct DsSyncBindInfo *b) +{ + NTSTATUS status; + bool ret = true; + + status = dcerpc_pipe_connect_b(ctx, + &b->pipe, ctx->drsuapi_binding, + &ndr_table_drsuapi, + credentials, tctx->ev, tctx->lp_ctx); + + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to connect to server as a BDC: %s\n", nt_errstr(status)); + return false; + } + + status = dcerpc_drsuapi_DsBind(b->pipe, ctx, &b->req); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(ctx, b->pipe->last_fault_code); + } + printf("dcerpc_drsuapi_DsBind failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(b->req.out.result)) { + printf("DsBind failed - %s\n", win_errstr(b->req.out.result)); + ret = false; + } + + ZERO_STRUCT(b->peer_bind_info28); + if (b->req.out.bind_info) { + switch (b->req.out.bind_info->length) { + case 24: { + struct drsuapi_DsBindInfo24 *info24; + info24 = &b->req.out.bind_info->info.info24; + b->peer_bind_info28.supported_extensions= info24->supported_extensions; + b->peer_bind_info28.site_guid = info24->site_guid; + b->peer_bind_info28.pid = info24->pid; + b->peer_bind_info28.repl_epoch = 0; + break; + } + case 48: { + struct drsuapi_DsBindInfo48 *info48; + info48 = &b->req.out.bind_info->info.info48; + b->peer_bind_info28.supported_extensions= info48->supported_extensions; + b->peer_bind_info28.site_guid = info48->site_guid; + b->peer_bind_info28.pid = info48->pid; + b->peer_bind_info28.repl_epoch = info48->repl_epoch; + break; + } + case 28: + b->peer_bind_info28 = b->req.out.bind_info->info.info28; + break; + default: + printf("DsBind - warning: unknown BindInfo length: %u\n", + b->req.out.bind_info->length); + } + } + + return ret; +} + +static bool test_LDAPBind(struct torture_context *tctx, struct DsSyncTest *ctx, + struct cli_credentials *credentials, struct DsSyncLDAPInfo *l) +{ + NTSTATUS status; + bool ret = true; + + status = torture_ldap_connection(tctx, &l->conn, ctx->ldap_url); + if (!NT_STATUS_IS_OK(status)) { + printf("failed to connect to LDAP: %s\n", ctx->ldap_url); + return false; + } + + printf("connected to LDAP: %s\n", ctx->ldap_url); + + status = torture_ldap_bind_sasl(l->conn, credentials, tctx->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + printf("failed to bind to LDAP:\n"); + return false; + } + printf("bound to LDAP.\n"); + + return ret; +} + +static bool test_GetInfo(struct torture_context *tctx, struct DsSyncTest *ctx) +{ + NTSTATUS status; + struct drsuapi_DsCrackNames r; + struct drsuapi_DsNameString names[1]; + bool ret = true; + struct cldap_socket *cldap; + struct cldap_netlogon search; + + cldap = cldap_socket_init(ctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx)); + + r.in.bind_handle = &ctx->admin.drsuapi.bind_handle; + r.in.level = 1; + r.in.req.req1.codepage = 1252; /* western european */ + r.in.req.req1.language = 0x00000407; /* german */ + r.in.req.req1.count = 1; + r.in.req.req1.names = names; + r.in.req.req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS; + r.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT; + r.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; + names[0].str = talloc_asprintf(ctx, "%s\\", lp_workgroup(tctx->lp_ctx)); + + status = dcerpc_drsuapi_DsCrackNames(ctx->admin.drsuapi.pipe, ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(ctx, ctx->admin.drsuapi.pipe->last_fault_code); + } + printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr); + return false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsCrackNames failed - %s\n", win_errstr(r.out.result)); + return false; + } + + ctx->domain_dn = r.out.ctr.ctr1->array[0].result_name; + + ZERO_STRUCT(search); + search.in.dest_address = ctx->drsuapi_binding->host; + search.in.dest_port = lp_cldap_port(tctx->lp_ctx); + search.in.acct_control = -1; + search.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX; + search.in.map_response = true; + status = cldap_netlogon(cldap, ctx, &search); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + ctx->site_name = talloc_asprintf(ctx, "%s", "Default-First-Site-Name"); + printf("cldap_netlogon() returned %s. Defaulting to Site-Name: %s\n", errstr, ctx->site_name); + } else { + ctx->site_name = talloc_steal(ctx, search.out.netlogon.nt5_ex.client_site); + printf("cldap_netlogon() returned Client Site-Name: %s.\n",ctx->site_name); + printf("cldap_netlogon() returned Server Site-Name: %s.\n",search.out.netlogon.nt5_ex.server_site); + } + + if (!ctx->domain_dn) { + struct ldb_context *ldb = ldb_init(ctx, tctx->ev); + struct ldb_dn *dn = samdb_dns_domain_to_dn(ldb, ctx, search.out.netlogon.nt5_ex.dns_domain); + ctx->domain_dn = ldb_dn_alloc_linearized(ctx, dn); + talloc_free(dn); + talloc_free(ldb); + } + + return ret; +} + +static DATA_BLOB decrypt_blob(TALLOC_CTX *mem_ctx, + const DATA_BLOB *gensec_skey, + bool rcrypt, + struct drsuapi_DsReplicaObjectIdentifier *id, + uint32_t rid, + const DATA_BLOB *buffer) +{ + DATA_BLOB confounder; + DATA_BLOB enc_buffer; + + struct MD5Context md5; + uint8_t _enc_key[16]; + DATA_BLOB enc_key; + + DATA_BLOB dec_buffer; + + uint32_t crc32_given; + uint32_t crc32_calc; + DATA_BLOB checked_buffer; + + DATA_BLOB plain_buffer; + + /* + * the combination "c[3] s[1] e[1] d[0]..." + * was successful!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + + /* + * the first 16 bytes at the beginning are the confounder + * followed by the 4 byte crc32 checksum + */ + if (buffer->length < 20) { + return data_blob_const(NULL, 0); + } + confounder = data_blob_const(buffer->data, 16); + enc_buffer = data_blob_const(buffer->data + 16, buffer->length - 16); + + /* + * build the encryption key md5 over the session key followed + * by the confounder + * + * here the gensec session key is used and + * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key! + */ + enc_key = data_blob_const(_enc_key, sizeof(_enc_key)); + MD5Init(&md5); + MD5Update(&md5, gensec_skey->data, gensec_skey->length); + MD5Update(&md5, confounder.data, confounder.length); + MD5Final(enc_key.data, &md5); + + /* + * copy the encrypted buffer part and + * decrypt it using the created encryption key using arcfour + */ + dec_buffer = data_blob_talloc(mem_ctx, enc_buffer.data, enc_buffer.length); + if (!dec_buffer.data) { + return data_blob_const(NULL, 0); + } + arcfour_crypt_blob(dec_buffer.data, dec_buffer.length, &enc_key); + + /* + * the first 4 byte are the crc32 checksum + * of the remaining bytes + */ + crc32_given = IVAL(dec_buffer.data, 0); + crc32_calc = crc32_calc_buffer(dec_buffer.data + 4 , dec_buffer.length - 4); + if (crc32_given != crc32_calc) { + DEBUG(0,("CRC32: given[0x%08X] calc[0x%08X]\n", + crc32_given, crc32_calc)); + return data_blob_const(NULL, 0); + } + checked_buffer = data_blob_talloc(mem_ctx, dec_buffer.data + 4, dec_buffer.length - 4); + if (!checked_buffer.data) { + return data_blob_const(NULL, 0); + } + + /* + * some attributes seem to be in a usable form after this decryption + * (supplementalCredentials, priorValue, currentValue, trustAuthOutgoing, + * trustAuthIncoming, initialAuthOutgoing, initialAuthIncoming) + * At least supplementalCredentials contains plaintext + * like "Primary:Kerberos" (in unicode form) + * + * some attributes seem to have some additional encryption + * dBCSPwd, unicodePwd, ntPwdHistory, lmPwdHistory + * + * it's the sam_rid_crypt() function, as the value is constant, + * so it doesn't depend on sessionkeys. + */ + if (rcrypt) { + uint32_t i, num_hashes; + + if ((checked_buffer.length % 16) != 0) { + return data_blob_const(NULL, 0); + } + + plain_buffer = data_blob_talloc(mem_ctx, checked_buffer.data, checked_buffer.length); + if (!plain_buffer.data) { + return data_blob_const(NULL, 0); + } + + num_hashes = plain_buffer.length / 16; + for (i = 0; i < num_hashes; i++) { + uint32_t offset = i * 16; + sam_rid_crypt(rid, checked_buffer.data + offset, plain_buffer.data + offset, 0); + } + } else { + plain_buffer = checked_buffer; + } + + return plain_buffer; +} + +static void test_analyse_objects(struct torture_context *tctx, + struct DsSyncTest *ctx, + const DATA_BLOB *gensec_skey, + struct drsuapi_DsReplicaObjectListItemEx *cur) +{ + static uint32_t object_id; + const char *save_values_dir; + + if (!lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "print_pwd_blobs", false)) { + return; + } + + save_values_dir = lp_parm_string(tctx->lp_ctx, NULL, "dssync", "save_pwd_blobs_dir"); + + for (; cur; cur = cur->next_object) { + const char *dn; + struct dom_sid *sid = NULL; + uint32_t rid = 0; + bool dn_printed = false; + uint32_t i; + + if (!cur->object.identifier) continue; + + dn = cur->object.identifier->dn; + if (cur->object.identifier->sid.num_auths > 0) { + sid = &cur->object.identifier->sid; + rid = sid->sub_auths[sid->num_auths - 1]; + } + + for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) { + const char *name = NULL; + bool rcrypt = false; + DATA_BLOB *enc_data = NULL; + DATA_BLOB plain_data; + struct drsuapi_DsReplicaAttribute *attr; + ndr_pull_flags_fn_t pull_fn = NULL; + ndr_print_fn_t print_fn = NULL; + void *ptr = NULL; + attr = &cur->object.attribute_ctr.attributes[i]; + + switch (attr->attid) { + case DRSUAPI_ATTRIBUTE_dBCSPwd: + name = "dBCSPwd"; + rcrypt = true; + break; + case DRSUAPI_ATTRIBUTE_unicodePwd: + name = "unicodePwd"; + rcrypt = true; + break; + case DRSUAPI_ATTRIBUTE_ntPwdHistory: + name = "ntPwdHistory"; + rcrypt = true; + break; + case DRSUAPI_ATTRIBUTE_lmPwdHistory: + name = "lmPwdHistory"; + rcrypt = true; + break; + case DRSUAPI_ATTRIBUTE_supplementalCredentials: + name = "supplementalCredentials"; + pull_fn = (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob; + print_fn = (ndr_print_fn_t)ndr_print_supplementalCredentialsBlob; + ptr = talloc(ctx, struct supplementalCredentialsBlob); + break; + case DRSUAPI_ATTRIBUTE_priorValue: + name = "priorValue"; + break; + case DRSUAPI_ATTRIBUTE_currentValue: + name = "currentValue"; + break; + case DRSUAPI_ATTRIBUTE_trustAuthOutgoing: + name = "trustAuthOutgoing"; + pull_fn = (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob; + print_fn = (ndr_print_fn_t)ndr_print_trustAuthInOutBlob; + ptr = talloc(ctx, struct trustAuthInOutBlob); + break; + case DRSUAPI_ATTRIBUTE_trustAuthIncoming: + name = "trustAuthIncoming"; + pull_fn = (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob; + print_fn = (ndr_print_fn_t)ndr_print_trustAuthInOutBlob; + ptr = talloc(ctx, struct trustAuthInOutBlob); + break; + case DRSUAPI_ATTRIBUTE_initialAuthOutgoing: + name = "initialAuthOutgoing"; + break; + case DRSUAPI_ATTRIBUTE_initialAuthIncoming: + name = "initialAuthIncoming"; + break; + default: + continue; + } + + if (attr->value_ctr.num_values != 1) continue; + + if (!attr->value_ctr.values[0].blob) continue; + + enc_data = attr->value_ctr.values[0].blob; + ZERO_STRUCT(plain_data); + + plain_data = decrypt_blob(ctx, gensec_skey, rcrypt, + cur->object.identifier, rid, + enc_data); + if (!dn_printed) { + object_id++; + DEBUG(0,("DN[%u] %s\n", object_id, dn)); + dn_printed = true; + } + DEBUGADD(0,("ATTR: %s enc.length=%lu plain.length=%lu\n", + name, (long)enc_data->length, (long)plain_data.length)); + if (plain_data.length) { + enum ndr_err_code ndr_err; + dump_data(0, plain_data.data, plain_data.length); + if (save_values_dir) { + char *fname; + fname = talloc_asprintf(ctx, "%s/%s%02d", + save_values_dir, + name, object_id); + if (fname) { + bool ok; + ok = file_save(fname, plain_data.data, plain_data.length); + if (!ok) { + DEBUGADD(0,("Failed to save '%s'\n", fname)); + } + } + talloc_free(fname); + } + + if (pull_fn) { + /* Can't use '_all' because of PIDL bugs with relative pointers */ + ndr_err = ndr_pull_struct_blob(&plain_data, ptr, + lp_iconv_convenience(tctx->lp_ctx), ptr, + pull_fn); + if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + ndr_print_debug(print_fn, name, ptr); + } else { + DEBUG(0, ("Failed to decode %s\n", name)); + } + } + } else { + dump_data(0, enc_data->data, enc_data->length); + } + talloc_free(ptr); + } + } +} + +static bool test_FetchData(struct torture_context *tctx, struct DsSyncTest *ctx) +{ + NTSTATUS status; + bool ret = true; + int i, y = 0; + uint64_t highest_usn = 0; + const char *partition = NULL; + struct drsuapi_DsGetNCChanges r; + struct drsuapi_DsReplicaObjectIdentifier nc; + struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL; + struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL; + int32_t out_level = 0; + struct GUID null_guid; + struct dom_sid null_sid; + DATA_BLOB gensec_skey; + struct { + int32_t level; + } array[] = { +/* { + 5 + }, +*/ { + 8 + } + }; + + ZERO_STRUCT(null_guid); + ZERO_STRUCT(null_sid); + + partition = lp_parm_string(tctx->lp_ctx, NULL, "dssync", "partition"); + if (partition == NULL) { + partition = ctx->domain_dn; + printf("dssync:partition not specified, defaulting to %s.\n", ctx->domain_dn); + } + + highest_usn = lp_parm_int(tctx->lp_ctx, NULL, "dssync", "highest_usn", 0); + + array[0].level = lp_parm_int(tctx->lp_ctx, NULL, "dssync", "get_nc_changes_level", array[0].level); + + if (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "print_pwd_blobs", false)) { + const struct samr_Password *nthash; + nthash = cli_credentials_get_nt_hash(ctx->new_dc.credentials, ctx); + if (nthash) { + dump_data_pw("CREDENTIALS nthash:", nthash->hash, sizeof(nthash->hash)); + } + } + status = gensec_session_key(ctx->new_dc.drsuapi.pipe->conn->security_state.generic_state, + &gensec_skey); + if (!NT_STATUS_IS_OK(status)) { + printf("failed to get gensec session key: %s\n", nt_errstr(status)); + return false; + } + + for (i=0; i < ARRAY_SIZE(array); i++) { + printf("testing DsGetNCChanges level %d\n", + array[i].level); + + r.in.bind_handle = &ctx->new_dc.drsuapi.bind_handle; + r.in.level = &array[i].level; + + switch (*r.in.level) { + case 5: + nc.guid = null_guid; + nc.sid = null_sid; + nc.dn = partition; + + r.in.req.req5.destination_dsa_guid = ctx->new_dc.invocation_id; + r.in.req.req5.source_dsa_invocation_id = null_guid; + r.in.req.req5.naming_context = &nc; + r.in.req.req5.highwatermark.tmp_highest_usn = highest_usn; + r.in.req.req5.highwatermark.reserved_usn = 0; + r.in.req.req5.highwatermark.highest_usn = highest_usn; + r.in.req.req5.uptodateness_vector = NULL; + r.in.req.req5.replica_flags = 0; + if (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "compression", false)) { + r.in.req.req5.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES; + } + if (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "neighbour_writeable", true)) { + r.in.req.req5.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE; + } + r.in.req.req5.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP + | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS + | DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS + | DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED + ; + r.in.req.req5.max_object_count = 133; + r.in.req.req5.max_ndr_size = 1336770; + r.in.req.req5.extended_op = DRSUAPI_EXOP_NONE; + r.in.req.req5.fsmo_info = 0; + + break; + case 8: + nc.guid = null_guid; + nc.sid = null_sid; + nc.dn = partition; + /* nc.dn can be set to any other ad partition */ + + r.in.req.req8.destination_dsa_guid = ctx->new_dc.invocation_id; + r.in.req.req8.source_dsa_invocation_id = null_guid; + r.in.req.req8.naming_context = &nc; + r.in.req.req8.highwatermark.tmp_highest_usn = highest_usn; + r.in.req.req8.highwatermark.reserved_usn = 0; + r.in.req.req8.highwatermark.highest_usn = highest_usn; + r.in.req.req8.uptodateness_vector = NULL; + r.in.req.req8.replica_flags = 0; + if (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "compression", false)) { + r.in.req.req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES; + } + if (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "neighbour_writeable", true)) { + r.in.req.req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE; + } + r.in.req.req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP + | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS + | DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS + | DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED + ; + r.in.req.req8.max_object_count = 402; + r.in.req.req8.max_ndr_size = 402116; + + r.in.req.req8.extended_op = DRSUAPI_EXOP_NONE; + r.in.req.req8.fsmo_info = 0; + r.in.req.req8.partial_attribute_set = NULL; + r.in.req.req8.partial_attribute_set_ex = NULL; + r.in.req.req8.mapping_ctr.num_mappings = 0; + r.in.req.req8.mapping_ctr.mappings = NULL; + + break; + } + + printf("Dumping AD partition: %s\n", nc.dn); + for (y=0; ;y++) { + int32_t _level = 0; + ZERO_STRUCT(r.out); + r.out.level = &_level; + + if (*r.in.level == 5) { + DEBUG(0,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y, + (long long)r.in.req.req5.highwatermark.tmp_highest_usn, + (long long)r.in.req.req5.highwatermark.highest_usn)); + } + + if (*r.in.level == 8) { + DEBUG(0,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y, + (long long)r.in.req.req8.highwatermark.tmp_highest_usn, + (long long)r.in.req.req8.highwatermark.highest_usn)); + } + + status = dcerpc_drsuapi_DsGetNCChanges(ctx->new_dc.drsuapi.pipe, ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(ctx, ctx->new_dc.drsuapi.pipe->last_fault_code); + } + printf("dcerpc_drsuapi_DsGetNCChanges failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsGetNCChanges failed - %s\n", win_errstr(r.out.result)); + ret = false; + } + + if (ret == true && *r.out.level == 1) { + out_level = 1; + ctr1 = &r.out.ctr.ctr1; + } else if (ret == true && *r.out.level == 2 && + r.out.ctr.ctr2.mszip1.ts) { + out_level = 1; + ctr1 = &r.out.ctr.ctr2.mszip1.ts->ctr1; + } + + if (out_level == 1) { + DEBUG(0,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y, + (long long)ctr1->new_highwatermark.tmp_highest_usn, + (long long)ctr1->new_highwatermark.highest_usn)); + + test_analyse_objects(tctx, ctx, &gensec_skey, ctr1->first_object); + + if (ctr1->more_data) { + r.in.req.req5.highwatermark = ctr1->new_highwatermark; + continue; + } + } + + if (ret == true && *r.out.level == 6) { + out_level = 6; + ctr6 = &r.out.ctr.ctr6; + } else if (ret == true && *r.out.level == 7 + && r.out.ctr.ctr7.level == 6 + && r.out.ctr.ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP + && r.out.ctr.ctr7.ctr.mszip6.ts) { + out_level = 6; + ctr6 = &r.out.ctr.ctr7.ctr.mszip6.ts->ctr6; + } else if (ret == true && *r.out.level == 7 + && r.out.ctr.ctr7.level == 6 + && r.out.ctr.ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS + && r.out.ctr.ctr7.ctr.xpress6.ts) { + out_level = 6; + ctr6 = &r.out.ctr.ctr7.ctr.xpress6.ts->ctr6; + } + + if (out_level == 6) { + DEBUG(0,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y, + (long long)ctr6->new_highwatermark.tmp_highest_usn, + (long long)ctr6->new_highwatermark.highest_usn)); + + test_analyse_objects(tctx, ctx, &gensec_skey, ctr6->first_object); + + if (ctr6->more_data) { + r.in.req.req8.highwatermark = ctr6->new_highwatermark; + continue; + } + } + + break; + } + } + + return ret; +} + +static bool test_FetchNT4Data(struct torture_context *tctx, + struct DsSyncTest *ctx) +{ + NTSTATUS status; + bool ret = true; + struct drsuapi_DsGetNT4ChangeLog r; + struct GUID null_guid; + struct dom_sid null_sid; + DATA_BLOB cookie; + + ZERO_STRUCT(null_guid); + ZERO_STRUCT(null_sid); + ZERO_STRUCT(cookie); + + ZERO_STRUCT(r); + r.in.bind_handle = &ctx->new_dc.drsuapi.bind_handle; + r.in.level = 1; + + r.in.req.req1.unknown1 = lp_parm_int(tctx->lp_ctx, NULL, "dssync", "nt4-1", 3); + r.in.req.req1.unknown2 = lp_parm_int(tctx->lp_ctx, NULL, "dssync", "nt4-2", 0x00004000); + + while (1) { + r.in.req.req1.length = cookie.length; + r.in.req.req1.data = cookie.data; + + status = dcerpc_drsuapi_DsGetNT4ChangeLog(ctx->new_dc.drsuapi.pipe, ctx, &r); + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) { + printf("DsGetNT4ChangeLog not supported by target server\n"); + break; + } else if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(ctx, ctx->new_dc.drsuapi.pipe->last_fault_code); + } + printf("dcerpc_drsuapi_DsGetNT4ChangeLog failed - %s\n", errstr); + ret = false; + } else if (W_ERROR_EQUAL(r.out.result, WERR_INVALID_DOMAIN_ROLE)) { + printf("DsGetNT4ChangeLog not supported by target server\n"); + break; + } else if (!W_ERROR_IS_OK(r.out.result)) { + printf("DsGetNT4ChangeLog failed - %s\n", win_errstr(r.out.result)); + ret = false; + } else if (r.out.level != 1) { + printf("DsGetNT4ChangeLog unknown level - %u\n", r.out.level); + ret = false; + } else if (NT_STATUS_IS_OK(r.out.info.info1.status)) { + } else if (NT_STATUS_EQUAL(r.out.info.info1.status, STATUS_MORE_ENTRIES)) { + cookie.length = r.out.info.info1.length1; + cookie.data = r.out.info.info1.data1; + continue; + } else { + printf("DsGetNT4ChangeLog failed - %s\n", nt_errstr(r.out.info.info1.status)); + ret = false; + } + + break; + } + + return ret; +} + +bool torture_rpc_dssync(struct torture_context *torture) +{ + bool ret = true; + TALLOC_CTX *mem_ctx; + struct DsSyncTest *ctx; + + mem_ctx = talloc_init("torture_rpc_dssync"); + ctx = test_create_context(torture); + + ret &= _test_DsBind(torture, ctx, ctx->admin.credentials, &ctx->admin.drsuapi); + if (!ret) { + return ret; + } + ret &= test_LDAPBind(torture, ctx, ctx->admin.credentials, &ctx->admin.ldap); + if (!ret) { + return ret; + } + ret &= test_GetInfo(torture, ctx); + ret &= _test_DsBind(torture, ctx, ctx->new_dc.credentials, &ctx->new_dc.drsuapi); + if (!ret) { + return ret; + } + ret &= test_FetchData(torture, ctx); + ret &= test_FetchNT4Data(torture, ctx); + + return ret; +} diff --git a/source4/torture/rpc/echo.c b/source4/torture/rpc/echo.c new file mode 100644 index 0000000000..5c027d3299 --- /dev/null +++ b/source4/torture/rpc/echo.c @@ -0,0 +1,452 @@ +/* + Unix SMB/CIFS implementation. + test suite for echo rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 2005 + Copyright (C) Jelmer Vernooij 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/rpc/rpc.h" +#include "lib/events/events.h" +#include "librpc/gen_ndr/ndr_echo_c.h" + + +/* + test the AddOne interface +*/ +#define TEST_ADDONE(tctx, value) do { \ + n = i = value; \ + r.in.in_data = n; \ + r.out.out_data = &n; \ + status = dcerpc_echo_AddOne(p, tctx, &r); \ + torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "AddOne(%d) failed", i)); \ + torture_assert (tctx, n == i+1, talloc_asprintf(tctx, "%d + 1 != %u (should be %u)\n", i, n, i+1)); \ + torture_comment (tctx, "%d + 1 = %u\n", i, n); \ +} while(0) + +static bool test_addone(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + uint32_t i; + NTSTATUS status; + uint32_t n; + struct echo_AddOne r; + + for (i=0;i<10;i++) { + TEST_ADDONE(tctx, i); + } + + TEST_ADDONE(tctx, 0x7FFFFFFE); + TEST_ADDONE(tctx, 0xFFFFFFFE); + TEST_ADDONE(tctx, 0xFFFFFFFF); + TEST_ADDONE(tctx, random() & 0xFFFFFFFF); + return true; +} + +/* + test the EchoData interface +*/ +static bool test_echodata(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + int i; + NTSTATUS status; + uint8_t *data_in, *data_out; + int len; + struct echo_EchoData r; + + if (torture_setting_bool(tctx, "quick", false) && + (p->conn->flags & DCERPC_DEBUG_VALIDATE_BOTH)) { + len = 1 + (random() % 500); + } else { + len = 1 + (random() % 5000); + } + + data_in = talloc_array(tctx, uint8_t, len); + data_out = talloc_array(tctx, uint8_t, len); + for (i=0;i<len;i++) { + data_in[i] = i; + } + + r.in.len = len; + r.in.in_data = data_in; + + status = dcerpc_echo_EchoData(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, + "EchoData(%d) failed\n", len)); + + data_out = r.out.out_data; + + for (i=0;i<len;i++) { + if (data_in[i] != data_out[i]) { + torture_comment(tctx, "Bad data returned for len %d at offset %d\n", + len, i); + torture_comment(tctx, "in:\n"); + dump_data(0, data_in+i, MIN(len-i, 16)); + torture_comment(tctx, "out:\n"); + dump_data(0, data_out+i, MIN(len-1, 16)); + return false; + } + } + return true; +} + +/* + test the SourceData interface +*/ +static bool test_sourcedata(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + int i; + NTSTATUS status; + int len; + struct echo_SourceData r; + + if (torture_setting_bool(tctx, "quick", false) && + (p->conn->flags & DCERPC_DEBUG_VALIDATE_BOTH)) { + len = 100 + (random() % 500); + } else { + len = 200000 + (random() % 5000); + } + + r.in.len = len; + + status = dcerpc_echo_SourceData(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, + "SourceData(%d) failed", len)); + + for (i=0;i<len;i++) { + uint8_t *v = (uint8_t *)r.out.data; + torture_assert(tctx, v[i] == (i & 0xFF), + talloc_asprintf(tctx, + "bad data 0x%x at %d\n", (uint8_t)r.out.data[i], i)); + } + return true; +} + +/* + test the SinkData interface +*/ +static bool test_sinkdata(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + int i; + NTSTATUS status; + uint8_t *data_in; + int len; + struct echo_SinkData r; + + if (torture_setting_bool(tctx, "quick", false) && + (p->conn->flags & DCERPC_DEBUG_VALIDATE_BOTH)) { + len = 100 + (random() % 5000); + } else { + len = 200000 + (random() % 5000); + } + + data_in = talloc_array(tctx, uint8_t, len); + for (i=0;i<len;i++) { + data_in[i] = i+1; + } + + r.in.len = len; + r.in.data = data_in; + + status = dcerpc_echo_SinkData(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, + "SinkData(%d) failed", + len)); + + torture_comment(tctx, "sunk %d bytes\n", len); + return true; +} + + +/* + test the testcall interface +*/ +static bool test_testcall(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct echo_TestCall r; + const char *s = NULL; + + r.in.s1 = "input string"; + r.out.s2 = &s; + + status = dcerpc_echo_TestCall(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "TestCall failed"); + + torture_assert_str_equal(tctx, s, "input string", "Didn't receive back same string"); + + return true; +} + +/* + test the testcall interface +*/ +static bool test_testcall2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct echo_TestCall2 r; + int i; + + for (i=1;i<=7;i++) { + r.in.level = i; + r.out.info = talloc(tctx, union echo_Info); + + torture_comment(tctx, "Testing TestCall2 level %d\n", i); + status = dcerpc_echo_TestCall2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "TestCall2 failed"); + } + return true; +} + +/* + test the TestSleep interface +*/ +static bool test_sleep(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + int i; + NTSTATUS status; +#define ASYNC_COUNT 3 + struct rpc_request *req[ASYNC_COUNT]; + struct echo_TestSleep r[ASYNC_COUNT]; + bool done[ASYNC_COUNT]; + struct timeval snd[ASYNC_COUNT]; + struct timeval rcv[ASYNC_COUNT]; + struct timeval diff[ASYNC_COUNT]; + struct event_context *ctx; + int total_done = 0; + + if (torture_setting_bool(tctx, "quick", false)) { + torture_skip(tctx, "TestSleep disabled - use \"torture:quick=no\" to enable\n"); + } + torture_comment(tctx, "Testing TestSleep - use \"torture:quick=yes\" to disable\n"); + + for (i=0;i<ASYNC_COUNT;i++) { + done[i] = false; + snd[i] = timeval_current(); + rcv[i] = timeval_zero(); + r[i].in.seconds = ASYNC_COUNT-i; + req[i] = dcerpc_echo_TestSleep_send(p, tctx, &r[i]); + torture_assert(tctx, req[i], "Failed to send async sleep request\n"); + } + + ctx = dcerpc_event_context(p); + while (total_done < ASYNC_COUNT) { + torture_assert(tctx, event_loop_once(ctx) == 0, + "Event context loop failed"); + for (i=0;i<ASYNC_COUNT;i++) { + if (done[i] == false && req[i]->state == RPC_REQUEST_DONE) { + int rounded_tdiff; + total_done++; + done[i] = true; + rcv[i] = timeval_current(); + diff[i] = timeval_until(&snd[i], &rcv[i]); + rounded_tdiff = (int)(0.5 + diff[i].tv_sec + (1.0e-6*diff[i].tv_usec)); + status = dcerpc_ndr_request_recv(req[i]); + torture_comment(tctx, "rounded_tdiff=%d\n", rounded_tdiff); + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "TestSleep(%d) failed", i)); + torture_assert(tctx, r[i].out.result == r[i].in.seconds, + talloc_asprintf(tctx, "Failed - Asked to sleep for %u seconds (server replied with %u seconds and the reply takes only %u seconds)", + r[i].out.result, r[i].in.seconds, (uint_t)diff[i].tv_sec)); + torture_assert(tctx, r[i].out.result <= rounded_tdiff, + talloc_asprintf(tctx, "Failed - Slept for %u seconds (but reply takes only %u.%06u seconds)", + r[i].out.result, (uint_t)diff[i].tv_sec, (uint_t)diff[i].tv_usec)); + if (r[i].out.result+1 == rounded_tdiff) { + torture_comment(tctx, "Slept for %u seconds (but reply takes %u.%06u seconds - busy server?)\n", + r[i].out.result, (uint_t)diff[i].tv_sec, (uint_t)diff[i].tv_usec); + } else if (r[i].out.result == rounded_tdiff) { + torture_comment(tctx, "Slept for %u seconds (reply takes %u.%06u seconds - ok)\n", + r[i].out.result, (uint_t)diff[i].tv_sec, (uint_t)diff[i].tv_usec); + } else { + torture_comment(tctx, "(Failed) - Not async - Slept for %u seconds (but reply takes %u.%06u seconds)", + r[i].out.result, (uint_t)diff[i].tv_sec, (uint_t)diff[i].tv_usec); + /* TODO: let the test fail here, when we support async rpc on ncacn_np */ + } + } + } + } + torture_comment(tctx, "\n"); + return true; +} + +/* + test enum handling +*/ +static bool test_enum(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct echo_TestEnum r; + enum echo_Enum1 v = ECHO_ENUM1; + struct echo_Enum2 e2; + union echo_Enum3 e3; + + r.in.foo1 = &v; + r.in.foo2 = &e2; + r.in.foo3 = &e3; + r.out.foo1 = &v; + r.out.foo2 = &e2; + r.out.foo3 = &e3; + + e2.e1 = 76; + e2.e2 = ECHO_ENUM1_32; + e3.e1 = ECHO_ENUM2; + + status = dcerpc_echo_TestEnum(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "TestEnum failed"); + return true; +} + +/* + test surrounding conformant array handling +*/ +static bool test_surrounding(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct echo_TestSurrounding r; + + ZERO_STRUCT(r); + r.in.data = talloc(tctx, struct echo_Surrounding); + + r.in.data->x = 20; + r.in.data->surrounding = talloc_zero_array(tctx, uint16_t, r.in.data->x); + + r.out.data = talloc(tctx, struct echo_Surrounding); + + status = dcerpc_echo_TestSurrounding(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "TestSurrounding failed"); + + torture_assert(tctx, r.out.data->x == 2 * r.in.data->x, + "TestSurrounding did not make the array twice as large"); + + return true; +} + +/* + test multiple levels of pointers +*/ +static bool test_doublepointer(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct echo_TestDoublePointer r; + uint16_t value = 12; + uint16_t *pvalue = &value; + uint16_t **ppvalue = &pvalue; + + ZERO_STRUCT(r); + r.in.data = &ppvalue; + + status = dcerpc_echo_TestDoublePointer(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "TestDoublePointer failed"); + + torture_assert_int_equal(tctx, value, r.out.result, + "TestDoublePointer did not return original value"); + return true; +} + + +/* + test request timeouts +*/ +static bool test_timeout(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct rpc_request *req; + struct echo_TestSleep r; + int timeout_saved = p->request_timeout; + + if (torture_setting_bool(tctx, "quick", false)) { + torture_skip(tctx, "timeout testing disabled - use \"torture:quick=no\" to enable\n"); + } + + torture_comment(tctx, "testing request timeouts\n"); + r.in.seconds = 2; + p->request_timeout = 1; + + req = dcerpc_echo_TestSleep_send(p, tctx, &r); + if (!req) { + torture_comment(tctx, "Failed to send async sleep request\n"); + goto failed; + } + req->ignore_timeout = true; + + status = dcerpc_ndr_request_recv(req); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_IO_TIMEOUT, + "request should have timed out"); + + torture_comment(tctx, "testing request destruction\n"); + req = dcerpc_echo_TestSleep_send(p, tctx, &r); + if (!req) { + torture_comment(tctx, "Failed to send async sleep request\n"); + goto failed; + } + talloc_free(req); + + req = dcerpc_echo_TestSleep_send(p, tctx, &r); + if (!req) { + torture_comment(tctx, "Failed to send async sleep request\n"); + goto failed; + } + req->ignore_timeout = true; + status = dcerpc_ndr_request_recv(req); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_IO_TIMEOUT, + "request should have timed out"); + + p->request_timeout = timeout_saved; + + return test_addone(tctx, p); + +failed: + p->request_timeout = timeout_saved; + return false; +} + + +struct torture_suite *torture_rpc_echo(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create( + mem_ctx, "ECHO"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "echo", + &ndr_table_rpcecho); + + torture_rpc_tcase_add_test(tcase, "addone", test_addone); + torture_rpc_tcase_add_test(tcase, "sinkdata", test_sinkdata); + torture_rpc_tcase_add_test(tcase, "echodata", test_echodata); + torture_rpc_tcase_add_test(tcase, "sourcedata", test_sourcedata); + torture_rpc_tcase_add_test(tcase, "testcall", test_testcall); + torture_rpc_tcase_add_test(tcase, "testcall2", test_testcall2); + torture_rpc_tcase_add_test(tcase, "enum", test_enum); + torture_rpc_tcase_add_test(tcase, "surrounding", test_surrounding); + torture_rpc_tcase_add_test(tcase, "doublepointer", test_doublepointer); + torture_rpc_tcase_add_test(tcase, "sleep", test_sleep); + torture_rpc_tcase_add_test(tcase, "timeout", test_timeout); + + return suite; +} diff --git a/source4/torture/rpc/epmapper.c b/source4/torture/rpc/epmapper.c new file mode 100644 index 0000000000..708d46a5dc --- /dev/null +++ b/source4/torture/rpc/epmapper.c @@ -0,0 +1,277 @@ +/* + Unix SMB/CIFS implementation. + test suite for epmapper rpc operations + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_epmapper_c.h" +#include "librpc/ndr/ndr_table.h" +#include "librpc/rpc/dcerpc_proto.h" +#include "torture/rpc/rpc.h" + + +/* + display any protocol tower + */ +static void display_tower(TALLOC_CTX *mem_ctx, struct epm_tower *twr) +{ + int i; + + for (i=0;i<twr->num_floors;i++) { + printf(" %s", epm_floor_string(mem_ctx, &twr->floors[i])); + } + printf("\n"); +} + + +static bool test_Map(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct epm_twr_t *twr) +{ + NTSTATUS status; + struct epm_Map r; + struct GUID uuid; + struct policy_handle handle; + int i; + struct ndr_syntax_id syntax; + uint32_t num_towers; + + ZERO_STRUCT(uuid); + ZERO_STRUCT(handle); + + r.in.object = &uuid; + r.in.map_tower = twr; + r.in.entry_handle = &handle; + r.out.entry_handle = &handle; + r.in.max_towers = 100; + r.out.num_towers = &num_towers; + + dcerpc_floor_get_lhs_data(&twr->tower.floors[0], &syntax); + + printf("epm_Map results for '%s':\n", + ndr_interface_name(&syntax.uuid, syntax.if_version)); + + twr->tower.floors[2].lhs.protocol = EPM_PROTOCOL_NCACN; + twr->tower.floors[2].lhs.lhs_data = data_blob(NULL, 0); + twr->tower.floors[2].rhs.ncacn.minor_version = 0; + + twr->tower.floors[3].lhs.protocol = EPM_PROTOCOL_TCP; + twr->tower.floors[3].lhs.lhs_data = data_blob(NULL, 0); + twr->tower.floors[3].rhs.tcp.port = 0; + + twr->tower.floors[4].lhs.protocol = EPM_PROTOCOL_IP; + twr->tower.floors[4].lhs.lhs_data = data_blob(NULL, 0); + twr->tower.floors[4].rhs.ip.ipaddr = "0.0.0.0"; + + status = dcerpc_epm_Map(p, mem_ctx, &r); + if (NT_STATUS_IS_OK(status) && r.out.result == 0) { + for (i=0;i<*r.out.num_towers;i++) { + if (r.out.towers[i].twr) { + display_tower(mem_ctx, &r.out.towers[i].twr->tower); + } + } + } + + twr->tower.floors[3].lhs.protocol = EPM_PROTOCOL_HTTP; + twr->tower.floors[3].lhs.lhs_data = data_blob(NULL, 0); + twr->tower.floors[3].rhs.http.port = 0; + + status = dcerpc_epm_Map(p, mem_ctx, &r); + if (NT_STATUS_IS_OK(status) && r.out.result == 0) { + for (i=0;i<*r.out.num_towers;i++) { + if (r.out.towers[i].twr) { + display_tower(mem_ctx, &r.out.towers[i].twr->tower); + } + } + } + + twr->tower.floors[3].lhs.protocol = EPM_PROTOCOL_UDP; + twr->tower.floors[3].lhs.lhs_data = data_blob(NULL, 0); + twr->tower.floors[3].rhs.http.port = 0; + + status = dcerpc_epm_Map(p, mem_ctx, &r); + if (NT_STATUS_IS_OK(status) && r.out.result == 0) { + for (i=0;i<*r.out.num_towers;i++) { + if (r.out.towers[i].twr) { + display_tower(mem_ctx, &r.out.towers[i].twr->tower); + } + } + } + + twr->tower.floors[3].lhs.protocol = EPM_PROTOCOL_SMB; + twr->tower.floors[3].lhs.lhs_data = data_blob(NULL, 0); + twr->tower.floors[3].rhs.smb.unc = ""; + + twr->tower.floors[4].lhs.protocol = EPM_PROTOCOL_NETBIOS; + twr->tower.floors[4].lhs.lhs_data = data_blob(NULL, 0); + twr->tower.floors[4].rhs.netbios.name = ""; + + status = dcerpc_epm_Map(p, mem_ctx, &r); + if (NT_STATUS_IS_OK(status) && r.out.result == 0) { + for (i=0;i<*r.out.num_towers;i++) { + if (r.out.towers[i].twr) { + display_tower(mem_ctx, &r.out.towers[i].twr->tower); + } + } + } + + /* FIXME: Extend to do other protocols as well (ncacn_unix_stream, ncalrpc) */ + + return true; +} + +static bool test_Lookup(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct epm_Lookup r; + struct GUID uuid; + struct rpc_if_id_t iface; + struct policy_handle handle; + uint32_t num_ents; + + ZERO_STRUCT(handle); + + r.in.inquiry_type = 0; + r.in.object = &uuid; + r.in.interface_id = &iface; + r.in.vers_option = 0; + r.in.entry_handle = &handle; + r.out.entry_handle = &handle; + r.in.max_ents = 10; + r.out.num_ents = &num_ents; + + do { + int i; + + ZERO_STRUCT(uuid); + ZERO_STRUCT(iface); + + status = dcerpc_epm_Lookup(p, tctx, &r); + if (!NT_STATUS_IS_OK(status) || r.out.result != 0) { + break; + } + + printf("epm_Lookup returned %d events GUID %s\n", + *r.out.num_ents, GUID_string(tctx, &handle.uuid)); + + for (i=0;i<*r.out.num_ents;i++) { + printf("\nFound '%s'\n", r.out.entries[i].annotation); + display_tower(tctx, &r.out.entries[i].tower->tower); + if (r.out.entries[i].tower->tower.num_floors == 5) { + test_Map(p, tctx, r.out.entries[i].tower); + } + } + } while (NT_STATUS_IS_OK(status) && + r.out.result == 0 && + *r.out.num_ents == r.in.max_ents && + !policy_handle_empty(&handle)); + + torture_assert_ntstatus_ok(tctx, status, "Lookup failed"); + + return true; +} + +static bool test_Delete(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct epm_entry_t *entries) +{ + NTSTATUS status; + struct epm_Delete r; + + r.in.num_ents = 1; + r.in.entries = entries; + + status = dcerpc_epm_Delete(p, mem_ctx, &r); + if (NT_STATUS_IS_ERR(status)) { + printf("Delete failed - %s\n", nt_errstr(status)); + return false; + } + + if (r.out.result != 0) { + printf("Delete failed - %d\n", r.out.result); + return false; + } + + return true; +} + +static bool test_Insert(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct epm_Insert r; + struct dcerpc_binding *bd; + + r.in.num_ents = 1; + + r.in.entries = talloc_array(tctx, struct epm_entry_t, 1); + ZERO_STRUCT(r.in.entries[0].object); + r.in.entries[0].annotation = "smbtorture endpoint"; + status = dcerpc_parse_binding(tctx, "ncalrpc:[SMBTORTURE]", &bd); + torture_assert_ntstatus_ok(tctx, status, + "Unable to generate dcerpc_binding struct"); + + r.in.entries[0].tower = talloc(tctx, struct epm_twr_t); + + status = dcerpc_binding_build_tower(tctx, bd, &r.in.entries[0].tower->tower); + torture_assert_ntstatus_ok(tctx, status, + "Unable to build tower from binding struct"); + + r.in.replace = 0; + + status = dcerpc_epm_Insert(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "Insert failed"); + + torture_assert(tctx, r.out.result == 0, "Insert failed"); + + if (!test_Delete(p, tctx, r.in.entries)) { + return false; + } + + return true; +} + +static bool test_InqObject(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct epm_InqObject r; + + r.in.epm_object = talloc(tctx, struct GUID); + *r.in.epm_object = ndr_table_epmapper.syntax_id.uuid; + + status = dcerpc_epm_InqObject(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "InqObject failed"); + + return true; +} + +struct torture_suite *torture_rpc_epmapper(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "EPMAPPER"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "epmapper", + &ndr_table_epmapper); + + torture_rpc_tcase_add_test(tcase, "Lookup", test_Lookup); + + torture_rpc_tcase_add_test(tcase, "Insert", test_Insert); + + torture_rpc_tcase_add_test(tcase, "InqObject", test_InqObject); + + return suite; +} diff --git a/source4/torture/rpc/eventlog.c b/source4/torture/rpc/eventlog.c new file mode 100644 index 0000000000..feeeb9330b --- /dev/null +++ b/source4/torture/rpc/eventlog.c @@ -0,0 +1,260 @@ +/* + Unix SMB/CIFS implementation. + test suite for eventlog rpc operations + + Copyright (C) Tim Potter 2003,2005 + Copyright (C) Jelmer Vernooij 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_eventlog.h" +#include "librpc/gen_ndr/ndr_eventlog_c.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "torture/rpc/rpc.h" +#include "param/param.h" + +static void init_lsa_String(struct lsa_String *name, const char *s) +{ + name->string = s; + name->length = 2*strlen_m(s); + name->size = name->length; +} + +static bool get_policy_handle(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + struct eventlog_OpenEventLogW r; + struct eventlog_OpenUnknown0 unknown0; + + unknown0.unknown0 = 0x005c; + unknown0.unknown1 = 0x0001; + + r.in.unknown0 = &unknown0; + init_lsa_String(&r.in.logname, "dns server"); + init_lsa_String(&r.in.servername, NULL); + r.in.unknown2 = 0x00000001; + r.in.unknown3 = 0x00000001; + r.out.handle = handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_OpenEventLogW(p, tctx, &r), + "OpenEventLog failed"); + + torture_assert_ntstatus_ok(tctx, r.out.result, "OpenEventLog failed"); + + return true; +} + + + +static bool test_GetNumRecords(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct eventlog_GetNumRecords r; + struct eventlog_CloseEventLog cr; + struct policy_handle handle; + uint32_t number = 0; + + if (!get_policy_handle(tctx, p, &handle)) + return false; + + ZERO_STRUCT(r); + r.in.handle = &handle; + r.out.number = &number; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_GetNumRecords(p, tctx, &r), + "GetNumRecords failed"); + + torture_comment(tctx, "%d records\n", *r.out.number); + + cr.in.handle = cr.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_CloseEventLog(p, tctx, &cr), + "CloseEventLog failed"); + return true; +} + +static bool test_ReadEventLog(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct eventlog_ReadEventLogW r; + struct eventlog_CloseEventLog cr; + struct policy_handle handle; + + if (!get_policy_handle(tctx, p, &handle)) + return false; + + ZERO_STRUCT(r); + r.in.offset = 0; + r.in.handle = &handle; + r.in.flags = EVENTLOG_BACKWARDS_READ|EVENTLOG_SEQUENTIAL_READ; + + while (1) { + DATA_BLOB blob; + struct eventlog_Record rec; + struct ndr_pull *ndr; + enum ndr_err_code ndr_err; + uint32_t sent_size = 0; + uint32_t real_size = 0; + + /* Read first for number of bytes in record */ + + r.in.number_of_bytes = 0; + r.out.data = NULL; + r.out.sent_size = &sent_size; + r.out.real_size = &real_size; + + status = dcerpc_eventlog_ReadEventLogW(p, tctx, &r); + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_END_OF_FILE)) { + break; + } + + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_BUFFER_TOO_SMALL, + "ReadEventLog failed"); + + /* Now read the actual record */ + + r.in.number_of_bytes = *r.out.real_size; + r.out.data = talloc_array(tctx, uint8_t, r.in.number_of_bytes); + + status = dcerpc_eventlog_ReadEventLogW(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "ReadEventLog failed"); + + /* Decode a user-marshalled record */ + + blob.length = *r.out.sent_size; + blob.data = talloc_steal(tctx, r.out.data); + + ndr = ndr_pull_init_blob(&blob, tctx, lp_iconv_convenience(tctx->lp_ctx)); + + ndr_err = ndr_pull_eventlog_Record( + ndr, NDR_SCALARS|NDR_BUFFERS, &rec); + status = ndr_map_error2ntstatus(ndr_err); + + NDR_PRINT_DEBUG(eventlog_Record, &rec); + + torture_assert_ntstatus_ok(tctx, status, + "ReadEventLog failed parsing event log record"); + + r.in.offset++; + } + + cr.in.handle = cr.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_CloseEventLog(p, tctx, &cr), + "CloseEventLog failed"); + + return true; +} + +static bool test_FlushEventLog(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct eventlog_FlushEventLog r; + struct eventlog_CloseEventLog cr; + struct policy_handle handle; + + if (!get_policy_handle(tctx, p, &handle)) + return false; + + r.in.handle = &handle; + + /* Huh? Does this RPC always return access denied? */ + torture_assert_ntstatus_equal(tctx, + dcerpc_eventlog_FlushEventLog(p, tctx, &r), + NT_STATUS_ACCESS_DENIED, + "FlushEventLog failed"); + + cr.in.handle = cr.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_CloseEventLog(p, tctx, &cr), + "CloseEventLog failed"); + + return true; +} + +static bool test_ClearEventLog(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct eventlog_ClearEventLogW r; + struct eventlog_CloseEventLog cr; + struct policy_handle handle; + + if (!get_policy_handle(tctx, p, &handle)) + return false; + + r.in.handle = &handle; + r.in.unknown = NULL; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_ClearEventLogW(p, tctx, &r), + "ClearEventLog failed"); + + cr.in.handle = cr.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_CloseEventLog(p, tctx, &cr), + "CloseEventLog failed"); + + return true; +} + +static bool test_OpenEventLog(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct policy_handle handle; + struct eventlog_CloseEventLog cr; + + if (!get_policy_handle(tctx, p, &handle)) + return false; + + cr.in.handle = cr.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_CloseEventLog(p, tctx, &cr), + "CloseEventLog failed"); + + return true; +} + +struct torture_suite *torture_rpc_eventlog(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + struct torture_rpc_tcase *tcase; + struct torture_test *test; + + suite = torture_suite_create(mem_ctx, "EVENTLOG"); + tcase = torture_suite_add_rpc_iface_tcase(suite, "eventlog", + &ndr_table_eventlog); + + torture_rpc_tcase_add_test(tcase, "OpenEventLog", test_OpenEventLog); + test = torture_rpc_tcase_add_test(tcase, "ClearEventLog", + test_ClearEventLog); + test->dangerous = true; + torture_rpc_tcase_add_test(tcase, "GetNumRecords", test_GetNumRecords); + torture_rpc_tcase_add_test(tcase, "ReadEventLog", test_ReadEventLog); + torture_rpc_tcase_add_test(tcase, "FlushEventLog", test_FlushEventLog); + + return suite; +} diff --git a/source4/torture/rpc/frsapi.c b/source4/torture/rpc/frsapi.c new file mode 100644 index 0000000000..c8e421a674 --- /dev/null +++ b/source4/torture/rpc/frsapi.c @@ -0,0 +1,275 @@ +/* + Unix SMB/CIFS implementation. + test suite for rpc frsapi operations + + Copyright (C) Guenther Deschner 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/rpc/rpc.h" +#include "librpc/gen_ndr/ndr_frsapi_c.h" +#include "torture/util.h" +#include "param/param.h" + +static bool test_GetDsPollingIntervalW(struct torture_context *tctx, + struct dcerpc_pipe *p, + uint32_t *CurrentInterval, + uint32_t *DsPollingLongInterval, + uint32_t *DsPollingShortInterval) +{ + struct frsapi_GetDsPollingIntervalW r; + + ZERO_STRUCT(r); + + r.out.CurrentInterval = CurrentInterval; + r.out.DsPollingLongInterval = DsPollingLongInterval; + r.out.DsPollingShortInterval = DsPollingShortInterval; + + torture_assert_ntstatus_ok(tctx, + dcerpc_frsapi_GetDsPollingIntervalW(p, tctx, &r), + "GetDsPollingIntervalW failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "GetDsPollingIntervalW failed"); + + return true; +} + +static bool test_SetDsPollingIntervalW(struct torture_context *tctx, + struct dcerpc_pipe *p, + uint32_t CurrentInterval, + uint32_t DsPollingLongInterval, + uint32_t DsPollingShortInterval) +{ + struct frsapi_SetDsPollingIntervalW r; + + ZERO_STRUCT(r); + + r.in.CurrentInterval = CurrentInterval; + r.in.DsPollingLongInterval = DsPollingLongInterval; + r.in.DsPollingShortInterval = DsPollingShortInterval; + + torture_assert_ntstatus_ok(tctx, + dcerpc_frsapi_SetDsPollingIntervalW(p, tctx, &r), + "SetDsPollingIntervalW failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "SetDsPollingIntervalW failed"); + + return true; +} + +static bool test_DsPollingIntervalW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + uint32_t i1, i2, i3; + uint32_t k1, k2, k3; + + if (!test_GetDsPollingIntervalW(tctx, p, &i1, &i2, &i3)) { + return false; + } + + if (!test_SetDsPollingIntervalW(tctx, p, i1, i2, i3)) { + return false; + } + + k1 = i1; + k2 = k3 = 0; + + if (!test_SetDsPollingIntervalW(tctx, p, k1, k2, k3)) { + return false; + } + + if (!test_GetDsPollingIntervalW(tctx, p, &k1, &k2, &k3)) { + return false; + } + + if ((i1 != k1) || (i2 != k2) || (i3 != k3)) { + return false; + } + + return true; +} + +static bool test_IsPathReplicated_err(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *path, + uint32_t type, + WERROR werr) +{ + struct frsapi_IsPathReplicated r; + struct GUID guid; + uint32_t unknown1, unknown2, unknown3 = 0; + + ZERO_STRUCT(r); + + r.in.path = path; + r.in.replica_set_type = type; + r.out.unknown1 = &unknown1; + r.out.unknown2 = &unknown2; + r.out.unknown3 = &unknown3; + r.out.replica_set_guid = &guid; + + torture_assert_ntstatus_ok(tctx, + dcerpc_frsapi_IsPathReplicated(p, tctx, &r), + "IsPathReplicated failed"); + + torture_assert_werr_equal(tctx, r.out.result, werr, + "GetDsPollingIntervalW failed"); + + return true; +} + +static bool _test_IsPathReplicated(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *path, + uint32_t type) +{ + return test_IsPathReplicated_err(tctx, p, path, type, WERR_OK); +} + +static bool test_IsPathReplicated(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + const uint32_t lvls[] = { + FRSAPI_REPLICA_SET_TYPE_0, + FRSAPI_REPLICA_SET_TYPE_DOMAIN, + FRSAPI_REPLICA_SET_TYPE_DFS }; + int i; + bool ret = true; + + if (!test_IsPathReplicated_err(tctx, p, NULL, 0, + WERR_FRS_INVALID_SERVICE_PARAMETER)) { + ret = false; + } + + for (i=0; i<ARRAY_SIZE(lvls); i++) { + if (!_test_IsPathReplicated(tctx, p, dcerpc_server_name(p), + lvls[i])) { + ret = false; + } + } + + for (i=0; i<ARRAY_SIZE(lvls); i++) { + const char *path = talloc_asprintf(tctx, "\\\\%s\\SYSVOL", + dcerpc_server_name(p)); + if (!_test_IsPathReplicated(tctx, p, path, lvls[i])) { + ret = false; + } + } + + for (i=0; i<ARRAY_SIZE(lvls); i++) { + if (!_test_IsPathReplicated(tctx, p, + "C:\\windows\\sysvol\\domain", + lvls[i])) { + ret = false; + } + } + + return ret; +} + +static bool test_ForceReplication(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct frsapi_ForceReplication r; + + ZERO_STRUCT(r); + + r.in.guid1 = NULL; + r.in.guid2 = NULL; + r.in.replica_set = talloc_asprintf(tctx, "%s", + lp_realm(tctx->lp_ctx)); + r.in.partner_name = dcerpc_server_name(p); + + torture_assert_ntstatus_ok(tctx, + dcerpc_frsapi_ForceReplication(p, tctx, &r), + "ForceReplication failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "ForceReplication failed"); + + return true; +} + +static bool test_InfoW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + int i; + + for (i=0; i<10; i++) { + + struct frsapi_InfoW r; + struct frsapi_Info *info; + int d; + DATA_BLOB blob; + + ZERO_STRUCT(r); + + info = talloc_zero(tctx, struct frsapi_Info); + + r.in.length = 0x1000; + r.in.info = r.out.info = info; + + info->length = r.in.length; + info->length2 = r.in.length; + info->level = i; + info->offset = 0x2c; + info->blob_len = 0x2c; + + torture_assert_ntstatus_ok(tctx, + dcerpc_frsapi_InfoW(p, tctx, &r), + "InfoW failed"); + + torture_assert_werr_ok(tctx, r.out.result, "InfoW failed"); + + /* display the formatted blob text */ + blob = r.out.info->blob; + for (d = 0; d < blob.length; d++) { + if (blob.data[d]) { + printf("%c", blob.data[d]); + } + } + printf("\n"); + } + + return true; +} + +struct torture_suite *torture_rpc_frsapi(TALLOC_CTX *mem_ctx) +{ + struct torture_rpc_tcase *tcase; + struct torture_suite *suite = torture_suite_create(mem_ctx, "FRSAPI"); + struct torture_test *test; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "frsapi", + &ndr_table_frsapi); + + test = torture_rpc_tcase_add_test(tcase, "DsPollingIntervalW", + test_DsPollingIntervalW); + + test = torture_rpc_tcase_add_test(tcase, "IsPathReplicated", + test_IsPathReplicated); + + test = torture_rpc_tcase_add_test(tcase, "ForceReplication", + test_ForceReplication); + + test = torture_rpc_tcase_add_test(tcase, "InfoW", + test_InfoW); + return suite; +} diff --git a/source4/torture/rpc/handles.c b/source4/torture/rpc/handles.c new file mode 100644 index 0000000000..f35897b3df --- /dev/null +++ b/source4/torture/rpc/handles.c @@ -0,0 +1,580 @@ +/* + Unix SMB/CIFS implementation. + + test suite for behaviour of rpc policy handles + + Copyright (C) Andrew Tridgell 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_drsuapi_c.h" +#include "torture/rpc/rpc.h" + +/* + this tests the use of policy handles between connections +*/ + +static bool test_handles_lsa(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p1, *p2; + struct policy_handle handle; + struct policy_handle handle2; + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy r; + struct lsa_Close c; + uint16_t system_name = '\\'; + TALLOC_CTX *mem_ctx = talloc_new(torture); + + torture_comment(torture, "RPC-HANDLE-LSARPC\n"); + + status = torture_rpc_connection(torture, &p1, &ndr_table_lsarpc); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe1"); + + status = torture_rpc_connection(torture, &p2, &ndr_table_lsarpc); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe1"); + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = &system_name; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + status = dcerpc_lsa_OpenPolicy(p1, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "lsa_OpenPolicy not supported - skipping\n"); + talloc_free(mem_ctx); + return true; + } + + c.in.handle = &handle; + c.out.handle = &handle2; + + status = dcerpc_lsa_Close(p2, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, + "closing policy handle on p2"); + torture_assert_int_equal(torture, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, + "closing policy handle on p2"); + + status = dcerpc_lsa_Close(p1, mem_ctx, &c); + torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1"); + + status = dcerpc_lsa_Close(p1, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, + "closing policy handle on p1 again"); + torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, + "closing policy handle on p1 again"); + + talloc_free(mem_ctx); + + return true; +} + +static bool test_handles_lsa_shared(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p1, *p2, *p3, *p4, *p5; + struct policy_handle handle; + struct policy_handle handle2; + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy r; + struct lsa_Close c; + struct lsa_QuerySecurity qsec; + uint16_t system_name = '\\'; + TALLOC_CTX *mem_ctx = talloc_new(torture); + enum dcerpc_transport_t transport; + uint32_t assoc_group_id; + + torture_comment(torture, "RPC-HANDLE-LSARPC-SHARED\n"); + + torture_comment(torture, "connect lsa pipe1\n"); + status = torture_rpc_connection(torture, &p1, &ndr_table_lsarpc); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe1"); + + transport = p1->conn->transport.transport, + assoc_group_id = p1->assoc_group_id; + + torture_comment(torture, "use assoc_group_id[0x%08X] for new connections\n", assoc_group_id); + + torture_comment(torture, "connect lsa pipe2\n"); + status = torture_rpc_connection_transport(torture, &p2, &ndr_table_lsarpc, + transport, + assoc_group_id); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe2"); + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = &system_name; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + torture_comment(torture, "open lsa policy handle\n"); + status = dcerpc_lsa_OpenPolicy(p1, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "lsa_OpenPolicy not supported - skipping\n"); + talloc_free(mem_ctx); + return true; + } + + /* + * connect p3 after the policy handle is opened + */ + torture_comment(torture, "connect lsa pipe3 after the policy handle is opened\n"); + status = torture_rpc_connection_transport(torture, &p3, &ndr_table_lsarpc, + transport, + assoc_group_id); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe3"); + + qsec.in.handle = &handle; + qsec.in.sec_info = 0; + c.in.handle = &handle; + c.out.handle = &handle2; + + /* + * use policy handle on all 3 connections + */ + torture_comment(torture, "use the policy handle on p1,p2,p3\n"); + status = dcerpc_lsa_QuerySecurity(p1, mem_ctx, &qsec); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "use policy handle on p1"); + + status = dcerpc_lsa_QuerySecurity(p2, mem_ctx, &qsec); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "use policy handle on p2"); + + status = dcerpc_lsa_QuerySecurity(p3, mem_ctx, &qsec); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "use policy handle on p3"); + + /* + * close policy handle on connection 2 and the others get a fault + */ + torture_comment(torture, "close the policy handle on p2 others get a fault\n"); + status = dcerpc_lsa_Close(p2, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "closing policy handle on p2"); + + status = dcerpc_lsa_Close(p1, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, + "closing policy handle on p1 again"); + torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, + "closing policy handle on p1 again"); + + status = dcerpc_lsa_Close(p3, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, + "closing policy handle on p3"); + torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, + "closing policy handle on p3"); + + status = dcerpc_lsa_Close(p2, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, + "closing policy handle on p2 again"); + torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, + "closing policy handle on p2 again"); + + /* + * open a new policy handle on p3 + */ + torture_comment(torture, "open a new policy handle on p3\n"); + status = dcerpc_lsa_OpenPolicy(p3, mem_ctx, &r); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "open policy handle on p3"); + + /* + * use policy handle on all 3 connections + */ + torture_comment(torture, "use the policy handle on p1,p2,p3\n"); + status = dcerpc_lsa_QuerySecurity(p1, mem_ctx, &qsec); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "use policy handle on p1"); + + status = dcerpc_lsa_QuerySecurity(p2, mem_ctx, &qsec); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "use policy handle on p2"); + + status = dcerpc_lsa_QuerySecurity(p3, mem_ctx, &qsec); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "use policy handle on p3"); + + /* + * close policy handle on connection 2 and the others get a fault + */ + torture_comment(torture, "close the policy handle on p2 others get a fault\n"); + status = dcerpc_lsa_Close(p2, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "closing policy handle on p2"); + + status = dcerpc_lsa_Close(p1, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, + "closing policy handle on p1 again"); + torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, + "closing policy handle on p1 again"); + + status = dcerpc_lsa_Close(p3, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, + "closing policy handle on p3"); + torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, + "closing policy handle on p3"); + + status = dcerpc_lsa_Close(p2, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, + "closing policy handle on p2 again"); + torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, + "closing policy handle on p2 again"); + + /* + * open a new policy handle + */ + torture_comment(torture, "open a new policy handle on p1 and use it\n"); + status = dcerpc_lsa_OpenPolicy(p1, mem_ctx, &r); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "open 2nd policy handle on p1"); + + status = dcerpc_lsa_QuerySecurity(p1, mem_ctx, &qsec); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "QuerySecurity handle on p1"); + + /* close first connection */ + torture_comment(torture, "disconnect p1\n"); + talloc_free(p1); + msleep(5); + + /* + * and it's still available on p2,p3 + */ + torture_comment(torture, "use policy handle on p2,p3\n"); + status = dcerpc_lsa_QuerySecurity(p2, mem_ctx, &qsec); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "QuerySecurity handle on p2 after p1 was disconnected"); + + status = dcerpc_lsa_QuerySecurity(p3, mem_ctx, &qsec); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "QuerySecurity handle on p3 after p1 was disconnected"); + + /* + * now open p4 + * and use the handle on it + */ + torture_comment(torture, "connect lsa pipe4 and use policy handle\n"); + status = torture_rpc_connection_transport(torture, &p4, &ndr_table_lsarpc, + transport, + assoc_group_id); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe4"); + + status = dcerpc_lsa_QuerySecurity(p4, mem_ctx, &qsec); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "using policy handle on p4"); + + /* + * now close p2,p3,p4 + * without closing the policy handle + */ + torture_comment(torture, "disconnect p2,p3,p4\n"); + talloc_free(p2); + talloc_free(p3); + talloc_free(p4); + msleep(10); + + /* + * now open p5 + */ + torture_comment(torture, "connect lsa pipe5 - should fail\n"); + status = torture_rpc_connection_transport(torture, &p5, &ndr_table_lsarpc, + transport, + assoc_group_id); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL, + "opening lsa pipe5"); + + talloc_free(mem_ctx); + + return true; +} + + +static bool test_handles_samr(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p1, *p2; + struct policy_handle handle; + struct policy_handle handle2; + struct samr_Connect r; + struct samr_Close c; + TALLOC_CTX *mem_ctx = talloc_new(torture); + + torture_comment(torture, "RPC-HANDLE-SAMR\n"); + + status = torture_rpc_connection(torture, &p1, &ndr_table_samr); + torture_assert_ntstatus_ok(torture, status, "opening samr pipe1"); + + status = torture_rpc_connection(torture, &p2, &ndr_table_samr); + torture_assert_ntstatus_ok(torture, status, "opening samr pipe1"); + + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.connect_handle = &handle; + + status = dcerpc_samr_Connect(p1, mem_ctx, &r); + torture_assert_ntstatus_ok(torture, status, "opening policy handle on p1"); + + c.in.handle = &handle; + c.out.handle = &handle2; + + status = dcerpc_samr_Close(p2, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, + "closing policy handle on p2"); + torture_assert_int_equal(torture, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, + "closing policy handle on p2"); + + status = dcerpc_samr_Close(p1, mem_ctx, &c); + torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1"); + + status = dcerpc_samr_Close(p1, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, + "closing policy handle on p1 again"); + torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, + "closing policy handle on p1 again"); + + talloc_free(mem_ctx); + + return true; +} + +static bool test_handles_mixed_shared(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p1, *p2, *p3, *p4, *p5, *p6; + struct policy_handle handle; + struct policy_handle handle2; + struct samr_Connect r; + struct lsa_Close lc; + struct samr_Close sc; + TALLOC_CTX *mem_ctx = talloc_new(torture); + enum dcerpc_transport_t transport; + uint32_t assoc_group_id; + + torture_comment(torture, "RPC-HANDLE-MIXED-SHARED\n"); + + torture_comment(torture, "connect samr pipe1\n"); + status = torture_rpc_connection(torture, &p1, &ndr_table_samr); + torture_assert_ntstatus_ok(torture, status, "opening samr pipe1"); + + transport = p1->conn->transport.transport, + assoc_group_id = p1->assoc_group_id; + + torture_comment(torture, "use assoc_group_id[0x%08X] for new connections\n", assoc_group_id); + + torture_comment(torture, "connect lsa pipe2\n"); + status = torture_rpc_connection_transport(torture, &p2, &ndr_table_lsarpc, + transport, + assoc_group_id); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe2"); + + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.connect_handle = &handle; + + torture_comment(torture, "samr_Connect to open a policy handle on samr p1\n"); + status = dcerpc_samr_Connect(p1, mem_ctx, &r); + torture_assert_ntstatus_ok(torture, status, "opening policy handle on p1"); + + lc.in.handle = &handle; + lc.out.handle = &handle2; + sc.in.handle = &handle; + sc.out.handle = &handle2; + + torture_comment(torture, "use policy handle on lsa p2 - should fail\n"); + status = dcerpc_lsa_Close(p2, mem_ctx, &lc); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, + "closing handle on lsa p2"); + torture_assert_int_equal(torture, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, + "closing handle on lsa p2"); + + torture_comment(torture, "closing policy handle on samr p1\n"); + status = dcerpc_samr_Close(p1, mem_ctx, &sc); + torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1"); + + talloc_free(p1); + talloc_free(p2); + msleep(10); + + torture_comment(torture, "connect samr pipe3 - should fail\n"); + status = torture_rpc_connection_transport(torture, &p3, &ndr_table_samr, + transport, + assoc_group_id); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL, + "opening samr pipe3"); + + torture_comment(torture, "connect lsa pipe4 - should fail\n"); + status = torture_rpc_connection_transport(torture, &p4, &ndr_table_lsarpc, + transport, + assoc_group_id); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL, + "opening lsa pipe4"); + + torture_comment(torture, "connect samr pipe5 with assoc_group_id[0x%08X]- should fail\n", ++assoc_group_id); + status = torture_rpc_connection_transport(torture, &p5, &ndr_table_samr, + transport, + assoc_group_id); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL, + "opening samr pipe5"); + + torture_comment(torture, "connect lsa pipe6 with assoc_group_id[0x%08X]- should fail\n", ++assoc_group_id); + status = torture_rpc_connection_transport(torture, &p6, &ndr_table_lsarpc, + transport, + assoc_group_id); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL, + "opening lsa pipe6"); + + talloc_free(mem_ctx); + + return true; +} + +static bool test_handles_random_assoc(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p1, *p2, *p3; + TALLOC_CTX *mem_ctx = talloc_new(torture); + enum dcerpc_transport_t transport; + uint32_t assoc_group_id; + + torture_comment(torture, "RPC-HANDLE-RANDOM-ASSOC\n"); + + torture_comment(torture, "connect samr pipe1\n"); + status = torture_rpc_connection(torture, &p1, &ndr_table_samr); + torture_assert_ntstatus_ok(torture, status, "opening samr pipe1"); + + transport = p1->conn->transport.transport, + assoc_group_id = p1->assoc_group_id; + + torture_comment(torture, "pip1 use assoc_group_id[0x%08X]\n", assoc_group_id); + + torture_comment(torture, "connect samr pipe2 with assoc_group_id[0x%08X]- should fail\n", ++assoc_group_id); + status = torture_rpc_connection_transport(torture, &p2, &ndr_table_samr, + transport, + assoc_group_id); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL, + "opening samr pipe2"); + + torture_comment(torture, "connect samr pipe3 with assoc_group_id[0x%08X]- should fail\n", ++assoc_group_id); + status = torture_rpc_connection_transport(torture, &p3, &ndr_table_samr, + transport, + assoc_group_id); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL, + "opening samr pipe3"); + + talloc_free(mem_ctx); + + return true; +} + + +static bool test_handles_drsuapi(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p1, *p2; + struct policy_handle handle; + struct policy_handle handle2; + struct GUID bind_guid; + struct drsuapi_DsBind r; + struct drsuapi_DsUnbind c; + TALLOC_CTX *mem_ctx = talloc_new(torture); + + torture_comment(torture, "RPC-HANDLE-DRSUAPI\n"); + + status = torture_rpc_connection(torture, &p1, &ndr_table_drsuapi); + torture_assert_ntstatus_ok(torture, status, "opening drsuapi pipe1"); + + status = torture_rpc_connection(torture, &p2, &ndr_table_drsuapi); + torture_assert_ntstatus_ok(torture, status, "opening drsuapi pipe1"); + + GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid); + + r.in.bind_guid = &bind_guid; + r.in.bind_info = NULL; + r.out.bind_handle = &handle; + + status = dcerpc_drsuapi_DsBind(p1, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "drsuapi_DsBind not supported - skipping\n"); + talloc_free(mem_ctx); + return true; + } + + c.in.bind_handle = &handle; + c.out.bind_handle = &handle2; + + status = dcerpc_drsuapi_DsUnbind(p2, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, + "closing policy handle on p2"); + torture_assert_int_equal(torture, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, + "closing policy handle on p2"); + + status = dcerpc_drsuapi_DsUnbind(p1, mem_ctx, &c); + torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1"); + + status = dcerpc_drsuapi_DsUnbind(p1, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, + "closing policy handle on p1 again"); + torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, + "closing policy handle on p1 again"); + + talloc_free(mem_ctx); + + return true; +} + + +struct torture_suite *torture_rpc_handles(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + + suite = torture_suite_create(mem_ctx, "HANDLES"); + torture_suite_add_simple_test(suite, "lsarpc", test_handles_lsa); + torture_suite_add_simple_test(suite, "lsarpc-shared", test_handles_lsa_shared); + torture_suite_add_simple_test(suite, "samr", test_handles_samr); + torture_suite_add_simple_test(suite, "mixed-shared", test_handles_mixed_shared); + torture_suite_add_simple_test(suite, "random-assoc", test_handles_random_assoc); + torture_suite_add_simple_test(suite, "drsuapi", test_handles_drsuapi); + return suite; +} diff --git a/source4/torture/rpc/initshutdown.c b/source4/torture/rpc/initshutdown.c new file mode 100644 index 0000000000..92fec5be0c --- /dev/null +++ b/source4/torture/rpc/initshutdown.c @@ -0,0 +1,114 @@ +/* + Unix SMB/CIFS implementation. + test suite for initshutdown operations + + Copyright (C) Tim Potter 2003 + Copyright (C) Jelmer Vernooij 2004-2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_initshutdown_c.h" +#include "torture/rpc/rpc.h" + +static void init_lsa_StringLarge(struct lsa_StringLarge *name, const char *s) +{ + name->string = s; +} + + +static bool test_Abort(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct initshutdown_Abort r; + NTSTATUS status; + uint16_t server = 0x0; + + r.in.server = &server; + + status = dcerpc_initshutdown_Abort(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, + "initshutdown_Abort failed"); + + torture_assert_werr_ok(tctx, r.out.result, "initshutdown_Abort failed"); + + return true; +} + +static bool test_Init(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct initshutdown_Init r; + NTSTATUS status; + uint16_t hostname = 0x0; + + r.in.hostname = &hostname; + r.in.message = talloc(tctx, struct lsa_StringLarge); + init_lsa_StringLarge(r.in.message, "spottyfood"); + r.in.force_apps = 1; + r.in.timeout = 30; + r.in.reboot = 1; + + status = dcerpc_initshutdown_Init(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "initshutdown_Init failed"); + torture_assert_werr_ok(tctx, r.out.result, "initshutdown_Init failed"); + + return test_Abort(tctx, p); +} + +static bool test_InitEx(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct initshutdown_InitEx r; + NTSTATUS status; + uint16_t hostname = 0x0; + + r.in.hostname = &hostname; + r.in.message = talloc(tctx, struct lsa_StringLarge); + init_lsa_StringLarge(r.in.message, "spottyfood"); + r.in.force_apps = 1; + r.in.timeout = 30; + r.in.reboot = 1; + r.in.reason = 0; + + status = dcerpc_initshutdown_InitEx(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "initshutdown_InitEx failed"); + + torture_assert_werr_ok(tctx, r.out.result, "initshutdown_InitEx failed"); + + return test_Abort(tctx, p); +} + + +struct torture_suite *torture_rpc_initshutdown(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "INITSHUTDOWN"); + struct torture_rpc_tcase *tcase; + struct torture_test *test; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "initshutdown", + &ndr_table_initshutdown); + + test = torture_rpc_tcase_add_test(tcase, "Init", test_Init); + test->dangerous = true; + test = torture_rpc_tcase_add_test(tcase, "InitEx", test_InitEx); + test->dangerous = true; + + return suite; +} diff --git a/source4/torture/rpc/join.c b/source4/torture/rpc/join.c new file mode 100644 index 0000000000..77da32d572 --- /dev/null +++ b/source4/torture/rpc/join.c @@ -0,0 +1,82 @@ +#include "includes.h" +#include "libnet/libnet.h" +#include "libcli/libcli.h" + +#include "auth/credentials/credentials.h" +#include "torture/rpc/rpc.h" + +#include "libcli/resolve/resolve.h" +#include "param/param.h" + +#define TORTURE_NETBIOS_NAME "smbtorturejoin" + + +bool torture_rpc_join(struct torture_context *torture) +{ + NTSTATUS status; + struct test_join *tj; + struct cli_credentials *machine_account; + struct smbcli_state *cli; + const char *host = torture_setting_string(torture, "host", NULL); + struct smbcli_options options; + + /* Join domain as a member server. */ + tj = torture_join_domain(torture, + TORTURE_NETBIOS_NAME, + ACB_WSTRUST, + &machine_account); + + if (!tj) { + DEBUG(0, ("%s failed to join domain as workstation\n", + TORTURE_NETBIOS_NAME)); + return false; + } + + lp_smbcli_options(torture->lp_ctx, &options); + + status = smbcli_full_connection(tj, &cli, host, + lp_smb_ports(torture->lp_ctx), + "IPC$", NULL, + machine_account, + lp_resolve_context(torture->lp_ctx), + torture->ev, &options); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("%s failed to connect to IPC$ with workstation credentials\n", + TORTURE_NETBIOS_NAME)); + return false; + } + smbcli_tdis(cli); + + /* Leave domain. */ + torture_leave_domain(torture, tj); + + /* Join domain as a domain controller. */ + tj = torture_join_domain(torture, TORTURE_NETBIOS_NAME, + ACB_SVRTRUST, + &machine_account); + if (!tj) { + DEBUG(0, ("%s failed to join domain as domain controller\n", + TORTURE_NETBIOS_NAME)); + return false; + } + + status = smbcli_full_connection(tj, &cli, host, + lp_smb_ports(torture->lp_ctx), + "IPC$", NULL, + machine_account, + lp_resolve_context(torture->lp_ctx), + torture->ev, &options); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("%s failed to connect to IPC$ with workstation credentials\n", + TORTURE_NETBIOS_NAME)); + return false; + } + + smbcli_tdis(cli); + + /* Leave domain. */ + torture_leave_domain(torture, tj); + + return true; +} + diff --git a/source4/torture/rpc/lsa.c b/source4/torture/rpc/lsa.c new file mode 100644 index 0000000000..a13a8d8818 --- /dev/null +++ b/source4/torture/rpc/lsa.c @@ -0,0 +1,2558 @@ +/* + Unix SMB/CIFS implementation. + test suite for lsa rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/netlogon.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "lib/events/events.h" +#include "libcli/security/security.h" +#include "libcli/auth/libcli_auth.h" +#include "torture/rpc/rpc.h" +#include "param/param.h" +#include "lib/crypto/crypto.h" +#define TEST_MACHINENAME "lsatestmach" + +static void init_lsa_String(struct lsa_String *name, const char *s) +{ + name->string = s; +} + +static bool test_OpenPolicy(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx) +{ + struct lsa_ObjectAttribute attr; + struct policy_handle handle; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy r; + NTSTATUS status; + uint16_t system_name = '\\'; + + printf("\ntesting OpenPolicy\n"); + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = &system_name; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + status = dcerpc_lsa_OpenPolicy(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) { + printf("not considering %s to be an error\n", nt_errstr(status)); + return true; + } + printf("OpenPolicy failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + + +bool test_lsa_OpenPolicy2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle **handle) +{ + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy2 r; + NTSTATUS status; + + printf("\ntesting OpenPolicy2\n"); + + *handle = talloc(mem_ctx, struct policy_handle); + if (!*handle) { + return false; + } + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = "\\"; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = *handle; + + status = dcerpc_lsa_OpenPolicy2(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) { + printf("not considering %s to be an error\n", nt_errstr(status)); + talloc_free(*handle); + *handle = NULL; + return true; + } + printf("OpenPolicy2 failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + + +static const char *sid_type_lookup(enum lsa_SidType r) +{ + switch (r) { + case SID_NAME_USE_NONE: return "SID_NAME_USE_NONE"; break; + case SID_NAME_USER: return "SID_NAME_USER"; break; + case SID_NAME_DOM_GRP: return "SID_NAME_DOM_GRP"; break; + case SID_NAME_DOMAIN: return "SID_NAME_DOMAIN"; break; + case SID_NAME_ALIAS: return "SID_NAME_ALIAS"; break; + case SID_NAME_WKN_GRP: return "SID_NAME_WKN_GRP"; break; + case SID_NAME_DELETED: return "SID_NAME_DELETED"; break; + case SID_NAME_INVALID: return "SID_NAME_INVALID"; break; + case SID_NAME_UNKNOWN: return "SID_NAME_UNKNOWN"; break; + case SID_NAME_COMPUTER: return "SID_NAME_COMPUTER"; break; + } + return "Invalid sid type\n"; +} + +static bool test_LookupNames(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct lsa_TransNameArray *tnames) +{ + struct lsa_LookupNames r; + struct lsa_TransSidArray sids; + struct lsa_String *names; + uint32_t count = 0; + NTSTATUS status; + int i; + + printf("\nTesting LookupNames with %d names\n", tnames->count); + + sids.count = 0; + sids.sids = NULL; + + names = talloc_array(mem_ctx, struct lsa_String, tnames->count); + for (i=0;i<tnames->count;i++) { + init_lsa_String(&names[i], tnames->names[i].name.string); + } + + r.in.handle = handle; + r.in.num_names = tnames->count; + r.in.names = names; + r.in.sids = &sids; + r.in.level = 1; + r.in.count = &count; + r.out.count = &count; + r.out.sids = &sids; + + status = dcerpc_lsa_LookupNames(p, mem_ctx, &r); + + if (NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED) || + NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { + for (i=0;i< tnames->count;i++) { + if (i < count && sids.sids[i].sid_type == SID_NAME_UNKNOWN) { + printf("LookupName of %s was unmapped\n", + tnames->names[i].name.string); + } else if (i >=count) { + printf("LookupName of %s failed to return a result\n", + tnames->names[i].name.string); + } + } + printf("LookupNames failed - %s\n", nt_errstr(status)); + return false; + } else if (!NT_STATUS_IS_OK(status)) { + printf("LookupNames failed - %s\n", nt_errstr(status)); + return false; + } + + for (i=0;i< tnames->count;i++) { + if (i < count && sids.sids[i].sid_type != tnames->names[i].sid_type) { + printf("LookupName of %s got unexpected name type: %s\n", + tnames->names[i].name.string, sid_type_lookup(sids.sids[i].sid_type)); + } else if (i >=count) { + printf("LookupName of %s failed to return a result\n", + tnames->names[i].name.string); + } + } + printf("\n"); + + return true; +} + +static bool test_LookupNames_bogus(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + struct lsa_LookupNames r; + struct lsa_TransSidArray sids; + struct lsa_String *names; + uint32_t count = 0; + NTSTATUS status; + int i; + + struct lsa_TranslatedName name; + struct lsa_TransNameArray tnames; + + tnames.names = &name; + tnames.count = 1; + name.name.string = "NT AUTHORITY\\BOGUS"; + + printf("\nTesting LookupNames with bogus names\n"); + + sids.count = 0; + sids.sids = NULL; + + names = talloc_array(mem_ctx, struct lsa_String, tnames.count); + for (i=0;i<tnames.count;i++) { + init_lsa_String(&names[i], tnames.names[i].name.string); + } + + r.in.handle = handle; + r.in.num_names = tnames.count; + r.in.names = names; + r.in.sids = &sids; + r.in.level = 1; + r.in.count = &count; + r.out.count = &count; + r.out.sids = &sids; + + status = dcerpc_lsa_LookupNames(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { + printf("LookupNames failed - %s\n", nt_errstr(status)); + return false; + } + + printf("\n"); + + return true; +} + +static bool test_LookupNames_wellknown(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + struct lsa_TranslatedName name; + struct lsa_TransNameArray tnames; + bool ret = true; + + printf("Testing LookupNames with well known names\n"); + + tnames.names = &name; + tnames.count = 1; + name.name.string = "NT AUTHORITY\\SYSTEM"; + name.sid_type = SID_NAME_WKN_GRP; + ret &= test_LookupNames(p, mem_ctx, handle, &tnames); + + name.name.string = "NT AUTHORITY\\ANONYMOUS LOGON"; + name.sid_type = SID_NAME_WKN_GRP; + ret &= test_LookupNames(p, mem_ctx, handle, &tnames); + + name.name.string = "NT AUTHORITY\\Authenticated Users"; + name.sid_type = SID_NAME_WKN_GRP; + ret &= test_LookupNames(p, mem_ctx, handle, &tnames); + +#if 0 + name.name.string = "NT AUTHORITY"; + ret &= test_LookupNames(p, mem_ctx, handle, &tnames); + + name.name.string = "NT AUTHORITY\\"; + ret &= test_LookupNames(p, mem_ctx, handle, &tnames); +#endif + + name.name.string = "BUILTIN\\"; + name.sid_type = SID_NAME_DOMAIN; + ret &= test_LookupNames(p, mem_ctx, handle, &tnames); + + name.name.string = "BUILTIN\\Administrators"; + name.sid_type = SID_NAME_ALIAS; + ret &= test_LookupNames(p, mem_ctx, handle, &tnames); + + name.name.string = "SYSTEM"; + name.sid_type = SID_NAME_WKN_GRP; + ret &= test_LookupNames(p, mem_ctx, handle, &tnames); + + name.name.string = "Everyone"; + name.sid_type = SID_NAME_WKN_GRP; + ret &= test_LookupNames(p, mem_ctx, handle, &tnames); + return ret; +} + +static bool test_LookupNames2(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct lsa_TransNameArray2 *tnames) +{ + struct lsa_LookupNames2 r; + struct lsa_TransSidArray2 sids; + struct lsa_String *names; + uint32_t count = 0; + NTSTATUS status; + int i; + + printf("\nTesting LookupNames2 with %d names\n", tnames->count); + + sids.count = 0; + sids.sids = NULL; + + names = talloc_array(mem_ctx, struct lsa_String, tnames->count); + for (i=0;i<tnames->count;i++) { + init_lsa_String(&names[i], tnames->names[i].name.string); + } + + r.in.handle = handle; + r.in.num_names = tnames->count; + r.in.names = names; + r.in.sids = &sids; + r.in.level = 1; + r.in.count = &count; + r.in.unknown1 = 0; + r.in.unknown2 = 0; + r.out.count = &count; + r.out.sids = &sids; + + status = dcerpc_lsa_LookupNames2(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupNames2 failed - %s\n", nt_errstr(status)); + return false; + } + + printf("\n"); + + return true; +} + + +static bool test_LookupNames3(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct lsa_TransNameArray2 *tnames) +{ + struct lsa_LookupNames3 r; + struct lsa_TransSidArray3 sids; + struct lsa_String *names; + uint32_t count = 0; + NTSTATUS status; + int i; + + printf("\nTesting LookupNames3 with %d names\n", tnames->count); + + sids.count = 0; + sids.sids = NULL; + + names = talloc_array(mem_ctx, struct lsa_String, tnames->count); + for (i=0;i<tnames->count;i++) { + init_lsa_String(&names[i], tnames->names[i].name.string); + } + + r.in.handle = handle; + r.in.num_names = tnames->count; + r.in.names = names; + r.in.sids = &sids; + r.in.level = 1; + r.in.count = &count; + r.in.unknown1 = 0; + r.in.unknown2 = 0; + r.out.count = &count; + r.out.sids = &sids; + + status = dcerpc_lsa_LookupNames3(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupNames3 failed - %s\n", nt_errstr(status)); + return false; + } + + printf("\n"); + + return true; +} + +static bool test_LookupNames4(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct lsa_TransNameArray2 *tnames) +{ + struct lsa_LookupNames4 r; + struct lsa_TransSidArray3 sids; + struct lsa_String *names; + uint32_t count = 0; + NTSTATUS status; + int i; + + printf("\nTesting LookupNames4 with %d names\n", tnames->count); + + sids.count = 0; + sids.sids = NULL; + + names = talloc_array(mem_ctx, struct lsa_String, tnames->count); + for (i=0;i<tnames->count;i++) { + init_lsa_String(&names[i], tnames->names[i].name.string); + } + + r.in.num_names = tnames->count; + r.in.names = names; + r.in.sids = &sids; + r.in.level = 1; + r.in.count = &count; + r.in.unknown1 = 0; + r.in.unknown2 = 0; + r.out.count = &count; + r.out.sids = &sids; + + status = dcerpc_lsa_LookupNames4(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupNames4 failed - %s\n", nt_errstr(status)); + return false; + } + + printf("\n"); + + return true; +} + + +static bool test_LookupSids(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct lsa_SidArray *sids) +{ + struct lsa_LookupSids r; + struct lsa_TransNameArray names; + uint32_t count = sids->num_sids; + NTSTATUS status; + + printf("\nTesting LookupSids\n"); + + names.count = 0; + names.names = NULL; + + r.in.handle = handle; + r.in.sids = sids; + r.in.names = &names; + r.in.level = 1; + r.in.count = &count; + r.out.count = &count; + r.out.names = &names; + + status = dcerpc_lsa_LookupSids(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupSids failed - %s\n", nt_errstr(status)); + return false; + } + + printf("\n"); + + if (!test_LookupNames(p, mem_ctx, handle, &names)) { + return false; + } + + return true; +} + + +static bool test_LookupSids2(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct lsa_SidArray *sids) +{ + struct lsa_LookupSids2 r; + struct lsa_TransNameArray2 names; + uint32_t count = sids->num_sids; + NTSTATUS status; + + printf("\nTesting LookupSids2\n"); + + names.count = 0; + names.names = NULL; + + r.in.handle = handle; + r.in.sids = sids; + r.in.names = &names; + r.in.level = 1; + r.in.count = &count; + r.in.unknown1 = 0; + r.in.unknown2 = 0; + r.out.count = &count; + r.out.names = &names; + + status = dcerpc_lsa_LookupSids2(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupSids2 failed - %s\n", nt_errstr(status)); + return false; + } + + printf("\n"); + + if (!test_LookupNames2(p, mem_ctx, handle, &names)) { + return false; + } + + if (!test_LookupNames3(p, mem_ctx, handle, &names)) { + return false; + } + + return true; +} + +static bool test_LookupSids3(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct lsa_SidArray *sids) +{ + struct lsa_LookupSids3 r; + struct lsa_TransNameArray2 names; + uint32_t count = sids->num_sids; + NTSTATUS status; + + printf("\nTesting LookupSids3\n"); + + names.count = 0; + names.names = NULL; + + r.in.sids = sids; + r.in.names = &names; + r.in.level = 1; + r.in.count = &count; + r.in.unknown1 = 0; + r.in.unknown2 = 0; + r.out.count = &count; + r.out.names = &names; + + status = dcerpc_lsa_LookupSids3(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) { + printf("not considering %s to be an error\n", nt_errstr(status)); + return true; + } + printf("LookupSids3 failed - %s - not considered an error\n", + nt_errstr(status)); + return false; + } + + printf("\n"); + + if (!test_LookupNames4(p, mem_ctx, &names)) { + return false; + } + + return true; +} + +bool test_many_LookupSids(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + uint32_t count; + NTSTATUS status; + struct lsa_SidArray sids; + int i; + + printf("\nTesting LookupSids with lots of SIDs\n"); + + sids.num_sids = 100; + + sids.sids = talloc_array(mem_ctx, struct lsa_SidPtr, sids.num_sids); + + for (i=0; i<sids.num_sids; i++) { + const char *sidstr = "S-1-5-32-545"; + sids.sids[i].sid = dom_sid_parse_talloc(mem_ctx, sidstr); + } + + count = sids.num_sids; + + if (handle) { + struct lsa_LookupSids r; + struct lsa_TransNameArray names; + names.count = 0; + names.names = NULL; + + r.in.handle = handle; + r.in.sids = &sids; + r.in.names = &names; + r.in.level = 1; + r.in.count = &names.count; + r.out.count = &count; + r.out.names = &names; + + status = dcerpc_lsa_LookupSids(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupSids failed - %s\n", nt_errstr(status)); + return false; + } + + printf("\n"); + + if (!test_LookupNames(p, mem_ctx, handle, &names)) { + return false; + } + } else if (p->conn->security_state.auth_info->auth_type == DCERPC_AUTH_TYPE_SCHANNEL && + p->conn->security_state.auth_info->auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) { + struct lsa_LookupSids3 r; + struct lsa_TransNameArray2 names; + + names.count = 0; + names.names = NULL; + + printf("\nTesting LookupSids3\n"); + + r.in.sids = &sids; + r.in.names = &names; + r.in.level = 1; + r.in.count = &count; + r.in.unknown1 = 0; + r.in.unknown2 = 0; + r.out.count = &count; + r.out.names = &names; + + status = dcerpc_lsa_LookupSids3(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) { + printf("not considering %s to be an error\n", nt_errstr(status)); + return true; + } + printf("LookupSids3 failed - %s\n", + nt_errstr(status)); + return false; + } + if (!test_LookupNames4(p, mem_ctx, &names)) { + return false; + } + } + + printf("\n"); + + + + return true; +} + +static void lookupsids_cb(struct rpc_request *req) +{ + int *replies = (int *)req->async.private_data; + NTSTATUS status; + + status = dcerpc_ndr_request_recv(req); + if (!NT_STATUS_IS_OK(status)) { + printf("lookupsids returned %s\n", nt_errstr(status)); + *replies = -1; + } + + if (*replies >= 0) { + *replies += 1; + } +} + +static bool test_LookupSids_async(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + struct lsa_SidArray sids; + struct lsa_SidPtr sidptr; + uint32_t *count; + struct lsa_TransNameArray *names; + struct lsa_LookupSids *r; + struct rpc_request **req; + int i, replies; + bool ret = true; + const int num_async_requests = 50; + + count = talloc_array(mem_ctx, uint32_t, num_async_requests); + names = talloc_array(mem_ctx, struct lsa_TransNameArray, num_async_requests); + r = talloc_array(mem_ctx, struct lsa_LookupSids, num_async_requests); + + printf("\nTesting %d async lookupsids request\n", num_async_requests); + + req = talloc_array(mem_ctx, struct rpc_request *, num_async_requests); + + sids.num_sids = 1; + sids.sids = &sidptr; + sidptr.sid = dom_sid_parse_talloc(mem_ctx, "S-1-5-32-545"); + + replies = 0; + + for (i=0; i<num_async_requests; i++) { + count[i] = 0; + names[i].count = 0; + names[i].names = NULL; + + r[i].in.handle = handle; + r[i].in.sids = &sids; + r[i].in.names = &names[i]; + r[i].in.level = 1; + r[i].in.count = &names[i].count; + r[i].out.count = &count[i]; + r[i].out.names = &names[i]; + + req[i] = dcerpc_lsa_LookupSids_send(p, req, &r[i]); + if (req[i] == NULL) { + ret = false; + break; + } + + req[i]->async.callback = lookupsids_cb; + req[i]->async.private_data = &replies; + } + + while (replies >= 0 && replies < num_async_requests) { + event_loop_once(p->conn->event_ctx); + } + + talloc_free(req); + + if (replies < 0) { + ret = false; + } + + return ret; +} + +static bool test_LookupPrivValue(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct lsa_String *name) +{ + NTSTATUS status; + struct lsa_LookupPrivValue r; + struct lsa_LUID luid; + + r.in.handle = handle; + r.in.name = name; + r.out.luid = &luid; + + status = dcerpc_lsa_LookupPrivValue(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("\nLookupPrivValue failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + +static bool test_LookupPrivName(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct lsa_LUID *luid) +{ + NTSTATUS status; + struct lsa_LookupPrivName r; + + r.in.handle = handle; + r.in.luid = luid; + + status = dcerpc_lsa_LookupPrivName(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("\nLookupPrivName failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + +static bool test_RemovePrivilegesFromAccount(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct policy_handle *acct_handle, + struct lsa_LUID *luid) +{ + NTSTATUS status; + struct lsa_RemovePrivilegesFromAccount r; + struct lsa_PrivilegeSet privs; + bool ret = true; + + printf("Testing RemovePrivilegesFromAccount\n"); + + r.in.handle = acct_handle; + r.in.remove_all = 0; + r.in.privs = &privs; + + privs.count = 1; + privs.unknown = 0; + privs.set = talloc_array(mem_ctx, struct lsa_LUIDAttribute, 1); + privs.set[0].luid = *luid; + privs.set[0].attribute = 0; + + status = dcerpc_lsa_RemovePrivilegesFromAccount(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + + struct lsa_LookupPrivName r_name; + + r_name.in.handle = handle; + r_name.in.luid = luid; + + status = dcerpc_lsa_LookupPrivName(p, mem_ctx, &r_name); + if (!NT_STATUS_IS_OK(status)) { + printf("\nLookupPrivName failed - %s\n", nt_errstr(status)); + return false; + } + /* Windows 2008 does not allow this to be removed */ + if (strcmp("SeAuditPrivilege", r_name.out.name->string) == 0) { + return ret; + } + + printf("RemovePrivilegesFromAccount failed to remove %s - %s\n", + r_name.out.name->string, + nt_errstr(status)); + return false; + } + + return ret; +} + +static bool test_AddPrivilegesToAccount(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *acct_handle, + struct lsa_LUID *luid) +{ + NTSTATUS status; + struct lsa_AddPrivilegesToAccount r; + struct lsa_PrivilegeSet privs; + bool ret = true; + + printf("Testing AddPrivilegesToAccount\n"); + + r.in.handle = acct_handle; + r.in.privs = &privs; + + privs.count = 1; + privs.unknown = 0; + privs.set = talloc_array(mem_ctx, struct lsa_LUIDAttribute, 1); + privs.set[0].luid = *luid; + privs.set[0].attribute = 0; + + status = dcerpc_lsa_AddPrivilegesToAccount(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("AddPrivilegesToAccount failed - %s\n", nt_errstr(status)); + return false; + } + + return ret; +} + +static bool test_EnumPrivsAccount(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct policy_handle *acct_handle) +{ + NTSTATUS status; + struct lsa_EnumPrivsAccount r; + bool ret = true; + + printf("Testing EnumPrivsAccount\n"); + + r.in.handle = acct_handle; + + status = dcerpc_lsa_EnumPrivsAccount(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("EnumPrivsAccount failed - %s\n", nt_errstr(status)); + return false; + } + + if (r.out.privs && r.out.privs->count > 0) { + int i; + for (i=0;i<r.out.privs->count;i++) { + test_LookupPrivName(p, mem_ctx, handle, + &r.out.privs->set[i].luid); + } + + ret &= test_RemovePrivilegesFromAccount(p, mem_ctx, handle, acct_handle, + &r.out.privs->set[0].luid); + ret &= test_AddPrivilegesToAccount(p, mem_ctx, acct_handle, + &r.out.privs->set[0].luid); + } + + return ret; +} + +static bool test_Delete(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct lsa_Delete r; + + printf("testing Delete\n"); + + r.in.handle = handle; + status = dcerpc_lsa_Delete(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + printf("Delete should have failed NT_STATUS_NOT_SUPPORTED - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + +static bool test_DeleteObject(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct lsa_DeleteObject r; + + printf("testing DeleteObject\n"); + + r.in.handle = handle; + r.out.handle = handle; + status = dcerpc_lsa_DeleteObject(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("Delete failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + + +static bool test_CreateAccount(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct lsa_CreateAccount r; + struct dom_sid2 *newsid; + struct policy_handle acct_handle; + + newsid = dom_sid_parse_talloc(mem_ctx, "S-1-5-12349876-4321-2854"); + + printf("Testing CreateAccount\n"); + + r.in.handle = handle; + r.in.sid = newsid; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.acct_handle = &acct_handle; + + status = dcerpc_lsa_CreateAccount(p, mem_ctx, &r); + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) { + struct lsa_OpenAccount r_o; + r_o.in.handle = handle; + r_o.in.sid = newsid; + r_o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r_o.out.acct_handle = &acct_handle; + + status = dcerpc_lsa_OpenAccount(p, mem_ctx, &r_o); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenAccount failed - %s\n", nt_errstr(status)); + return false; + } + } else if (!NT_STATUS_IS_OK(status)) { + printf("CreateAccount failed - %s\n", nt_errstr(status)); + return false; + } + + if (!test_Delete(p, mem_ctx, &acct_handle)) { + return false; + } + + if (!test_DeleteObject(p, mem_ctx, &acct_handle)) { + return false; + } + + return true; +} + +static bool test_DeleteTrustedDomain(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct lsa_StringLarge name) +{ + NTSTATUS status; + struct lsa_OpenTrustedDomainByName r; + struct policy_handle trustdom_handle; + + r.in.handle = handle; + r.in.name.string = name.string; + r.in.access_mask = SEC_STD_DELETE; + r.out.trustdom_handle = &trustdom_handle; + + status = dcerpc_lsa_OpenTrustedDomainByName(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("lsa_OpenTrustedDomainByName failed - %s\n", nt_errstr(status)); + return false; + } + + if (!test_Delete(p, mem_ctx, &trustdom_handle)) { + return false; + } + + if (!test_DeleteObject(p, mem_ctx, &trustdom_handle)) { + return false; + } + + return true; +} + +static bool test_DeleteTrustedDomainBySid(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct dom_sid *sid) +{ + NTSTATUS status; + struct lsa_DeleteTrustedDomain r; + + r.in.handle = handle; + r.in.dom_sid = sid; + + status = dcerpc_lsa_DeleteTrustedDomain(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("lsa_DeleteTrustedDomain failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + + +static bool test_CreateSecret(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct lsa_CreateSecret r; + struct lsa_OpenSecret r2; + struct lsa_SetSecret r3; + struct lsa_QuerySecret r4; + struct lsa_SetSecret r5; + struct lsa_QuerySecret r6; + struct lsa_SetSecret r7; + struct lsa_QuerySecret r8; + struct policy_handle sec_handle, sec_handle2, sec_handle3; + struct lsa_DeleteObject d_o; + struct lsa_DATA_BUF buf1; + struct lsa_DATA_BUF_PTR bufp1; + struct lsa_DATA_BUF_PTR bufp2; + DATA_BLOB enc_key; + bool ret = true; + DATA_BLOB session_key; + NTTIME old_mtime, new_mtime; + DATA_BLOB blob1, blob2; + const char *secret1 = "abcdef12345699qwerty"; + char *secret2; + const char *secret3 = "ABCDEF12345699QWERTY"; + char *secret4; + const char *secret5 = "NEW-SAMBA4-SECRET"; + char *secret6; + char *secname[2]; + int i; + const int LOCAL = 0; + const int GLOBAL = 1; + + secname[LOCAL] = talloc_asprintf(mem_ctx, "torturesecret-%u", (uint_t)random()); + secname[GLOBAL] = talloc_asprintf(mem_ctx, "G$torturesecret-%u", (uint_t)random()); + + for (i=0; i< 2; i++) { + printf("Testing CreateSecret of %s\n", secname[i]); + + init_lsa_String(&r.in.name, secname[i]); + + r.in.handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.sec_handle = &sec_handle; + + status = dcerpc_lsa_CreateSecret(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("CreateSecret failed - %s\n", nt_errstr(status)); + return false; + } + + r.in.handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.sec_handle = &sec_handle3; + + status = dcerpc_lsa_CreateSecret(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) { + printf("CreateSecret should have failed OBJECT_NAME_COLLISION - %s\n", nt_errstr(status)); + return false; + } + + r2.in.handle = handle; + r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r2.in.name = r.in.name; + r2.out.sec_handle = &sec_handle2; + + printf("Testing OpenSecret\n"); + + status = dcerpc_lsa_OpenSecret(p, mem_ctx, &r2); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenSecret failed - %s\n", nt_errstr(status)); + return false; + } + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + printf("dcerpc_fetch_session_key failed - %s\n", nt_errstr(status)); + return false; + } + + enc_key = sess_encrypt_string(secret1, &session_key); + + r3.in.sec_handle = &sec_handle; + r3.in.new_val = &buf1; + r3.in.old_val = NULL; + r3.in.new_val->data = enc_key.data; + r3.in.new_val->length = enc_key.length; + r3.in.new_val->size = enc_key.length; + + printf("Testing SetSecret\n"); + + status = dcerpc_lsa_SetSecret(p, mem_ctx, &r3); + if (!NT_STATUS_IS_OK(status)) { + printf("SetSecret failed - %s\n", nt_errstr(status)); + return false; + } + + r3.in.sec_handle = &sec_handle; + r3.in.new_val = &buf1; + r3.in.old_val = NULL; + r3.in.new_val->data = enc_key.data; + r3.in.new_val->length = enc_key.length; + r3.in.new_val->size = enc_key.length; + + /* break the encrypted data */ + enc_key.data[0]++; + + printf("Testing SetSecret with broken key\n"); + + status = dcerpc_lsa_SetSecret(p, mem_ctx, &r3); + if (!NT_STATUS_EQUAL(status, NT_STATUS_UNKNOWN_REVISION)) { + printf("SetSecret should have failed UNKNOWN_REVISION - %s\n", nt_errstr(status)); + ret = false; + } + + data_blob_free(&enc_key); + + ZERO_STRUCT(new_mtime); + ZERO_STRUCT(old_mtime); + + /* fetch the secret back again */ + r4.in.sec_handle = &sec_handle; + r4.in.new_val = &bufp1; + r4.in.new_mtime = &new_mtime; + r4.in.old_val = NULL; + r4.in.old_mtime = NULL; + + bufp1.buf = NULL; + + printf("Testing QuerySecret\n"); + status = dcerpc_lsa_QuerySecret(p, mem_ctx, &r4); + if (!NT_STATUS_IS_OK(status)) { + printf("QuerySecret failed - %s\n", nt_errstr(status)); + ret = false; + } else { + if (r4.out.new_val == NULL || r4.out.new_val->buf == NULL) { + printf("No secret buffer returned\n"); + ret = false; + } else { + blob1.data = r4.out.new_val->buf->data; + blob1.length = r4.out.new_val->buf->size; + + blob2 = data_blob_talloc(mem_ctx, NULL, blob1.length); + + secret2 = sess_decrypt_string(mem_ctx, + &blob1, &session_key); + + if (strcmp(secret1, secret2) != 0) { + printf("Returned secret (r4) '%s' doesn't match '%s'\n", + secret2, secret1); + ret = false; + } + } + } + + enc_key = sess_encrypt_string(secret3, &session_key); + + r5.in.sec_handle = &sec_handle; + r5.in.new_val = &buf1; + r5.in.old_val = NULL; + r5.in.new_val->data = enc_key.data; + r5.in.new_val->length = enc_key.length; + r5.in.new_val->size = enc_key.length; + + + msleep(200); + printf("Testing SetSecret (existing value should move to old)\n"); + + status = dcerpc_lsa_SetSecret(p, mem_ctx, &r5); + if (!NT_STATUS_IS_OK(status)) { + printf("SetSecret failed - %s\n", nt_errstr(status)); + ret = false; + } + + data_blob_free(&enc_key); + + ZERO_STRUCT(new_mtime); + ZERO_STRUCT(old_mtime); + + /* fetch the secret back again */ + r6.in.sec_handle = &sec_handle; + r6.in.new_val = &bufp1; + r6.in.new_mtime = &new_mtime; + r6.in.old_val = &bufp2; + r6.in.old_mtime = &old_mtime; + + bufp1.buf = NULL; + bufp2.buf = NULL; + + status = dcerpc_lsa_QuerySecret(p, mem_ctx, &r6); + if (!NT_STATUS_IS_OK(status)) { + printf("QuerySecret failed - %s\n", nt_errstr(status)); + ret = false; + secret4 = NULL; + } else { + + if (r6.out.new_val->buf == NULL || r6.out.old_val->buf == NULL + || r6.out.new_mtime == NULL || r6.out.old_mtime == NULL) { + printf("Both secret buffers and both times not returned\n"); + ret = false; + secret4 = NULL; + } else { + blob1.data = r6.out.new_val->buf->data; + blob1.length = r6.out.new_val->buf->size; + + blob2 = data_blob_talloc(mem_ctx, NULL, blob1.length); + + secret4 = sess_decrypt_string(mem_ctx, + &blob1, &session_key); + + if (strcmp(secret3, secret4) != 0) { + printf("Returned NEW secret %s doesn't match %s\n", secret4, secret3); + ret = false; + } + + blob1.data = r6.out.old_val->buf->data; + blob1.length = r6.out.old_val->buf->length; + + blob2 = data_blob_talloc(mem_ctx, NULL, blob1.length); + + secret2 = sess_decrypt_string(mem_ctx, + &blob1, &session_key); + + if (strcmp(secret1, secret2) != 0) { + printf("Returned OLD secret %s doesn't match %s\n", secret2, secret1); + ret = false; + } + + if (*r6.out.new_mtime == *r6.out.old_mtime) { + printf("Returned secret (r6-%d) %s must not have same mtime for both secrets: %s != %s\n", + i, + secname[i], + nt_time_string(mem_ctx, *r6.out.old_mtime), + nt_time_string(mem_ctx, *r6.out.new_mtime)); + ret = false; + } + } + } + + enc_key = sess_encrypt_string(secret5, &session_key); + + r7.in.sec_handle = &sec_handle; + r7.in.old_val = &buf1; + r7.in.old_val->data = enc_key.data; + r7.in.old_val->length = enc_key.length; + r7.in.old_val->size = enc_key.length; + r7.in.new_val = NULL; + + printf("Testing SetSecret of old Secret only\n"); + + status = dcerpc_lsa_SetSecret(p, mem_ctx, &r7); + if (!NT_STATUS_IS_OK(status)) { + printf("SetSecret failed - %s\n", nt_errstr(status)); + ret = false; + } + + data_blob_free(&enc_key); + + /* fetch the secret back again */ + r8.in.sec_handle = &sec_handle; + r8.in.new_val = &bufp1; + r8.in.new_mtime = &new_mtime; + r8.in.old_val = &bufp2; + r8.in.old_mtime = &old_mtime; + + bufp1.buf = NULL; + bufp2.buf = NULL; + + status = dcerpc_lsa_QuerySecret(p, mem_ctx, &r8); + if (!NT_STATUS_IS_OK(status)) { + printf("QuerySecret failed - %s\n", nt_errstr(status)); + ret = false; + } else { + if (!r8.out.new_val || !r8.out.old_val) { + printf("in/out pointers not returned, despite being set on in for QuerySecret\n"); + ret = false; + } else if (r8.out.new_val->buf != NULL) { + printf("NEW secret buffer must not be returned after OLD set\n"); + ret = false; + } else if (r8.out.old_val->buf == NULL) { + printf("OLD secret buffer was not returned after OLD set\n"); + ret = false; + } else if (r8.out.new_mtime == NULL || r8.out.old_mtime == NULL) { + printf("Both times not returned after OLD set\n"); + ret = false; + } else { + blob1.data = r8.out.old_val->buf->data; + blob1.length = r8.out.old_val->buf->size; + + blob2 = data_blob_talloc(mem_ctx, NULL, blob1.length); + + secret6 = sess_decrypt_string(mem_ctx, + &blob1, &session_key); + + if (strcmp(secret5, secret6) != 0) { + printf("Returned OLD secret %s doesn't match %s\n", secret5, secret6); + ret = false; + } + + if (*r8.out.new_mtime != *r8.out.old_mtime) { + printf("Returned secret (r8) %s did not had same mtime for both secrets: %s != %s\n", + secname[i], + nt_time_string(mem_ctx, *r8.out.old_mtime), + nt_time_string(mem_ctx, *r8.out.new_mtime)); + ret = false; + } + } + } + + if (!test_Delete(p, mem_ctx, &sec_handle)) { + ret = false; + } + + if (!test_DeleteObject(p, mem_ctx, &sec_handle)) { + return false; + } + + d_o.in.handle = &sec_handle2; + d_o.out.handle = &sec_handle2; + status = dcerpc_lsa_DeleteObject(p, mem_ctx, &d_o); + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) { + printf("Second delete expected INVALID_HANDLE - %s\n", nt_errstr(status)); + ret = false; + } else { + + printf("Testing OpenSecret of just-deleted secret\n"); + + status = dcerpc_lsa_OpenSecret(p, mem_ctx, &r2); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + printf("OpenSecret expected OBJECT_NAME_NOT_FOUND - %s\n", nt_errstr(status)); + ret = false; + } + } + + } + + return ret; +} + + +static bool test_EnumAccountRights(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *acct_handle, + struct dom_sid *sid) +{ + NTSTATUS status; + struct lsa_EnumAccountRights r; + struct lsa_RightSet rights; + + printf("Testing EnumAccountRights\n"); + + r.in.handle = acct_handle; + r.in.sid = sid; + r.out.rights = &rights; + + status = dcerpc_lsa_EnumAccountRights(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("EnumAccountRights of %s failed - %s\n", + dom_sid_string(mem_ctx, sid), nt_errstr(status)); + return false; + } + + return true; +} + + +static bool test_QuerySecurity(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + struct policy_handle *acct_handle) +{ + NTSTATUS status; + struct lsa_QuerySecurity r; + + if (torture_setting_bool(tctx, "samba4", false)) { + printf("skipping QuerySecurity test against Samba4\n"); + return true; + } + + printf("Testing QuerySecurity\n"); + + r.in.handle = acct_handle; + r.in.sec_info = 7; + + status = dcerpc_lsa_QuerySecurity(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("QuerySecurity failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + +static bool test_OpenAccount(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct dom_sid *sid) +{ + NTSTATUS status; + struct lsa_OpenAccount r; + struct policy_handle acct_handle; + + printf("Testing OpenAccount\n"); + + r.in.handle = handle; + r.in.sid = sid; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.acct_handle = &acct_handle; + + status = dcerpc_lsa_OpenAccount(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenAccount failed - %s\n", nt_errstr(status)); + return false; + } + + if (!test_EnumPrivsAccount(p, mem_ctx, handle, &acct_handle)) { + return false; + } + + if (!test_QuerySecurity(p, mem_ctx, handle, &acct_handle)) { + return false; + } + + return true; +} + +static bool test_EnumAccounts(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct lsa_EnumAccounts r; + struct lsa_SidArray sids1, sids2; + uint32_t resume_handle = 0; + int i; + bool ret = true; + + printf("\ntesting EnumAccounts\n"); + + r.in.handle = handle; + r.in.resume_handle = &resume_handle; + r.in.num_entries = 100; + r.out.resume_handle = &resume_handle; + r.out.sids = &sids1; + + resume_handle = 0; + while (true) { + status = dcerpc_lsa_EnumAccounts(p, mem_ctx, &r); + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) { + break; + } + if (!NT_STATUS_IS_OK(status)) { + printf("EnumAccounts failed - %s\n", nt_errstr(status)); + return false; + } + + if (!test_LookupSids(p, mem_ctx, handle, &sids1)) { + return false; + } + + if (!test_LookupSids2(p, mem_ctx, handle, &sids1)) { + return false; + } + + /* Can't test lookupSids3 here, as clearly we must not + * be on schannel, or we would not be able to do the + * rest */ + + printf("testing all accounts\n"); + for (i=0;i<sids1.num_sids;i++) { + ret &= test_OpenAccount(p, mem_ctx, handle, sids1.sids[i].sid); + ret &= test_EnumAccountRights(p, mem_ctx, handle, sids1.sids[i].sid); + } + printf("\n"); + } + + if (sids1.num_sids < 3) { + return ret; + } + + printf("trying EnumAccounts partial listing (asking for 1 at 2)\n"); + resume_handle = 2; + r.in.num_entries = 1; + r.out.sids = &sids2; + + status = dcerpc_lsa_EnumAccounts(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("EnumAccounts failed - %s\n", nt_errstr(status)); + return false; + } + + if (sids2.num_sids != 1) { + printf("Returned wrong number of entries (%d)\n", sids2.num_sids); + return false; + } + + return true; +} + +static bool test_LookupPrivDisplayName(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct lsa_String *priv_name) +{ + struct lsa_LookupPrivDisplayName r; + NTSTATUS status; + /* produce a reasonable range of language output without screwing up + terminals */ + uint16_t language_id = (random() % 4) + 0x409; + + printf("testing LookupPrivDisplayName(%s)\n", priv_name->string); + + r.in.handle = handle; + r.in.name = priv_name; + r.in.language_id = &language_id; + r.out.language_id = &language_id; + r.in.unknown = 0; + + status = dcerpc_lsa_LookupPrivDisplayName(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupPrivDisplayName failed - %s\n", nt_errstr(status)); + return false; + } + printf("%s -> \"%s\" (language 0x%x/0x%x)\n", + priv_name->string, r.out.disp_name->string, + *r.in.language_id, *r.out.language_id); + + return true; +} + +static bool test_EnumAccountsWithUserRight(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct lsa_String *priv_name) +{ + struct lsa_EnumAccountsWithUserRight r; + struct lsa_SidArray sids; + NTSTATUS status; + + ZERO_STRUCT(sids); + + printf("testing EnumAccountsWithUserRight(%s)\n", priv_name->string); + + r.in.handle = handle; + r.in.name = priv_name; + r.out.sids = &sids; + + status = dcerpc_lsa_EnumAccountsWithUserRight(p, mem_ctx, &r); + + /* NT_STATUS_NO_MORE_ENTRIES means noone has this privilege */ + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) { + return true; + } + + if (!NT_STATUS_IS_OK(status)) { + printf("EnumAccountsWithUserRight failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + + +static bool test_EnumPrivs(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct lsa_EnumPrivs r; + struct lsa_PrivArray privs1; + uint32_t resume_handle = 0; + int i; + bool ret = true; + + printf("\ntesting EnumPrivs\n"); + + r.in.handle = handle; + r.in.resume_handle = &resume_handle; + r.in.max_count = 100; + r.out.resume_handle = &resume_handle; + r.out.privs = &privs1; + + resume_handle = 0; + status = dcerpc_lsa_EnumPrivs(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("EnumPrivs failed - %s\n", nt_errstr(status)); + return false; + } + + for (i = 0; i< privs1.count; i++) { + test_LookupPrivDisplayName(p, mem_ctx, handle, (struct lsa_String *)&privs1.privs[i].name); + test_LookupPrivValue(p, mem_ctx, handle, (struct lsa_String *)&privs1.privs[i].name); + if (!test_EnumAccountsWithUserRight(p, mem_ctx, handle, (struct lsa_String *)&privs1.privs[i].name)) { + ret = false; + } + } + + return ret; +} + +static bool test_QueryForestTrustInformation(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *trusted_domain_name) +{ + bool ret = true; + struct lsa_lsaRQueryForestTrustInformation r; + NTSTATUS status; + struct lsa_String string; + struct lsa_ForestTrustInformation info, *info_ptr; + + printf("\nTesting lsaRQueryForestTrustInformation\n"); + + if (torture_setting_bool(tctx, "samba4", false)) { + printf("skipping QueryForestTrustInformation against Samba4\n"); + return true; + } + + ZERO_STRUCT(string); + + if (trusted_domain_name) { + init_lsa_String(&string, trusted_domain_name); + } + + info_ptr = &info; + + r.in.handle = handle; + r.in.trusted_domain_name = &string; + r.in.unknown = 0; + r.out.forest_trust_info = &info_ptr; + + status = dcerpc_lsa_lsaRQueryForestTrustInformation(p, tctx, &r); + + if (!NT_STATUS_IS_OK(status)) { + printf("lsaRQueryForestTrustInformation failed - %s\n", nt_errstr(status)); + ret = false; + } + + return ret; +} + +static bool test_query_each_TrustDomEx(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct lsa_DomainListEx *domains) +{ + int i; + bool ret = true; + + for (i=0; i< domains->count; i++) { + + if (domains->domains[i].trust_attributes & NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) { + ret &= test_QueryForestTrustInformation(p, mem_ctx, handle, + domains->domains[i].domain_name.string); + } + } + + return ret; +} + +static bool test_query_each_TrustDom(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + struct lsa_DomainList *domains) +{ + NTSTATUS status; + int i,j; + bool ret = true; + + printf("\nTesting OpenTrustedDomain, OpenTrustedDomainByName and QueryInfoTrustedDomain\n"); + for (i=0; i< domains->count; i++) { + struct lsa_OpenTrustedDomain trust; + struct lsa_OpenTrustedDomainByName trust_by_name; + struct policy_handle trustdom_handle; + struct policy_handle handle2; + struct lsa_Close c; + struct lsa_CloseTrustedDomainEx c_trust; + int levels [] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; + int ok[] = {1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1}; + + if (domains->domains[i].sid) { + trust.in.handle = handle; + trust.in.sid = domains->domains[i].sid; + trust.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + trust.out.trustdom_handle = &trustdom_handle; + + status = dcerpc_lsa_OpenTrustedDomain(p, mem_ctx, &trust); + + if (!NT_STATUS_IS_OK(status)) { + printf("OpenTrustedDomain failed - %s\n", nt_errstr(status)); + return false; + } + + c.in.handle = &trustdom_handle; + c.out.handle = &handle2; + + c_trust.in.handle = &trustdom_handle; + c_trust.out.handle = &handle2; + + for (j=0; j < ARRAY_SIZE(levels); j++) { + struct lsa_QueryTrustedDomainInfo q; + union lsa_TrustedDomainInfo info; + q.in.trustdom_handle = &trustdom_handle; + q.in.level = levels[j]; + q.out.info = &info; + status = dcerpc_lsa_QueryTrustedDomainInfo(p, mem_ctx, &q); + if (!NT_STATUS_IS_OK(status) && ok[j]) { + printf("QueryTrustedDomainInfo level %d failed - %s\n", + levels[j], nt_errstr(status)); + ret = false; + } else if (NT_STATUS_IS_OK(status) && !ok[j]) { + printf("QueryTrustedDomainInfo level %d unexpectedly succeeded - %s\n", + levels[j], nt_errstr(status)); + ret = false; + } + } + + status = dcerpc_lsa_CloseTrustedDomainEx(p, mem_ctx, &c_trust); + if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) { + printf("Expected CloseTrustedDomainEx to return NT_STATUS_NOT_IMPLEMENTED, instead - %s\n", nt_errstr(status)); + return false; + } + + c.in.handle = &trustdom_handle; + c.out.handle = &handle2; + + status = dcerpc_lsa_Close(p, mem_ctx, &c); + if (!NT_STATUS_IS_OK(status)) { + printf("Close of trusted domain failed - %s\n", nt_errstr(status)); + return false; + } + + for (j=0; j < ARRAY_SIZE(levels); j++) { + struct lsa_QueryTrustedDomainInfoBySid q; + union lsa_TrustedDomainInfo info; + + if (!domains->domains[i].sid) { + continue; + } + + q.in.handle = handle; + q.in.dom_sid = domains->domains[i].sid; + q.in.level = levels[j]; + q.out.info = &info; + status = dcerpc_lsa_QueryTrustedDomainInfoBySid(p, mem_ctx, &q); + if (!NT_STATUS_IS_OK(status) && ok[j]) { + printf("QueryTrustedDomainInfoBySid level %d failed - %s\n", + levels[j], nt_errstr(status)); + ret = false; + } else if (NT_STATUS_IS_OK(status) && !ok[j]) { + printf("QueryTrustedDomainInfoBySid level %d unexpectedly succeeded - %s\n", + levels[j], nt_errstr(status)); + ret = false; + } + } + } + + trust_by_name.in.handle = handle; + trust_by_name.in.name.string = domains->domains[i].name.string; + trust_by_name.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + trust_by_name.out.trustdom_handle = &trustdom_handle; + + status = dcerpc_lsa_OpenTrustedDomainByName(p, mem_ctx, &trust_by_name); + + if (!NT_STATUS_IS_OK(status)) { + printf("OpenTrustedDomainByName failed - %s\n", nt_errstr(status)); + return false; + } + + for (j=0; j < ARRAY_SIZE(levels); j++) { + struct lsa_QueryTrustedDomainInfo q; + union lsa_TrustedDomainInfo info; + q.in.trustdom_handle = &trustdom_handle; + q.in.level = levels[j]; + q.out.info = &info; + status = dcerpc_lsa_QueryTrustedDomainInfo(p, mem_ctx, &q); + if (!NT_STATUS_IS_OK(status) && ok[j]) { + printf("QueryTrustedDomainInfo level %d failed - %s\n", + levels[j], nt_errstr(status)); + ret = false; + } else if (NT_STATUS_IS_OK(status) && !ok[j]) { + printf("QueryTrustedDomainInfo level %d unexpectedly succeeded - %s\n", + levels[j], nt_errstr(status)); + ret = false; + } + } + + c.in.handle = &trustdom_handle; + c.out.handle = &handle2; + + status = dcerpc_lsa_Close(p, mem_ctx, &c); + if (!NT_STATUS_IS_OK(status)) { + printf("Close of trusted domain failed - %s\n", nt_errstr(status)); + return false; + } + + for (j=0; j < ARRAY_SIZE(levels); j++) { + struct lsa_QueryTrustedDomainInfoByName q; + union lsa_TrustedDomainInfo info; + q.in.handle = handle; + q.in.trusted_domain.string = domains->domains[i].name.string; + q.in.level = levels[j]; + q.out.info = &info; + status = dcerpc_lsa_QueryTrustedDomainInfoByName(p, mem_ctx, &q); + if (!NT_STATUS_IS_OK(status) && ok[j]) { + printf("QueryTrustedDomainInfoByName level %d failed - %s\n", + levels[j], nt_errstr(status)); + ret = false; + } else if (NT_STATUS_IS_OK(status) && !ok[j]) { + printf("QueryTrustedDomainInfoByName level %d unexpectedly succeeded - %s\n", + levels[j], nt_errstr(status)); + ret = false; + } + } + } + return ret; +} + +static bool test_EnumTrustDom(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + struct lsa_EnumTrustDom r; + struct lsa_EnumTrustedDomainsEx r_ex; + NTSTATUS enum_status; + uint32_t resume_handle = 0; + struct lsa_DomainList domains; + struct lsa_DomainListEx domains_ex; + bool ret = true; + + printf("\nTesting EnumTrustDom\n"); + + r.in.handle = handle; + r.in.resume_handle = &resume_handle; + r.in.max_size = 0; + r.out.domains = &domains; + r.out.resume_handle = &resume_handle; + + enum_status = dcerpc_lsa_EnumTrustDom(p, mem_ctx, &r); + + if (NT_STATUS_IS_OK(enum_status)) { + if (domains.count == 0) { + printf("EnumTrustDom failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n"); + return false; + } + } else if (!(NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES) || NT_STATUS_EQUAL(enum_status, NT_STATUS_NO_MORE_ENTRIES))) { + printf("EnumTrustDom of zero size failed - %s\n", nt_errstr(enum_status)); + return false; + } + + /* Start from the bottom again */ + resume_handle = 0; + + do { + r.in.handle = handle; + r.in.resume_handle = &resume_handle; + r.in.max_size = LSA_ENUM_TRUST_DOMAIN_MULTIPLIER * 3; + r.out.domains = &domains; + r.out.resume_handle = &resume_handle; + + enum_status = dcerpc_lsa_EnumTrustDom(p, mem_ctx, &r); + + /* NO_MORE_ENTRIES is allowed */ + if (NT_STATUS_EQUAL(enum_status, NT_STATUS_NO_MORE_ENTRIES)) { + return true; + } else if (NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES)) { + /* Windows 2003 gets this off by one on the first run */ + if (r.out.domains->count < 3 || r.out.domains->count > 4) { + printf("EnumTrustDom didn't fill the buffer we " + "asked it to (got %d, expected %d / %d == %d entries)\n", + r.out.domains->count, LSA_ENUM_TRUST_DOMAIN_MULTIPLIER * 3, + LSA_ENUM_TRUST_DOMAIN_MULTIPLIER, r.in.max_size); + ret = false; + } + } else if (!NT_STATUS_IS_OK(enum_status)) { + printf("EnumTrustDom failed - %s\n", nt_errstr(enum_status)); + return false; + } + + if (domains.count == 0) { + printf("EnumTrustDom failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n"); + return false; + } + + ret &= test_query_each_TrustDom(p, mem_ctx, handle, &domains); + + } while ((NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES))); + + printf("\nTesting EnumTrustedDomainsEx\n"); + + r_ex.in.handle = handle; + r_ex.in.resume_handle = &resume_handle; + r_ex.in.max_size = LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER * 3; + r_ex.out.domains = &domains_ex; + r_ex.out.resume_handle = &resume_handle; + + enum_status = dcerpc_lsa_EnumTrustedDomainsEx(p, mem_ctx, &r_ex); + + if (!(NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES) || NT_STATUS_EQUAL(enum_status, NT_STATUS_NO_MORE_ENTRIES))) { + printf("EnumTrustedDomainEx of zero size failed - %s\n", nt_errstr(enum_status)); + return false; + } + + resume_handle = 0; + do { + r_ex.in.handle = handle; + r_ex.in.resume_handle = &resume_handle; + r_ex.in.max_size = LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER * 3; + r_ex.out.domains = &domains_ex; + r_ex.out.resume_handle = &resume_handle; + + enum_status = dcerpc_lsa_EnumTrustedDomainsEx(p, mem_ctx, &r_ex); + + /* NO_MORE_ENTRIES is allowed */ + if (NT_STATUS_EQUAL(enum_status, NT_STATUS_NO_MORE_ENTRIES)) { + return true; + } else if (NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES)) { + /* Windows 2003 gets this off by one on the first run */ + if (r_ex.out.domains->count < 3 || r_ex.out.domains->count > 4) { + printf("EnumTrustDom didn't fill the buffer we " + "asked it to (got %d, expected %d / %d == %d entries)\n", + r_ex.out.domains->count, + r_ex.in.max_size, + LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER, + r_ex.in.max_size / LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER); + } + } else if (!NT_STATUS_IS_OK(enum_status)) { + printf("EnumTrustedDomainEx failed - %s\n", nt_errstr(enum_status)); + return false; + } + + if (domains_ex.count == 0) { + printf("EnumTrustDomainEx failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n"); + return false; + } + + ret &= test_query_each_TrustDomEx(p, mem_ctx, handle, &domains_ex); + + } while ((NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES))); + + return ret; +} + +static bool test_CreateTrustedDomain(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + bool ret = true; + struct lsa_CreateTrustedDomain r; + struct lsa_DomainInfo trustinfo; + struct dom_sid *domsid[12]; + struct policy_handle trustdom_handle[12]; + struct lsa_QueryTrustedDomainInfo q; + int i; + + printf("Testing CreateTrustedDomain for 12 domains\n"); + + if (!test_EnumTrustDom(p, mem_ctx, handle)) { + ret = false; + } + + for (i=0; i< 12; i++) { + char *trust_name = talloc_asprintf(mem_ctx, "torturedom%02d", i); + char *trust_sid = talloc_asprintf(mem_ctx, "S-1-5-21-97398-379795-100%02d", i); + + domsid[i] = dom_sid_parse_talloc(mem_ctx, trust_sid); + + trustinfo.sid = domsid[i]; + init_lsa_String((struct lsa_String *)&trustinfo.name, trust_name); + + r.in.policy_handle = handle; + r.in.info = &trustinfo; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.trustdom_handle = &trustdom_handle[i]; + + status = dcerpc_lsa_CreateTrustedDomain(p, mem_ctx, &r); + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) { + test_DeleteTrustedDomain(p, mem_ctx, handle, trustinfo.name); + status = dcerpc_lsa_CreateTrustedDomain(p, mem_ctx, &r); + } + if (!NT_STATUS_IS_OK(status)) { + printf("CreateTrustedDomain failed - %s\n", nt_errstr(status)); + ret = false; + } else { + + q.in.trustdom_handle = &trustdom_handle[i]; + q.in.level = LSA_TRUSTED_DOMAIN_INFO_INFO_EX; + status = dcerpc_lsa_QueryTrustedDomainInfo(p, mem_ctx, &q); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryTrustedDomainInfo level 1 failed - %s\n", nt_errstr(status)); + ret = false; + } else if (!q.out.info) { + ret = false; + } else { + if (strcmp(q.out.info->info_ex.netbios_name.string, trustinfo.name.string) != 0) { + printf("QueryTrustedDomainInfo returned inconsistant short name: %s != %s\n", + q.out.info->info_ex.netbios_name.string, trustinfo.name.string); + ret = false; + } + if (q.out.info->info_ex.trust_type != LSA_TRUST_TYPE_DOWNLEVEL) { + printf("QueryTrustedDomainInfo of %s returned incorrect trust type %d != %d\n", + trust_name, q.out.info->info_ex.trust_type, LSA_TRUST_TYPE_DOWNLEVEL); + ret = false; + } + if (q.out.info->info_ex.trust_attributes != 0) { + printf("QueryTrustedDomainInfo of %s returned incorrect trust attributes %d != %d\n", + trust_name, q.out.info->info_ex.trust_attributes, 0); + ret = false; + } + if (q.out.info->info_ex.trust_direction != LSA_TRUST_DIRECTION_OUTBOUND) { + printf("QueryTrustedDomainInfo of %s returned incorrect trust direction %d != %d\n", + trust_name, q.out.info->info_ex.trust_direction, LSA_TRUST_DIRECTION_OUTBOUND); + ret = false; + } + } + } + } + + /* now that we have some domains to look over, we can test the enum calls */ + if (!test_EnumTrustDom(p, mem_ctx, handle)) { + ret = false; + } + + for (i=0; i<12; i++) { + if (!test_DeleteTrustedDomainBySid(p, mem_ctx, handle, domsid[i])) { + ret = false; + } + } + + return ret; +} + +static bool test_CreateTrustedDomainEx2(struct dcerpc_pipe *p, + struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + bool ret = true; + struct lsa_CreateTrustedDomainEx2 r; + struct lsa_TrustDomainInfoInfoEx trustinfo; + struct lsa_TrustDomainInfoAuthInfoInternal authinfo; + struct trustAuthInAndOutBlob auth_struct; + DATA_BLOB auth_blob; + struct dom_sid *domsid[12]; + struct policy_handle trustdom_handle[12]; + struct lsa_QueryTrustedDomainInfo q; + DATA_BLOB session_key; + enum ndr_err_code ndr_err; + int i; + + printf("Testing CreateTrustedDomainEx2 for 12 domains\n"); + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + printf("dcerpc_fetch_session_key failed - %s\n", nt_errstr(status)); + return false; + } + + for (i=0; i< 12; i++) { + char *trust_name = talloc_asprintf(mem_ctx, "torturedom%02d", i); + char *trust_name_dns = talloc_asprintf(mem_ctx, "torturedom%02d.samba.example.com", i); + char *trust_sid = talloc_asprintf(mem_ctx, "S-1-5-21-97398-379795-100%02d", i); + + domsid[i] = dom_sid_parse_talloc(mem_ctx, trust_sid); + + trustinfo.sid = domsid[i]; + trustinfo.netbios_name.string = trust_name; + trustinfo.domain_name.string = trust_name_dns; + + /* Create inbound, some outbound, and some + * bi-directional trusts in a repeating pattern based + * on i */ + + /* 1 == inbound, 2 == outbound, 3 == both */ + trustinfo.trust_direction = (i % 3) + 1; + + /* Try different trust types too */ + + /* 1 == downleven (NT4), 2 == uplevel (ADS), 3 == MIT (kerberos but not AD) */ + trustinfo.trust_type = (((i / 3) + 1) % 3) + 1; + + trustinfo.trust_attributes = LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION; + + generate_random_buffer(auth_struct.confounder, sizeof(auth_struct.confounder)); + + auth_struct.outgoing.count = 0; + auth_struct.incoming.count = 0; + + ndr_err = ndr_push_struct_blob(&auth_blob, mem_ctx, lp_iconv_convenience(tctx->lp_ctx), &auth_struct, + (ndr_push_flags_fn_t)ndr_push_trustAuthInAndOutBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + printf("ndr_push_struct_blob of trustAuthInAndOutBlob structure failed"); + ret = false; + } + + arcfour_crypt_blob(auth_blob.data, auth_blob.length, &session_key); + + authinfo.auth_blob.size = auth_blob.length; + authinfo.auth_blob.data = auth_blob.data; + + r.in.policy_handle = handle; + r.in.info = &trustinfo; + r.in.auth_info = &authinfo; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.trustdom_handle = &trustdom_handle[i]; + + status = dcerpc_lsa_CreateTrustedDomainEx2(p, mem_ctx, &r); + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) { + test_DeleteTrustedDomain(p, mem_ctx, handle, trustinfo.netbios_name); + status = dcerpc_lsa_CreateTrustedDomainEx2(p, mem_ctx, &r); + } + if (!NT_STATUS_IS_OK(status)) { + printf("CreateTrustedDomainEx failed2 - %s\n", nt_errstr(status)); + ret = false; + } else { + + q.in.trustdom_handle = &trustdom_handle[i]; + q.in.level = LSA_TRUSTED_DOMAIN_INFO_INFO_EX; + status = dcerpc_lsa_QueryTrustedDomainInfo(p, mem_ctx, &q); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryTrustedDomainInfo level 1 failed - %s\n", nt_errstr(status)); + ret = false; + } else if (!q.out.info) { + ret = false; + } else { + if (strcmp(q.out.info->info_ex.netbios_name.string, trustinfo.netbios_name.string) != 0) { + printf("QueryTrustedDomainInfo returned inconsistant short name: %s != %s\n", + q.out.info->info_ex.netbios_name.string, trustinfo.netbios_name.string); + ret = false; + } + if (q.out.info->info_ex.trust_type != trustinfo.trust_type) { + printf("QueryTrustedDomainInfo of %s returned incorrect trust type %d != %d\n", + trust_name, q.out.info->info_ex.trust_type, trustinfo.trust_type); + ret = false; + } + if (q.out.info->info_ex.trust_attributes != LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION) { + printf("QueryTrustedDomainInfo of %s returned incorrect trust attributes %d != %d\n", + trust_name, q.out.info->info_ex.trust_attributes, LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION); + ret = false; + } + if (q.out.info->info_ex.trust_direction != trustinfo.trust_direction) { + printf("QueryTrustedDomainInfo of %s returned incorrect trust direction %d != %d\n", + trust_name, q.out.info->info_ex.trust_direction, trustinfo.trust_direction); + ret = false; + } + } + } + } + + /* now that we have some domains to look over, we can test the enum calls */ + if (!test_EnumTrustDom(p, mem_ctx, handle)) { + ret = false; + } + + for (i=0; i<12; i++) { + if (!test_DeleteTrustedDomainBySid(p, mem_ctx, handle, domsid[i])) { + ret = false; + } + } + + return ret; +} + +static bool test_QueryDomainInfoPolicy(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct lsa_QueryDomainInformationPolicy r; + NTSTATUS status; + int i; + bool ret = true; + + printf("\nTesting QueryDomainInformationPolicy\n"); + + for (i=2;i<4;i++) { + r.in.handle = handle; + r.in.level = i; + + printf("\ntrying QueryDomainInformationPolicy level %d\n", i); + + status = dcerpc_lsa_QueryDomainInformationPolicy(p, tctx, &r); + + /* If the server does not support EFS, then this is the correct return */ + if (i == LSA_DOMAIN_INFO_POLICY_EFS && NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + continue; + } else if (!NT_STATUS_IS_OK(status)) { + printf("QueryDomainInformationPolicy failed - %s\n", nt_errstr(status)); + ret = false; + continue; + } + } + + return ret; +} + + +static bool test_QueryInfoPolicy(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct lsa_QueryInfoPolicy r; + NTSTATUS status; + int i; + bool ret = true; + printf("\nTesting QueryInfoPolicy\n"); + + for (i=1;i<13;i++) { + r.in.handle = handle; + r.in.level = i; + + printf("\ntrying QueryInfoPolicy level %d\n", i); + + status = dcerpc_lsa_QueryInfoPolicy(p, tctx, &r); + + switch (i) { + case LSA_POLICY_INFO_DB: + case LSA_POLICY_INFO_AUDIT_FULL_SET: + case LSA_POLICY_INFO_AUDIT_FULL_QUERY: + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { + printf("server should have failed level %u: %s\n", i, nt_errstr(status)); + ret = false; + } + break; + case LSA_POLICY_INFO_DOMAIN: + case LSA_POLICY_INFO_ACCOUNT_DOMAIN: + case LSA_POLICY_INFO_DNS: + if (!NT_STATUS_IS_OK(status)) { + printf("QueryInfoPolicy failed - %s\n", nt_errstr(status)); + ret = false; + } + break; + default: + if (torture_setting_bool(tctx, "samba4", false)) { + /* Other levels not implemented yet */ + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) { + printf("QueryInfoPolicy failed - %s\n", nt_errstr(status)); + ret = false; + } + } else if (!NT_STATUS_IS_OK(status)) { + printf("QueryInfoPolicy failed - %s\n", nt_errstr(status)); + ret = false; + } + break; + } + + if (NT_STATUS_IS_OK(status) && i == LSA_POLICY_INFO_DNS) { + /* Let's look up some of these names */ + + struct lsa_TransNameArray tnames; + tnames.count = 14; + tnames.names = talloc_zero_array(tctx, struct lsa_TranslatedName, tnames.count); + tnames.names[0].name.string = r.out.info->dns.name.string; + tnames.names[0].sid_type = SID_NAME_DOMAIN; + tnames.names[1].name.string = r.out.info->dns.dns_domain.string; + tnames.names[1].sid_type = SID_NAME_DOMAIN; + tnames.names[2].name.string = talloc_asprintf(tctx, "%s\\", r.out.info->dns.name.string); + tnames.names[2].sid_type = SID_NAME_DOMAIN; + tnames.names[3].name.string = talloc_asprintf(tctx, "%s\\", r.out.info->dns.dns_domain.string); + tnames.names[3].sid_type = SID_NAME_DOMAIN; + tnames.names[4].name.string = talloc_asprintf(tctx, "%s\\guest", r.out.info->dns.name.string); + tnames.names[4].sid_type = SID_NAME_USER; + tnames.names[5].name.string = talloc_asprintf(tctx, "%s\\krbtgt", r.out.info->dns.name.string); + tnames.names[5].sid_type = SID_NAME_USER; + tnames.names[6].name.string = talloc_asprintf(tctx, "%s\\guest", r.out.info->dns.dns_domain.string); + tnames.names[6].sid_type = SID_NAME_USER; + tnames.names[7].name.string = talloc_asprintf(tctx, "%s\\krbtgt", r.out.info->dns.dns_domain.string); + tnames.names[7].sid_type = SID_NAME_USER; + tnames.names[8].name.string = talloc_asprintf(tctx, "krbtgt@%s", r.out.info->dns.name.string); + tnames.names[8].sid_type = SID_NAME_USER; + tnames.names[9].name.string = talloc_asprintf(tctx, "krbtgt@%s", r.out.info->dns.dns_domain.string); + tnames.names[9].sid_type = SID_NAME_USER; + tnames.names[10].name.string = talloc_asprintf(tctx, "%s\\"TEST_MACHINENAME "$", r.out.info->dns.name.string); + tnames.names[10].sid_type = SID_NAME_USER; + tnames.names[11].name.string = talloc_asprintf(tctx, "%s\\"TEST_MACHINENAME "$", r.out.info->dns.dns_domain.string); + tnames.names[11].sid_type = SID_NAME_USER; + tnames.names[12].name.string = talloc_asprintf(tctx, TEST_MACHINENAME "$@%s", r.out.info->dns.name.string); + tnames.names[12].sid_type = SID_NAME_USER; + tnames.names[13].name.string = talloc_asprintf(tctx, TEST_MACHINENAME "$@%s", r.out.info->dns.dns_domain.string); + tnames.names[13].sid_type = SID_NAME_USER; + ret &= test_LookupNames(p, tctx, handle, &tnames); + + } + } + + return ret; +} + +static bool test_QueryInfoPolicy2(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct lsa_QueryInfoPolicy2 r; + NTSTATUS status; + int i; + bool ret = true; + printf("\nTesting QueryInfoPolicy2\n"); + for (i=1;i<13;i++) { + r.in.handle = handle; + r.in.level = i; + + printf("\ntrying QueryInfoPolicy2 level %d\n", i); + + status = dcerpc_lsa_QueryInfoPolicy2(p, tctx, &r); + + switch (i) { + case LSA_POLICY_INFO_DB: + case LSA_POLICY_INFO_AUDIT_FULL_SET: + case LSA_POLICY_INFO_AUDIT_FULL_QUERY: + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { + printf("server should have failed level %u: %s\n", i, nt_errstr(status)); + ret = false; + } + break; + case LSA_POLICY_INFO_DOMAIN: + case LSA_POLICY_INFO_ACCOUNT_DOMAIN: + case LSA_POLICY_INFO_DNS: + if (!NT_STATUS_IS_OK(status)) { + printf("QueryInfoPolicy2 failed - %s\n", nt_errstr(status)); + ret = false; + } + break; + default: + if (torture_setting_bool(tctx, "samba4", false)) { + /* Other levels not implemented yet */ + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) { + printf("QueryInfoPolicy2 failed - %s\n", nt_errstr(status)); + ret = false; + } + } else if (!NT_STATUS_IS_OK(status)) { + printf("QueryInfoPolicy2 failed - %s\n", nt_errstr(status)); + ret = false; + } + break; + } + } + + return ret; +} + +static bool test_GetUserName(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx) +{ + struct lsa_GetUserName r; + NTSTATUS status; + bool ret = true; + struct lsa_StringPointer authority_name_p; + + printf("\nTesting GetUserName\n"); + + r.in.system_name = "\\"; + r.in.account_name = NULL; + r.in.authority_name = &authority_name_p; + authority_name_p.string = NULL; + + status = dcerpc_lsa_GetUserName(p, mem_ctx, &r); + + if (!NT_STATUS_IS_OK(status)) { + printf("GetUserName failed - %s\n", nt_errstr(status)); + ret = false; + } + + return ret; +} + +bool test_lsa_Close(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct lsa_Close r; + struct policy_handle handle2; + + printf("\ntesting Close\n"); + + r.in.handle = handle; + r.out.handle = &handle2; + + status = dcerpc_lsa_Close(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + + status = dcerpc_lsa_Close(p, mem_ctx, &r); + /* its really a fault - we need a status code for rpc fault */ + if (!NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + + printf("\n"); + + return true; +} + +bool torture_rpc_lsa(struct torture_context *tctx) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct policy_handle *handle; + struct test_join *join = NULL; + struct cli_credentials *machine_creds; + + status = torture_rpc_connection(tctx, &p, &ndr_table_lsarpc); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + if (!test_OpenPolicy(p, tctx)) { + ret = false; + } + + if (!test_lsa_OpenPolicy2(p, tctx, &handle)) { + ret = false; + } + + if (handle) { + join = torture_join_domain(tctx, TEST_MACHINENAME, ACB_WSTRUST, &machine_creds); + if (!join) { + ret = false; + } + + if (!test_LookupNames_wellknown(p, tctx, handle)) { + ret = false; + } + + if (!test_LookupNames_bogus(p, tctx, handle)) { + ret = false; + } + + if (!test_LookupSids_async(p, tctx, handle)) { + ret = false; + } + + if (!test_QueryDomainInfoPolicy(p, tctx, handle)) { + ret = false; + } + + if (!test_CreateAccount(p, tctx, handle)) { + ret = false; + } + + if (!test_CreateSecret(p, tctx, handle)) { + ret = false; + } + + if (!test_CreateTrustedDomain(p, tctx, handle)) { + ret = false; + } + + if (!test_CreateTrustedDomainEx2(p, tctx, tctx, handle)) { + ret = false; + } + + if (!test_EnumAccounts(p, tctx, handle)) { + ret = false; + } + + if (!test_EnumPrivs(p, tctx, handle)) { + ret = false; + } + + if (!test_QueryInfoPolicy(p, tctx, handle)) { + ret = false; + } + + if (!test_QueryInfoPolicy2(p, tctx, handle)) { + ret = false; + } + + if (!test_Delete(p, tctx, handle)) { + ret = false; + } + + if (!test_many_LookupSids(p, tctx, handle)) { + ret = false; + } + + if (!test_lsa_Close(p, tctx, handle)) { + ret = false; + } + + torture_leave_domain(tctx, join); + + } else { + if (!test_many_LookupSids(p, tctx, handle)) { + ret = false; + } + } + + if (!test_GetUserName(p, tctx)) { + ret = false; + } + + return ret; +} + +bool torture_rpc_lsa_get_user(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *mem_ctx; + bool ret = true; + + mem_ctx = talloc_init("torture_rpc_lsa_get_user"); + + status = torture_rpc_connection(torture, &p, &ndr_table_lsarpc); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return false; + } + + if (!test_GetUserName(p, mem_ctx)) { + ret = false; + } + + talloc_free(mem_ctx); + + return ret; +} diff --git a/source4/torture/rpc/lsa_lookup.c b/source4/torture/rpc/lsa_lookup.c new file mode 100644 index 0000000000..9c817a7061 --- /dev/null +++ b/source4/torture/rpc/lsa_lookup.c @@ -0,0 +1,319 @@ +/* + Unix SMB/CIFS implementation. + test suite for lsa rpc lookup operations + + Copyright (C) Volker Lendecke 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "lib/events/events.h" +#include "libnet/libnet_join.h" +#include "torture/rpc/rpc.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "libcli/security/security.h" + +static bool open_policy(TALLOC_CTX *mem_ctx, struct dcerpc_pipe *p, + struct policy_handle **handle) +{ + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy2 r; + NTSTATUS status; + + *handle = talloc(mem_ctx, struct policy_handle); + if (!*handle) { + return false; + } + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = "\\"; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = *handle; + + status = dcerpc_lsa_OpenPolicy2(p, mem_ctx, &r); + + return NT_STATUS_IS_OK(status); +} + +static bool get_domainsid(TALLOC_CTX *mem_ctx, struct dcerpc_pipe *p, + struct policy_handle *handle, + struct dom_sid **sid) +{ + struct lsa_QueryInfoPolicy r; + NTSTATUS status; + + r.in.level = LSA_POLICY_INFO_DOMAIN; + r.in.handle = handle; + + status = dcerpc_lsa_QueryInfoPolicy(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) return false; + + *sid = r.out.info->domain.sid; + return true; +} + +static NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, uint16_t level, + struct dcerpc_pipe *p, + struct policy_handle *handle, + struct dom_sid **sids, uint32_t num_sids, + struct lsa_TransNameArray *names) +{ + struct lsa_LookupSids r; + struct lsa_SidArray sidarray; + uint32_t count = 0; + uint32_t i; + + names->count = 0; + names->names = NULL; + + sidarray.num_sids = num_sids; + sidarray.sids = talloc_array(mem_ctx, struct lsa_SidPtr, num_sids); + + for (i=0; i<num_sids; i++) { + sidarray.sids[i].sid = sids[i]; + } + + r.in.handle = handle; + r.in.sids = &sidarray; + r.in.names = names; + r.in.level = level; + r.in.count = &count; + r.out.names = names; + r.out.count = &count; + + return dcerpc_lsa_LookupSids(p, mem_ctx, &r); +} + +static const char *sid_type_lookup(enum lsa_SidType r) +{ + switch (r) { + case SID_NAME_USE_NONE: return "SID_NAME_USE_NONE"; break; + case SID_NAME_USER: return "SID_NAME_USER"; break; + case SID_NAME_DOM_GRP: return "SID_NAME_DOM_GRP"; break; + case SID_NAME_DOMAIN: return "SID_NAME_DOMAIN"; break; + case SID_NAME_ALIAS: return "SID_NAME_ALIAS"; break; + case SID_NAME_WKN_GRP: return "SID_NAME_WKN_GRP"; break; + case SID_NAME_DELETED: return "SID_NAME_DELETED"; break; + case SID_NAME_INVALID: return "SID_NAME_INVALID"; break; + case SID_NAME_UNKNOWN: return "SID_NAME_UNKNOWN"; break; + case SID_NAME_COMPUTER: return "SID_NAME_COMPUTER"; break; + } + return "Invalid sid type\n"; +} + +static bool test_lookupsids(TALLOC_CTX *mem_ctx, struct dcerpc_pipe *p, + struct policy_handle *handle, + struct dom_sid **sids, uint32_t num_sids, + int level, NTSTATUS expected_result, + enum lsa_SidType *types) +{ + struct lsa_TransNameArray names; + NTSTATUS status; + uint32_t i; + bool ret = true; + + status = lookup_sids(mem_ctx, level, p, handle, sids, num_sids, + &names); + if (!NT_STATUS_EQUAL(status, expected_result)) { + printf("For level %d expected %s, got %s\n", + level, nt_errstr(expected_result), + nt_errstr(status)); + return false; + } + + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK) && + !NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) { + return true; + } + + for (i=0; i<num_sids; i++) { + if (names.names[i].sid_type != types[i]) { + printf("In level %d, for sid %s expected %s, " + "got %s\n", level, + dom_sid_string(mem_ctx, sids[i]), + sid_type_lookup(types[i]), + sid_type_lookup(names.names[i].sid_type)); + ret = false; + } + } + return ret; +} + +static bool get_downleveltrust(struct torture_context *tctx, struct dcerpc_pipe *p, + struct policy_handle *handle, + struct dom_sid **sid) +{ + struct lsa_EnumTrustDom r; + uint32_t resume_handle = 0; + struct lsa_DomainList domains; + NTSTATUS status; + int i; + + r.in.handle = handle; + r.in.resume_handle = &resume_handle; + r.in.max_size = 1000; + r.out.domains = &domains; + r.out.resume_handle = &resume_handle; + + status = dcerpc_lsa_EnumTrustDom(p, tctx, &r); + + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) + torture_fail(tctx, "no trusts"); + + if (domains.count == 0) { + torture_fail(tctx, "no trusts"); + } + + for (i=0; i<domains.count; i++) { + struct lsa_QueryTrustedDomainInfoBySid q; + + if (domains.domains[i].sid == NULL) + continue; + + q.in.handle = handle; + q.in.dom_sid = domains.domains[i].sid; + q.in.level = 6; + status = dcerpc_lsa_QueryTrustedDomainInfoBySid(p, tctx, &q); + if (!NT_STATUS_IS_OK(status)) continue; + + if ((q.out.info->info_ex.trust_direction & 2) && + (q.out.info->info_ex.trust_type == 1)) { + *sid = domains.domains[i].sid; + return true; + } + } + + torture_fail(tctx, "I need a AD DC with an outgoing trust to NT4"); +} + +#define NUM_SIDS 8 + +bool torture_rpc_lsa_lookup(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct policy_handle *handle; + struct dom_sid *dom_sid; + struct dom_sid *trusted_sid; + struct dom_sid *sids[NUM_SIDS]; + + status = torture_rpc_connection(torture, &p, &ndr_table_lsarpc); + if (!NT_STATUS_IS_OK(status)) { + torture_fail(torture, "unable to connect to table"); + } + + ret &= open_policy(torture, p, &handle); + if (!ret) return false; + + ret &= get_domainsid(torture, p, handle, &dom_sid); + if (!ret) return false; + + ret &= get_downleveltrust(torture, p, handle, &trusted_sid); + if (!ret) return false; + + torture_comment(torture, "domain sid: %s\n", + dom_sid_string(torture, dom_sid)); + + sids[0] = dom_sid_parse_talloc(torture, "S-1-1-0"); + sids[1] = dom_sid_parse_talloc(torture, "S-1-5-4"); + sids[2] = dom_sid_parse_talloc(torture, "S-1-5-32"); + sids[3] = dom_sid_parse_talloc(torture, "S-1-5-32-545"); + sids[4] = dom_sid_dup(torture, dom_sid); + sids[5] = dom_sid_add_rid(torture, dom_sid, 512); + sids[6] = dom_sid_dup(torture, trusted_sid); + sids[7] = dom_sid_add_rid(torture, trusted_sid, 512); + + ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 0, + NT_STATUS_INVALID_PARAMETER, NULL); + + { + enum lsa_SidType types[NUM_SIDS] = + { SID_NAME_WKN_GRP, SID_NAME_WKN_GRP, SID_NAME_DOMAIN, + SID_NAME_ALIAS, SID_NAME_DOMAIN, SID_NAME_DOM_GRP, + SID_NAME_DOMAIN, SID_NAME_DOM_GRP }; + + ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 1, + NT_STATUS_OK, types); + } + + { + enum lsa_SidType types[NUM_SIDS] = + { SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_DOMAIN, SID_NAME_DOM_GRP, + SID_NAME_DOMAIN, SID_NAME_DOM_GRP }; + ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 2, + STATUS_SOME_UNMAPPED, types); + } + + { + enum lsa_SidType types[NUM_SIDS] = + { SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_DOMAIN, SID_NAME_DOM_GRP, + SID_NAME_UNKNOWN, SID_NAME_UNKNOWN }; + ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 3, + STATUS_SOME_UNMAPPED, types); + } + + { + enum lsa_SidType types[NUM_SIDS] = + { SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_DOMAIN, SID_NAME_DOM_GRP, + SID_NAME_UNKNOWN, SID_NAME_UNKNOWN }; + ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 4, + STATUS_SOME_UNMAPPED, types); + } + + ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 5, + NT_STATUS_NONE_MAPPED, NULL); + + { + enum lsa_SidType types[NUM_SIDS] = + { SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_DOMAIN, SID_NAME_DOM_GRP, + SID_NAME_UNKNOWN, SID_NAME_UNKNOWN }; + ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 6, + STATUS_SOME_UNMAPPED, types); + } + + ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 7, + NT_STATUS_INVALID_PARAMETER, NULL); + ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 8, + NT_STATUS_INVALID_PARAMETER, NULL); + ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 9, + NT_STATUS_INVALID_PARAMETER, NULL); + ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 10, + NT_STATUS_INVALID_PARAMETER, NULL); + + return ret; +} diff --git a/source4/torture/rpc/mgmt.c b/source4/torture/rpc/mgmt.c new file mode 100644 index 0000000000..fed432f31c --- /dev/null +++ b/source4/torture/rpc/mgmt.c @@ -0,0 +1,270 @@ +/* + Unix SMB/CIFS implementation. + test suite for mgmt rpc operations + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_mgmt_c.h" +#include "auth/gensec/gensec.h" +#include "librpc/ndr/ndr_table.h" +#include "torture/rpc/rpc.h" +#include "param/param.h" +#include "librpc/rpc/dcerpc_proto.h" + + +/* + ask the server what interface IDs are available on this endpoint +*/ +bool test_inq_if_ids(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + bool (*per_id_test)(struct torture_context *, + const struct ndr_interface_table *iface, + TALLOC_CTX *mem_ctx, + struct ndr_syntax_id *id), + const void *priv) +{ + NTSTATUS status; + struct mgmt_inq_if_ids r; + struct rpc_if_id_vector_t *vector; + int i; + + vector = talloc(mem_ctx, struct rpc_if_id_vector_t); + r.out.if_id_vector = &vector; + + status = dcerpc_mgmt_inq_if_ids(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("inq_if_ids failed - %s\n", nt_errstr(status)); + return false; + } + + if (!W_ERROR_IS_OK(r.out.result)) { + printf("inq_if_ids gave error code %s\n", win_errstr(r.out.result)); + return false; + } + + if (!vector) { + printf("inq_if_ids gave NULL if_id_vector\n"); + return false; + } + + for (i=0;i<vector->count;i++) { + struct ndr_syntax_id *id = vector->if_id[i].id; + if (!id) continue; + + printf("\tuuid %s version 0x%08x '%s'\n", + GUID_string(mem_ctx, &id->uuid), + id->if_version, + ndr_interface_name(&id->uuid, id->if_version)); + + if (per_id_test) { + per_id_test(tctx, priv, mem_ctx, id); + } + } + + return true; +} + +static bool test_inq_stats(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + struct mgmt_inq_stats r; + struct mgmt_statistics statistics; + + r.in.max_count = MGMT_STATS_ARRAY_MAX_SIZE; + r.in.unknown = 0; + r.out.statistics = &statistics; + + status = dcerpc_mgmt_inq_stats(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("inq_stats failed - %s\n", nt_errstr(status)); + return false; + } + + if (statistics.count != MGMT_STATS_ARRAY_MAX_SIZE) { + printf("Unexpected array size %d\n", statistics.count); + return false; + } + + printf("\tcalls_in %6d calls_out %6d\n\tpkts_in %6d pkts_out %6d\n", + statistics.statistics[MGMT_STATS_CALLS_IN], + statistics.statistics[MGMT_STATS_CALLS_OUT], + statistics.statistics[MGMT_STATS_PKTS_IN], + statistics.statistics[MGMT_STATS_PKTS_OUT]); + + return true; +} + +static bool test_inq_princ_name(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + struct mgmt_inq_princ_name r; + int i; + bool ret = false; + + for (i=0;i<100;i++) { + r.in.authn_proto = i; /* DCERPC_AUTH_TYPE_* */ + r.in.princ_name_size = 100; + + status = dcerpc_mgmt_inq_princ_name(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + continue; + } + if (W_ERROR_IS_OK(r.out.result)) { + const char *name = gensec_get_name_by_authtype(i); + ret = true; + if (name) { + printf("\tprinciple name for proto %u (%s) is '%s'\n", + i, name, r.out.princ_name); + } else { + printf("\tprinciple name for proto %u is '%s'\n", + i, r.out.princ_name); + } + } + } + + if (!ret) { + printf("\tno principle names?\n"); + } + + return true; +} + +static bool test_is_server_listening(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + struct mgmt_is_server_listening r; + r.out.status = talloc(mem_ctx, uint32_t); + + status = dcerpc_mgmt_is_server_listening(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("is_server_listening failed - %s\n", nt_errstr(status)); + return false; + } + + if (*r.out.status != 0 || r.out.result == 0) { + printf("\tserver is NOT listening\n"); + } else { + printf("\tserver is listening\n"); + } + + return true; +} + +static bool test_stop_server_listening(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + struct mgmt_stop_server_listening r; + + status = dcerpc_mgmt_stop_server_listening(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("stop_server_listening failed - %s\n", nt_errstr(status)); + return false; + } + + if (!W_ERROR_IS_OK(r.out.result)) { + printf("\tserver refused to stop listening - %s\n", win_errstr(r.out.result)); + } else { + printf("\tserver allowed a stop_server_listening request\n"); + return false; + } + + return true; +} + + +bool torture_rpc_mgmt(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *mem_ctx, *loop_ctx; + bool ret = true; + const struct ndr_interface_list *l; + struct dcerpc_binding *b; + + mem_ctx = talloc_init("torture_rpc_mgmt"); + + status = torture_rpc_binding(torture, &b); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return false; + } + + for (l=ndr_table_list();l;l=l->next) { + loop_ctx = talloc_named(mem_ctx, 0, "torture_rpc_mgmt loop context"); + + /* some interfaces are not mappable */ + if (l->table->num_calls == 0 || + strcmp(l->table->name, "mgmt") == 0) { + talloc_free(loop_ctx); + continue; + } + + printf("\nTesting pipe '%s'\n", l->table->name); + + status = dcerpc_epm_map_binding(loop_ctx, b, l->table, NULL, torture->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to map port for uuid %s\n", + GUID_string(loop_ctx, &l->table->syntax_id.uuid)); + talloc_free(loop_ctx); + continue; + } + + lp_set_cmdline(torture->lp_ctx, "torture:binding", dcerpc_binding_string(loop_ctx, b)); + + status = torture_rpc_connection(torture, &p, &ndr_table_mgmt); + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + printf("Interface not available - skipping\n"); + talloc_free(loop_ctx); + continue; + } + + if (!NT_STATUS_IS_OK(status)) { + talloc_free(loop_ctx); + ret = false; + continue; + } + + if (!test_is_server_listening(p, loop_ctx)) { + ret = false; + } + + if (!test_stop_server_listening(p, loop_ctx)) { + ret = false; + } + + if (!test_inq_stats(p, loop_ctx)) { + ret = false; + } + + if (!test_inq_princ_name(p, loop_ctx)) { + ret = false; + } + + if (!test_inq_if_ids(torture, p, loop_ctx, NULL, NULL)) { + ret = false; + } + + } + + return ret; +} diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c new file mode 100644 index 0000000000..5ec2c29a20 --- /dev/null +++ b/source4/torture/rpc/netlogon.c @@ -0,0 +1,1612 @@ +/* + Unix SMB/CIFS implementation. + + test suite for netlogon rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004 + Copyright (C) Tim Potter 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "lib/events/events.h" +#include "auth/auth.h" +#include "auth/gensec/gensec.h" +#include "lib/cmdline/popt_common.h" +#include "torture/rpc/rpc.h" +#include "torture/rpc/netlogon.h" +#include "libcli/auth/libcli_auth.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "param/param.h" + +#define TEST_MACHINE_NAME "torturetest" + +static bool test_LogonUasLogon(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_LogonUasLogon r; + + r.in.server_name = NULL; + r.in.account_name = cli_credentials_get_username(cmdline_credentials); + r.in.workstation = TEST_MACHINE_NAME; + + status = dcerpc_netr_LogonUasLogon(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonUasLogon"); + + return true; +} + +static bool test_LogonUasLogoff(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_LogonUasLogoff r; + + r.in.server_name = NULL; + r.in.account_name = cli_credentials_get_username(cmdline_credentials); + r.in.workstation = TEST_MACHINE_NAME; + + status = dcerpc_netr_LogonUasLogoff(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonUasLogoff"); + + return true; +} + +static bool test_SetupCredentials(struct dcerpc_pipe *p, struct torture_context *tctx, + struct cli_credentials *credentials, + struct creds_CredentialState **creds_out) +{ + NTSTATUS status; + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate a; + struct netr_Credential credentials1, credentials2, credentials3; + struct creds_CredentialState *creds; + struct samr_Password mach_password; + const char *plain_pass; + const char *machine_name; + + plain_pass = cli_credentials_get_password(credentials); + machine_name = cli_credentials_get_workstation(credentials); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + creds = talloc(tctx, struct creds_CredentialState); + torture_assert(tctx, creds != NULL, "memory allocation"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.credentials = &credentials2; + + generate_random_buffer(credentials1.data, sizeof(credentials1.data)); + + status = dcerpc_netr_ServerReqChallenge(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ServerReqChallenge"); + + E_md4hash(plain_pass, mach_password.hash); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = SEC_CHAN_BDC; + a.in.computer_name = machine_name; + a.in.credentials = &credentials3; + a.out.credentials = &credentials3; + + creds_client_init(creds, &credentials1, &credentials2, + &mach_password, &credentials3, + 0); + + torture_comment(tctx, "Testing ServerAuthenticate\n"); + + status = dcerpc_netr_ServerAuthenticate(p, tctx, &a); + + /* This allows the tests to continue against the more fussy windows 2008 */ + if (NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED)) { + return test_SetupCredentials2(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS, + credentials, SEC_CHAN_BDC, creds_out); + } + + torture_assert_ntstatus_ok(tctx, status, "ServerAuthenticate"); + + torture_assert(tctx, creds_client_check(creds, &credentials3), + "Credential chaining failed"); + + *creds_out = creds; + return true; +} + +bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx, + uint32_t negotiate_flags, + struct cli_credentials *machine_credentials, + int sec_chan_type, + struct creds_CredentialState **creds_out) +{ + NTSTATUS status; + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate2 a; + struct netr_Credential credentials1, credentials2, credentials3; + struct creds_CredentialState *creds; + struct samr_Password mach_password; + const char *machine_name; + const char *plain_pass; + + machine_name = cli_credentials_get_workstation(machine_credentials); + plain_pass = cli_credentials_get_password(machine_credentials); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + creds = talloc(tctx, struct creds_CredentialState); + torture_assert(tctx, creds != NULL, "memory allocation"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.credentials = &credentials2; + + generate_random_buffer(credentials1.data, sizeof(credentials1.data)); + + status = dcerpc_netr_ServerReqChallenge(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ServerReqChallenge"); + + E_md4hash(plain_pass, mach_password.hash); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = sec_chan_type; + a.in.computer_name = machine_name; + a.in.negotiate_flags = &negotiate_flags; + a.out.negotiate_flags = &negotiate_flags; + a.in.credentials = &credentials3; + a.out.credentials = &credentials3; + + creds_client_init(creds, &credentials1, &credentials2, + &mach_password, &credentials3, + negotiate_flags); + + torture_comment(tctx, "Testing ServerAuthenticate2\n"); + + status = dcerpc_netr_ServerAuthenticate2(p, tctx, &a); + torture_assert_ntstatus_ok(tctx, status, "ServerAuthenticate2"); + + torture_assert(tctx, creds_client_check(creds, &credentials3), + "Credential chaining failed"); + + torture_comment(tctx, "negotiate_flags=0x%08x\n", negotiate_flags); + + *creds_out = creds; + return true; +} + + +static bool test_SetupCredentials3(struct dcerpc_pipe *p, struct torture_context *tctx, + uint32_t negotiate_flags, + struct cli_credentials *machine_credentials, + struct creds_CredentialState **creds_out) +{ + NTSTATUS status; + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential credentials1, credentials2, credentials3; + struct creds_CredentialState *creds; + struct samr_Password mach_password; + uint32_t rid; + const char *machine_name; + const char *plain_pass; + + machine_name = cli_credentials_get_workstation(machine_credentials); + plain_pass = cli_credentials_get_password(machine_credentials); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + creds = talloc(tctx, struct creds_CredentialState); + torture_assert(tctx, creds != NULL, "memory allocation"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.credentials = &credentials2; + + generate_random_buffer(credentials1.data, sizeof(credentials1.data)); + + status = dcerpc_netr_ServerReqChallenge(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ServerReqChallenge"); + + E_md4hash(plain_pass, mach_password.hash); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = SEC_CHAN_BDC; + a.in.computer_name = machine_name; + a.in.negotiate_flags = &negotiate_flags; + a.in.credentials = &credentials3; + a.out.credentials = &credentials3; + a.out.negotiate_flags = &negotiate_flags; + a.out.rid = &rid; + + creds_client_init(creds, &credentials1, &credentials2, + &mach_password, &credentials3, + negotiate_flags); + + torture_comment(tctx, "Testing ServerAuthenticate3\n"); + + status = dcerpc_netr_ServerAuthenticate3(p, tctx, &a); + torture_assert_ntstatus_ok(tctx, status, "ServerAuthenticate3"); + torture_assert(tctx, creds_client_check(creds, &credentials3), "Credential chaining failed"); + + torture_comment(tctx, "negotiate_flags=0x%08x\n", negotiate_flags); + + *creds_out = creds; + return true; +} + +/* + try a change password for our machine account +*/ +static bool test_SetPassword(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct netr_ServerPasswordSet r; + const char *password; + struct creds_CredentialState *creds; + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = SEC_CHAN_BDC; + r.in.computer_name = TEST_MACHINE_NAME; + + password = generate_random_str(tctx, 8); + E_md4hash(password, r.in.new_password.hash); + + creds_des_encrypt(creds, &r.in.new_password); + + torture_comment(tctx, "Testing ServerPasswordSet on machine account\n"); + torture_comment(tctx, "Changing machine account password to '%s'\n", + password); + + creds_client_authenticator(creds, &r.in.credential); + + status = dcerpc_netr_ServerPasswordSet(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet"); + + if (!creds_client_check(creds, &r.out.return_authenticator.cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + /* by changing the machine password twice we test the + credentials chaining fully, and we verify that the server + allows the password to be set to the same value twice in a + row (match win2k3) */ + torture_comment(tctx, + "Testing a second ServerPasswordSet on machine account\n"); + torture_comment(tctx, + "Changing machine account password to '%s' (same as previous run)\n", password); + + creds_client_authenticator(creds, &r.in.credential); + + status = dcerpc_netr_ServerPasswordSet(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet (2)"); + + if (!creds_client_check(creds, &r.out.return_authenticator.cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + cli_credentials_set_password(machine_credentials, password, CRED_SPECIFIED); + + torture_assert(tctx, + test_SetupCredentials(p, tctx, machine_credentials, &creds), + "ServerPasswordSet failed to actually change the password"); + + return true; +} + +/* + try a change password for our machine account +*/ +static bool test_SetPassword2(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct netr_ServerPasswordSet2 r; + const char *password; + struct creds_CredentialState *creds; + struct samr_CryptPassword password_buf; + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = SEC_CHAN_BDC; + r.in.computer_name = TEST_MACHINE_NAME; + + password = generate_random_str(tctx, 8); + encode_pw_buffer(password_buf.data, password, STR_UNICODE); + creds_arcfour_crypt(creds, password_buf.data, 516); + + memcpy(r.in.new_password.data, password_buf.data, 512); + r.in.new_password.length = IVAL(password_buf.data, 512); + + torture_comment(tctx, "Testing ServerPasswordSet2 on machine account\n"); + torture_comment(tctx, "Changing machine account password to '%s'\n", password); + + creds_client_authenticator(creds, &r.in.credential); + + status = dcerpc_netr_ServerPasswordSet2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet2"); + + if (!creds_client_check(creds, &r.out.return_authenticator.cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + cli_credentials_set_password(machine_credentials, password, CRED_SPECIFIED); + + if (!torture_setting_bool(tctx, "dangerous", false)) { + torture_comment(tctx, + "Not testing ability to set password to '', enable dangerous tests to perform this test\n"); + } else { + /* by changing the machine password to "" + * we check if the server uses password restrictions + * for ServerPasswordSet2 + * (win2k3 accepts "") + */ + password = ""; + encode_pw_buffer(password_buf.data, password, STR_UNICODE); + creds_arcfour_crypt(creds, password_buf.data, 516); + + memcpy(r.in.new_password.data, password_buf.data, 512); + r.in.new_password.length = IVAL(password_buf.data, 512); + + torture_comment(tctx, + "Testing ServerPasswordSet2 on machine account\n"); + torture_comment(tctx, + "Changing machine account password to '%s'\n", password); + + creds_client_authenticator(creds, &r.in.credential); + + status = dcerpc_netr_ServerPasswordSet2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet2"); + + if (!creds_client_check(creds, &r.out.return_authenticator.cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + cli_credentials_set_password(machine_credentials, password, CRED_SPECIFIED); + } + + torture_assert(tctx, test_SetupCredentials(p, tctx, machine_credentials, &creds), + "ServerPasswordSet failed to actually change the password"); + + /* now try a random password */ + password = generate_random_str(tctx, 8); + encode_pw_buffer(password_buf.data, password, STR_UNICODE); + creds_arcfour_crypt(creds, password_buf.data, 516); + + memcpy(r.in.new_password.data, password_buf.data, 512); + r.in.new_password.length = IVAL(password_buf.data, 512); + + torture_comment(tctx, "Testing second ServerPasswordSet2 on machine account\n"); + torture_comment(tctx, "Changing machine account password to '%s'\n", password); + + creds_client_authenticator(creds, &r.in.credential); + + status = dcerpc_netr_ServerPasswordSet2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet2 (2)"); + + if (!creds_client_check(creds, &r.out.return_authenticator.cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + /* by changing the machine password twice we test the + credentials chaining fully, and we verify that the server + allows the password to be set to the same value twice in a + row (match win2k3) */ + torture_comment(tctx, + "Testing a second ServerPasswordSet2 on machine account\n"); + torture_comment(tctx, + "Changing machine account password to '%s' (same as previous run)\n", password); + + creds_client_authenticator(creds, &r.in.credential); + + status = dcerpc_netr_ServerPasswordSet2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet (3)"); + + if (!creds_client_check(creds, &r.out.return_authenticator.cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + cli_credentials_set_password(machine_credentials, password, CRED_SPECIFIED); + + torture_assert (tctx, + test_SetupCredentials(p, tctx, machine_credentials, &creds), + "ServerPasswordSet failed to actually change the password"); + + return true; +} + +static bool test_GetPassword(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + struct netr_ServerPasswordGet r; + struct creds_CredentialState *creds; + struct netr_Authenticator credential; + NTSTATUS status; + struct netr_Authenticator return_authenticator; + struct samr_Password password; + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + creds_client_authenticator(creds, &credential); + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = SEC_CHAN_BDC; + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.out.return_authenticator = &return_authenticator; + r.out.password = &password; + + status = dcerpc_netr_ServerPasswordGet(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ServerPasswordGet"); + + return true; +} + +static bool test_GetTrustPasswords(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + struct netr_ServerTrustPasswordsGet r; + struct creds_CredentialState *creds; + struct netr_Authenticator credential; + NTSTATUS status; + struct netr_Authenticator return_authenticator; + struct samr_Password password, password2; + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + creds_client_authenticator(creds, &credential); + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = SEC_CHAN_BDC; + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.out.return_authenticator = &return_authenticator; + r.out.password = &password; + r.out.password2 = &password2; + + status = dcerpc_netr_ServerTrustPasswordsGet(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ServerTrustPasswordsGet"); + + return true; +} + +/* + try a netlogon SamLogon +*/ +bool test_netlogon_ops(struct dcerpc_pipe *p, struct torture_context *tctx, + struct cli_credentials *credentials, + struct creds_CredentialState *creds) +{ + NTSTATUS status; + struct netr_LogonSamLogon r; + struct netr_Authenticator auth, auth2; + struct netr_NetworkInfo ninfo; + DATA_BLOB names_blob, chal, lm_resp, nt_resp; + int i; + int flags = CLI_CRED_NTLM_AUTH; + if (lp_client_lanman_auth(tctx->lp_ctx)) { + flags |= CLI_CRED_LANMAN_AUTH; + } + + if (lp_client_ntlmv2_auth(tctx->lp_ctx)) { + flags |= CLI_CRED_NTLMv2_AUTH; + } + + cli_credentials_get_ntlm_username_domain(cmdline_credentials, tctx, + &ninfo.identity_info.account_name.string, + &ninfo.identity_info.domain_name.string); + + generate_random_buffer(ninfo.challenge, + sizeof(ninfo.challenge)); + chal = data_blob_const(ninfo.challenge, + sizeof(ninfo.challenge)); + + names_blob = NTLMv2_generate_names_blob(tctx, lp_iconv_convenience(tctx->lp_ctx), cli_credentials_get_workstation(credentials), + cli_credentials_get_domain(credentials)); + + status = cli_credentials_get_ntlm_response(cmdline_credentials, tctx, + &flags, + chal, + names_blob, + &lm_resp, &nt_resp, + NULL, NULL); + torture_assert_ntstatus_ok(tctx, status, "cli_credentials_get_ntlm_response failed"); + + ninfo.lm.data = lm_resp.data; + ninfo.lm.length = lm_resp.length; + + ninfo.nt.data = nt_resp.data; + ninfo.nt.length = nt_resp.length; + + ninfo.identity_info.parameter_control = 0; + ninfo.identity_info.logon_id_low = 0; + ninfo.identity_info.logon_id_high = 0; + ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials); + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(credentials); + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = 2; + r.in.logon.network = &ninfo; + + d_printf("Testing LogonSamLogon with name %s\n", ninfo.identity_info.account_name.string); + + for (i=2;i<3;i++) { + ZERO_STRUCT(auth2); + creds_client_authenticator(creds, &auth); + + r.in.validation_level = i; + + status = dcerpc_netr_LogonSamLogon(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed"); + + torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred), + "Credential chaining failed"); + } + + r.in.credential = NULL; + + for (i=2;i<=3;i++) { + + r.in.validation_level = i; + + torture_comment(tctx, "Testing SamLogon with validation level %d and a NULL credential\n", i); + + status = dcerpc_netr_LogonSamLogon(p, tctx, &r); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_PARAMETER, + "LogonSamLogon expected INVALID_PARAMETER"); + + } + + return true; +} + +/* + try a netlogon SamLogon +*/ +static bool test_SamLogon(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct creds_CredentialState *creds; + + if (!test_SetupCredentials(p, tctx, credentials, &creds)) { + return false; + } + + return test_netlogon_ops(p, tctx, credentials, creds); +} + +/* we remember the sequence numbers so we can easily do a DatabaseDelta */ +static uint64_t sequence_nums[3]; + +/* + try a netlogon DatabaseSync +*/ +static bool test_DatabaseSync(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct netr_DatabaseSync r; + struct creds_CredentialState *creds; + const uint32_t database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS}; + int i; + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.preferredmaximumlength = (uint32_t)-1; + ZERO_STRUCT(r.in.return_authenticator); + + for (i=0;i<ARRAY_SIZE(database_ids);i++) { + r.in.sync_context = 0; + r.in.database_id = database_ids[i]; + + torture_comment(tctx, "Testing DatabaseSync of id %d\n", r.in.database_id); + + do { + creds_client_authenticator(creds, &r.in.credential); + + status = dcerpc_netr_DatabaseSync(p, tctx, &r); + if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) + break; + + /* Native mode servers don't do this */ + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + return true; + } + torture_assert_ntstatus_ok(tctx, status, "DatabaseSync"); + + if (!creds_client_check(creds, &r.out.return_authenticator.cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + r.in.sync_context = r.out.sync_context; + + if (r.out.delta_enum_array && + r.out.delta_enum_array->num_deltas > 0 && + r.out.delta_enum_array->delta_enum[0].delta_type == NETR_DELTA_DOMAIN && + r.out.delta_enum_array->delta_enum[0].delta_union.domain) { + sequence_nums[r.in.database_id] = + r.out.delta_enum_array->delta_enum[0].delta_union.domain->sequence_num; + torture_comment(tctx, "\tsequence_nums[%d]=%llu\n", + r.in.database_id, + (unsigned long long)sequence_nums[r.in.database_id]); + } + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + } + + return true; +} + + +/* + try a netlogon DatabaseDeltas +*/ +static bool test_DatabaseDeltas(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct netr_DatabaseDeltas r; + struct creds_CredentialState *creds; + const uint32_t database_ids[] = {0, 1, 2}; + int i; + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.preferredmaximumlength = (uint32_t)-1; + ZERO_STRUCT(r.in.return_authenticator); + + for (i=0;i<ARRAY_SIZE(database_ids);i++) { + r.in.database_id = database_ids[i]; + r.in.sequence_num = sequence_nums[r.in.database_id]; + + if (r.in.sequence_num == 0) continue; + + r.in.sequence_num -= 1; + + torture_comment(tctx, "Testing DatabaseDeltas of id %d at %llu\n", + r.in.database_id, (unsigned long long)r.in.sequence_num); + + do { + creds_client_authenticator(creds, &r.in.credential); + + status = dcerpc_netr_DatabaseDeltas(p, tctx, &r); + if (NT_STATUS_EQUAL(status, + NT_STATUS_SYNCHRONIZATION_REQUIRED)) { + torture_comment(tctx, "not considering %s to be an error\n", + nt_errstr(status)); + return true; + } + if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) + break; + + torture_assert_ntstatus_ok(tctx, status, "DatabaseDeltas"); + + if (!creds_client_check(creds, &r.out.return_authenticator.cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + r.in.sequence_num++; + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + } + + return true; +} + + +/* + try a netlogon AccountDeltas +*/ +static bool test_AccountDeltas(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct netr_AccountDeltas r; + struct creds_CredentialState *creds; + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computername = TEST_MACHINE_NAME; + ZERO_STRUCT(r.in.return_authenticator); + creds_client_authenticator(creds, &r.in.credential); + ZERO_STRUCT(r.in.uas); + r.in.count=10; + r.in.level=0; + r.in.buffersize=100; + + /* w2k3 returns "NOT IMPLEMENTED" for this call */ + status = dcerpc_netr_AccountDeltas(p, tctx, &r); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_IMPLEMENTED, "AccountDeltas"); + + return true; +} + +/* + try a netlogon AccountSync +*/ +static bool test_AccountSync(struct torture_context *tctx, struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct netr_AccountSync r; + struct creds_CredentialState *creds; + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computername = TEST_MACHINE_NAME; + ZERO_STRUCT(r.in.return_authenticator); + creds_client_authenticator(creds, &r.in.credential); + ZERO_STRUCT(r.in.recordid); + r.in.reference=0; + r.in.level=0; + r.in.buffersize=100; + + /* w2k3 returns "NOT IMPLEMENTED" for this call */ + status = dcerpc_netr_AccountSync(p, tctx, &r); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_IMPLEMENTED, "AccountSync"); + + return true; +} + +/* + try a netlogon GetDcName +*/ +static bool test_GetDcName(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_GetDcName r; + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.domainname = lp_workgroup(tctx->lp_ctx); + + status = dcerpc_netr_GetDcName(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetDcName"); + torture_assert_werr_ok(tctx, r.out.result, "GetDcName"); + + torture_comment(tctx, "\tDC is at '%s'\n", r.out.dcname); + + return true; +} + +/* + try a netlogon LogonControl +*/ +static bool test_LogonControl(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_LogonControl r; + int i; + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.function_code = 1; + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl level %d\n", i); + + status = dcerpc_netr_LogonControl(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl"); + } + + return true; +} + + +/* + try a netlogon GetAnyDCName +*/ +static bool test_GetAnyDCName(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_GetAnyDCName r; + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.domainname = lp_workgroup(tctx->lp_ctx); + + status = dcerpc_netr_GetAnyDCName(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetAnyDCName"); + + if (r.out.dcname) { + torture_comment(tctx, "\tDC is at '%s'\n", r.out.dcname); + } + + return true; +} + + +/* + try a netlogon LogonControl2 +*/ +static bool test_LogonControl2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_LogonControl2 r; + int i; + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + r.in.function_code = NETLOGON_CONTROL_REDISCOVER; + r.in.data.domain = lp_workgroup(tctx->lp_ctx); + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2 level %d function %d\n", + i, r.in.function_code); + + status = dcerpc_netr_LogonControl2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl"); + } + + r.in.function_code = NETLOGON_CONTROL_TC_QUERY; + r.in.data.domain = lp_workgroup(tctx->lp_ctx); + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2 level %d function %d\n", + i, r.in.function_code); + + status = dcerpc_netr_LogonControl2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl"); + } + + r.in.function_code = NETLOGON_CONTROL_TRANSPORT_NOTIFY; + r.in.data.domain = lp_workgroup(tctx->lp_ctx); + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2 level %d function %d\n", + i, r.in.function_code); + + status = dcerpc_netr_LogonControl2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl"); + } + + r.in.function_code = NETLOGON_CONTROL_SET_DBFLAG; + r.in.data.debug_level = ~0; + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2 level %d function %d\n", + i, r.in.function_code); + + status = dcerpc_netr_LogonControl2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl"); + } + + return true; +} + +/* + try a netlogon DatabaseSync2 +*/ +static bool test_DatabaseSync2(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct netr_DatabaseSync2 r; + struct creds_CredentialState *creds; + const uint32_t database_ids[] = {0, 1, 2}; + int i; + + if (!test_SetupCredentials2(p, tctx, NETLOGON_NEG_AUTH2_FLAGS, + machine_credentials, + SEC_CHAN_BDC, &creds)) { + return false; + } + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.preferredmaximumlength = (uint32_t)-1; + ZERO_STRUCT(r.in.return_authenticator); + + for (i=0;i<ARRAY_SIZE(database_ids);i++) { + r.in.sync_context = 0; + r.in.database_id = database_ids[i]; + r.in.restart_state = 0; + + torture_comment(tctx, "Testing DatabaseSync2 of id %d\n", r.in.database_id); + + do { + creds_client_authenticator(creds, &r.in.credential); + + status = dcerpc_netr_DatabaseSync2(p, tctx, &r); + if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) + break; + + /* Native mode servers don't do this */ + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + return true; + } + + torture_assert_ntstatus_ok(tctx, status, "DatabaseSync2"); + + if (!creds_client_check(creds, &r.out.return_authenticator.cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + r.in.sync_context = r.out.sync_context; + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + } + + return true; +} + + +/* + try a netlogon LogonControl2Ex +*/ +static bool test_LogonControl2Ex(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_LogonControl2Ex r; + int i; + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + r.in.function_code = NETLOGON_CONTROL_REDISCOVER; + r.in.data.domain = lp_workgroup(tctx->lp_ctx); + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2Ex level %d function %d\n", + i, r.in.function_code); + + status = dcerpc_netr_LogonControl2Ex(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl"); + } + + r.in.function_code = NETLOGON_CONTROL_TC_QUERY; + r.in.data.domain = lp_workgroup(tctx->lp_ctx); + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2Ex level %d function %d\n", + i, r.in.function_code); + + status = dcerpc_netr_LogonControl2Ex(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl"); + } + + r.in.function_code = NETLOGON_CONTROL_TRANSPORT_NOTIFY; + r.in.data.domain = lp_workgroup(tctx->lp_ctx); + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2Ex level %d function %d\n", + i, r.in.function_code); + + status = dcerpc_netr_LogonControl2Ex(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl"); + } + + r.in.function_code = NETLOGON_CONTROL_SET_DBFLAG; + r.in.data.debug_level = ~0; + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2Ex level %d function %d\n", + i, r.in.function_code); + + status = dcerpc_netr_LogonControl2Ex(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl"); + } + + return true; +} + +static bool test_netr_DsRGetForestTrustInformation(struct torture_context *tctx, + struct dcerpc_pipe *p, const char *trusted_domain_name) +{ + NTSTATUS status; + struct netr_DsRGetForestTrustInformation r; + struct lsa_ForestTrustInformation info, *info_ptr; + + info_ptr = &info; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.trusted_domain_name = trusted_domain_name; + r.in.flags = 0; + r.out.forest_trust_info = &info_ptr; + + torture_comment(tctx ,"Testing netr_DsRGetForestTrustInformation\n"); + + status = dcerpc_netr_DsRGetForestTrustInformation(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "DsRGetForestTrustInformation"); + torture_assert_werr_ok(tctx, r.out.result, "DsRGetForestTrustInformation"); + + return true; +} + +/* + try a netlogon netr_DsrEnumerateDomainTrusts +*/ +static bool test_DsrEnumerateDomainTrusts(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_DsrEnumerateDomainTrusts r; + int i; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.trust_flags = 0x3f; + + status = dcerpc_netr_DsrEnumerateDomainTrusts(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "DsrEnumerateDomaintrusts"); + torture_assert_werr_ok(tctx, r.out.result, "DsrEnumerateDomaintrusts"); + + /* when trusted_domain_name is NULL, netr_DsRGetForestTrustInformation + * will show non-forest trusts and all UPN suffixes of the own forest + * as LSA_FOREST_TRUST_TOP_LEVEL_NAME types */ + + if (r.out.count) { + if (!test_netr_DsRGetForestTrustInformation(tctx, p, NULL)) { + return false; + } + } + + for (i=0; i<r.out.count; i++) { + + /* get info for transitive forest trusts */ + + if (r.out.trusts[i].trust_attributes & NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) { + if (!test_netr_DsRGetForestTrustInformation(tctx, p, + r.out.trusts[i].dns_name)) { + return false; + } + } + } + + return true; +} + +static bool test_netr_NetrEnumerateTrustedDomains(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_NetrEnumerateTrustedDomains r; + struct netr_Blob trusted_domains_blob; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.out.trusted_domains_blob = &trusted_domains_blob; + + status = dcerpc_netr_NetrEnumerateTrustedDomains(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "netr_NetrEnumerateTrustedDomains"); + torture_assert_werr_ok(tctx, r.out.result, "NetrEnumerateTrustedDomains"); + + return true; +} + +static bool test_netr_NetrEnumerateTrustedDomainsEx(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_NetrEnumerateTrustedDomainsEx r; + struct netr_DomainTrustList dom_trust_list; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.out.dom_trust_list = &dom_trust_list; + + status = dcerpc_netr_NetrEnumerateTrustedDomainsEx(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "netr_NetrEnumerateTrustedDomainsEx"); + torture_assert_werr_ok(tctx, r.out.result, "NetrEnumerateTrustedDomainsEx"); + + return true; +} + + +static bool test_netr_DsRGetSiteName(struct dcerpc_pipe *p, struct torture_context *tctx, + const char *computer_name, + const char *expected_site) +{ + NTSTATUS status; + struct netr_DsRGetSiteName r; + + if (torture_setting_bool(tctx, "samba4", false)) + torture_skip(tctx, "skipping DsRGetSiteName test against Samba4"); + + r.in.computer_name = computer_name; + torture_comment(tctx, "Testing netr_DsRGetSiteName\n"); + + status = dcerpc_netr_DsRGetSiteName(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "DsRGetSiteName"); + torture_assert_werr_ok(tctx, r.out.result, "DsRGetSiteName"); + torture_assert_str_equal(tctx, expected_site, r.out.site, "netr_DsRGetSiteName"); + + r.in.computer_name = talloc_asprintf(tctx, "\\\\%s", computer_name); + torture_comment(tctx, + "Testing netr_DsRGetSiteName with broken computer name: %s\n", r.in.computer_name); + + status = dcerpc_netr_DsRGetSiteName(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "DsRGetSiteName"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_COMPUTERNAME, "netr_DsRGetSiteName"); + + return true; +} + +/* + try a netlogon netr_DsRGetDCName +*/ +static bool test_netr_DsRGetDCName(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_DsRGetDCName r; + + r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.domain_name = talloc_asprintf(tctx, "%s", lp_realm(tctx->lp_ctx)); + r.in.domain_guid = NULL; + r.in.site_guid = NULL; + r.in.flags = DS_RETURN_DNS_NAME; + + status = dcerpc_netr_DsRGetDCName(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "DsRGetDCName"); + torture_assert_werr_ok(tctx, r.out.result, "DsRGetDCName"); + return test_netr_DsRGetSiteName(p, tctx, + r.out.info->dc_unc, + r.out.info->dc_site_name); +} + +/* + try a netlogon netr_DsRGetDCNameEx +*/ +static bool test_netr_DsRGetDCNameEx(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_DsRGetDCNameEx r; + + r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.domain_name = talloc_asprintf(tctx, "%s", lp_realm(tctx->lp_ctx)); + r.in.domain_guid = NULL; + r.in.site_name = NULL; + r.in.flags = DS_RETURN_DNS_NAME; + + status = dcerpc_netr_DsRGetDCNameEx(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx"); + torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx"); + + return test_netr_DsRGetSiteName(p, tctx, r.out.info->dc_unc, + r.out.info->dc_site_name); +} + +/* + try a netlogon netr_DsRGetDCNameEx2 +*/ +static bool test_netr_DsRGetDCNameEx2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_DsRGetDCNameEx2 r; + + r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.client_account = NULL; + r.in.mask = 0x00000000; + r.in.domain_name = talloc_asprintf(tctx, "%s", lp_realm(tctx->lp_ctx)); + r.in.domain_guid = NULL; + r.in.site_name = NULL; + r.in.flags = DS_RETURN_DNS_NAME; + + torture_comment(tctx, "Testing netr_DsRGetDCNameEx2 without client account\n"); + + status = dcerpc_netr_DsRGetDCNameEx2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx2"); + torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx2"); + + torture_comment(tctx, "Testing netr_DsRGetDCNameEx2 with client acount\n"); + r.in.client_account = TEST_MACHINE_NAME"$"; + r.in.mask = ACB_SVRTRUST; + r.in.flags = DS_RETURN_FLAT_NAME; + + status = dcerpc_netr_DsRGetDCNameEx2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx2"); + torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx2"); + return test_netr_DsRGetSiteName(p, tctx, r.out.info->dc_unc, + r.out.info->dc_site_name); +} + +static bool test_netr_DsrGetDcSiteCoverageW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_DsrGetDcSiteCoverageW r; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + status = dcerpc_netr_DsrGetDcSiteCoverageW(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "failed"); + torture_assert_werr_ok(tctx, r.out.result, "failed"); + + return true; +} + +static bool test_netr_DsRAddressToSitenamesW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_DsRAddressToSitenamesW r; + struct netr_DsRAddress addr; + struct netr_DsRAddressToSitenamesWCtr *ctr; + + ctr = talloc(tctx, struct netr_DsRAddressToSitenamesWCtr); + + addr.size = 16; + addr.buffer = talloc_zero_array(tctx, uint8_t, addr.size); + + addr.buffer[0] = 2; /* AF_INET */ + addr.buffer[4] = 127; + addr.buffer[5] = 0; + addr.buffer[6] = 0; + addr.buffer[7] = 1; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.count = 1; + r.in.addresses = talloc_zero_array(tctx, struct netr_DsRAddress, r.in.count); + r.in.addresses[0] = addr; + r.out.ctr = &ctr; + + status = dcerpc_netr_DsRAddressToSitenamesW(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "failed"); + torture_assert_werr_ok(tctx, r.out.result, "failed"); + + return true; +} + +static bool test_netr_DsRAddressToSitenamesExW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_DsRAddressToSitenamesExW r; + struct netr_DsRAddress addr; + struct netr_DsRAddressToSitenamesExWCtr *ctr; + + ctr = talloc(tctx, struct netr_DsRAddressToSitenamesExWCtr); + + addr.size = 16; + addr.buffer = talloc_zero_array(tctx, uint8_t, addr.size); + + addr.buffer[0] = 2; /* AF_INET */ + addr.buffer[4] = 127; + addr.buffer[5] = 0; + addr.buffer[6] = 0; + addr.buffer[7] = 1; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.count = 1; + r.in.addresses = talloc_zero_array(tctx, struct netr_DsRAddress, r.in.count); + r.in.addresses[0] = addr; + r.out.ctr = &ctr; + + status = dcerpc_netr_DsRAddressToSitenamesExW(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "failed"); + torture_assert_werr_ok(tctx, r.out.result, "failed"); + + return true; +} + +static bool test_GetDomainInfo(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct netr_LogonGetDomainInfo r; + struct netr_DomainQuery1 q1; + struct netr_Authenticator a; + struct creds_CredentialState *creds; + + if (!test_SetupCredentials3(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS, + machine_credentials, &creds)) { + return false; + } + + ZERO_STRUCT(r); + + creds_client_authenticator(creds, &a); + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.level = 1; + r.in.credential = &a; + r.in.return_authenticator = &a; + r.out.return_authenticator = &a; + + r.in.query.query1 = &q1; + ZERO_STRUCT(q1); + + /* this should really be the fully qualified name */ + q1.workstation_domain = TEST_MACHINE_NAME; + q1.workstation_site = "Default-First-Site-Name"; + q1.blob2.length = 0; + q1.blob2.size = 0; + q1.blob2.data = NULL; + q1.product.string = "product string"; + + torture_comment(tctx, "Testing netr_uogonGetDomainInfo\n"); + + status = dcerpc_netr_LogonGetDomainInfo(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "netr_LogonGetDomainInfo"); + torture_assert(tctx, creds_client_check(creds, &a.cred), "Credential chaining failed"); + + return true; +} + + +static void async_callback(struct rpc_request *req) +{ + int *counter = (int *)req->async.private_data; + if (NT_STATUS_IS_OK(req->status)) { + (*counter)++; + } +} + +static bool test_GetDomainInfo_async(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct netr_LogonGetDomainInfo r; + struct netr_DomainQuery1 q1; + struct netr_Authenticator a; +#define ASYNC_COUNT 100 + struct creds_CredentialState *creds; + struct creds_CredentialState *creds_async[ASYNC_COUNT]; + struct rpc_request *req[ASYNC_COUNT]; + int i; + int *async_counter = talloc(tctx, int); + + if (!test_SetupCredentials3(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS, + machine_credentials, &creds)) { + return false; + } + + ZERO_STRUCT(r); + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.level = 1; + r.in.credential = &a; + r.in.return_authenticator = &a; + r.out.return_authenticator = &a; + + r.in.query.query1 = &q1; + ZERO_STRUCT(q1); + + /* this should really be the fully qualified name */ + q1.workstation_domain = TEST_MACHINE_NAME; + q1.workstation_site = "Default-First-Site-Name"; + q1.blob2.length = 0; + q1.blob2.size = 0; + q1.blob2.data = NULL; + q1.product.string = "product string"; + + torture_comment(tctx, "Testing netr_LogonGetDomainInfo - async count %d\n", ASYNC_COUNT); + + *async_counter = 0; + + for (i=0;i<ASYNC_COUNT;i++) { + creds_client_authenticator(creds, &a); + + creds_async[i] = (struct creds_CredentialState *)talloc_memdup(creds, creds, sizeof(*creds)); + req[i] = dcerpc_netr_LogonGetDomainInfo_send(p, tctx, &r); + + req[i]->async.callback = async_callback; + req[i]->async.private_data = async_counter; + + /* even with this flush per request a w2k3 server seems to + clag with multiple outstanding requests. bleergh. */ + torture_assert_int_equal(tctx, event_loop_once(dcerpc_event_context(p)), 0, + "event_loop_once failed"); + } + + for (i=0;i<ASYNC_COUNT;i++) { + status = dcerpc_ndr_request_recv(req[i]); + + torture_assert_ntstatus_ok(tctx, status, "netr_LogonGetDomainInfo_async"); + torture_assert_ntstatus_ok(tctx, r.out.result, "netr_LogonGetDomainInfo_async"); + + torture_assert(tctx, creds_client_check(creds_async[i], &a.cred), + "Credential chaining failed at async"); + } + + torture_comment(tctx, + "Testing netr_LogonGetDomainInfo - async count %d OK\n", *async_counter); + + torture_assert_int_equal(tctx, (*async_counter), ASYNC_COUNT, "int"); + + return true; +} + +static bool test_ManyGetDCName(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct dcerpc_pipe *p2; + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy2 o; + struct policy_handle lsa_handle; + struct lsa_DomainList domains; + + struct lsa_EnumTrustDom t; + uint32_t resume_handle = 0; + struct netr_GetAnyDCName d; + + int i; + + if (p->conn->transport.transport != NCACN_NP) { + return true; + } + + torture_comment(tctx, "Torturing GetDCName\n"); + + status = dcerpc_secondary_connection(p, &p2, p->binding); + torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection"); + + status = dcerpc_bind_auth_none(p2, &ndr_table_lsarpc); + torture_assert_ntstatus_ok(tctx, status, "Failed to create bind on secondary connection"); + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + o.in.system_name = "\\"; + o.in.attr = &attr; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.out.handle = &lsa_handle; + + status = dcerpc_lsa_OpenPolicy2(p2, tctx, &o); + torture_assert_ntstatus_ok(tctx, status, "OpenPolicy2 failed"); + + t.in.handle = &lsa_handle; + t.in.resume_handle = &resume_handle; + t.in.max_size = 1000; + t.out.domains = &domains; + t.out.resume_handle = &resume_handle; + + status = dcerpc_lsa_EnumTrustDom(p2, tctx, &t); + + if ((!NT_STATUS_IS_OK(status) && + (!NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)))) + torture_fail(tctx, "Could not list domains"); + + talloc_free(p2); + + d.in.logon_server = talloc_asprintf(tctx, "\\\\%s", + dcerpc_server_name(p)); + + for (i=0; i<domains.count * 4; i++) { + struct lsa_DomainInfo *info = + &domains.domains[rand()%domains.count]; + + d.in.domainname = info->name.string; + + status = dcerpc_netr_GetAnyDCName(p, tctx, &d); + torture_assert_ntstatus_ok(tctx, status, "GetAnyDCName"); + + torture_comment(tctx, "\tDC for domain %s is %s\n", info->name.string, + d.out.dcname ? d.out.dcname : "unknown"); + } + + return true; +} + +struct torture_suite *torture_rpc_netlogon(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "NETLOGON"); + struct torture_rpc_tcase *tcase; + struct torture_test *test; + + tcase = torture_suite_add_machine_rpc_iface_tcase(suite, "netlogon", + &ndr_table_netlogon, TEST_MACHINE_NAME); + torture_rpc_tcase_add_test(tcase, "LogonUasLogon", test_LogonUasLogon); + torture_rpc_tcase_add_test(tcase, "LogonUasLogoff", test_LogonUasLogoff); + torture_rpc_tcase_add_test_creds(tcase, "SamLogon", test_SamLogon); + torture_rpc_tcase_add_test_creds(tcase, "SetPassword", test_SetPassword); + torture_rpc_tcase_add_test_creds(tcase, "SetPassword2", test_SetPassword2); + torture_rpc_tcase_add_test_creds(tcase, "GetPassword", test_GetPassword); + torture_rpc_tcase_add_test_creds(tcase, "GetTrustPasswords", test_GetTrustPasswords); + torture_rpc_tcase_add_test_creds(tcase, "GetDomainInfo", test_GetDomainInfo); + torture_rpc_tcase_add_test_creds(tcase, "DatabaseSync", test_DatabaseSync); + torture_rpc_tcase_add_test_creds(tcase, "DatabaseDeltas", test_DatabaseDeltas); + torture_rpc_tcase_add_test_creds(tcase, "AccountDeltas", test_AccountDeltas); + torture_rpc_tcase_add_test_creds(tcase, "AccountSync", test_AccountSync); + torture_rpc_tcase_add_test(tcase, "GetDcName", test_GetDcName); + torture_rpc_tcase_add_test(tcase, "ManyGetDCName", test_ManyGetDCName); + torture_rpc_tcase_add_test(tcase, "LogonControl", test_LogonControl); + torture_rpc_tcase_add_test(tcase, "GetAnyDCName", test_GetAnyDCName); + torture_rpc_tcase_add_test(tcase, "LogonControl2", test_LogonControl2); + torture_rpc_tcase_add_test_creds(tcase, "DatabaseSync2", test_DatabaseSync2); + torture_rpc_tcase_add_test(tcase, "LogonControl2Ex", test_LogonControl2Ex); + torture_rpc_tcase_add_test(tcase, "DsrEnumerateDomainTrusts", test_DsrEnumerateDomainTrusts); + torture_rpc_tcase_add_test(tcase, "NetrEnumerateTrustedDomains", test_netr_NetrEnumerateTrustedDomains); + torture_rpc_tcase_add_test(tcase, "NetrEnumerateTrustedDomainsEx", test_netr_NetrEnumerateTrustedDomainsEx); + test = torture_rpc_tcase_add_test_creds(tcase, "GetDomainInfo_async", test_GetDomainInfo_async); + test->dangerous = true; + torture_rpc_tcase_add_test(tcase, "DsRGetDCName", test_netr_DsRGetDCName); + torture_rpc_tcase_add_test(tcase, "DsRGetDCNameEx", test_netr_DsRGetDCNameEx); + torture_rpc_tcase_add_test(tcase, "DsRGetDCNameEx2", test_netr_DsRGetDCNameEx2); + torture_rpc_tcase_add_test(tcase, "DsrGetDcSiteCoverageW", test_netr_DsrGetDcSiteCoverageW); + torture_rpc_tcase_add_test(tcase, "DsRAddressToSitenamesW", test_netr_DsRAddressToSitenamesW); + torture_rpc_tcase_add_test(tcase, "DsRAddressToSitenamesExW", test_netr_DsRAddressToSitenamesExW); + + return suite; +} diff --git a/source4/torture/rpc/netlogon.h b/source4/torture/rpc/netlogon.h new file mode 100644 index 0000000000..92d366b46a --- /dev/null +++ b/source4/torture/rpc/netlogon.h @@ -0,0 +1,6 @@ + +bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx, + uint32_t negotiate_flags, + struct cli_credentials *machine_credentials, + int sec_chan_type, + struct creds_CredentialState **creds_out); diff --git a/source4/torture/rpc/oxidresolve.c b/source4/torture/rpc/oxidresolve.c new file mode 100644 index 0000000000..02edb306b3 --- /dev/null +++ b/source4/torture/rpc/oxidresolve.c @@ -0,0 +1,237 @@ +/* + Unix SMB/CIFS implementation. + test suite for oxidresolve operations + + Copyright (C) Jelmer Vernooij 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_oxidresolver_c.h" +#include "librpc/gen_ndr/ndr_remact_c.h" +#include "librpc/gen_ndr/epmapper.h" +#include "torture/rpc/rpc.h" + +#define CLSID_IMAGEDOC "02B01C80-E03D-101A-B294-00DD010F2BF9" + +const struct GUID IUnknown_uuid = { + 0x00000000,0x0000,0x0000,{0xc0,0x00},{0x00,0x00,0x00,0x00,0x00,0x46} +}; + +static bool test_RemoteActivation(struct torture_context *tctx, + uint64_t *oxid, struct GUID *oid) +{ + struct RemoteActivation r; + NTSTATUS status; + struct GUID iids[2]; + uint16_t protseq[3] = { EPM_PROTOCOL_TCP, EPM_PROTOCOL_NCALRPC, EPM_PROTOCOL_UUID }; + struct dcerpc_pipe *p; + + status = torture_rpc_connection(tctx, &p, + &ndr_table_IRemoteActivation); + + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ZERO_STRUCT(r.in); + r.in.this.version.MajorVersion = 5; + r.in.this.version.MinorVersion = 1; + r.in.this.cid = GUID_random(); + GUID_from_string(CLSID_IMAGEDOC, &r.in.Clsid); + r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY; + r.in.num_protseqs = 3; + r.in.protseq = protseq; + r.in.Interfaces = 1; + iids[0] = IUnknown_uuid; + r.in.pIIDs = iids; + r.out.pOxid = oxid; + r.out.ipidRemUnknown = oid; + + status = dcerpc_RemoteActivation(p, tctx, &r); + if(NT_STATUS_IS_ERR(status)) { + fprintf(stderr, "RemoteActivation: %s\n", nt_errstr(status)); + return false; + } + + if(!W_ERROR_IS_OK(r.out.result)) { + fprintf(stderr, "RemoteActivation: %s\n", win_errstr(r.out.result)); + return false; + } + + if(!W_ERROR_IS_OK(*r.out.hr)) { + fprintf(stderr, "RemoteActivation: %s\n", win_errstr(*r.out.hr)); + return false; + } + + if(!W_ERROR_IS_OK(r.out.results[0])) { + fprintf(stderr, "RemoteActivation: %s\n", win_errstr(r.out.results[0])); + return false; + } + + return true; +} + +static bool test_SimplePing(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct SimplePing r; + NTSTATUS status; + uint64_t setid; + + r.in.SetId = &setid; + + status = dcerpc_SimplePing(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "SimplePing"); + torture_assert_werr_ok(tctx, r.out.result, "SimplePing"); + + return true; +} + +static bool test_ComplexPing(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct ComplexPing r; + NTSTATUS status; + uint64_t setid; + struct GUID oid; + uint64_t oxid; + + if (!test_RemoteActivation(tctx, &oxid, &oid)) + return false; + + setid = 0; + ZERO_STRUCT(r.in); + + r.in.SequenceNum = 0; + r.in.SetId = &setid; + r.in.cAddToSet = 1; + r.in.AddToSet = &oid; + + status = dcerpc_ComplexPing(p, tctx, &r); + if(NT_STATUS_IS_ERR(status)) { + fprintf(stderr, "ComplexPing: %s\n", nt_errstr(status)); + return 0; + } + + if(!W_ERROR_IS_OK(r.out.result)) { + fprintf(stderr, "ComplexPing: %s\n", win_errstr(r.out.result)); + return 0; + } + + + + return 1; +} + +static bool test_ServerAlive(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct ServerAlive r; + NTSTATUS status; + + status = dcerpc_ServerAlive(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ServerAlive"); + torture_assert_werr_ok(tctx, r.out.result, "ServerAlive"); + + return true; +} + +static bool test_ResolveOxid(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct ResolveOxid r; + NTSTATUS status; + uint16_t protseq[2] = { EPM_PROTOCOL_TCP, EPM_PROTOCOL_SMB }; + uint64_t oxid; + struct GUID oid; + + if (!test_RemoteActivation(tctx, &oxid, &oid)) + return false; + + r.in.pOxid = oxid; + r.in.cRequestedProtseqs = 2; + r.in.arRequestedProtseqs = protseq; + + status = dcerpc_ResolveOxid(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ResolveOxid"); + torture_assert_werr_ok(tctx, r.out.result, "ResolveOxid"); + + return true; +} + +static bool test_ResolveOxid2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct ResolveOxid2 r; + NTSTATUS status; + uint16_t protseq[2] = { EPM_PROTOCOL_TCP, EPM_PROTOCOL_SMB }; + uint64_t oxid; + struct GUID oid; + + if (!test_RemoteActivation(tctx, &oxid, &oid)) + return false; + + r.in.pOxid = oxid; + r.in.cRequestedProtseqs = 2; + r.in.arRequestedProtseqs = protseq; + + status = dcerpc_ResolveOxid2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ResolveOxid2"); + + torture_assert_werr_ok(tctx, r.out.result, "ResolveOxid2"); + + torture_comment(tctx, "Remote server versions: %d, %d\n", r.out.ComVersion->MajorVersion, r.out.ComVersion->MinorVersion); + + return true; +} + +static bool test_ServerAlive2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct ServerAlive2 r; + NTSTATUS status; + + status = dcerpc_ServerAlive2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ServerAlive2"); + torture_assert_werr_ok(tctx, r.out.result, "ServerAlive2"); + + return true; +} + +struct torture_suite *torture_rpc_oxidresolve(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, + "OXIDRESOLVE"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "oxidresolver", + &ndr_table_IOXIDResolver); + + torture_rpc_tcase_add_test(tcase, "ServerAlive", test_ServerAlive); + + torture_rpc_tcase_add_test(tcase, "ServerAlive2", test_ServerAlive2); + + torture_rpc_tcase_add_test(tcase, "ComplexPing", test_ComplexPing); + + torture_rpc_tcase_add_test(tcase, "SimplePing", test_SimplePing); + + torture_rpc_tcase_add_test(tcase, "ResolveOxid", test_ResolveOxid); + + torture_rpc_tcase_add_test(tcase, "ResolveOxid2", test_ResolveOxid2); + + return suite; +} diff --git a/source4/torture/rpc/remact.c b/source4/torture/rpc/remact.c new file mode 100644 index 0000000000..1fc3c1376b --- /dev/null +++ b/source4/torture/rpc/remact.c @@ -0,0 +1,89 @@ +/* + Unix SMB/CIFS implementation. + test suite for remoteactivation operations + + Copyright (C) Jelmer Vernooij 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_remact_c.h" +#include "librpc/gen_ndr/ndr_epmapper_c.h" +#include "torture/rpc/rpc.h" + +#define CLSID_IMAGEDOC "02B01C80-E03D-101A-B294-00DD010F2BF9" +#define DCERPC_IUNKNOWN_UUID "00000000-0000-0000-c000-000000000046" +#define DCERPC_ICLASSFACTORY_UUID "00000001-0000-0000-c000-000000000046" + +static bool test_RemoteActivation(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct RemoteActivation r; + NTSTATUS status; + struct GUID iids[1]; + uint16_t protseq[3] = { EPM_PROTOCOL_TCP, EPM_PROTOCOL_NCALRPC, EPM_PROTOCOL_UUID }; + + ZERO_STRUCT(r.in); + r.in.this.version.MajorVersion = 5; + r.in.this.version.MinorVersion = 1; + r.in.this.cid = GUID_random(); + GUID_from_string(CLSID_IMAGEDOC, &r.in.Clsid); + r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY; + r.in.num_protseqs = 3; + r.in.protseq = protseq; + r.in.Interfaces = 1; + GUID_from_string(DCERPC_IUNKNOWN_UUID, &iids[0]); + r.in.pIIDs = iids; + + status = dcerpc_RemoteActivation(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "RemoteActivation"); + + torture_assert_werr_ok(tctx, r.out.result, "RemoteActivation"); + + torture_assert_werr_ok(tctx, *r.out.hr, "RemoteActivation"); + + torture_assert_werr_ok(tctx, r.out.results[0], "RemoteActivation"); + + GUID_from_string(DCERPC_ICLASSFACTORY_UUID, &iids[0]); + r.in.Interfaces = 1; + r.in.Mode = MODE_GET_CLASS_OBJECT; + + status = dcerpc_RemoteActivation(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "RemoteActivation(GetClassObject)"); + + torture_assert_werr_ok(tctx, r.out.result, + "RemoteActivation(GetClassObject)"); + + torture_assert_werr_ok(tctx, *r.out.hr, "RemoteActivation(GetClassObject)"); + + torture_assert_werr_ok(tctx, r.out.results[0], + "RemoteActivation(GetClassObject)"); + + return true; +} + +struct torture_suite *torture_rpc_remact(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "REMACT"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "remact", &ndr_table_IRemoteActivation); + + torture_rpc_tcase_add_test(tcase, "RemoteActivation", test_RemoteActivation); + + return suite; +} diff --git a/source4/torture/rpc/remote_pac.c b/source4/torture/rpc/remote_pac.c new file mode 100644 index 0000000000..6419e40014 --- /dev/null +++ b/source4/torture/rpc/remote_pac.c @@ -0,0 +1,380 @@ +/* + Unix SMB/CIFS implementation. + + test suite for netlogon PAC operations + + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "lib/events/events.h" +#include "auth/auth.h" +#include "auth/gensec/gensec.h" +#include "lib/cmdline/popt_common.h" +#include "torture/rpc/rpc.h" +#include "torture/rpc/netlogon.h" +#include "libcli/auth/libcli_auth.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_krb5pac.h" +#include "param/param.h" +#include "lib/messaging/irpc.h" +#include "cluster/cluster.h" + +#define TEST_MACHINE_NAME "torturepactest" + +/* Check to see if we can pass the PAC across to the NETLOGON server for validation */ + +/* Also happens to be a really good one-step verfication of our Kerberos stack */ + +static bool test_PACVerify(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + NTSTATUS status; + + struct netr_LogonSamLogon r; + + struct netr_GenericInfo generic; + struct netr_Authenticator auth, auth2; + + + struct creds_CredentialState *creds; + struct gensec_security *gensec_client_context; + struct gensec_security *gensec_server_context; + + struct messaging_context *msg_server_ctx; + DATA_BLOB client_to_server, server_to_client, pac_wrapped, payload; + struct PAC_Validate pac_wrapped_struct; + + enum ndr_err_code ndr_err; + + struct auth_session_info *session_info; + + char *tmp_dir; + + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + + int i; + + torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed"); + + if (!test_SetupCredentials2(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS, + credentials, SEC_CHAN_BDC, + &creds)) { + return false; + } + + status = torture_temp_dir(tctx, "PACVerify", &tmp_dir); + torture_assert_ntstatus_ok(tctx, status, "torture_temp_dir failed"); + + msg_server_ctx = messaging_init(tctx, + tmp_dir, + cluster_id(0, 1), + lp_iconv_convenience(tctx->lp_ctx), + tctx->ev); + + torture_assert(tctx, msg_server_ctx != NULL, "Failed to init messaging context"); + + status = gensec_client_start(tctx, &gensec_client_context, tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, "gensec_client_start (client) failed"); + + status = gensec_set_target_hostname(gensec_client_context, TEST_MACHINE_NAME); + + status = gensec_set_credentials(gensec_client_context, cmdline_credentials); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (client) failed"); + + status = gensec_start_mech_by_sasl_name(gensec_client_context, "GSSAPI"); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (client) failed"); + + status = gensec_server_start(tctx, tctx->ev, tctx->lp_ctx, msg_server_ctx, &gensec_server_context); + torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed"); + + status = gensec_set_credentials(gensec_server_context, credentials); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (server) failed"); + + status = gensec_start_mech_by_sasl_name(gensec_server_context, "GSSAPI"); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (server) failed"); + + server_to_client = data_blob(NULL, 0); + + do { + /* Do a client-server update dance */ + status = gensec_update(gensec_client_context, tmp_ctx, server_to_client, &client_to_server); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (client) failed"); + } + + if (client_to_server.length == 0) { + break; + } + + status = gensec_update(gensec_server_context, tmp_ctx, client_to_server, &server_to_client); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (server) failed"); + } + + if (server_to_client.length == 0) { + break; + } + } while (1); + + /* Extract the PAC using Samba's code */ + + status = gensec_session_info(gensec_server_context, &session_info); + torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed"); + + pac_wrapped_struct.MessageType = 0x3; + pac_wrapped_struct.ChecksumLength = session_info->server_info->pac_srv_sig.signature.length; + pac_wrapped_struct.SignatureType = session_info->server_info->pac_kdc_sig.type; + pac_wrapped_struct.SignatureLength = session_info->server_info->pac_kdc_sig.signature.length; + pac_wrapped_struct.ChecksumAndSignature = payload + = data_blob_talloc(tmp_ctx, NULL, + pac_wrapped_struct.ChecksumLength + + pac_wrapped_struct.SignatureLength); + memcpy(&payload.data[0], + session_info->server_info->pac_srv_sig.signature.data, + pac_wrapped_struct.ChecksumLength); + memcpy(&payload.data[pac_wrapped_struct.ChecksumLength], + session_info->server_info->pac_kdc_sig.signature.data, + pac_wrapped_struct.SignatureLength); + + ndr_err = ndr_push_struct_blob(&pac_wrapped, tmp_ctx, lp_iconv_convenience(tctx->lp_ctx), &pac_wrapped_struct, + (ndr_push_flags_fn_t)ndr_push_PAC_Validate); + torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_push_struct_blob of PACValidate structure failed"); + + torture_assert(tctx, (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR), "not willing to even try a PACValidate without RC4 encryption"); + creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length); + + generic.length = pac_wrapped.length; + generic.data = pac_wrapped.data; + + /* Validate it over the netlogon pipe */ + + generic.identity_info.parameter_control = 0; + generic.identity_info.logon_id_high = 0; + generic.identity_info.logon_id_low = 0; + generic.identity_info.domain_name.string = session_info->server_info->domain_name; + generic.identity_info.account_name.string = session_info->server_info->account_name; + generic.identity_info.workstation.string = TEST_MACHINE_NAME; + + generic.package_name.string = "Kerberos"; + + ZERO_STRUCT(auth2); + creds_client_authenticator(creds, &auth); + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = NetlogonGenericInformation; + r.in.logon.generic = &generic; + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(credentials); + r.in.validation_level = NetlogonValidationGenericInfo2; + + status = dcerpc_netr_LogonSamLogon(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed"); + + /* This will break the signature nicely (even in the crypto wrapping), check we get a logon failure */ + generic.data[generic.length-1]++; + + ZERO_STRUCT(auth2); + creds_client_authenticator(creds, &auth); + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = NetlogonGenericInformation; + r.in.logon.generic = &generic; + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(credentials); + r.in.validation_level = NetlogonValidationGenericInfo2; + + status = dcerpc_netr_LogonSamLogon(p, tctx, &r); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOGON_FAILURE, "LogonSamLogon failed"); + + torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred), + "Credential chaining failed"); + + /* This will break message type, check that however we still get NT_STATUS_OK */ + for (i=0; i < 256; i++) { + pac_wrapped_struct.MessageType = i; + pac_wrapped_struct.ChecksumLength = session_info->server_info->pac_srv_sig.signature.length; + pac_wrapped_struct.SignatureType = session_info->server_info->pac_kdc_sig.type; + pac_wrapped_struct.SignatureLength = session_info->server_info->pac_kdc_sig.signature.length; + pac_wrapped_struct.ChecksumAndSignature = payload + = data_blob_talloc(tmp_ctx, NULL, + pac_wrapped_struct.ChecksumLength + + pac_wrapped_struct.SignatureLength); + memcpy(&payload.data[0], + session_info->server_info->pac_srv_sig.signature.data, + pac_wrapped_struct.ChecksumLength); + memcpy(&payload.data[pac_wrapped_struct.ChecksumLength], + session_info->server_info->pac_kdc_sig.signature.data, + pac_wrapped_struct.SignatureLength); + + ndr_err = ndr_push_struct_blob(&pac_wrapped, tmp_ctx, lp_iconv_convenience(tctx->lp_ctx), &pac_wrapped_struct, + (ndr_push_flags_fn_t)ndr_push_PAC_Validate); + torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_push_struct_blob of PACValidate structure failed"); + + torture_assert(tctx, (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR), "not willing to even try a PACValidate without RC4 encryption"); + creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length); + + generic.length = pac_wrapped.length; + generic.data = pac_wrapped.data; + + ZERO_STRUCT(auth2); + creds_client_authenticator(creds, &auth); + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = NetlogonGenericInformation; + r.in.logon.generic = &generic; + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(credentials); + r.in.validation_level = NetlogonValidationGenericInfo2; + + status = dcerpc_netr_LogonSamLogon(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed"); + + torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred), + "Credential chaining failed"); + } + + /* This will break the parsing nicely (even in the crypto wrapping), check we get INVALID_PARAMETER */ + generic.length--; + + ZERO_STRUCT(auth2); + creds_client_authenticator(creds, &auth); + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = NetlogonGenericInformation; + r.in.logon.generic = &generic; + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(credentials); + r.in.validation_level = NetlogonValidationGenericInfo2; + + status = dcerpc_netr_LogonSamLogon(p, tctx, &r); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_PARAMETER, "LogonSamLogon failed"); + + torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred), + "Credential chaining failed"); + + pac_wrapped_struct.MessageType = 0x3; + pac_wrapped_struct.ChecksumLength = session_info->server_info->pac_srv_sig.signature.length; + pac_wrapped_struct.SignatureType = session_info->server_info->pac_kdc_sig.type; + + /* Break the SignatureType */ + pac_wrapped_struct.SignatureType++; + + pac_wrapped_struct.SignatureLength = session_info->server_info->pac_kdc_sig.signature.length; + pac_wrapped_struct.ChecksumAndSignature = payload + = data_blob_talloc(tmp_ctx, NULL, + pac_wrapped_struct.ChecksumLength + + pac_wrapped_struct.SignatureLength); + memcpy(&payload.data[0], + session_info->server_info->pac_srv_sig.signature.data, + pac_wrapped_struct.ChecksumLength); + memcpy(&payload.data[pac_wrapped_struct.ChecksumLength], + session_info->server_info->pac_kdc_sig.signature.data, + pac_wrapped_struct.SignatureLength); + + ndr_err = ndr_push_struct_blob(&pac_wrapped, tmp_ctx, lp_iconv_convenience(tctx->lp_ctx), &pac_wrapped_struct, + (ndr_push_flags_fn_t)ndr_push_PAC_Validate); + torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_push_struct_blob of PACValidate structure failed"); + + torture_assert(tctx, (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR), "not willing to even try a PACValidate without RC4 encryption"); + creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length); + + generic.length = pac_wrapped.length; + generic.data = pac_wrapped.data; + + ZERO_STRUCT(auth2); + creds_client_authenticator(creds, &auth); + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = NetlogonGenericInformation; + r.in.logon.generic = &generic; + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(credentials); + r.in.validation_level = NetlogonValidationGenericInfo2; + + status = dcerpc_netr_LogonSamLogon(p, tctx, &r); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOGON_FAILURE, "LogonSamLogon failed"); + + torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred), + "Credential chaining failed"); + + + pac_wrapped_struct.MessageType = 0x3; + pac_wrapped_struct.ChecksumLength = session_info->server_info->pac_srv_sig.signature.length; + pac_wrapped_struct.SignatureType = session_info->server_info->pac_kdc_sig.type; + pac_wrapped_struct.SignatureLength = session_info->server_info->pac_kdc_sig.signature.length; + + pac_wrapped_struct.ChecksumAndSignature = payload + = data_blob_talloc(tmp_ctx, NULL, + pac_wrapped_struct.ChecksumLength + + pac_wrapped_struct.SignatureLength); + memcpy(&payload.data[0], + session_info->server_info->pac_srv_sig.signature.data, + pac_wrapped_struct.ChecksumLength); + memcpy(&payload.data[pac_wrapped_struct.ChecksumLength], + session_info->server_info->pac_kdc_sig.signature.data, + pac_wrapped_struct.SignatureLength); + + /* Break the signature length */ + pac_wrapped_struct.SignatureLength++; + + ndr_err = ndr_push_struct_blob(&pac_wrapped, tmp_ctx, lp_iconv_convenience(tctx->lp_ctx), &pac_wrapped_struct, + (ndr_push_flags_fn_t)ndr_push_PAC_Validate); + torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_push_struct_blob of PACValidate structure failed"); + + torture_assert(tctx, (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR), "not willing to even try a PACValidate without RC4 encryption"); + creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length); + + generic.length = pac_wrapped.length; + generic.data = pac_wrapped.data; + + ZERO_STRUCT(auth2); + creds_client_authenticator(creds, &auth); + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = NetlogonGenericInformation; + r.in.logon.generic = &generic; + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(credentials); + r.in.validation_level = NetlogonValidationGenericInfo2; + + status = dcerpc_netr_LogonSamLogon(p, tctx, &r); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_PARAMETER, "LogonSamLogon failed"); + + torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred), + "Credential chaining failed"); + return true; +} + +struct torture_suite *torture_rpc_remote_pac(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "PAC"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_rpc_iface_tcase(suite, "netlogon", + &ndr_table_netlogon, TEST_MACHINE_NAME); + torture_rpc_tcase_add_test_creds(tcase, "verify", test_PACVerify); + + return suite; +} diff --git a/source4/torture/rpc/rpc.c b/source4/torture/rpc/rpc.c new file mode 100644 index 0000000000..85f7bde16c --- /dev/null +++ b/source4/torture/rpc/rpc.c @@ -0,0 +1,446 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 1997-2003 + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/popt_common.h" +#include "librpc/rpc/dcerpc.h" +#include "torture/rpc/rpc.h" +#include "torture/smbtorture.h" +#include "librpc/ndr/ndr_table.h" +#include "lib/util/dlinklist.h" + +static bool torture_rpc_teardown (struct torture_context *tcase, + void *data) +{ + struct torture_rpc_tcase_data *tcase_data = + (struct torture_rpc_tcase_data *)data; + if (tcase_data->join_ctx != NULL) + torture_leave_domain(tcase, tcase_data->join_ctx); + talloc_free(tcase_data); + return true; +} + +/** + * Obtain the DCE/RPC binding context associated with a torture context. + * + * @param tctx Torture context + * @param binding Pointer to store DCE/RPC binding + */ +NTSTATUS torture_rpc_binding(struct torture_context *tctx, + struct dcerpc_binding **binding) +{ + NTSTATUS status; + const char *binding_string = torture_setting_string(tctx, "binding", + NULL); + + if (binding_string == NULL) { + torture_comment(tctx, + "You must specify a DCE/RPC binding string\n"); + return NT_STATUS_INVALID_PARAMETER; + } + + status = dcerpc_parse_binding(tctx, binding_string, binding); + if (NT_STATUS_IS_ERR(status)) { + DEBUG(0,("Failed to parse dcerpc binding '%s'\n", + binding_string)); + return status; + } + + return NT_STATUS_OK; +} + +/** + * open a rpc connection to the chosen binding string + */ +_PUBLIC_ NTSTATUS torture_rpc_connection(struct torture_context *tctx, + struct dcerpc_pipe **p, + const struct ndr_interface_table *table) +{ + NTSTATUS status; + struct dcerpc_binding *binding; + + status = torture_rpc_binding(tctx, &binding); + if (NT_STATUS_IS_ERR(status)) + return status; + + status = dcerpc_pipe_connect_b(tctx, + p, binding, table, + cmdline_credentials, tctx->ev, tctx->lp_ctx); + + if (NT_STATUS_IS_ERR(status)) { + printf("Failed to connect to remote server: %s %s\n", + dcerpc_binding_string(tctx, binding), nt_errstr(status)); + } + + return status; +} + +/** + * open a rpc connection to a specific transport + */ +NTSTATUS torture_rpc_connection_transport(struct torture_context *tctx, + struct dcerpc_pipe **p, + const struct ndr_interface_table *table, + enum dcerpc_transport_t transport, + uint32_t assoc_group_id) +{ + NTSTATUS status; + struct dcerpc_binding *binding; + + status = torture_rpc_binding(tctx, &binding); + if (NT_STATUS_IS_ERR(status)) + return status; + + binding->transport = transport; + binding->assoc_group_id = assoc_group_id; + + status = dcerpc_pipe_connect_b(tctx, p, binding, table, + cmdline_credentials, tctx->ev, tctx->lp_ctx); + + if (NT_STATUS_IS_ERR(status)) { + *p = NULL; + } + + return status; +} + +static bool torture_rpc_setup_machine(struct torture_context *tctx, + void **data) +{ + NTSTATUS status; + struct dcerpc_binding *binding; + struct torture_rpc_tcase *tcase = talloc_get_type(tctx->active_tcase, + struct torture_rpc_tcase); + struct torture_rpc_tcase_data *tcase_data; + + status = torture_rpc_binding(tctx, &binding); + if (NT_STATUS_IS_ERR(status)) + return false; + + *data = tcase_data = talloc_zero(tctx, struct torture_rpc_tcase_data); + tcase_data->credentials = cmdline_credentials; + tcase_data->join_ctx = torture_join_domain(tctx, tcase->machine_name, + ACB_SVRTRUST, + &tcase_data->credentials); + if (tcase_data->join_ctx == NULL) + torture_fail(tctx, "Failed to join as BDC"); + + status = dcerpc_pipe_connect_b(tctx, + &(tcase_data->pipe), + binding, + tcase->table, + tcase_data->credentials, tctx->ev, tctx->lp_ctx); + + torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); + + return true; +} + +_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_machine_rpc_iface_tcase( + struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table, + const char *machine_name) +{ + struct torture_rpc_tcase *tcase = talloc(suite, + struct torture_rpc_tcase); + + torture_suite_init_rpc_tcase(suite, tcase, name, table); + + tcase->machine_name = talloc_strdup(tcase, machine_name); + tcase->tcase.setup = torture_rpc_setup_machine; + tcase->tcase.teardown = torture_rpc_teardown; + + return tcase; +} + +_PUBLIC_ bool torture_suite_init_rpc_tcase(struct torture_suite *suite, + struct torture_rpc_tcase *tcase, + const char *name, + const struct ndr_interface_table *table) +{ + if (!torture_suite_init_tcase(suite, (struct torture_tcase *)tcase, name)) + return false; + + tcase->table = table; + + return true; +} + +static bool torture_rpc_setup_anonymous(struct torture_context *tctx, + void **data) +{ + NTSTATUS status; + struct dcerpc_binding *binding; + struct torture_rpc_tcase_data *tcase_data; + struct torture_rpc_tcase *tcase = talloc_get_type(tctx->active_tcase, + struct torture_rpc_tcase); + + status = torture_rpc_binding(tctx, &binding); + if (NT_STATUS_IS_ERR(status)) + return false; + + *data = tcase_data = talloc_zero(tctx, struct torture_rpc_tcase_data); + tcase_data->credentials = cli_credentials_init_anon(tctx); + + status = dcerpc_pipe_connect_b(tctx, + &(tcase_data->pipe), + binding, + tcase->table, + tcase_data->credentials, tctx->ev, tctx->lp_ctx); + + torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); + + return true; +} + +static bool torture_rpc_setup (struct torture_context *tctx, void **data) +{ + NTSTATUS status; + struct torture_rpc_tcase *tcase = talloc_get_type( + tctx->active_tcase, struct torture_rpc_tcase); + struct torture_rpc_tcase_data *tcase_data; + + *data = tcase_data = talloc_zero(tctx, struct torture_rpc_tcase_data); + tcase_data->credentials = cmdline_credentials; + + status = torture_rpc_connection(tctx, + &(tcase_data->pipe), + tcase->table); + + torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); + + return true; +} + + + +_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_anon_rpc_iface_tcase(struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table) +{ + struct torture_rpc_tcase *tcase = talloc(suite, struct torture_rpc_tcase); + + torture_suite_init_rpc_tcase(suite, tcase, name, table); + + tcase->tcase.setup = torture_rpc_setup_anonymous; + tcase->tcase.teardown = torture_rpc_teardown; + + return tcase; +} + + +_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_rpc_iface_tcase(struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table) +{ + struct torture_rpc_tcase *tcase = talloc(suite, struct torture_rpc_tcase); + + torture_suite_init_rpc_tcase(suite, tcase, name, table); + + tcase->tcase.setup = torture_rpc_setup; + tcase->tcase.teardown = torture_rpc_teardown; + + return tcase; +} + +static bool torture_rpc_wrap_test(struct torture_context *tctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn) (struct torture_context *, struct dcerpc_pipe *); + struct torture_rpc_tcase_data *tcase_data = + (struct torture_rpc_tcase_data *)tcase->data; + + fn = test->fn; + + return fn(tctx, tcase_data->pipe); +} + +static bool torture_rpc_wrap_test_ex(struct torture_context *tctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, const void *); + struct torture_rpc_tcase_data *tcase_data = + (struct torture_rpc_tcase_data *)tcase->data; + + fn = test->fn; + + return fn(tctx, tcase_data->pipe, test->data); +} + + +static bool torture_rpc_wrap_test_creds(struct torture_context *tctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, struct cli_credentials *); + struct torture_rpc_tcase_data *tcase_data = + (struct torture_rpc_tcase_data *)tcase->data; + + fn = test->fn; + + return fn(tctx, tcase_data->pipe, tcase_data->credentials); +} + +_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn) (struct torture_context *, struct dcerpc_pipe *)) +{ + struct torture_test *test; + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = torture_rpc_wrap_test; + test->dangerous = false; + test->data = NULL; + test->fn = fn; + + DLIST_ADD(tcase->tcase.tests, test); + + return test; +} + +_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test_creds( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, struct cli_credentials *)) +{ + struct torture_test *test; + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = torture_rpc_wrap_test_creds; + test->dangerous = false; + test->data = NULL; + test->fn = fn; + + DLIST_ADD(tcase->tcase.tests, test); + + return test; +} + +_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test_ex( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, + void *), + void *userdata) +{ + struct torture_test *test; + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = torture_rpc_wrap_test_ex; + test->dangerous = false; + test->data = userdata; + test->fn = fn; + + DLIST_ADD(tcase->tcase.tests, test); + + return test; +} + +NTSTATUS torture_rpc_init(void) +{ + struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "RPC"); + + dcerpc_init(); + + ndr_table_init(); + + torture_suite_add_simple_test(suite, "LSA", torture_rpc_lsa); + torture_suite_add_simple_test(suite, "LSALOOKUP", torture_rpc_lsa_lookup); + torture_suite_add_simple_test(suite, "LSA-GETUSER", torture_rpc_lsa_get_user); + torture_suite_add_suite(suite, torture_rpc_lsa_secrets(suite)); + torture_suite_add_suite(suite, torture_rpc_echo(suite)); + torture_suite_add_simple_test(suite, "DFS", torture_rpc_dfs); + torture_suite_add_suite(suite, torture_rpc_frsapi(suite)); + torture_suite_add_suite(suite, torture_rpc_unixinfo(suite)); + torture_suite_add_suite(suite, torture_rpc_eventlog(suite)); + torture_suite_add_suite(suite, torture_rpc_atsvc(suite)); + torture_suite_add_suite(suite, torture_rpc_wkssvc(suite)); + torture_suite_add_suite(suite, torture_rpc_handles(suite)); + torture_suite_add_suite(suite, torture_rpc_winreg(suite)); + torture_suite_add_simple_test(suite, "SPOOLSS", torture_rpc_spoolss); + torture_suite_add_suite(suite, torture_rpc_spoolss_notify(suite)); + torture_suite_add_suite(suite, torture_rpc_spoolss_win(suite)); + torture_suite_add_simple_test(suite, "SAMR", torture_rpc_samr); + torture_suite_add_simple_test(suite, "SAMR-USERS", torture_rpc_samr_users); + torture_suite_add_simple_test(suite, "SAMR-PASSWORDS", torture_rpc_samr_passwords); + torture_suite_add_suite(suite, torture_rpc_netlogon(suite)); + torture_suite_add_suite(suite, torture_rpc_remote_pac(suite)); + torture_suite_add_simple_test(suite, "SAMLOGON", torture_rpc_samlogon); + torture_suite_add_simple_test(suite, "SAMSYNC", torture_rpc_samsync); + torture_suite_add_simple_test(suite, "SCHANNEL", torture_rpc_schannel); + torture_suite_add_simple_test(suite, "SCHANNEL2", torture_rpc_schannel2); + torture_suite_add_simple_test(suite, "BENCH-SCHANNEL1", torture_rpc_schannel_bench1); + torture_suite_add_suite(suite, torture_rpc_srvsvc(suite)); + torture_suite_add_suite(suite, torture_rpc_svcctl(suite)); + torture_suite_add_suite(suite, torture_rpc_samr_accessmask(suite)); + torture_suite_add_suite(suite, torture_rpc_epmapper(suite)); + torture_suite_add_suite(suite, torture_rpc_initshutdown(suite)); + torture_suite_add_suite(suite, torture_rpc_oxidresolve(suite)); + torture_suite_add_suite(suite, torture_rpc_remact(suite)); + torture_suite_add_simple_test(suite, "MGMT", torture_rpc_mgmt); + torture_suite_add_simple_test(suite, "SCANNER", torture_rpc_scanner); + torture_suite_add_simple_test(suite, "AUTOIDL", torture_rpc_autoidl); + torture_suite_add_simple_test(suite, "COUNTCALLS", torture_rpc_countcalls); + torture_suite_add_simple_test(suite, "MULTIBIND", torture_multi_bind); + torture_suite_add_simple_test(suite, "AUTHCONTEXT", torture_bind_authcontext); + torture_suite_add_simple_test(suite, "BINDSAMBA3", torture_bind_samba3); + torture_suite_add_simple_test(suite, "NETLOGSAMBA3", torture_netlogon_samba3); + torture_suite_add_simple_test(suite, "SAMBA3SESSIONKEY", torture_samba3_sessionkey); + torture_suite_add_simple_test(suite, "SAMBA3-SRVSVC", torture_samba3_rpc_srvsvc); + torture_suite_add_simple_test(suite, "SAMBA3-SHARESEC", + torture_samba3_rpc_sharesec); + torture_suite_add_simple_test(suite, "SAMBA3-GETUSERNAME", + torture_samba3_rpc_getusername); + torture_suite_add_simple_test(suite, "SAMBA3-RANDOMAUTH2", + torture_samba3_rpc_randomauth2); + torture_suite_add_simple_test(suite, "SAMBA3-LSA", torture_samba3_rpc_lsa); + torture_suite_add_simple_test(suite, "SAMBA3-SPOOLSS", torture_samba3_rpc_spoolss); + torture_suite_add_simple_test(suite, "SAMBA3-WKSSVC", torture_samba3_rpc_wkssvc); + torture_suite_add_simple_test(suite, "SAMBA3-WINREG", torture_samba3_rpc_winreg); + torture_suite_add_simple_test(suite, "DRSUAPI", torture_rpc_drsuapi); + torture_suite_add_simple_test(suite, "CRACKNAMES", torture_rpc_drsuapi_cracknames); + torture_suite_add_suite(suite, torture_rpc_dssetup(suite)); + torture_suite_add_simple_test(suite, "SAMBA3-REGCONFIG", torture_samba3_regconfig); + torture_suite_add_simple_test(suite, "ALTERCONTEXT", torture_rpc_alter_context); + torture_suite_add_simple_test(suite, "JOIN", torture_rpc_join); + torture_suite_add_simple_test(suite, "DSSYNC", torture_rpc_dssync); + torture_suite_add_simple_test(suite, "BENCH-RPC", torture_bench_rpc); + torture_suite_add_simple_test(suite, "ASYNCBIND", torture_async_bind); + + suite->description = talloc_strdup(suite, "DCE/RPC protocol and interface tests"); + + torture_register_suite(suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/rpc/rpc.h b/source4/torture/rpc/rpc.h new file mode 100644 index 0000000000..29b1ebee54 --- /dev/null +++ b/source4/torture/rpc/rpc.h @@ -0,0 +1,89 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 1997-2003 + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __TORTURE_RPC_H__ +#define __TORTURE_RPC_H__ + +#include "torture/torture.h" +#include "auth/credentials/credentials.h" +#include "torture/rpc/drsuapi.h" +#include "libnet/libnet_join.h" +#include "librpc/rpc/dcerpc.h" +#include "libcli/raw/libcliraw.h" +#include "torture/rpc/proto.h" +#include "torture/torture.h" + +struct torture_rpc_tcase { + struct torture_tcase tcase; + const struct ndr_interface_table *table; + const char *machine_name; +}; + +struct torture_rpc_tcase_data { + struct test_join *join_ctx; + struct dcerpc_pipe *pipe; + struct cli_credentials *credentials; +}; + +NTSTATUS torture_rpc_connection(struct torture_context *tctx, + struct dcerpc_pipe **p, + const struct ndr_interface_table *table); + +struct test_join *torture_join_domain(struct torture_context *tctx, + const char *machine_name, + uint32_t acct_flags, + struct cli_credentials **machine_credentials); +const struct dom_sid *torture_join_sid(struct test_join *join); +void torture_leave_domain(struct torture_context *tctx, struct test_join *join); +struct torture_rpc_tcase *torture_suite_add_rpc_iface_tcase(struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table); + +struct torture_test *torture_rpc_tcase_add_test( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn) (struct torture_context *, struct dcerpc_pipe *)); +struct torture_rpc_tcase *torture_suite_add_anon_rpc_iface_tcase(struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table); + +struct torture_test *torture_rpc_tcase_add_test_ex( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, + void *), + void *userdata); +struct torture_rpc_tcase *torture_suite_add_machine_rpc_iface_tcase( + struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table, + const char *machine_name); +struct torture_test *torture_rpc_tcase_add_test_creds( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, struct cli_credentials *)); +bool torture_suite_init_rpc_tcase(struct torture_suite *suite, + struct torture_rpc_tcase *tcase, + const char *name, + const struct ndr_interface_table *table); + + + +#endif /* __TORTURE_RPC_H__ */ diff --git a/source4/torture/rpc/samba3rpc.c b/source4/torture/rpc/samba3rpc.c new file mode 100644 index 0000000000..17342f9b86 --- /dev/null +++ b/source4/torture/rpc/samba3rpc.c @@ -0,0 +1,3400 @@ +/* + Unix SMB/CIFS implementation. + + dcerpc torture tests, designed to walk Samba3 code paths + + Copyright (C) Volker Lendecke 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/rap/rap.h" +#include "torture/torture.h" +#include "torture/util.h" +#include "torture/rap/proto.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_samr.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_netlogon.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_srvsvc.h" +#include "librpc/gen_ndr/ndr_srvsvc_c.h" +#include "librpc/gen_ndr/ndr_spoolss.h" +#include "librpc/gen_ndr/ndr_spoolss_c.h" +#include "librpc/gen_ndr/ndr_winreg.h" +#include "librpc/gen_ndr/ndr_winreg_c.h" +#include "librpc/gen_ndr/ndr_wkssvc.h" +#include "librpc/gen_ndr/ndr_wkssvc_c.h" +#include "lib/cmdline/popt_common.h" +#include "librpc/rpc/dcerpc.h" +#include "torture/rpc/rpc.h" +#include "libcli/libcli.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/auth/libcli_auth.h" +#include "lib/crypto/crypto.h" +#include "auth/ntlmssp/ntlmssp.h" +#include "libcli/security/proto.h" +#include "param/param.h" +#include "lib/registry/registry.h" +#include "libcli/resolve/resolve.h" + +/* + * This tests a RPC call using an invalid vuid + */ + +bool torture_bind_authcontext(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct lsa_ObjectAttribute objectattr; + struct lsa_OpenPolicy2 openpolicy; + struct policy_handle handle; + struct lsa_Close close_handle; + struct smbcli_session *tmp; + struct smbcli_session *session2; + struct smbcli_state *cli; + struct dcerpc_pipe *lsa_pipe; + struct cli_credentials *anon_creds; + struct smb_composite_sesssetup setup; + struct smbcli_options options; + + mem_ctx = talloc_init("torture_bind_authcontext"); + + if (mem_ctx == NULL) { + d_printf("talloc_init failed\n"); + return false; + } + + lp_smbcli_options(torture->lp_ctx, &options); + + status = smbcli_full_connection(mem_ctx, &cli, + torture_setting_string(torture, "host", NULL), + lp_smb_ports(torture->lp_ctx), + "IPC$", NULL, cmdline_credentials, + lp_resolve_context(torture->lp_ctx), + torture->ev, &options); + if (!NT_STATUS_IS_OK(status)) { + d_printf("smbcli_full_connection failed: %s\n", + nt_errstr(status)); + goto done; + } + + lsa_pipe = dcerpc_pipe_init(mem_ctx, cli->transport->socket->event.ctx, + lp_iconv_convenience(torture->lp_ctx)); + if (lsa_pipe == NULL) { + d_printf("dcerpc_pipe_init failed\n"); + goto done; + } + + status = dcerpc_pipe_open_smb(lsa_pipe, cli->tree, "\\lsarpc"); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_pipe_open_smb failed: %s\n", + nt_errstr(status)); + goto done; + } + + status = dcerpc_bind_auth_none(lsa_pipe, &ndr_table_lsarpc); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_bind_auth_none failed: %s\n", + nt_errstr(status)); + goto done; + } + + openpolicy.in.system_name =talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(lsa_pipe)); + ZERO_STRUCT(objectattr); + openpolicy.in.attr = &objectattr; + openpolicy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + openpolicy.out.handle = &handle; + + status = dcerpc_lsa_OpenPolicy2(lsa_pipe, mem_ctx, &openpolicy); + + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_lsa_OpenPolicy2 failed: %s\n", + nt_errstr(status)); + goto done; + } + + close_handle.in.handle = &handle; + close_handle.out.handle = &handle; + + status = dcerpc_lsa_Close(lsa_pipe, mem_ctx, &close_handle); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_lsa_Close failed: %s\n", + nt_errstr(status)); + goto done; + } + + session2 = smbcli_session_init(cli->transport, mem_ctx, false); + if (session2 == NULL) { + d_printf("smbcli_session_init failed\n"); + goto done; + } + + if (!(anon_creds = cli_credentials_init_anon(mem_ctx))) { + d_printf("create_anon_creds failed\n"); + goto done; + } + + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities = cli->transport->negotiate.capabilities; + setup.in.workgroup = ""; + setup.in.credentials = anon_creds; + + status = smb_composite_sesssetup(session2, &setup); + if (!NT_STATUS_IS_OK(status)) { + d_printf("anon session setup failed: %s\n", + nt_errstr(status)); + goto done; + } + session2->vuid = setup.out.vuid; + + tmp = cli->tree->session; + cli->tree->session = session2; + + status = dcerpc_lsa_OpenPolicy2(lsa_pipe, mem_ctx, &openpolicy); + + cli->tree->session = tmp; + talloc_free(lsa_pipe); + lsa_pipe = NULL; + + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) { + d_printf("dcerpc_lsa_OpenPolicy2 with wrong vuid gave %s, " + "expected NT_STATUS_INVALID_HANDLE\n", + nt_errstr(status)); + goto done; + } + + ret = true; + done: + talloc_free(mem_ctx); + return ret; +} + +/* + * Bind to lsa using a specific auth method + */ + +static bool bindtest(struct smbcli_state *cli, + struct cli_credentials *credentials, + struct loadparm_context *lp_ctx, + uint8_t auth_type, uint8_t auth_level) +{ + TALLOC_CTX *mem_ctx; + bool ret = false; + NTSTATUS status; + + struct dcerpc_pipe *lsa_pipe; + struct lsa_ObjectAttribute objectattr; + struct lsa_OpenPolicy2 openpolicy; + struct lsa_QueryInfoPolicy query; + struct policy_handle handle; + struct lsa_Close close_handle; + + if ((mem_ctx = talloc_init("bindtest")) == NULL) { + d_printf("talloc_init failed\n"); + return false; + } + + lsa_pipe = dcerpc_pipe_init(mem_ctx, + cli->transport->socket->event.ctx, + lp_iconv_convenience(lp_ctx)); + if (lsa_pipe == NULL) { + d_printf("dcerpc_pipe_init failed\n"); + goto done; + } + + status = dcerpc_pipe_open_smb(lsa_pipe, cli->tree, "\\lsarpc"); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_pipe_open_smb failed: %s\n", + nt_errstr(status)); + goto done; + } + + status = dcerpc_bind_auth(lsa_pipe, &ndr_table_lsarpc, + credentials, lp_ctx, auth_type, auth_level, + NULL); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_bind_auth failed: %s\n", nt_errstr(status)); + goto done; + } + + openpolicy.in.system_name =talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(lsa_pipe)); + ZERO_STRUCT(objectattr); + openpolicy.in.attr = &objectattr; + openpolicy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + openpolicy.out.handle = &handle; + + status = dcerpc_lsa_OpenPolicy2(lsa_pipe, mem_ctx, &openpolicy); + + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_lsa_OpenPolicy2 failed: %s\n", + nt_errstr(status)); + goto done; + } + + query.in.handle = &handle; + query.in.level = LSA_POLICY_INFO_DOMAIN; + + status = dcerpc_lsa_QueryInfoPolicy(lsa_pipe, mem_ctx, &query); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_lsa_QueryInfoPolicy failed: %s\n", + nt_errstr(status)); + goto done; + } + + close_handle.in.handle = &handle; + close_handle.out.handle = &handle; + + status = dcerpc_lsa_Close(lsa_pipe, mem_ctx, &close_handle); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_lsa_Close failed: %s\n", + nt_errstr(status)); + goto done; + } + + ret = true; + done: + talloc_free(mem_ctx); + return ret; +} + +/* + * test authenticated RPC binds with the variants Samba3 does support + */ + +bool torture_bind_samba3(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct smbcli_state *cli; + struct smbcli_options options; + + mem_ctx = talloc_init("torture_bind_authcontext"); + + if (mem_ctx == NULL) { + d_printf("talloc_init failed\n"); + return false; + } + + lp_smbcli_options(torture->lp_ctx, &options); + + status = smbcli_full_connection(mem_ctx, &cli, + torture_setting_string(torture, "host", NULL), + lp_smb_ports(torture->lp_ctx), + "IPC$", NULL, cmdline_credentials, + lp_resolve_context(torture->lp_ctx), + torture->ev, &options); + if (!NT_STATUS_IS_OK(status)) { + d_printf("smbcli_full_connection failed: %s\n", + nt_errstr(status)); + goto done; + } + + ret = true; + + ret &= bindtest(cli, cmdline_credentials, torture->lp_ctx, DCERPC_AUTH_TYPE_NTLMSSP, + DCERPC_AUTH_LEVEL_INTEGRITY); + ret &= bindtest(cli, cmdline_credentials, torture->lp_ctx, DCERPC_AUTH_TYPE_NTLMSSP, + DCERPC_AUTH_LEVEL_PRIVACY); + ret &= bindtest(cli, cmdline_credentials, torture->lp_ctx, DCERPC_AUTH_TYPE_SPNEGO, + DCERPC_AUTH_LEVEL_INTEGRITY); + ret &= bindtest(cli, cmdline_credentials, torture->lp_ctx, DCERPC_AUTH_TYPE_SPNEGO, + DCERPC_AUTH_LEVEL_PRIVACY); + + done: + talloc_free(mem_ctx); + return ret; +} + +/* + * Lookup or create a user and return all necessary info + */ + +static NTSTATUS get_usr_handle(struct smbcli_state *cli, + TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + struct cli_credentials *admin_creds, + uint8_t auth_type, + uint8_t auth_level, + const char *username, + char **domain, + struct dcerpc_pipe **result_pipe, + struct policy_handle **result_handle, + struct dom_sid **sid) +{ + struct dcerpc_pipe *samr_pipe; + NTSTATUS status; + struct policy_handle conn_handle; + struct policy_handle domain_handle; + struct policy_handle *user_handle; + struct samr_Connect2 conn; + struct samr_EnumDomains enumdom; + uint32_t resume_handle = 0; + struct samr_LookupDomain l; + int dom_idx; + struct lsa_String domain_name; + struct lsa_String user_name; + struct samr_OpenDomain o; + struct samr_CreateUser2 c; + uint32_t user_rid,access_granted; + + samr_pipe = dcerpc_pipe_init(mem_ctx, + cli->transport->socket->event.ctx, + lp_iconv_convenience(lp_ctx)); + if (samr_pipe == NULL) { + d_printf("dcerpc_pipe_init failed\n"); + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + status = dcerpc_pipe_open_smb(samr_pipe, cli->tree, "\\samr"); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_pipe_open_smb failed: %s\n", + nt_errstr(status)); + goto fail; + } + + if (admin_creds != NULL) { + status = dcerpc_bind_auth(samr_pipe, &ndr_table_samr, + admin_creds, lp_ctx, auth_type, auth_level, + NULL); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_bind_auth failed: %s\n", + nt_errstr(status)); + goto fail; + } + } else { + /* We must have an authenticated SMB connection */ + status = dcerpc_bind_auth_none(samr_pipe, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_bind_auth_none failed: %s\n", + nt_errstr(status)); + goto fail; + } + } + + conn.in.system_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(samr_pipe)); + conn.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + conn.out.connect_handle = &conn_handle; + + status = dcerpc_samr_Connect2(samr_pipe, mem_ctx, &conn); + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_Connect2 failed: %s\n", nt_errstr(status)); + goto fail; + } + + enumdom.in.connect_handle = &conn_handle; + enumdom.in.resume_handle = &resume_handle; + enumdom.in.buf_size = (uint32_t)-1; + enumdom.out.resume_handle = &resume_handle; + + status = dcerpc_samr_EnumDomains(samr_pipe, mem_ctx, &enumdom); + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_EnumDomains failed: %s\n", nt_errstr(status)); + goto fail; + } + + if (enumdom.out.num_entries != 2) { + d_printf("samr_EnumDomains returned %d entries, expected 2\n", + enumdom.out.num_entries); + status = NT_STATUS_UNSUCCESSFUL; + goto fail; + } + + dom_idx = strequal(enumdom.out.sam->entries[0].name.string, + "builtin") ? 1:0; + + l.in.connect_handle = &conn_handle; + domain_name.string = enumdom.out.sam->entries[dom_idx].name.string; + *domain = talloc_strdup(mem_ctx, domain_name.string); + l.in.domain_name = &domain_name; + + status = dcerpc_samr_LookupDomain(samr_pipe, mem_ctx, &l); + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_LookupDomain failed: %s\n", nt_errstr(status)); + goto fail; + } + + o.in.connect_handle = &conn_handle; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.in.sid = l.out.sid; + o.out.domain_handle = &domain_handle; + + status = dcerpc_samr_OpenDomain(samr_pipe, mem_ctx, &o); + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_OpenDomain failed: %s\n", nt_errstr(status)); + goto fail; + } + + c.in.domain_handle = &domain_handle; + user_name.string = username; + c.in.account_name = &user_name; + c.in.acct_flags = ACB_NORMAL; + c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + user_handle = talloc(mem_ctx, struct policy_handle); + c.out.user_handle = user_handle; + c.out.access_granted = &access_granted; + c.out.rid = &user_rid; + + status = dcerpc_samr_CreateUser2(samr_pipe, mem_ctx, &c); + + if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) { + struct samr_LookupNames ln; + struct samr_OpenUser ou; + + ln.in.domain_handle = &domain_handle; + ln.in.num_names = 1; + ln.in.names = &user_name; + + status = dcerpc_samr_LookupNames(samr_pipe, mem_ctx, &ln); + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_LookupNames failed: %s\n", + nt_errstr(status)); + goto fail; + } + + ou.in.domain_handle = &domain_handle; + ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + user_rid = ou.in.rid = ln.out.rids.ids[0]; + ou.out.user_handle = user_handle; + + status = dcerpc_samr_OpenUser(samr_pipe, mem_ctx, &ou); + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_OpenUser failed: %s\n", + nt_errstr(status)); + goto fail; + } + } + + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_CreateUser failed: %s\n", nt_errstr(status)); + goto fail; + } + + *result_pipe = samr_pipe; + *result_handle = user_handle; + if (sid != NULL) { + *sid = dom_sid_add_rid(mem_ctx, l.out.sid, user_rid); + } + return NT_STATUS_OK; + + fail: + return status; +} + +/* + * Create a test user + */ + +static bool create_user(TALLOC_CTX *mem_ctx, struct smbcli_state *cli, + struct loadparm_context *lp_ctx, + struct cli_credentials *admin_creds, + const char *username, const char *password, + char **domain_name, + struct dom_sid **user_sid) +{ + TALLOC_CTX *tmp_ctx; + NTSTATUS status; + struct dcerpc_pipe *samr_pipe; + struct policy_handle *wks_handle; + bool ret = false; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + d_printf("talloc_init failed\n"); + return false; + } + + status = get_usr_handle(cli, tmp_ctx, lp_ctx, admin_creds, + DCERPC_AUTH_TYPE_NTLMSSP, + DCERPC_AUTH_LEVEL_INTEGRITY, + username, domain_name, &samr_pipe, &wks_handle, + user_sid); + if (!NT_STATUS_IS_OK(status)) { + d_printf("get_usr_handle failed: %s\n", nt_errstr(status)); + goto done; + } + + { + struct samr_SetUserInfo2 sui2; + struct samr_SetUserInfo sui; + struct samr_QueryUserInfo qui; + union samr_UserInfo u_info; + DATA_BLOB session_key; + + + ZERO_STRUCT(u_info); + encode_pw_buffer(u_info.info23.password.data, password, + STR_UNICODE); + + status = dcerpc_fetch_session_key(samr_pipe, &session_key); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_fetch_session_key failed\n"); + goto done; + } + arcfour_crypt_blob(u_info.info23.password.data, 516, + &session_key); + u_info.info23.info.password_expired = 0; + u_info.info23.info.fields_present = SAMR_FIELD_PASSWORD | + SAMR_FIELD_PASSWORD2 | + SAMR_FIELD_EXPIRED_FLAG; + sui2.in.user_handle = wks_handle; + sui2.in.info = &u_info; + sui2.in.level = 23; + + status = dcerpc_samr_SetUserInfo2(samr_pipe, tmp_ctx, &sui2); + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_SetUserInfo(23) failed: %s\n", + nt_errstr(status)); + goto done; + } + + u_info.info16.acct_flags = ACB_NORMAL; + sui.in.user_handle = wks_handle; + sui.in.info = &u_info; + sui.in.level = 16; + + status = dcerpc_samr_SetUserInfo(samr_pipe, tmp_ctx, &sui); + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_SetUserInfo(16) failed\n"); + goto done; + } + + qui.in.user_handle = wks_handle; + qui.in.level = 21; + + status = dcerpc_samr_QueryUserInfo(samr_pipe, tmp_ctx, &qui); + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_QueryUserInfo(21) failed\n"); + goto done; + } + + qui.out.info->info21.allow_password_change = 0; + qui.out.info->info21.force_password_change = 0; + qui.out.info->info21.account_name.string = NULL; + qui.out.info->info21.rid = 0; + qui.out.info->info21.acct_expiry = 0; + qui.out.info->info21.fields_present = 0x81827fa; /* copy usrmgr.exe */ + + u_info.info21 = qui.out.info->info21; + sui.in.user_handle = wks_handle; + sui.in.info = &u_info; + sui.in.level = 21; + + status = dcerpc_samr_SetUserInfo(samr_pipe, tmp_ctx, &sui); + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_SetUserInfo(21) failed\n"); + goto done; + } + } + + *domain_name= talloc_steal(mem_ctx, *domain_name); + *user_sid = talloc_steal(mem_ctx, *user_sid); + ret = true; + done: + talloc_free(tmp_ctx); + return ret; +} + +/* + * Delete a test user + */ + +static bool delete_user(struct smbcli_state *cli, + struct loadparm_context *lp_ctx, + struct cli_credentials *admin_creds, + const char *username) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + char *dom_name; + struct dcerpc_pipe *samr_pipe; + struct policy_handle *user_handle; + bool ret = false; + + if ((mem_ctx = talloc_init("leave")) == NULL) { + d_printf("talloc_init failed\n"); + return false; + } + + status = get_usr_handle(cli, mem_ctx, lp_ctx, admin_creds, + DCERPC_AUTH_TYPE_NTLMSSP, + DCERPC_AUTH_LEVEL_INTEGRITY, + username, &dom_name, &samr_pipe, + &user_handle, NULL); + + if (!NT_STATUS_IS_OK(status)) { + d_printf("get_wks_handle failed: %s\n", nt_errstr(status)); + goto done; + } + + { + struct samr_DeleteUser d; + + d.in.user_handle = user_handle; + d.out.user_handle = user_handle; + + status = dcerpc_samr_DeleteUser(samr_pipe, mem_ctx, &d); + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_DeleteUser failed %s\n", nt_errstr(status)); + goto done; + } + } + + ret = true; + + done: + talloc_free(mem_ctx); + return ret; +} + +/* + * Do a Samba3-style join + */ + +static bool join3(struct smbcli_state *cli, + struct loadparm_context *lp_ctx, + bool use_level25, + struct cli_credentials *admin_creds, + struct cli_credentials *wks_creds) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + char *dom_name; + struct dcerpc_pipe *samr_pipe; + struct policy_handle *wks_handle; + bool ret = false; + NTTIME last_password_change; + + if ((mem_ctx = talloc_init("join3")) == NULL) { + d_printf("talloc_init failed\n"); + return false; + } + + status = get_usr_handle( + cli, mem_ctx, lp_ctx, admin_creds, + DCERPC_AUTH_TYPE_NTLMSSP, + DCERPC_AUTH_LEVEL_PRIVACY, + talloc_asprintf(mem_ctx, "%s$", + cli_credentials_get_workstation(wks_creds)), + &dom_name, &samr_pipe, &wks_handle, NULL); + + if (!NT_STATUS_IS_OK(status)) { + d_printf("get_wks_handle failed: %s\n", nt_errstr(status)); + goto done; + } + + { + struct samr_QueryUserInfo q; + + q.in.user_handle = wks_handle; + q.in.level = 21; + + status = dcerpc_samr_QueryUserInfo(samr_pipe, mem_ctx, &q); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) QueryUserInfo failed: %s\n", + __location__, nt_errstr(status)); + goto done; + } + + last_password_change = q.out.info->info21.last_password_change; + } + + cli_credentials_set_domain(wks_creds, dom_name, CRED_SPECIFIED); + + if (use_level25) { + struct samr_SetUserInfo2 sui2; + union samr_UserInfo u_info; + struct samr_UserInfo21 *i21 = &u_info.info25.info; + DATA_BLOB session_key; + DATA_BLOB confounded_session_key = data_blob_talloc( + mem_ctx, NULL, 16); + struct MD5Context ctx; + uint8_t confounder[16]; + + ZERO_STRUCT(u_info); + + i21->full_name.string = talloc_asprintf( + mem_ctx, "%s$", + cli_credentials_get_workstation(wks_creds)); + i21->acct_flags = ACB_WSTRUST; + i21->fields_present = SAMR_FIELD_FULL_NAME | + SAMR_FIELD_ACCT_FLAGS | SAMR_FIELD_PASSWORD; + + encode_pw_buffer(u_info.info25.password.data, + cli_credentials_get_password(wks_creds), + STR_UNICODE); + status = dcerpc_fetch_session_key(samr_pipe, &session_key); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_fetch_session_key failed: %s\n", + nt_errstr(status)); + goto done; + } + generate_random_buffer((uint8_t *)confounder, 16); + + MD5Init(&ctx); + MD5Update(&ctx, confounder, 16); + MD5Update(&ctx, session_key.data, session_key.length); + MD5Final(confounded_session_key.data, &ctx); + + arcfour_crypt_blob(u_info.info25.password.data, 516, + &confounded_session_key); + memcpy(&u_info.info25.password.data[516], confounder, 16); + + sui2.in.user_handle = wks_handle; + sui2.in.level = 25; + sui2.in.info = &u_info; + + status = dcerpc_samr_SetUserInfo2(samr_pipe, mem_ctx, &sui2); + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_SetUserInfo2(25) failed: %s\n", + nt_errstr(status)); + goto done; + } + } else { + struct samr_SetUserInfo2 sui2; + struct samr_SetUserInfo sui; + union samr_UserInfo u_info; + DATA_BLOB session_key; + + encode_pw_buffer(u_info.info24.password.data, + cli_credentials_get_password(wks_creds), + STR_UNICODE); + u_info.info24.pw_len = + strlen_m(cli_credentials_get_password(wks_creds))*2; + + status = dcerpc_fetch_session_key(samr_pipe, &session_key); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_fetch_session_key failed\n"); + goto done; + } + arcfour_crypt_blob(u_info.info24.password.data, 516, + &session_key); + sui2.in.user_handle = wks_handle; + sui2.in.info = &u_info; + sui2.in.level = 24; + + status = dcerpc_samr_SetUserInfo2(samr_pipe, mem_ctx, &sui2); + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_SetUserInfo(24) failed: %s\n", + nt_errstr(status)); + goto done; + } + + u_info.info16.acct_flags = ACB_WSTRUST; + sui.in.user_handle = wks_handle; + sui.in.info = &u_info; + sui.in.level = 16; + + status = dcerpc_samr_SetUserInfo(samr_pipe, mem_ctx, &sui); + if (!NT_STATUS_IS_OK(status)) { + d_printf("samr_SetUserInfo(16) failed\n"); + goto done; + } + } + + { + struct samr_QueryUserInfo q; + + q.in.user_handle = wks_handle; + q.in.level = 21; + + status = dcerpc_samr_QueryUserInfo(samr_pipe, mem_ctx, &q); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) QueryUserInfo failed: %s\n", + __location__, nt_errstr(status)); + goto done; + } + + if (use_level25) { + if (last_password_change + == q.out.info->info21.last_password_change) { + d_printf("(%s) last_password_change unchanged " + "during join, level25 must change " + "it\n", __location__); + goto done; + } + } + else { + if (last_password_change + != q.out.info->info21.last_password_change) { + d_printf("(%s) last_password_change changed " + "during join, level24 doesn't " + "change it\n", __location__); + goto done; + } + } + } + + ret = true; + + done: + talloc_free(mem_ctx); + return ret; +} + +/* + * Do a ReqChallenge/Auth2 and get the wks creds + */ + +static bool auth2(struct smbcli_state *cli, + struct loadparm_context *lp_ctx, + struct cli_credentials *wks_cred) +{ + TALLOC_CTX *mem_ctx; + struct dcerpc_pipe *net_pipe; + bool result = false; + NTSTATUS status; + struct netr_ServerReqChallenge r; + struct netr_Credential netr_cli_creds; + struct netr_Credential netr_srv_creds; + uint32_t negotiate_flags; + struct netr_ServerAuthenticate2 a; + struct creds_CredentialState *creds_state; + struct netr_Credential netr_cred; + struct samr_Password mach_pw; + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + d_printf("talloc_new failed\n"); + return false; + } + + net_pipe = dcerpc_pipe_init(mem_ctx, + cli->transport->socket->event.ctx, + lp_iconv_convenience(lp_ctx)); + if (net_pipe == NULL) { + d_printf("dcerpc_pipe_init failed\n"); + goto done; + } + + status = dcerpc_pipe_open_smb(net_pipe, cli->tree, "\\netlogon"); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_pipe_open_smb failed: %s\n", + nt_errstr(status)); + goto done; + } + + status = dcerpc_bind_auth_none(net_pipe, &ndr_table_netlogon); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_bind_auth_none failed: %s\n", + nt_errstr(status)); + goto done; + } + + r.in.computer_name = cli_credentials_get_workstation(wks_cred); + r.in.server_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe)); + if (r.in.server_name == NULL) { + d_printf("talloc_asprintf failed\n"); + goto done; + } + generate_random_buffer(netr_cli_creds.data, + sizeof(netr_cli_creds.data)); + r.in.credentials = &netr_cli_creds; + r.out.credentials = &netr_srv_creds; + + status = dcerpc_netr_ServerReqChallenge(net_pipe, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("netr_ServerReqChallenge failed: %s\n", + nt_errstr(status)); + goto done; + } + + negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS; + E_md4hash(cli_credentials_get_password(wks_cred), mach_pw.hash); + + creds_state = talloc(mem_ctx, struct creds_CredentialState); + creds_client_init(creds_state, r.in.credentials, + r.out.credentials, &mach_pw, + &netr_cred, negotiate_flags); + + a.in.server_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe)); + a.in.account_name = talloc_asprintf( + mem_ctx, "%s$", cli_credentials_get_workstation(wks_cred)); + a.in.computer_name = cli_credentials_get_workstation(wks_cred); + a.in.secure_channel_type = SEC_CHAN_WKSTA; + a.in.negotiate_flags = &negotiate_flags; + a.out.negotiate_flags = &negotiate_flags; + a.in.credentials = &netr_cred; + a.out.credentials = &netr_cred; + + status = dcerpc_netr_ServerAuthenticate2(net_pipe, mem_ctx, &a); + if (!NT_STATUS_IS_OK(status)) { + d_printf("netr_ServerServerAuthenticate2 failed: %s\n", + nt_errstr(status)); + goto done; + } + + if (!creds_client_check(creds_state, a.out.credentials)) { + d_printf("creds_client_check failed\n"); + goto done; + } + + cli_credentials_set_netlogon_creds(wks_cred, creds_state); + + result = true; + + done: + talloc_free(mem_ctx); + return result; +} + +/* + * Do a couple of schannel protected Netlogon ops: Interactive and Network + * login, and change the wks password + */ + +static bool schan(struct smbcli_state *cli, + struct loadparm_context *lp_ctx, + struct cli_credentials *wks_creds, + struct cli_credentials *user_creds) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct dcerpc_pipe *net_pipe; + int i; + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + d_printf("talloc_new failed\n"); + return false; + } + + net_pipe = dcerpc_pipe_init(mem_ctx, + cli->transport->socket->event.ctx, + lp_iconv_convenience(lp_ctx)); + if (net_pipe == NULL) { + d_printf("dcerpc_pipe_init failed\n"); + goto done; + } + + status = dcerpc_pipe_open_smb(net_pipe, cli->tree, "\\netlogon"); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_pipe_open_smb failed: %s\n", + nt_errstr(status)); + goto done; + } + +#if 0 + net_pipe->conn->flags |= DCERPC_DEBUG_PRINT_IN | + DCERPC_DEBUG_PRINT_OUT; +#endif +#if 1 + net_pipe->conn->flags |= (DCERPC_SIGN | DCERPC_SEAL); + status = dcerpc_bind_auth(net_pipe, &ndr_table_netlogon, + wks_creds, lp_ctx, DCERPC_AUTH_TYPE_SCHANNEL, + DCERPC_AUTH_LEVEL_PRIVACY, + NULL); +#else + status = dcerpc_bind_auth_none(net_pipe, &ndr_table_netlogon); +#endif + if (!NT_STATUS_IS_OK(status)) { + d_printf("schannel bind failed: %s\n", nt_errstr(status)); + goto done; + } + + + for (i=2; i<4; i++) { + int flags; + DATA_BLOB chal, nt_resp, lm_resp, names_blob, session_key; + struct creds_CredentialState *creds_state; + struct netr_Authenticator netr_auth, netr_auth2; + struct netr_NetworkInfo ninfo; + struct netr_PasswordInfo pinfo; + struct netr_LogonSamLogon r; + + flags = CLI_CRED_LANMAN_AUTH | CLI_CRED_NTLM_AUTH | + CLI_CRED_NTLMv2_AUTH; + + chal = data_blob_talloc(mem_ctx, NULL, 8); + if (chal.data == NULL) { + d_printf("data_blob_talloc failed\n"); + goto done; + } + + generate_random_buffer(chal.data, chal.length); + names_blob = NTLMv2_generate_names_blob( + mem_ctx, lp_iconv_convenience(lp_ctx), + cli_credentials_get_workstation(user_creds), + cli_credentials_get_domain(user_creds)); + status = cli_credentials_get_ntlm_response( + user_creds, mem_ctx, &flags, chal, names_blob, + &lm_resp, &nt_resp, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + d_printf("cli_credentials_get_ntlm_response failed:" + " %s\n", nt_errstr(status)); + goto done; + } + + creds_state = cli_credentials_get_netlogon_creds(wks_creds); + creds_client_authenticator(creds_state, &netr_auth); + + ninfo.identity_info.account_name.string = + cli_credentials_get_username(user_creds); + ninfo.identity_info.domain_name.string = + cli_credentials_get_domain(user_creds); + ninfo.identity_info.parameter_control = 0; + ninfo.identity_info.logon_id_low = 0; + ninfo.identity_info.logon_id_high = 0; + ninfo.identity_info.workstation.string = + cli_credentials_get_workstation(user_creds); + memcpy(ninfo.challenge, chal.data, sizeof(ninfo.challenge)); + ninfo.nt.length = nt_resp.length; + ninfo.nt.data = nt_resp.data; + ninfo.lm.length = lm_resp.length; + ninfo.lm.data = lm_resp.data; + + r.in.server_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe)); + ZERO_STRUCT(netr_auth2); + r.in.computer_name = + cli_credentials_get_workstation(wks_creds); + r.in.credential = &netr_auth; + r.in.return_authenticator = &netr_auth2; + r.in.logon_level = 2; + r.in.validation_level = i; + r.in.logon.network = &ninfo; + r.out.return_authenticator = NULL; + + status = dcerpc_netr_LogonSamLogon(net_pipe, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("netr_LogonSamLogon failed: %s\n", + nt_errstr(status)); + goto done; + } + + if ((r.out.return_authenticator == NULL) || + (!creds_client_check(creds_state, + &r.out.return_authenticator->cred))) { + d_printf("Credentials check failed!\n"); + goto done; + } + + creds_client_authenticator(creds_state, &netr_auth); + + pinfo.identity_info = ninfo.identity_info; + ZERO_STRUCT(pinfo.lmpassword.hash); + E_md4hash(cli_credentials_get_password(user_creds), + pinfo.ntpassword.hash); + session_key = data_blob_talloc(mem_ctx, + creds_state->session_key, 16); + arcfour_crypt_blob(pinfo.ntpassword.hash, + sizeof(pinfo.ntpassword.hash), + &session_key); + + r.in.logon_level = 1; + r.in.logon.password = &pinfo; + r.out.return_authenticator = NULL; + + status = dcerpc_netr_LogonSamLogon(net_pipe, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("netr_LogonSamLogon failed: %s\n", + nt_errstr(status)); + goto done; + } + + if ((r.out.return_authenticator == NULL) || + (!creds_client_check(creds_state, + &r.out.return_authenticator->cred))) { + d_printf("Credentials check failed!\n"); + goto done; + } + } + + { + struct netr_ServerPasswordSet s; + char *password = generate_random_str(wks_creds, 8); + struct creds_CredentialState *creds_state; + + s.in.server_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe)); + s.in.computer_name = cli_credentials_get_workstation(wks_creds); + s.in.account_name = talloc_asprintf( + mem_ctx, "%s$", s.in.computer_name); + s.in.secure_channel_type = SEC_CHAN_WKSTA; + E_md4hash(password, s.in.new_password.hash); + + creds_state = cli_credentials_get_netlogon_creds(wks_creds); + creds_des_encrypt(creds_state, &s.in.new_password); + creds_client_authenticator(creds_state, &s.in.credential); + + status = dcerpc_netr_ServerPasswordSet(net_pipe, mem_ctx, &s); + if (!NT_STATUS_IS_OK(status)) { + printf("ServerPasswordSet - %s\n", nt_errstr(status)); + goto done; + } + + if (!creds_client_check(creds_state, + &s.out.return_authenticator.cred)) { + printf("Credential chaining failed\n"); + } + + cli_credentials_set_password(wks_creds, password, + CRED_SPECIFIED); + } + + ret = true; + done: + talloc_free(mem_ctx); + return ret; +} + +/* + * Delete the wks account again + */ + +static bool leave(struct smbcli_state *cli, + struct loadparm_context *lp_ctx, + struct cli_credentials *admin_creds, + struct cli_credentials *wks_creds) +{ + char *wks_name = talloc_asprintf( + NULL, "%s$", cli_credentials_get_workstation(wks_creds)); + bool ret; + + ret = delete_user(cli, lp_ctx, admin_creds, wks_name); + talloc_free(wks_name); + return ret; +} + +/* + * Test the Samba3 DC code a bit. Join, do some schan netlogon ops, leave + */ + +bool torture_netlogon_samba3(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct smbcli_state *cli; + struct cli_credentials *anon_creds; + struct cli_credentials *wks_creds; + const char *wks_name; + int i; + struct smbcli_options options; + + wks_name = torture_setting_string(torture, "wksname", NULL); + if (wks_name == NULL) { + wks_name = get_myname(); + } + + mem_ctx = talloc_init("torture_netlogon_samba3"); + + if (mem_ctx == NULL) { + d_printf("talloc_init failed\n"); + return false; + } + + if (!(anon_creds = cli_credentials_init_anon(mem_ctx))) { + d_printf("create_anon_creds failed\n"); + goto done; + } + + lp_smbcli_options(torture->lp_ctx, &options); + + status = smbcli_full_connection(mem_ctx, &cli, + torture_setting_string(torture, "host", NULL), + lp_smb_ports(torture->lp_ctx), + "IPC$", NULL, anon_creds, + lp_resolve_context(torture->lp_ctx), + torture->ev, &options); + if (!NT_STATUS_IS_OK(status)) { + d_printf("smbcli_full_connection failed: %s\n", + nt_errstr(status)); + goto done; + } + + wks_creds = cli_credentials_init(mem_ctx); + if (wks_creds == NULL) { + d_printf("cli_credentials_init failed\n"); + goto done; + } + + cli_credentials_set_conf(wks_creds, torture->lp_ctx); + cli_credentials_set_secure_channel_type(wks_creds, SEC_CHAN_WKSTA); + cli_credentials_set_username(wks_creds, wks_name, CRED_SPECIFIED); + cli_credentials_set_workstation(wks_creds, wks_name, CRED_SPECIFIED); + cli_credentials_set_password(wks_creds, + generate_random_str(wks_creds, 8), + CRED_SPECIFIED); + + if (!join3(cli, torture->lp_ctx, false, cmdline_credentials, wks_creds)) { + d_printf("join failed\n"); + goto done; + } + + cli_credentials_set_domain( + cmdline_credentials, cli_credentials_get_domain(wks_creds), + CRED_SPECIFIED); + + for (i=0; i<2; i++) { + + /* Do this more than once, the routine "schan" changes + * the workstation password using the netlogon + * password change routine */ + + int j; + + if (!auth2(cli, torture->lp_ctx, wks_creds)) { + d_printf("auth2 failed\n"); + goto done; + } + + for (j=0; j<2; j++) { + if (!schan(cli, torture->lp_ctx, wks_creds, cmdline_credentials)) { + d_printf("schan failed\n"); + goto done; + } + } + } + + if (!leave(cli, torture->lp_ctx, cmdline_credentials, wks_creds)) { + d_printf("leave failed\n"); + goto done; + } + + ret = true; + + done: + talloc_free(mem_ctx); + return ret; +} + +/* + * Do a simple join, testjoin and leave using specified smb and samr + * credentials + */ + +static bool test_join3(struct torture_context *tctx, + bool use_level25, + struct cli_credentials *smb_creds, + struct cli_credentials *samr_creds, + const char *wks_name) +{ + NTSTATUS status; + bool ret = false; + struct smbcli_state *cli; + struct cli_credentials *wks_creds; + struct smbcli_options options; + + lp_smbcli_options(tctx->lp_ctx, &options); + + status = smbcli_full_connection(tctx, &cli, + torture_setting_string(tctx, "host", NULL), + lp_smb_ports(tctx->lp_ctx), + "IPC$", NULL, smb_creds, + lp_resolve_context(tctx->lp_ctx), + tctx->ev, &options); + if (!NT_STATUS_IS_OK(status)) { + d_printf("smbcli_full_connection failed: %s\n", + nt_errstr(status)); + goto done; + } + + wks_creds = cli_credentials_init(cli); + if (wks_creds == NULL) { + d_printf("cli_credentials_init failed\n"); + goto done; + } + + cli_credentials_set_conf(wks_creds, tctx->lp_ctx); + cli_credentials_set_secure_channel_type(wks_creds, SEC_CHAN_WKSTA); + cli_credentials_set_username(wks_creds, wks_name, CRED_SPECIFIED); + cli_credentials_set_workstation(wks_creds, wks_name, CRED_SPECIFIED); + cli_credentials_set_password(wks_creds, + generate_random_str(wks_creds, 8), + CRED_SPECIFIED); + + if (!join3(cli, tctx->lp_ctx, use_level25, samr_creds, wks_creds)) { + d_printf("join failed\n"); + goto done; + } + + cli_credentials_set_domain( + cmdline_credentials, cli_credentials_get_domain(wks_creds), + CRED_SPECIFIED); + + if (!auth2(cli, tctx->lp_ctx, wks_creds)) { + d_printf("auth2 failed\n"); + goto done; + } + + if (!leave(cli, tctx->lp_ctx, samr_creds, wks_creds)) { + d_printf("leave failed\n"); + goto done; + } + + talloc_free(cli); + + ret = true; + + done: + return ret; +} + +/* + * Test the different session key variants. Do it by joining, this uses the + * session key in the setpassword routine. Test the join by doing the auth2. + */ + +bool torture_samba3_sessionkey(struct torture_context *torture) +{ + bool ret = false; + struct cli_credentials *anon_creds; + const char *wks_name; + + wks_name = torture_setting_string(torture, "wksname", get_myname()); + + if (!(anon_creds = cli_credentials_init_anon(torture))) { + d_printf("create_anon_creds failed\n"); + goto done; + } + + cli_credentials_set_workstation(anon_creds, wks_name, CRED_SPECIFIED); + + ret = true; + + if (!torture_setting_bool(torture, "samba3", false)) { + + /* Samba3 in the build farm right now does this happily. Need + * to fix :-) */ + + if (test_join3(torture, false, anon_creds, NULL, wks_name)) { + d_printf("join using anonymous bind on an anonymous smb " + "connection succeeded -- HUH??\n"); + ret = false; + } + } + + if (!test_join3(torture, false, anon_creds, cmdline_credentials, + wks_name)) { + d_printf("join using ntlmssp bind on an anonymous smb " + "connection failed\n"); + ret = false; + } + + if (!test_join3(torture, false, cmdline_credentials, NULL, wks_name)) { + d_printf("join using anonymous bind on an authenticated smb " + "connection failed\n"); + ret = false; + } + + if (!test_join3(torture, false, cmdline_credentials, + cmdline_credentials, + wks_name)) { + d_printf("join using ntlmssp bind on an authenticated smb " + "connection failed\n"); + ret = false; + } + + /* + * The following two are tests for setuserinfolevel 25 + */ + + if (!test_join3(torture, true, anon_creds, cmdline_credentials, + wks_name)) { + d_printf("join using ntlmssp bind on an anonymous smb " + "connection failed\n"); + ret = false; + } + + if (!test_join3(torture, true, cmdline_credentials, NULL, wks_name)) { + d_printf("join using anonymous bind on an authenticated smb " + "connection failed\n"); + ret = false; + } + + done: + + return ret; +} + +/* + * open pipe and bind, given an IPC$ context + */ + +static NTSTATUS pipe_bind_smb(TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + struct smbcli_tree *tree, + const char *pipe_name, + const struct ndr_interface_table *iface, + struct dcerpc_pipe **p) +{ + struct dcerpc_pipe *result; + NTSTATUS status; + + if (!(result = dcerpc_pipe_init( + mem_ctx, tree->session->transport->socket->event.ctx, + lp_iconv_convenience(lp_ctx)))) { + return NT_STATUS_NO_MEMORY; + } + + status = dcerpc_pipe_open_smb(result, tree, pipe_name); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_pipe_open_smb failed: %s\n", + nt_errstr(status)); + talloc_free(result); + return status; + } + + status = dcerpc_bind_auth_none(result, iface); + if (!NT_STATUS_IS_OK(status)) { + d_printf("schannel bind failed: %s\n", nt_errstr(status)); + talloc_free(result); + return status; + } + + *p = result; + return NT_STATUS_OK; +} + +/* + * Sane wrapper around lsa_LookupNames + */ + +static struct dom_sid *name2sid(TALLOC_CTX *mem_ctx, + struct dcerpc_pipe *p, + const char *name, + const char *domain) +{ + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy2 r; + struct lsa_Close c; + NTSTATUS status; + struct policy_handle handle; + struct lsa_LookupNames l; + struct lsa_TransSidArray sids; + struct lsa_String lsa_name; + uint32_t count = 0; + struct dom_sid *result; + TALLOC_CTX *tmp_ctx; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + return NULL; + } + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = "\\"; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + status = dcerpc_lsa_OpenPolicy2(p, tmp_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenPolicy2 failed - %s\n", nt_errstr(status)); + talloc_free(tmp_ctx); + return NULL; + } + + sids.count = 0; + sids.sids = NULL; + + lsa_name.string = talloc_asprintf(tmp_ctx, "%s\\%s", domain, name); + + l.in.handle = &handle; + l.in.num_names = 1; + l.in.names = &lsa_name; + l.in.sids = &sids; + l.in.level = 1; + l.in.count = &count; + l.out.count = &count; + l.out.sids = &sids; + + status = dcerpc_lsa_LookupNames(p, tmp_ctx, &l); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupNames of %s failed - %s\n", lsa_name.string, + nt_errstr(status)); + talloc_free(tmp_ctx); + return NULL; + } + + result = dom_sid_add_rid(mem_ctx, l.out.domains->domains[0].sid, + l.out.sids->sids[0].rid); + + c.in.handle = &handle; + c.out.handle = &handle; + + status = dcerpc_lsa_Close(p, tmp_ctx, &c); + if (!NT_STATUS_IS_OK(status)) { + printf("dcerpc_lsa_Close failed - %s\n", nt_errstr(status)); + talloc_free(tmp_ctx); + return NULL; + } + + talloc_free(tmp_ctx); + return result; +} + +/* + * Find out the user SID on this connection + */ + +static struct dom_sid *whoami(TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + struct smbcli_tree *tree) +{ + struct dcerpc_pipe *lsa; + struct lsa_GetUserName r; + NTSTATUS status; + struct lsa_StringPointer authority_name_p; + struct dom_sid *result; + + status = pipe_bind_smb(mem_ctx, lp_ctx, tree, "\\pipe\\lsarpc", + &ndr_table_lsarpc, &lsa); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) Could not bind to LSA: %s\n", + __location__, nt_errstr(status)); + return NULL; + } + + r.in.system_name = "\\"; + r.in.account_name = NULL; + authority_name_p.string = NULL; + r.in.authority_name = &authority_name_p; + + status = dcerpc_lsa_GetUserName(lsa, mem_ctx, &r); + + if (!NT_STATUS_IS_OK(status)) { + printf("(%s) GetUserName failed - %s\n", + __location__, nt_errstr(status)); + talloc_free(lsa); + return NULL; + } + + result = name2sid(mem_ctx, lsa, r.out.account_name->string, + r.out.authority_name->string->string); + + talloc_free(lsa); + return result; +} + +static int destroy_tree(struct smbcli_tree *tree) +{ + smb_tree_disconnect(tree); + return 0; +} + +/* + * Do a tcon, given a session + */ + +NTSTATUS secondary_tcon(TALLOC_CTX *mem_ctx, + struct smbcli_session *session, + const char *sharename, + struct smbcli_tree **res) +{ + struct smbcli_tree *result; + TALLOC_CTX *tmp_ctx; + union smb_tcon tcon; + NTSTATUS status; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + return NT_STATUS_NO_MEMORY; + } + + if (!(result = smbcli_tree_init(session, mem_ctx, false))) { + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = 0; + tcon.tconx.in.password = data_blob(NULL, 0); + tcon.tconx.in.path = sharename; + tcon.tconx.in.device = "?????"; + + status = smb_raw_tcon(result, tmp_ctx, &tcon); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) smb_raw_tcon failed: %s\n", __location__, + nt_errstr(status)); + talloc_free(tmp_ctx); + return status; + } + + result->tid = tcon.tconx.out.tid; + result = talloc_steal(mem_ctx, result); + talloc_set_destructor(result, destroy_tree); + talloc_free(tmp_ctx); + *res = result; + return NT_STATUS_OK; +} + +/* + * Test the getusername behaviour + */ + +bool torture_samba3_rpc_getusername(struct torture_context *torture) +{ + NTSTATUS status; + struct smbcli_state *cli; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct dom_sid *user_sid; + struct dom_sid *created_sid; + struct cli_credentials *anon_creds; + struct cli_credentials *user_creds; + char *domain_name; + struct smbcli_options options; + + if (!(mem_ctx = talloc_new(torture))) { + return false; + } + + lp_smbcli_options(torture->lp_ctx, &options); + + status = smbcli_full_connection( + mem_ctx, &cli, torture_setting_string(torture, "host", NULL), + lp_smb_ports(torture->lp_ctx), + "IPC$", NULL, cmdline_credentials, + lp_resolve_context(torture->lp_ctx), + torture->ev, &options); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) smbcli_full_connection failed: %s\n", + __location__, nt_errstr(status)); + ret = false; + goto done; + } + + if (!(user_sid = whoami(mem_ctx, torture->lp_ctx, cli->tree))) { + d_printf("(%s) whoami on auth'ed connection failed\n", + __location__); + ret = false; + } + + talloc_free(cli); + + if (!(anon_creds = cli_credentials_init_anon(mem_ctx))) { + d_printf("(%s) create_anon_creds failed\n", __location__); + ret = false; + goto done; + } + + status = smbcli_full_connection( + mem_ctx, &cli, torture_setting_string(torture, "host", NULL), + lp_smb_ports(torture->lp_ctx), + "IPC$", NULL, anon_creds, + lp_resolve_context(torture->lp_ctx), + torture->ev, &options); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) anon smbcli_full_connection failed: %s\n", + __location__, nt_errstr(status)); + ret = false; + goto done; + } + + if (!(user_sid = whoami(mem_ctx, torture->lp_ctx, cli->tree))) { + d_printf("(%s) whoami on anon connection failed\n", + __location__); + ret = false; + goto done; + } + + if (!dom_sid_equal(user_sid, + dom_sid_parse_talloc(mem_ctx, "s-1-5-7"))) { + d_printf("(%s) Anon lsa_GetUserName returned %s, expected " + "S-1-5-7", __location__, + dom_sid_string(mem_ctx, user_sid)); + ret = false; + } + + if (!(user_creds = cli_credentials_init(mem_ctx))) { + d_printf("(%s) cli_credentials_init failed\n", __location__); + ret = false; + goto done; + } + + cli_credentials_set_conf(user_creds, torture->lp_ctx); + cli_credentials_set_username(user_creds, "torture_username", + CRED_SPECIFIED); + cli_credentials_set_password(user_creds, + generate_random_str(user_creds, 8), + CRED_SPECIFIED); + + if (!create_user(mem_ctx, cli, torture->lp_ctx, cmdline_credentials, + cli_credentials_get_username(user_creds), + cli_credentials_get_password(user_creds), + &domain_name, &created_sid)) { + d_printf("(%s) create_user failed\n", __location__); + ret = false; + goto done; + } + + cli_credentials_set_domain(user_creds, domain_name, + CRED_SPECIFIED); + + { + struct smbcli_session *session2; + struct smb_composite_sesssetup setup; + struct smbcli_tree *tree; + + session2 = smbcli_session_init(cli->transport, mem_ctx, false); + if (session2 == NULL) { + d_printf("(%s) smbcli_session_init failed\n", + __location__); + goto done; + } + + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities = cli->transport->negotiate.capabilities; + setup.in.workgroup = ""; + setup.in.credentials = user_creds; + + status = smb_composite_sesssetup(session2, &setup); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) session setup with new user failed: " + "%s\n", __location__, nt_errstr(status)); + ret = false; + goto done; + } + session2->vuid = setup.out.vuid; + + if (!NT_STATUS_IS_OK(secondary_tcon(mem_ctx, session2, + "IPC$", &tree))) { + d_printf("(%s) secondary_tcon failed\n", + __location__); + ret = false; + goto done; + } + + if (!(user_sid = whoami(mem_ctx, torture->lp_ctx, tree))) { + d_printf("(%s) whoami on user connection failed\n", + __location__); + ret = false; + goto delete; + } + + talloc_free(tree); + } + + d_printf("Created %s, found %s\n", + dom_sid_string(mem_ctx, created_sid), + dom_sid_string(mem_ctx, user_sid)); + + if (!dom_sid_equal(created_sid, user_sid)) { + ret = false; + } + + delete: + if (!delete_user(cli, torture->lp_ctx, + cmdline_credentials, + cli_credentials_get_username(user_creds))) { + d_printf("(%s) delete_user failed\n", __location__); + ret = false; + } + + done: + talloc_free(mem_ctx); + return ret; +} + +static bool test_NetShareGetInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + const char *sharename) +{ + NTSTATUS status; + struct srvsvc_NetShareGetInfo r; + uint32_t levels[] = { 0, 1, 2, 501, 502, 1004, 1005, 1006, 1007, 1501 }; + int i; + bool ret = true; + + r.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s", + dcerpc_server_name(p)); + r.in.share_name = sharename; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + r.in.level = levels[i]; + + ZERO_STRUCT(r.out); + + printf("testing NetShareGetInfo level %u on share '%s'\n", + r.in.level, r.in.share_name); + + status = dcerpc_srvsvc_NetShareGetInfo(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("NetShareGetInfo level %u on share '%s' failed" + " - %s\n", r.in.level, r.in.share_name, + nt_errstr(status)); + ret = false; + continue; + } + if (!W_ERROR_IS_OK(r.out.result)) { + printf("NetShareGetInfo level %u on share '%s' failed " + "- %s\n", r.in.level, r.in.share_name, + win_errstr(r.out.result)); + ret = false; + continue; + } + } + + return ret; +} + +static bool test_NetShareEnum(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + const char **one_sharename) +{ + NTSTATUS status; + struct srvsvc_NetShareEnum r; + struct srvsvc_NetShareCtr0 c0; + uint32_t levels[] = { 0, 1, 2, 501, 502, 1004, 1005, 1006, 1007 }; + int i; + bool ret = true; + + r.in.server_unc = talloc_asprintf(mem_ctx,"\\\\%s",dcerpc_server_name(p)); + r.in.ctr.ctr0 = &c0; + r.in.ctr.ctr0->count = 0; + r.in.ctr.ctr0->array = NULL; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + r.in.level = levels[i]; + + ZERO_STRUCT(r.out); + + printf("testing NetShareEnum level %u\n", r.in.level); + status = dcerpc_srvsvc_NetShareEnum(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("NetShareEnum level %u failed - %s\n", + r.in.level, nt_errstr(status)); + ret = false; + continue; + } + if (!W_ERROR_IS_OK(r.out.result)) { + printf("NetShareEnum level %u failed - %s\n", + r.in.level, win_errstr(r.out.result)); + continue; + } + if (r.in.level == 0) { + struct srvsvc_NetShareCtr0 *ctr = r.out.ctr.ctr0; + if (ctr->count > 0) { + *one_sharename = ctr->array[0].name; + } + } + } + + return ret; +} + +bool torture_samba3_rpc_srvsvc(struct torture_context *torture) +{ + struct dcerpc_pipe *p; + TALLOC_CTX *mem_ctx; + bool ret = true; + const char *sharename = NULL; + struct smbcli_state *cli; + NTSTATUS status; + + if (!(mem_ctx = talloc_new(torture))) { + return false; + } + + if (!(torture_open_connection_share( + mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL), + "IPC$", torture->ev))) { + talloc_free(mem_ctx); + return false; + } + + status = pipe_bind_smb(mem_ctx, torture->lp_ctx, cli->tree, + "\\pipe\\srvsvc", &ndr_table_srvsvc, &p); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) could not bind to srvsvc pipe: %s\n", + __location__, nt_errstr(status)); + ret = false; + goto done; + } + + ret &= test_NetShareEnum(p, mem_ctx, &sharename); + if (sharename == NULL) { + printf("did not get sharename\n"); + } else { + ret &= test_NetShareGetInfo(p, mem_ctx, sharename); + } + + done: + talloc_free(mem_ctx); + return ret; +} + +/* + * Do a ReqChallenge/Auth2 with a random wks name, make sure it returns + * NT_STATUS_NO_SAM_ACCOUNT + */ + +bool torture_samba3_rpc_randomauth2(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + struct dcerpc_pipe *net_pipe; + char *wksname; + bool result = false; + NTSTATUS status; + struct netr_ServerReqChallenge r; + struct netr_Credential netr_cli_creds; + struct netr_Credential netr_srv_creds; + uint32_t negotiate_flags; + struct netr_ServerAuthenticate2 a; + struct creds_CredentialState *creds_state; + struct netr_Credential netr_cred; + struct samr_Password mach_pw; + struct smbcli_state *cli; + + if (!(mem_ctx = talloc_new(torture))) { + d_printf("talloc_new failed\n"); + return false; + } + + if (!(wksname = generate_random_str_list( + mem_ctx, 14, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"))) { + d_printf("generate_random_str_list failed\n"); + goto done; + } + + if (!(torture_open_connection_share( + mem_ctx, &cli, + torture, torture_setting_string(torture, "host", NULL), + "IPC$", torture->ev))) { + d_printf("IPC$ connection failed\n"); + goto done; + } + + if (!(net_pipe = dcerpc_pipe_init( + mem_ctx, cli->transport->socket->event.ctx, + lp_iconv_convenience(torture->lp_ctx)))) { + d_printf("dcerpc_pipe_init failed\n"); + goto done; + } + + status = dcerpc_pipe_open_smb(net_pipe, cli->tree, "\\netlogon"); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_pipe_open_smb failed: %s\n", + nt_errstr(status)); + goto done; + } + + status = dcerpc_bind_auth_none(net_pipe, &ndr_table_netlogon); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_bind_auth_none failed: %s\n", + nt_errstr(status)); + goto done; + } + + r.in.computer_name = wksname; + r.in.server_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe)); + if (r.in.server_name == NULL) { + d_printf("talloc_asprintf failed\n"); + goto done; + } + generate_random_buffer(netr_cli_creds.data, + sizeof(netr_cli_creds.data)); + r.in.credentials = &netr_cli_creds; + r.out.credentials = &netr_srv_creds; + + status = dcerpc_netr_ServerReqChallenge(net_pipe, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("netr_ServerReqChallenge failed: %s\n", + nt_errstr(status)); + goto done; + } + + negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS; + E_md4hash("foobar", mach_pw.hash); + + creds_state = talloc(mem_ctx, struct creds_CredentialState); + creds_client_init(creds_state, r.in.credentials, + r.out.credentials, &mach_pw, + &netr_cred, negotiate_flags); + + a.in.server_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe)); + a.in.account_name = talloc_asprintf( + mem_ctx, "%s$", wksname); + a.in.computer_name = wksname; + a.in.secure_channel_type = SEC_CHAN_WKSTA; + a.in.negotiate_flags = &negotiate_flags; + a.out.negotiate_flags = &negotiate_flags; + a.in.credentials = &netr_cred; + a.out.credentials = &netr_cred; + + status = dcerpc_netr_ServerAuthenticate2(net_pipe, mem_ctx, &a); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_NO_TRUST_SAM_ACCOUNT)) { + d_printf("dcerpc_netr_ServerAuthenticate2 returned %s, " + "expected NT_STATUS_NO_TRUST_SAM_ACCOUNT\n", + nt_errstr(status)); + goto done; + } + + result = true; + done: + talloc_free(mem_ctx); + return result; +} + +static struct security_descriptor *get_sharesec(TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + struct smbcli_session *sess, + const char *sharename) +{ + struct smbcli_tree *tree; + TALLOC_CTX *tmp_ctx; + struct dcerpc_pipe *p; + NTSTATUS status; + struct srvsvc_NetShareGetInfo r; + struct security_descriptor *result; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + d_printf("talloc_new failed\n"); + return NULL; + } + + if (!NT_STATUS_IS_OK(secondary_tcon(tmp_ctx, sess, "IPC$", &tree))) { + d_printf("secondary_tcon failed\n"); + talloc_free(tmp_ctx); + return NULL; + } + + status = pipe_bind_smb(mem_ctx, lp_ctx, tree, "\\pipe\\srvsvc", + &ndr_table_srvsvc, &p); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) could not bind to srvsvc pipe: %s\n", + __location__, nt_errstr(status)); + talloc_free(tmp_ctx); + return NULL; + } + +#if 0 + p->conn->flags |= DCERPC_DEBUG_PRINT_IN | DCERPC_DEBUG_PRINT_OUT; +#endif + + r.in.server_unc = talloc_asprintf(tmp_ctx, "\\\\%s", + dcerpc_server_name(p)); + r.in.share_name = sharename; + r.in.level = 502; + + status = dcerpc_srvsvc_NetShareGetInfo(p, tmp_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("srvsvc_NetShareGetInfo failed: %s\n", + nt_errstr(status)); + talloc_free(tmp_ctx); + return NULL; + } + + result = talloc_steal(mem_ctx, r.out.info.info502->sd); + talloc_free(tmp_ctx); + return result; +} + +static NTSTATUS set_sharesec(TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + struct smbcli_session *sess, + const char *sharename, + struct security_descriptor *sd) +{ + struct smbcli_tree *tree; + TALLOC_CTX *tmp_ctx; + struct dcerpc_pipe *p; + NTSTATUS status; + struct sec_desc_buf i; + struct srvsvc_NetShareSetInfo r; + uint32_t error = 0; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + d_printf("talloc_new failed\n"); + return NT_STATUS_NO_MEMORY; + } + + if (!NT_STATUS_IS_OK(secondary_tcon(tmp_ctx, sess, "IPC$", &tree))) { + d_printf("secondary_tcon failed\n"); + talloc_free(tmp_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + + status = pipe_bind_smb(mem_ctx, lp_ctx, tree, "\\pipe\\srvsvc", + &ndr_table_srvsvc, &p); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) could not bind to srvsvc pipe: %s\n", + __location__, nt_errstr(status)); + talloc_free(tmp_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + +#if 0 + p->conn->flags |= DCERPC_DEBUG_PRINT_IN | DCERPC_DEBUG_PRINT_OUT; +#endif + + r.in.server_unc = talloc_asprintf(tmp_ctx, "\\\\%s", + dcerpc_server_name(p)); + r.in.share_name = sharename; + r.in.level = 1501; + i.sd = sd; + r.in.info.info1501 = &i; + r.in.parm_error = &error; + + status = dcerpc_srvsvc_NetShareSetInfo(p, tmp_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("srvsvc_NetShareGetInfo failed: %s\n", + nt_errstr(status)); + } + + talloc_free(tmp_ctx); + return status; +} + +bool try_tcon(TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + struct security_descriptor *orig_sd, + struct smbcli_session *session, + const char *sharename, const struct dom_sid *user_sid, + unsigned int access_mask, NTSTATUS expected_tcon, + NTSTATUS expected_mkdir) +{ + TALLOC_CTX *tmp_ctx; + struct smbcli_tree *rmdir_tree, *tree; + struct dom_sid *domain_sid; + uint32_t rid; + struct security_descriptor *sd; + NTSTATUS status; + bool ret = true; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + d_printf("talloc_new failed\n"); + return false; + } + + status = secondary_tcon(tmp_ctx, session, sharename, &rmdir_tree); + if (!NT_STATUS_IS_OK(status)) { + d_printf("first tcon to delete dir failed\n"); + talloc_free(tmp_ctx); + return false; + } + + smbcli_rmdir(rmdir_tree, "sharesec_testdir"); + + if (!NT_STATUS_IS_OK(dom_sid_split_rid(tmp_ctx, user_sid, + &domain_sid, &rid))) { + d_printf("dom_sid_split_rid failed\n"); + talloc_free(tmp_ctx); + return false; + } + + sd = security_descriptor_dacl_create( + tmp_ctx, 0, "S-1-5-32-544", + dom_sid_string(mem_ctx, dom_sid_add_rid(mem_ctx, domain_sid, + DOMAIN_RID_USERS)), + dom_sid_string(mem_ctx, user_sid), + SEC_ACE_TYPE_ACCESS_ALLOWED, access_mask, 0, NULL); + if (sd == NULL) { + d_printf("security_descriptor_dacl_create failed\n"); + talloc_free(tmp_ctx); + return false; + } + + status = set_sharesec(mem_ctx, lp_ctx, session, sharename, sd); + if (!NT_STATUS_IS_OK(status)) { + d_printf("custom set_sharesec failed: %s\n", + nt_errstr(status)); + talloc_free(tmp_ctx); + return false; + } + + status = secondary_tcon(tmp_ctx, session, sharename, &tree); + if (!NT_STATUS_EQUAL(status, expected_tcon)) { + d_printf("Expected %s, got %s\n", nt_errstr(expected_tcon), + nt_errstr(status)); + ret = false; + goto done; + } + + if (!NT_STATUS_IS_OK(status)) { + /* An expected non-access, no point in trying to write */ + goto done; + } + + status = smbcli_mkdir(tree, "sharesec_testdir"); + if (!NT_STATUS_EQUAL(status, expected_mkdir)) { + d_printf("(%s) Expected %s, got %s\n", __location__, + nt_errstr(expected_mkdir), nt_errstr(status)); + ret = false; + } + + done: + smbcli_rmdir(rmdir_tree, "sharesec_testdir"); + + status = set_sharesec(mem_ctx, lp_ctx, session, sharename, orig_sd); + if (!NT_STATUS_IS_OK(status)) { + d_printf("custom set_sharesec failed: %s\n", + nt_errstr(status)); + talloc_free(tmp_ctx); + return false; + } + + talloc_free(tmp_ctx); + return ret; +} + +bool torture_samba3_rpc_sharesec(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + bool ret = true; + struct smbcli_state *cli; + struct security_descriptor *sd; + struct dom_sid *user_sid; + + if (!(mem_ctx = talloc_new(torture))) { + return false; + } + + if (!(torture_open_connection_share( + mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL), + "IPC$", torture->ev))) { + d_printf("IPC$ connection failed\n"); + talloc_free(mem_ctx); + return false; + } + + if (!(user_sid = whoami(mem_ctx, torture->lp_ctx, cli->tree))) { + d_printf("whoami failed\n"); + talloc_free(mem_ctx); + return false; + } + + sd = get_sharesec(mem_ctx, torture->lp_ctx, cli->session, + torture_setting_string(torture, "share", NULL)); + + ret &= try_tcon(mem_ctx, torture->lp_ctx, sd, cli->session, + torture_setting_string(torture, "share", NULL), + user_sid, 0, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK); + + ret &= try_tcon(mem_ctx, torture->lp_ctx, sd, cli->session, + torture_setting_string(torture, "share", NULL), + user_sid, SEC_FILE_READ_DATA, NT_STATUS_OK, + NT_STATUS_MEDIA_WRITE_PROTECTED); + + ret &= try_tcon(mem_ctx, torture->lp_ctx, sd, cli->session, + torture_setting_string(torture, "share", NULL), + user_sid, SEC_FILE_ALL, NT_STATUS_OK, NT_STATUS_OK); + + talloc_free(mem_ctx); + return ret; +} + +bool torture_samba3_rpc_lsa(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + bool ret = true; + struct smbcli_state *cli; + struct dcerpc_pipe *p; + struct policy_handle lsa_handle; + NTSTATUS status; + struct dom_sid *domain_sid; + + if (!(mem_ctx = talloc_new(torture))) { + return false; + } + + if (!(torture_open_connection_share( + mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL), + "IPC$", torture->ev))) { + d_printf("IPC$ connection failed\n"); + talloc_free(mem_ctx); + return false; + } + + status = pipe_bind_smb(mem_ctx, torture->lp_ctx, cli->tree, "\\lsarpc", + &ndr_table_lsarpc, &p); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) pipe_bind_smb failed: %s\n", __location__, + nt_errstr(status)); + talloc_free(mem_ctx); + return false; + } + + { + struct lsa_ObjectAttribute attr; + struct lsa_OpenPolicy2 o; + o.in.system_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(p)); + ZERO_STRUCT(attr); + o.in.attr = &attr; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.out.handle = &lsa_handle; + status = dcerpc_lsa_OpenPolicy2(p, mem_ctx, &o); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) dcerpc_lsa_OpenPolicy2 failed: %s\n", + __location__, nt_errstr(status)); + talloc_free(mem_ctx); + return false; + } + } + +#if 0 + p->conn->flags |= DCERPC_DEBUG_PRINT_IN | DCERPC_DEBUG_PRINT_OUT; +#endif + + { + int i; + int levels[] = { 2,3,5,6 }; + + for (i=0; i<ARRAY_SIZE(levels); i++) { + struct lsa_QueryInfoPolicy r; + r.in.handle = &lsa_handle; + r.in.level = levels[i]; + status = dcerpc_lsa_QueryInfoPolicy(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) dcerpc_lsa_QueryInfoPolicy %d " + "failed: %s\n", __location__, + levels[i], nt_errstr(status)); + talloc_free(mem_ctx); + return false; + } + if (levels[i] == 5) { + domain_sid = r.out.info->account_domain.sid; + } + } + } + + return ret; +} + +static NTSTATUS get_servername(TALLOC_CTX *mem_ctx, struct smbcli_tree *tree, + struct smb_iconv_convenience *iconv_convenience, + char **name) +{ + struct rap_WserverGetInfo r; + NTSTATUS status; + char servername[17]; + + r.in.level = 0; + r.in.bufsize = 0xffff; + + status = smbcli_rap_netservergetinfo(tree, iconv_convenience, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + memcpy(servername, r.out.info.info0.name, 16); + servername[16] = '\0'; + + if (pull_ascii_talloc(mem_ctx, iconv_convenience, + name, servername) < 0) { + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + + +static NTSTATUS find_printers(TALLOC_CTX *ctx, struct loadparm_context *lp_ctx, + struct smbcli_tree *tree, + const char ***printers, int *num_printers) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + struct dcerpc_pipe *p; + struct srvsvc_NetShareEnum r; + struct srvsvc_NetShareCtr1 c1_in; + struct srvsvc_NetShareCtr1 *c1; + int i; + + mem_ctx = talloc_new(ctx); + if (mem_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = pipe_bind_smb(mem_ctx, lp_ctx, + tree, "\\srvsvc", &ndr_table_srvsvc, + &p); + if (!NT_STATUS_IS_OK(status)) { + d_printf("could not bind to srvsvc pipe\n"); + talloc_free(mem_ctx); + return status; + } + + r.in.server_unc = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(p)); + r.in.level = 1; + ZERO_STRUCT(c1_in); + r.in.ctr.ctr1 = &c1_in; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + + status = dcerpc_srvsvc_NetShareEnum(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("NetShareEnum level %u failed - %s\n", + r.in.level, nt_errstr(status)); + talloc_free(mem_ctx); + return status; + } + + *printers = NULL; + *num_printers = 0; + c1 = r.out.ctr.ctr1; + for (i=0; i<c1->count; i++) { + if (c1->array[i].type != STYPE_PRINTQ) { + continue; + } + if (!add_string_to_array(ctx, c1->array[i].name, + printers, num_printers)) { + talloc_free(ctx); + return NT_STATUS_NO_MEMORY; + } + } + + talloc_free(mem_ctx); + return NT_STATUS_OK; +} + +static bool enumprinters(TALLOC_CTX *mem_ctx, struct dcerpc_pipe *pipe, + const char *servername, int level, int *num_printers) +{ + struct spoolss_EnumPrinters r; + NTSTATUS status; + DATA_BLOB blob; + + r.in.flags = PRINTER_ENUM_LOCAL; + r.in.server = talloc_asprintf(mem_ctx, "\\\\%s", servername); + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + + status = dcerpc_spoolss_EnumPrinters(pipe, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) dcerpc_spoolss_EnumPrinters failed: %s\n", + __location__, nt_errstr(status)); + return false; + } + + if (!W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + d_printf("(%s) EnumPrinters unexpected return code %s, should " + "be WERR_INSUFFICIENT_BUFFER\n", __location__, + win_errstr(r.out.result)); + return false; + } + + blob = data_blob_talloc_zero(mem_ctx, r.out.needed); + if (blob.data == NULL) { + d_printf("(%s) data_blob_talloc failed\n", __location__); + return false; + } + + r.in.buffer = &blob; + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_EnumPrinters(pipe, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) { + d_printf("(%s) dcerpc_spoolss_EnumPrinters failed: %s, " + "%s\n", __location__, nt_errstr(status), + win_errstr(r.out.result)); + return false; + } + + *num_printers = r.out.count; + + return true; +} + +static NTSTATUS getprinterinfo(TALLOC_CTX *ctx, struct dcerpc_pipe *pipe, + struct policy_handle *handle, int level, + union spoolss_PrinterInfo **res) +{ + TALLOC_CTX *mem_ctx; + struct spoolss_GetPrinter r; + DATA_BLOB blob; + NTSTATUS status; + + mem_ctx = talloc_new(ctx); + if (mem_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + r.in.handle = handle; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + + status = dcerpc_spoolss_GetPrinter(pipe, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) dcerpc_spoolss_GetPrinter failed: %s\n", + __location__, nt_errstr(status)); + talloc_free(mem_ctx); + return status; + } + + if (!W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + printf("GetPrinter unexpected return code %s, should " + "be WERR_INSUFFICIENT_BUFFER\n", + win_errstr(r.out.result)); + talloc_free(mem_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + + r.in.handle = handle; + r.in.level = level; + blob = data_blob_talloc(mem_ctx, NULL, r.out.needed); + if (blob.data == NULL) { + talloc_free(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + memset(blob.data, 0, blob.length); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_GetPrinter(pipe, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) { + d_printf("(%s) dcerpc_spoolss_GetPrinter failed: %s, " + "%s\n", __location__, nt_errstr(status), + win_errstr(r.out.result)); + talloc_free(mem_ctx); + return NT_STATUS_IS_OK(status) ? + NT_STATUS_UNSUCCESSFUL : status; + } + + if (res != NULL) { + *res = talloc_steal(ctx, r.out.info); + } + + talloc_free(mem_ctx); + return NT_STATUS_OK; +} + +bool torture_samba3_rpc_spoolss(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + bool ret = true; + struct smbcli_state *cli; + struct dcerpc_pipe *p; + NTSTATUS status; + struct policy_handle server_handle, printer_handle; + const char **printers; + int num_printers; + struct spoolss_UserLevel1 userlevel1; + char *servername; + + if (!(mem_ctx = talloc_new(torture))) { + return false; + } + + if (!(torture_open_connection_share( + mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL), + "IPC$", torture->ev))) { + d_printf("IPC$ connection failed\n"); + talloc_free(mem_ctx); + return false; + } + + status = get_servername(mem_ctx, cli->tree, lp_iconv_convenience(torture->lp_ctx), &servername); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "(%s) get_servername returned %s\n", + __location__, nt_errstr(status)); + talloc_free(mem_ctx); + return false; + } + + if (!NT_STATUS_IS_OK(find_printers(mem_ctx, torture->lp_ctx, cli->tree, + &printers, &num_printers))) { + talloc_free(mem_ctx); + return false; + } + + if (num_printers == 0) { + d_printf("Did not find printers\n"); + talloc_free(mem_ctx); + return true; + } + + status = pipe_bind_smb(mem_ctx, torture->lp_ctx, cli->tree, "\\spoolss", + &ndr_table_spoolss, &p); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) pipe_bind_smb failed: %s\n", __location__, + nt_errstr(status)); + talloc_free(mem_ctx); + return false; + } + + ZERO_STRUCT(userlevel1); + userlevel1.client = talloc_asprintf( + mem_ctx, "\\\\%s", lp_netbios_name(torture->lp_ctx)); + userlevel1.user = cli_credentials_get_username(cmdline_credentials); + userlevel1.build = 2600; + userlevel1.major = 3; + userlevel1.minor = 0; + userlevel1.processor = 0; + + { + struct spoolss_OpenPrinterEx r; + + ZERO_STRUCT(r); + r.in.printername = talloc_asprintf(mem_ctx, "\\\\%s", + servername); + r.in.datatype = NULL; + r.in.access_mask = 0; + r.in.level = 1; + r.in.userlevel.level1 = &userlevel1; + r.out.handle = &server_handle; + + status = dcerpc_spoolss_OpenPrinterEx(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) { + d_printf("(%s) dcerpc_spoolss_OpenPrinterEx failed: " + "%s, %s\n", __location__, nt_errstr(status), + win_errstr(r.out.result)); + talloc_free(mem_ctx); + return false; + } + } + + { + struct spoolss_ClosePrinter r; + + r.in.handle = &server_handle; + r.out.handle = &server_handle; + + status = dcerpc_spoolss_ClosePrinter(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) { + d_printf("(%s) dcerpc_spoolss_ClosePrinter failed: " + "%s, %s\n", __location__, nt_errstr(status), + win_errstr(r.out.result)); + talloc_free(mem_ctx); + return false; + } + } + + { + struct spoolss_OpenPrinterEx r; + + ZERO_STRUCT(r); + r.in.printername = talloc_asprintf( + mem_ctx, "\\\\%s\\%s", servername, printers[0]); + r.in.datatype = NULL; + r.in.access_mask = 0; + r.in.level = 1; + r.in.userlevel.level1 = &userlevel1; + r.out.handle = &printer_handle; + + status = dcerpc_spoolss_OpenPrinterEx(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) { + d_printf("(%s) dcerpc_spoolss_OpenPrinterEx failed: " + "%s, %s\n", __location__, nt_errstr(status), + win_errstr(r.out.result)); + talloc_free(mem_ctx); + return false; + } + } + + { + int i; + + for (i=0; i<8; i++) { + status = getprinterinfo(mem_ctx, p, &printer_handle, + i, NULL); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) getprinterinfo %d failed: %s\n", + __location__, i, nt_errstr(status)); + ret = false; + } + } + } + + { + struct spoolss_ClosePrinter r; + + r.in.handle = &printer_handle; + r.out.handle = &printer_handle; + + status = dcerpc_spoolss_ClosePrinter(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) dcerpc_spoolss_ClosePrinter failed: " + "%s\n", __location__, nt_errstr(status)); + talloc_free(mem_ctx); + return false; + } + } + + { + int num_enumerated; + if (!enumprinters(mem_ctx, p, servername, 1, + &num_enumerated)) { + d_printf("(%s) enumprinters failed\n", __location__); + talloc_free(mem_ctx); + return false; + } + if (num_printers != num_enumerated) { + d_printf("(%s) netshareenum gave %d printers, " + "enumprinters lvl 1 gave %d\n", __location__, + num_printers, num_enumerated); + talloc_free(mem_ctx); + return false; + } + } + + { + int num_enumerated; + if (!enumprinters(mem_ctx, p, servername, 2, + &num_enumerated)) { + d_printf("(%s) enumprinters failed\n", __location__); + talloc_free(mem_ctx); + return false; + } + if (num_printers != num_enumerated) { + d_printf("(%s) netshareenum gave %d printers, " + "enumprinters lvl 2 gave %d\n", __location__, + num_printers, num_enumerated); + talloc_free(mem_ctx); + return false; + } + } + + talloc_free(mem_ctx); + + return ret; +} + +bool torture_samba3_rpc_wkssvc(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + struct smbcli_state *cli; + struct dcerpc_pipe *p; + NTSTATUS status; + char *servername; + + if (!(mem_ctx = talloc_new(torture))) { + return false; + } + + if (!(torture_open_connection_share( + mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL), + "IPC$", torture->ev))) { + d_printf("IPC$ connection failed\n"); + talloc_free(mem_ctx); + return false; + } + + status = get_servername(mem_ctx, cli->tree, lp_iconv_convenience(torture->lp_ctx), &servername); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "(%s) get_servername returned %s\n", + __location__, nt_errstr(status)); + talloc_free(mem_ctx); + return false; + } + + status = pipe_bind_smb(mem_ctx, torture->lp_ctx, cli->tree, "\\wkssvc", + &ndr_table_wkssvc, &p); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) pipe_bind_smb failed: %s\n", __location__, + nt_errstr(status)); + talloc_free(mem_ctx); + return false; + } + + { + struct wkssvc_NetWkstaInfo100 wks100; + union wkssvc_NetWkstaInfo info; + struct wkssvc_NetWkstaGetInfo r; + + r.in.server_name = "\\foo"; + r.in.level = 100; + info.info100 = &wks100; + r.out.info = &info; + + status = dcerpc_wkssvc_NetWkstaGetInfo(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) { + d_printf("(%s) dcerpc_wkssvc_NetWksGetInfo failed: " + "%s, %s\n", __location__, nt_errstr(status), + win_errstr(r.out.result)); + talloc_free(mem_ctx); + return false; + } + + if (strcmp(servername, + r.out.info->info100->server_name) != 0) { + d_printf("(%s) servername inconsistency: RAP=%s, " + "dcerpc_wkssvc_NetWksGetInfo=%s", + __location__, servername, + r.out.info->info100->server_name); + talloc_free(mem_ctx); + return false; + } + } + + talloc_free(mem_ctx); + return true; +} + +static NTSTATUS winreg_close(struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + struct winreg_CloseKey c; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + + c.in.handle = c.out.handle = handle; + + if (!(mem_ctx = talloc_new(p))) { + return NT_STATUS_NO_MEMORY; + } + + status = dcerpc_winreg_CloseKey(p, mem_ctx, &c); + talloc_free(mem_ctx); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (!W_ERROR_IS_OK(c.out.result)) { + return werror_to_ntstatus(c.out.result); + } + + return NT_STATUS_OK; +} + +static NTSTATUS enumvalues(struct dcerpc_pipe *p, struct policy_handle *handle, + TALLOC_CTX *mem_ctx) +{ + uint32_t enum_index = 0; + + while (1) { + struct winreg_EnumValue r; + struct winreg_StringBuf name; + enum winreg_Type type = 0; + uint8_t buf8[1024]; + NTSTATUS status; + uint32_t size, length; + + r.in.handle = handle; + r.in.enum_index = enum_index; + name.name = ""; + name.size = 1024; + r.in.name = r.out.name = &name; + size = 1024; + length = 5; + r.in.type = &type; + r.in.value = buf8; + r.in.size = &size; + r.in.length = &length; + + status = dcerpc_winreg_EnumValue(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) { + return NT_STATUS_OK; + } + enum_index += 1; + } +} + +static NTSTATUS enumkeys(struct dcerpc_pipe *p, struct policy_handle *handle, + TALLOC_CTX *mem_ctx, int depth) +{ + struct winreg_EnumKey r; + struct winreg_StringBuf class, name; + NTSTATUS status; + NTTIME t = 0; + + if (depth <= 0) { + return NT_STATUS_OK; + } + + class.name = ""; + class.size = 1024; + + r.in.handle = handle; + r.in.enum_index = 0; + r.in.name = &name; + r.in.keyclass = &class; + r.out.name = &name; + r.in.last_changed_time = &t; + + do { + TALLOC_CTX *tmp_ctx; + struct winreg_OpenKey o; + struct policy_handle key_handle; + int i; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + return NT_STATUS_NO_MEMORY; + } + + name.name = NULL; + name.size = 1024; + + status = dcerpc_winreg_EnumKey(p, tmp_ctx, &r); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) { + /* We're done enumerating */ + talloc_free(tmp_ctx); + return NT_STATUS_OK; + } + + for (i=0; i<10-depth; i++) + printf(" "); + printf("%s\n", r.out.name->name); + + + o.in.parent_handle = handle; + o.in.keyname.name = r.out.name->name; + o.in.unknown = 0; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.out.handle = &key_handle; + + status = dcerpc_winreg_OpenKey(p, tmp_ctx, &o); + if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(o.out.result)) { + enumkeys(p, &key_handle, tmp_ctx, depth-1); + enumvalues(p, &key_handle, tmp_ctx); + status = winreg_close(p, &key_handle); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + talloc_free(tmp_ctx); + + r.in.enum_index += 1; + } while(true); + + return NT_STATUS_OK; +} + +typedef NTSTATUS (*winreg_open_fn)(struct dcerpc_pipe *, TALLOC_CTX *, void *); + +static bool test_Open3(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + const char *name, winreg_open_fn open_fn) +{ + struct policy_handle handle; + struct winreg_OpenHKLM r; + NTSTATUS status; + + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + status = open_fn(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) { + d_printf("(%s) %s failed: %s, %s\n", __location__, name, + nt_errstr(status), win_errstr(r.out.result)); + return false; + } + + enumkeys(p, &handle, mem_ctx, 4); + + status = winreg_close(p, &handle); + if (!NT_STATUS_IS_OK(status)) { + d_printf("(%s) dcerpc_CloseKey failed: %s\n", + __location__, nt_errstr(status)); + return false; + } + + return true; +} + +bool torture_samba3_rpc_winreg(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct { + const char *name; + winreg_open_fn fn; + } open_fns[] = { + {"OpenHKLM", (winreg_open_fn)dcerpc_winreg_OpenHKLM }, + {"OpenHKU", (winreg_open_fn)dcerpc_winreg_OpenHKU }, + {"OpenHKPD", (winreg_open_fn)dcerpc_winreg_OpenHKPD }, + {"OpenHKPT", (winreg_open_fn)dcerpc_winreg_OpenHKPT }, + {"OpenHKCR", (winreg_open_fn)dcerpc_winreg_OpenHKCR }}; +#if 0 + int i; +#endif + + mem_ctx = talloc_init("torture_rpc_winreg"); + + status = torture_rpc_connection(torture, &p, &ndr_table_winreg); + + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return false; + } + +#if 1 + ret = test_Open3(p, mem_ctx, open_fns[0].name, open_fns[0].fn); +#else + for (i = 0; i < ARRAY_SIZE(open_fns); i++) { + if (!test_Open3(p, mem_ctx, open_fns[i].name, open_fns[i].fn)) + ret = false; + } +#endif + + talloc_free(mem_ctx); + + return ret; +} + +static NTSTATUS get_shareinfo(TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + struct smbcli_state *cli, + const char *share, + struct srvsvc_NetShareInfo502 **info) +{ + struct smbcli_tree *ipc; + struct dcerpc_pipe *p; + struct srvsvc_NetShareGetInfo r; + NTSTATUS status; + + if (!(p = dcerpc_pipe_init(cli, + cli->transport->socket->event.ctx, + lp_iconv_convenience(lp_ctx)))) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + status = secondary_tcon(p, cli->session, "IPC$", &ipc); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = dcerpc_pipe_open_smb(p, ipc, "\\pipe\\srvsvc"); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_pipe_open_smb failed: %s\n", + nt_errstr(status)); + goto fail; + } + + status = dcerpc_bind_auth_none(p, &ndr_table_srvsvc); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_bind_auth_none failed: %s\n", + nt_errstr(status)); + goto fail; + } + + r.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s", + dcerpc_server_name(p)); + r.in.share_name = share; + r.in.level = 502; + + status = dcerpc_srvsvc_NetShareGetInfo(p, p, &r); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) { + d_printf("(%s) OpenHKLM failed: %s, %s\n", __location__, + nt_errstr(status), win_errstr(r.out.result)); + goto fail; + } + + *info = talloc_move(mem_ctx, &r.out.info.info502); + return NT_STATUS_OK; + + fail: + talloc_free(p); + return status; +} + +/* + * Get us a handle on HKLM\ + */ + +static NTSTATUS get_hklm_handle(TALLOC_CTX *mem_ctx, + struct smbcli_state *cli, + struct smb_iconv_convenience *iconv_convenience, + struct dcerpc_pipe **pipe_p, + struct policy_handle **handle) +{ + struct smbcli_tree *ipc; + struct dcerpc_pipe *p; + struct winreg_OpenHKLM r; + NTSTATUS status; + struct policy_handle *result; + + result = talloc(mem_ctx, struct policy_handle); + + if (result == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (!(p = dcerpc_pipe_init(result, + cli->transport->socket->event.ctx, + iconv_convenience))) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + status = secondary_tcon(p, cli->session, "IPC$", &ipc); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = dcerpc_pipe_open_smb(p, ipc, "\\winreg"); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_pipe_open_smb failed: %s\n", + nt_errstr(status)); + goto fail; + } + + status = dcerpc_bind_auth_none(p, &ndr_table_winreg); + if (!NT_STATUS_IS_OK(status)) { + d_printf("dcerpc_bind_auth_none failed: %s\n", + nt_errstr(status)); + goto fail; + } + + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = result; + + status = dcerpc_winreg_OpenHKLM(p, p, &r); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) { + d_printf("(%s) OpenHKLM failed: %s, %s\n", __location__, + nt_errstr(status), win_errstr(r.out.result)); + goto fail; + } + + *pipe_p = p; + *handle = result; + return NT_STATUS_OK; + + fail: + talloc_free(result); + return status; +} + +static NTSTATUS torture_samba3_createshare(struct smbcli_state *cli, + struct smb_iconv_convenience *iconv_convenience, + const char *sharename) +{ + struct dcerpc_pipe *p; + struct policy_handle *hklm = NULL; + struct policy_handle new_handle; + struct winreg_CreateKey c; + struct winreg_CloseKey cl; + enum winreg_CreateAction action_taken; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(cli); + NT_STATUS_HAVE_NO_MEMORY(mem_ctx); + + status = get_hklm_handle(mem_ctx, cli, iconv_convenience, &p, &hklm); + if (!NT_STATUS_IS_OK(status)) { + d_printf("get_hklm_handle failed: %s\n", nt_errstr(status)); + goto fail; + } + + c.in.handle = hklm; + c.in.name.name = talloc_asprintf( + mem_ctx, "software\\samba\\smbconf\\%s", sharename); + if (c.in.name.name == NULL) { + d_printf("talloc_asprintf failed\n"); + status = NT_STATUS_NO_MEMORY; + goto fail; + } + c.in.keyclass.name = ""; + c.in.options = 0; + c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + c.in.secdesc = NULL; + c.in.action_taken = &action_taken; + c.out.new_handle = &new_handle; + c.out.action_taken = &action_taken; + + status = dcerpc_winreg_CreateKey(p, p, &c); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(c.out.result)) { + d_printf("(%s) OpenKey failed: %s, %s\n", __location__, + nt_errstr(status), win_errstr(c.out.result)); + goto fail; + } + + cl.in.handle = &new_handle; + cl.out.handle = &new_handle; + status = dcerpc_winreg_CloseKey(p, p, &cl); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(cl.out.result)) { + d_printf("(%s) OpenKey failed: %s, %s\n", __location__, + nt_errstr(status), win_errstr(cl.out.result)); + goto fail; + } + + + fail: + talloc_free(mem_ctx); + return status; +} + +static NTSTATUS torture_samba3_deleteshare(struct torture_context *torture, + struct smbcli_state *cli, + const char *sharename) +{ + struct dcerpc_pipe *p; + struct policy_handle *hklm = NULL; + struct winreg_DeleteKey d; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(cli); + NT_STATUS_HAVE_NO_MEMORY(mem_ctx); + + status = get_hklm_handle(cli, cli, lp_iconv_convenience(torture->lp_ctx), + &p, &hklm); + if (!NT_STATUS_IS_OK(status)) { + d_printf("get_hklm_handle failed: %s\n", nt_errstr(status)); + goto fail; + } + + d.in.handle = hklm; + d.in.key.name = talloc_asprintf( + mem_ctx, "software\\samba\\smbconf\\%s", sharename); + if (d.in.key.name == NULL) { + d_printf("talloc_asprintf failed\n"); + status = NT_STATUS_NO_MEMORY; + goto fail; + } + status = dcerpc_winreg_DeleteKey(p, p, &d); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(d.out.result)) { + d_printf("(%s) OpenKey failed: %s, %s\n", __location__, + nt_errstr(status), win_errstr(d.out.result)); + goto fail; + } + + fail: + talloc_free(mem_ctx); + return status; +} + +static NTSTATUS torture_samba3_setconfig(struct smbcli_state *cli, + struct loadparm_context *lp_ctx, + const char *sharename, + const char *parameter, + const char *value) +{ + struct dcerpc_pipe *p = NULL; + struct policy_handle *hklm = NULL, key_handle; + struct winreg_OpenKey o; + struct winreg_SetValue s; + uint32_t type; + DATA_BLOB val; + NTSTATUS status; + + status = get_hklm_handle(cli, cli, lp_iconv_convenience(lp_ctx), &p, &hklm); + if (!NT_STATUS_IS_OK(status)) { + d_printf("get_hklm_handle failed: %s\n", nt_errstr(status)); + return status;; + } + + o.in.parent_handle = hklm; + o.in.keyname.name = talloc_asprintf( + hklm, "software\\samba\\smbconf\\%s", sharename); + if (o.in.keyname.name == NULL) { + d_printf("talloc_asprintf failed\n"); + status = NT_STATUS_NO_MEMORY; + goto done; + } + o.in.unknown = 0; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.out.handle = &key_handle; + + status = dcerpc_winreg_OpenKey(p, p, &o); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(o.out.result)) { + d_printf("(%s) OpenKey failed: %s, %s\n", __location__, + nt_errstr(status), win_errstr(o.out.result)); + goto done; + } + + if (!reg_string_to_val(hklm, lp_iconv_convenience(lp_ctx), "REG_SZ", + value, &type, &val)) { + d_printf("(%s) reg_string_to_val failed\n", __location__); + goto done; + } + + s.in.handle = &key_handle; + s.in.name.name = parameter; + s.in.type = type; + s.in.data = val.data; + s.in.size = val.length; + + status = dcerpc_winreg_SetValue(p, p, &s); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(s.out.result)) { + d_printf("(%s) SetValue failed: %s, %s\n", __location__, + nt_errstr(status), win_errstr(s.out.result)); + goto done; + } + + done: + talloc_free(hklm); + return status; +} + +bool torture_samba3_regconfig(struct torture_context *torture) +{ + struct smbcli_state *cli; + struct srvsvc_NetShareInfo502 *i = NULL; + NTSTATUS status; + bool ret = false; + const char *comment = "Dummer Kommentar"; + + if (!(torture_open_connection(&cli, torture, 0))) { + return false; + } + + status = torture_samba3_createshare(cli, lp_iconv_convenience(torture->lp_ctx), "blubber"); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(torture, "torture_samba3_createshare failed: " + "%s\n", nt_errstr(status)); + goto done; + } + + status = torture_samba3_setconfig(cli, torture->lp_ctx, "blubber", "comment", comment); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(torture, "torture_samba3_setconfig failed: " + "%s\n", nt_errstr(status)); + goto done; + } + + status = get_shareinfo(torture, torture->lp_ctx, cli, "blubber", &i); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(torture, "get_shareinfo failed: " + "%s\n", nt_errstr(status)); + goto done; + } + + if (strcmp(comment, i->comment) != 0) { + torture_warning(torture, "Expected comment [%s], got [%s]\n", + comment, i->comment); + goto done; + } + + status = torture_samba3_deleteshare(torture, cli, "blubber"); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(torture, "torture_samba3_deleteshare failed: " + "%s\n", nt_errstr(status)); + goto done; + } + + ret = true; + done: + talloc_free(cli); + return ret; +} diff --git a/source4/torture/rpc/samlogon.c b/source4/torture/rpc/samlogon.c new file mode 100644 index 0000000000..e2558ff0a9 --- /dev/null +++ b/source4/torture/rpc/samlogon.c @@ -0,0 +1,1855 @@ +/* + Unix SMB/CIFS implementation. + + test suite for netlogon SamLogon operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004 + Copyright (C) Tim Potter 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_netlogon.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "auth/auth.h" +#include "lib/crypto/crypto.h" +#include "lib/cmdline/popt_common.h" +#include "torture/rpc/rpc.h" +#include "auth/gensec/schannel_proto.h" +#include "auth/gensec/gensec.h" +#include "libcli/auth/libcli_auth.h" +#include "param/param.h" + +#define TEST_MACHINE_NAME "samlogontest" +#define TEST_USER_NAME "samlogontestuser" +#define TEST_USER_NAME_WRONG_WKS "samlogontest2" +#define TEST_USER_NAME_WRONG_TIME "samlogontest3" + +enum ntlm_break { + BREAK_BOTH, + BREAK_NONE, + BREAK_LM, + BREAK_NT, + NO_LM, + NO_NT +}; + +struct samlogon_state { + TALLOC_CTX *mem_ctx; + const char *comment; + const char *account_name; + const char *account_domain; + const char *password; + struct dcerpc_pipe *p; + int function_level; + uint32_t parameter_control; + struct netr_LogonSamLogon r; + struct netr_LogonSamLogonEx r_ex; + struct netr_LogonSamLogonWithFlags r_flags; + struct netr_Authenticator auth, auth2; + struct creds_CredentialState *creds; + NTSTATUS expected_error; + bool old_password; /* Allow an old password to be accepted or rejected without error, as well as session key bugs */ + DATA_BLOB chall; + struct smb_iconv_convenience *iconv_convenience; +}; + +/* + Authenticate a user with a challenge/response, checking session key + and valid authentication types +*/ +static NTSTATUS check_samlogon(struct samlogon_state *samlogon_state, + enum ntlm_break break_which, + uint32_t parameter_control, + DATA_BLOB *chall, + DATA_BLOB *lm_response, + DATA_BLOB *nt_response, + uint8_t lm_key[8], + uint8_t user_session_key[16], + char **error_string) +{ + NTSTATUS status; + struct netr_LogonSamLogon *r = &samlogon_state->r; + struct netr_LogonSamLogonEx *r_ex = &samlogon_state->r_ex; + struct netr_LogonSamLogonWithFlags *r_flags = &samlogon_state->r_flags; + struct netr_NetworkInfo ninfo; + struct netr_SamBaseInfo *base = NULL; + uint16_t validation_level = 0; + + samlogon_state->r.in.logon.network = &ninfo; + samlogon_state->r_ex.in.logon.network = &ninfo; + samlogon_state->r_flags.in.logon.network = &ninfo; + + ninfo.identity_info.domain_name.string = samlogon_state->account_domain; + ninfo.identity_info.parameter_control = parameter_control; + ninfo.identity_info.logon_id_low = 0; + ninfo.identity_info.logon_id_high = 0; + ninfo.identity_info.account_name.string = samlogon_state->account_name; + ninfo.identity_info.workstation.string = TEST_MACHINE_NAME; + + memcpy(ninfo.challenge, chall->data, 8); + + switch (break_which) { + case BREAK_NONE: + break; + case BREAK_LM: + if (lm_response && lm_response->data) { + lm_response->data[0]++; + } + break; + case BREAK_NT: + if (nt_response && nt_response->data) { + nt_response->data[0]++; + } + break; + case BREAK_BOTH: + if (lm_response && lm_response->data) { + lm_response->data[0]++; + } + if (nt_response && nt_response->data) { + nt_response->data[0]++; + } + break; + case NO_LM: + data_blob_free(lm_response); + break; + case NO_NT: + data_blob_free(nt_response); + break; + } + + if (nt_response) { + ninfo.nt.data = nt_response->data; + ninfo.nt.length = nt_response->length; + } else { + ninfo.nt.data = NULL; + ninfo.nt.length = 0; + } + + if (lm_response) { + ninfo.lm.data = lm_response->data; + ninfo.lm.length = lm_response->length; + } else { + ninfo.lm.data = NULL; + ninfo.lm.length = 0; + } + + switch (samlogon_state->function_level) { + case NDR_NETR_LOGONSAMLOGON: + ZERO_STRUCT(samlogon_state->auth2); + creds_client_authenticator(samlogon_state->creds, &samlogon_state->auth); + + r->out.return_authenticator = NULL; + status = dcerpc_netr_LogonSamLogon(samlogon_state->p, samlogon_state->mem_ctx, r); + if (!r->out.return_authenticator || + !creds_client_check(samlogon_state->creds, &r->out.return_authenticator->cred)) { + d_printf("Credential chaining failed\n"); + } + if (!NT_STATUS_IS_OK(status)) { + if (error_string) { + *error_string = strdup(nt_errstr(status)); + } + return status; + } + + validation_level = r->in.validation_level; + + creds_decrypt_samlogon(samlogon_state->creds, validation_level, &r->out.validation); + + switch (validation_level) { + case 2: + base = &r->out.validation.sam2->base; + break; + case 3: + base = &r->out.validation.sam3->base; + break; + case 6: + base = &r->out.validation.sam6->base; + break; + } + break; + case NDR_NETR_LOGONSAMLOGONEX: + status = dcerpc_netr_LogonSamLogonEx(samlogon_state->p, samlogon_state->mem_ctx, r_ex); + if (!NT_STATUS_IS_OK(status)) { + if (error_string) { + *error_string = strdup(nt_errstr(status)); + } + return status; + } + + validation_level = r_ex->in.validation_level; + + creds_decrypt_samlogon(samlogon_state->creds, validation_level, &r_ex->out.validation); + + switch (validation_level) { + case 2: + base = &r_ex->out.validation.sam2->base; + break; + case 3: + base = &r_ex->out.validation.sam3->base; + break; + case 6: + base = &r_ex->out.validation.sam6->base; + break; + } + break; + case NDR_NETR_LOGONSAMLOGONWITHFLAGS: + ZERO_STRUCT(samlogon_state->auth2); + creds_client_authenticator(samlogon_state->creds, &samlogon_state->auth); + + r_flags->out.return_authenticator = NULL; + status = dcerpc_netr_LogonSamLogonWithFlags(samlogon_state->p, samlogon_state->mem_ctx, r_flags); + if (!r_flags->out.return_authenticator || + !creds_client_check(samlogon_state->creds, &r_flags->out.return_authenticator->cred)) { + d_printf("Credential chaining failed\n"); + } + if (!NT_STATUS_IS_OK(status)) { + if (error_string) { + *error_string = strdup(nt_errstr(status)); + } + return status; + } + + validation_level = r_flags->in.validation_level; + + creds_decrypt_samlogon(samlogon_state->creds, validation_level, &r_flags->out.validation); + + switch (validation_level) { + case 2: + base = &r_flags->out.validation.sam2->base; + break; + case 3: + base = &r_flags->out.validation.sam3->base; + break; + case 6: + base = &r_flags->out.validation.sam6->base; + break; + } + break; + default: + /* can't happen */ + return NT_STATUS_INVALID_PARAMETER; + } + + if (!base) { + d_printf("No user info returned from 'successful' SamLogon*() call!\n"); + return NT_STATUS_INVALID_PARAMETER; + } + + if (user_session_key) { + memcpy(user_session_key, base->key.key, 16); + } + if (lm_key) { + memcpy(lm_key, base->LMSessKey.key, 8); + } + + return status; +} + + +/* + * Test the normal 'LM and NTLM' combination + */ + +static bool test_lm_ntlm_broken(struct samlogon_state *samlogon_state, enum ntlm_break break_which, char **error_string) +{ + bool pass = true; + bool lm_good; + NTSTATUS nt_status; + DATA_BLOB lm_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24); + DATA_BLOB nt_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24); + DATA_BLOB session_key = data_blob_talloc(samlogon_state->mem_ctx, NULL, 16); + + uint8_t lm_key[8]; + uint8_t user_session_key[16]; + uint8_t lm_hash[16]; + uint8_t nt_hash[16]; + + ZERO_STRUCT(lm_key); + ZERO_STRUCT(user_session_key); + + lm_good = SMBencrypt(samlogon_state->password, samlogon_state->chall.data, lm_response.data); + if (!lm_good) { + ZERO_STRUCT(lm_hash); + } else { + E_deshash(samlogon_state->password, lm_hash); + } + + SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data, nt_response.data); + + E_md4hash(samlogon_state->password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, session_key.data); + + nt_status = check_samlogon(samlogon_state, + break_which, + samlogon_state->parameter_control, + &samlogon_state->chall, + &lm_response, + &nt_response, + lm_key, + user_session_key, + error_string); + + data_blob_free(&lm_response); + + if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) { + /* for 'long' passwords, the LM password is invalid */ + if (break_which == NO_NT && !lm_good) { + return true; + } + /* for 'old' passwords, we allow the server to be OK or wrong password */ + if (samlogon_state->old_password) { + return true; + } + return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH)); + } else if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, nt_status) && strchr_m(samlogon_state->account_name, '@')) { + return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH) || (break_which == NO_NT)); + } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) { + SAFE_FREE(*error_string); + asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status)); + return false; + } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) { + return true; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + if (break_which == NO_NT && !lm_good) { + *error_string = strdup("LM password is 'long' (> 14 chars and therefore invalid) but login did not fail!"); + return false; + } + + if (memcmp(lm_hash, lm_key, + sizeof(lm_key)) != 0) { + d_printf("LM Key does not match expectations!\n"); + d_printf("lm_key:\n"); + dump_data(1, lm_key, 8); + d_printf("expected:\n"); + dump_data(1, lm_hash, 8); + pass = false; + } + + switch (break_which) { + case NO_NT: + { + uint8_t lm_key_expected[16]; + memcpy(lm_key_expected, lm_hash, 8); + memset(lm_key_expected+8, '\0', 8); + if (memcmp(lm_key_expected, user_session_key, + 16) != 0) { + *error_string = strdup("NT Session Key does not match expectations (should be first-8 LM hash)!\n"); + d_printf("user_session_key:\n"); + dump_data(1, user_session_key, sizeof(user_session_key)); + d_printf("expected:\n"); + dump_data(1, lm_key_expected, sizeof(lm_key_expected)); + pass = false; + } + break; + } + default: + if (memcmp(session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + *error_string = strdup("NT Session Key does not match expectations!\n"); + d_printf("user_session_key:\n"); + dump_data(1, user_session_key, 16); + d_printf("expected:\n"); + dump_data(1, session_key.data, session_key.length); + pass = false; + } + } + return pass; +} + +/* + * Test LM authentication, no NT response supplied + */ + +static bool test_lm(struct samlogon_state *samlogon_state, char **error_string) +{ + + return test_lm_ntlm_broken(samlogon_state, NO_NT, error_string); +} + +/* + * Test the NTLM response only, no LM. + */ + +static bool test_ntlm(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lm_ntlm_broken(samlogon_state, NO_LM, error_string); +} + +/* + * Test the NTLM response only, but in the LM field. + */ + +static bool test_ntlm_in_lm(struct samlogon_state *samlogon_state, char **error_string) +{ + bool lm_good; + bool pass = true; + NTSTATUS nt_status; + DATA_BLOB nt_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24); + DATA_BLOB session_key = data_blob_talloc(samlogon_state->mem_ctx, NULL, 16); + + uint8_t lm_key[8]; + uint8_t lm_hash[16]; + uint8_t user_session_key[16]; + uint8_t nt_hash[16]; + + ZERO_STRUCT(lm_key); + ZERO_STRUCT(user_session_key); + + SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data, + nt_response.data); + E_md4hash(samlogon_state->password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, + session_key.data); + + lm_good = E_deshash(samlogon_state->password, lm_hash); + if (!lm_good) { + ZERO_STRUCT(lm_hash); + } + nt_status = check_samlogon(samlogon_state, + BREAK_NONE, + samlogon_state->parameter_control, + &samlogon_state->chall, + &nt_response, + NULL, + lm_key, + user_session_key, + error_string); + + if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) { + /* for 'old' passwords, we allow the server to be OK or wrong password */ + if (samlogon_state->old_password) { + return true; + } + return false; + } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) { + SAFE_FREE(*error_string); + asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status)); + return false; + } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) { + return true; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + if (lm_good) { + if (memcmp(lm_hash, lm_key, + sizeof(lm_key)) != 0) { + d_printf("LM Key does not match expectations!\n"); + d_printf("lm_key:\n"); + dump_data(1, lm_key, 8); + d_printf("expected:\n"); + dump_data(1, lm_hash, 8); + pass = false; + } +#if 0 + } else { + if (memcmp(session_key.data, lm_key, + sizeof(lm_key)) != 0) { + d_printf("LM Key does not match expectations (first 8 session key)!\n"); + d_printf("lm_key:\n"); + dump_data(1, lm_key, 8); + d_printf("expected:\n"); + dump_data(1, session_key.data, 8); + pass = false; + } +#endif + } + if (lm_good && memcmp(lm_hash, user_session_key, 8) != 0) { + uint8_t lm_key_expected[16]; + memcpy(lm_key_expected, lm_hash, 8); + memset(lm_key_expected+8, '\0', 8); + if (memcmp(lm_key_expected, user_session_key, + 16) != 0) { + d_printf("NT Session Key does not match expectations (should be first-8 LM hash)!\n"); + d_printf("user_session_key:\n"); + dump_data(1, user_session_key, sizeof(user_session_key)); + d_printf("expected:\n"); + dump_data(1, lm_key_expected, sizeof(lm_key_expected)); + pass = false; + } + } + return pass; +} + +/* + * Test the NTLM response only, but in the both the NT and LM fields. + */ + +static bool test_ntlm_in_both(struct samlogon_state *samlogon_state, char **error_string) +{ + bool pass = true; + bool lm_good; + NTSTATUS nt_status; + DATA_BLOB nt_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24); + DATA_BLOB session_key = data_blob_talloc(samlogon_state->mem_ctx, NULL, 16); + + uint8_t lm_key[8]; + uint8_t lm_hash[16]; + uint8_t user_session_key[16]; + uint8_t nt_hash[16]; + + ZERO_STRUCT(lm_key); + ZERO_STRUCT(user_session_key); + + SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data, + nt_response.data); + E_md4hash(samlogon_state->password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, + session_key.data); + + lm_good = E_deshash(samlogon_state->password, lm_hash); + if (!lm_good) { + ZERO_STRUCT(lm_hash); + } + + nt_status = check_samlogon(samlogon_state, + BREAK_NONE, + samlogon_state->parameter_control, + &samlogon_state->chall, + NULL, + &nt_response, + lm_key, + user_session_key, + error_string); + + if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) { + /* for 'old' passwords, we allow the server to be OK or wrong password */ + if (samlogon_state->old_password) { + return true; + } + return false; + } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) { + SAFE_FREE(*error_string); + asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status)); + return false; + } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) { + return true; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + if (memcmp(lm_hash, lm_key, + sizeof(lm_key)) != 0) { + d_printf("LM Key does not match expectations!\n"); + d_printf("lm_key:\n"); + dump_data(1, lm_key, 8); + d_printf("expected:\n"); + dump_data(1, lm_hash, 8); + pass = false; + } + if (memcmp(session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + d_printf("NT Session Key does not match expectations!\n"); + d_printf("user_session_key:\n"); + dump_data(1, user_session_key, 16); + d_printf("expected:\n"); + dump_data(1, session_key.data, session_key.length); + pass = false; + } + + + return pass; +} + +/* + * Test the NTLMv2 and LMv2 responses + */ + +enum ntlmv2_domain { + UPPER_DOMAIN, + NO_DOMAIN +}; + +static bool test_lmv2_ntlmv2_broken(struct samlogon_state *samlogon_state, + enum ntlm_break break_which, + enum ntlmv2_domain ntlmv2_domain, + char **error_string) +{ + bool pass = true; + NTSTATUS nt_status; + DATA_BLOB ntlmv2_response = data_blob(NULL, 0); + DATA_BLOB lmv2_response = data_blob(NULL, 0); + DATA_BLOB lmv2_session_key = data_blob(NULL, 0); + DATA_BLOB ntlmv2_session_key = data_blob(NULL, 0); + DATA_BLOB names_blob = NTLMv2_generate_names_blob(samlogon_state->mem_ctx, samlogon_state->iconv_convenience, TEST_MACHINE_NAME, lp_workgroup(global_loadparm)); + + uint8_t lm_session_key[8]; + uint8_t user_session_key[16]; + + ZERO_STRUCT(lm_session_key); + ZERO_STRUCT(user_session_key); + + switch (ntlmv2_domain) { + case UPPER_DOMAIN: + if (!SMBNTLMv2encrypt(samlogon_state->mem_ctx, + samlogon_state->account_name, samlogon_state->account_domain, + samlogon_state->password, &samlogon_state->chall, + &names_blob, + &lmv2_response, &ntlmv2_response, + &lmv2_session_key, &ntlmv2_session_key)) { + data_blob_free(&names_blob); + return false; + } + break; + case NO_DOMAIN: + if (!SMBNTLMv2encrypt(samlogon_state->mem_ctx, + samlogon_state->account_name, "", + samlogon_state->password, &samlogon_state->chall, + &names_blob, + &lmv2_response, &ntlmv2_response, + &lmv2_session_key, &ntlmv2_session_key)) { + data_blob_free(&names_blob); + return false; + } + break; + } + data_blob_free(&names_blob); + + nt_status = check_samlogon(samlogon_state, + break_which, + samlogon_state->parameter_control, + &samlogon_state->chall, + &lmv2_response, + &ntlmv2_response, + lm_session_key, + user_session_key, + error_string); + + data_blob_free(&lmv2_response); + data_blob_free(&ntlmv2_response); + + + if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) { + /* for 'old' passwords, we allow the server to be OK or wrong password */ + if (samlogon_state->old_password) { + return true; + } + return break_which == BREAK_BOTH; + } else if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, nt_status) && strchr_m(samlogon_state->account_name, '@')) { + return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH) || (break_which == NO_NT)); + } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) { + SAFE_FREE(*error_string); + asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status)); + return false; + } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) { + return true; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + + switch (break_which) { + case NO_NT: + if (memcmp(lmv2_session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + d_printf("USER (LMv2) Session Key does not match expectations!\n"); + d_printf("user_session_key:\n"); + dump_data(1, user_session_key, 16); + d_printf("expected:\n"); + dump_data(1, lmv2_session_key.data, ntlmv2_session_key.length); + pass = false; + } + if (memcmp(lmv2_session_key.data, lm_session_key, + sizeof(lm_session_key)) != 0) { + d_printf("LM (LMv2) Session Key does not match expectations!\n"); + d_printf("lm_session_key:\n"); + dump_data(1, lm_session_key, 8); + d_printf("expected:\n"); + dump_data(1, lmv2_session_key.data, 8); + pass = false; + } + break; + default: + if (memcmp(ntlmv2_session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + if (memcmp(lmv2_session_key.data, user_session_key, + sizeof(user_session_key)) == 0) { + d_printf("USER (NTLMv2) Session Key expected, got LMv2 sessesion key instead:\n"); + d_printf("user_session_key:\n"); + dump_data(1, user_session_key, 16); + d_printf("expected:\n"); + dump_data(1, ntlmv2_session_key.data, ntlmv2_session_key.length); + pass = false; + + } else { + d_printf("USER (NTLMv2) Session Key does not match expectations!\n"); + d_printf("user_session_key:\n"); + dump_data(1, user_session_key, 16); + d_printf("expected:\n"); + dump_data(1, ntlmv2_session_key.data, ntlmv2_session_key.length); + pass = false; + } + } + if (memcmp(ntlmv2_session_key.data, lm_session_key, + sizeof(lm_session_key)) != 0) { + if (memcmp(lmv2_session_key.data, lm_session_key, + sizeof(lm_session_key)) == 0) { + d_printf("LM (NTLMv2) Session Key expected, got LMv2 sessesion key instead:\n"); + d_printf("user_session_key:\n"); + dump_data(1, lm_session_key, 8); + d_printf("expected:\n"); + dump_data(1, ntlmv2_session_key.data, 8); + pass = false; + } else { + d_printf("LM (NTLMv2) Session Key does not match expectations!\n"); + d_printf("lm_session_key:\n"); + dump_data(1, lm_session_key, 8); + d_printf("expected:\n"); + dump_data(1, ntlmv2_session_key.data, 8); + pass = false; + } + } + } + + return pass; +} + +/* + * Test the NTLM and LMv2 responses + */ + +static bool test_lmv2_ntlm_broken(struct samlogon_state *samlogon_state, + enum ntlm_break break_which, + enum ntlmv2_domain ntlmv2_domain, + char **error_string) +{ + bool pass = true; + NTSTATUS nt_status; + DATA_BLOB ntlmv2_response = data_blob(NULL, 0); + DATA_BLOB lmv2_response = data_blob(NULL, 0); + DATA_BLOB lmv2_session_key = data_blob(NULL, 0); + DATA_BLOB ntlmv2_session_key = data_blob(NULL, 0); + DATA_BLOB names_blob = NTLMv2_generate_names_blob(samlogon_state->mem_ctx, samlogon_state->iconv_convenience, lp_netbios_name(global_loadparm), lp_workgroup(global_loadparm)); + + DATA_BLOB ntlm_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24); + DATA_BLOB ntlm_session_key = data_blob_talloc(samlogon_state->mem_ctx, NULL, 16); + + bool lm_good; + uint8_t lm_hash[16]; + uint8_t lm_session_key[8]; + uint8_t user_session_key[16]; + uint8_t nt_hash[16]; + + SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data, + ntlm_response.data); + E_md4hash(samlogon_state->password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, + ntlm_session_key.data); + + lm_good = E_deshash(samlogon_state->password, lm_hash); + if (!lm_good) { + ZERO_STRUCT(lm_hash); + } + + ZERO_STRUCT(lm_session_key); + ZERO_STRUCT(user_session_key); + + switch (ntlmv2_domain) { + case UPPER_DOMAIN: + /* TODO - test with various domain cases, and without domain */ + if (!SMBNTLMv2encrypt(samlogon_state->mem_ctx, + samlogon_state->account_name, samlogon_state->account_domain, + samlogon_state->password, &samlogon_state->chall, + &names_blob, + &lmv2_response, &ntlmv2_response, + &lmv2_session_key, &ntlmv2_session_key)) { + data_blob_free(&names_blob); + return false; + } + break; + case NO_DOMAIN: + /* TODO - test with various domain cases, and without domain */ + if (!SMBNTLMv2encrypt(samlogon_state->mem_ctx, + samlogon_state->account_name, "", + samlogon_state->password, &samlogon_state->chall, + &names_blob, + &lmv2_response, &ntlmv2_response, + &lmv2_session_key, &ntlmv2_session_key)) { + data_blob_free(&names_blob); + return false; + } + break; + } + + data_blob_free(&names_blob); + + nt_status = check_samlogon(samlogon_state, + break_which, + samlogon_state->parameter_control, + &samlogon_state->chall, + &lmv2_response, + &ntlm_response, + lm_session_key, + user_session_key, + error_string); + + data_blob_free(&lmv2_response); + data_blob_free(&ntlmv2_response); + + + if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) { + /* for 'old' passwords, we allow the server to be OK or wrong password */ + if (samlogon_state->old_password) { + return true; + } + return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH)); + } else if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, nt_status) && strchr_m(samlogon_state->account_name, '@')) { + return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH)); + } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) { + SAFE_FREE(*error_string); + asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status)); + return false; + } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) { + return true; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + switch (break_which) { + case NO_NT: + if (memcmp(lmv2_session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + d_printf("USER (LMv2) Session Key does not match expectations!\n"); + d_printf("user_session_key:\n"); + dump_data(1, user_session_key, 16); + d_printf("expected:\n"); + dump_data(1, lmv2_session_key.data, ntlmv2_session_key.length); + pass = false; + } + if (memcmp(lmv2_session_key.data, lm_session_key, + sizeof(lm_session_key)) != 0) { + d_printf("LM (LMv2) Session Key does not match expectations!\n"); + d_printf("lm_session_key:\n"); + dump_data(1, lm_session_key, 8); + d_printf("expected:\n"); + dump_data(1, lmv2_session_key.data, 8); + pass = false; + } + break; + case BREAK_LM: + if (memcmp(ntlm_session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + d_printf("USER (NTLMv2) Session Key does not match expectations!\n"); + d_printf("user_session_key:\n"); + dump_data(1, user_session_key, 16); + d_printf("expected:\n"); + dump_data(1, ntlm_session_key.data, ntlm_session_key.length); + pass = false; + } + if (lm_good) { + if (memcmp(lm_hash, lm_session_key, + sizeof(lm_session_key)) != 0) { + d_printf("LM Session Key does not match expectations!\n"); + d_printf("lm_session_key:\n"); + dump_data(1, lm_session_key, 8); + d_printf("expected:\n"); + dump_data(1, lm_hash, 8); + pass = false; + } + } else { + static const uint8_t zeros[8]; + if (memcmp(zeros, lm_session_key, + sizeof(lm_session_key)) != 0) { + d_printf("LM Session Key does not match expectations (zeros)!\n"); + d_printf("lm_session_key:\n"); + dump_data(1, lm_session_key, 8); + d_printf("expected:\n"); + dump_data(1, zeros, 8); + pass = false; + } + } + break; + default: + if (memcmp(ntlm_session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + d_printf("USER (NTLMv2) Session Key does not match expectations!\n"); + d_printf("user_session_key:\n"); + dump_data(1, user_session_key, 16); + d_printf("expected:\n"); + dump_data(1, ntlm_session_key.data, ntlm_session_key.length); + pass = false; + } + if (memcmp(ntlm_session_key.data, lm_session_key, + sizeof(lm_session_key)) != 0) { + d_printf("LM (NTLMv2) Session Key does not match expectations!\n"); + d_printf("lm_session_key:\n"); + dump_data(1, lm_session_key, 8); + d_printf("expected:\n"); + dump_data(1, ntlm_session_key.data, 8); + pass = false; + } + } + + return pass; +} + +/* + * Test the NTLMv2 and LMv2 responses + */ + +static bool test_lmv2_ntlmv2(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_NONE, UPPER_DOMAIN, error_string); +} + +#if 0 +static bool test_lmv2_ntlmv2_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_NONE, NO_DOMAIN, error_string); +} +#endif + +/* + * Test the LMv2 response only + */ + +static bool test_lmv2(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, NO_NT, UPPER_DOMAIN, error_string); +} + +static bool test_lmv2_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, NO_NT, NO_DOMAIN, error_string); +} + +/* + * Test the NTLMv2 response only + */ + +static bool test_ntlmv2(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, NO_LM, UPPER_DOMAIN, error_string); +} + +static bool test_ntlmv2_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, NO_LM, NO_DOMAIN, error_string); +} + +static bool test_lm_ntlm(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lm_ntlm_broken(samlogon_state, BREAK_NONE, error_string); +} + +static bool test_ntlm_lm_broken(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lm_ntlm_broken(samlogon_state, BREAK_LM, error_string); +} + +static bool test_ntlm_ntlm_broken(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lm_ntlm_broken(samlogon_state, BREAK_NT, error_string); +} + +static bool test_lm_ntlm_both_broken(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lm_ntlm_broken(samlogon_state, BREAK_BOTH, error_string); +} +static bool test_ntlmv2_lmv2_broken(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_LM, UPPER_DOMAIN, error_string); +} + +static bool test_ntlmv2_lmv2_broken_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_LM, NO_DOMAIN, error_string); +} + +static bool test_ntlmv2_ntlmv2_broken(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_NT, UPPER_DOMAIN, error_string); +} + +#if 0 +static bool test_ntlmv2_ntlmv2_broken_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_NT, NO_DOMAIN, error_string); +} +#endif + +static bool test_ntlmv2_both_broken(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_BOTH, UPPER_DOMAIN, error_string); +} + +static bool test_ntlmv2_both_broken_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_BOTH, NO_DOMAIN, error_string); +} + +static bool test_lmv2_ntlm_both_broken(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlm_broken(samlogon_state, BREAK_BOTH, UPPER_DOMAIN, error_string); +} + +static bool test_lmv2_ntlm_both_broken_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlm_broken(samlogon_state, BREAK_BOTH, NO_DOMAIN, error_string); +} + +static bool test_lmv2_ntlm_break_ntlm(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlm_broken(samlogon_state, BREAK_NT, UPPER_DOMAIN, error_string); +} + +static bool test_lmv2_ntlm_break_ntlm_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlm_broken(samlogon_state, BREAK_NT, NO_DOMAIN, error_string); +} + +static bool test_lmv2_ntlm_break_lm(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlm_broken(samlogon_state, BREAK_LM, UPPER_DOMAIN, error_string); +} + +static bool test_lmv2_ntlm_break_lm_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlm_broken(samlogon_state, BREAK_LM, NO_DOMAIN, error_string); +} + +/* + * Test the NTLM2 response (extra challenge in LM feild) + * + * This test is the same as the 'break LM' test, but checks that the + * server implements NTLM2 session security in the right place + * (NETLOGON is the wrong place). + */ + +static bool test_ntlm2(struct samlogon_state *samlogon_state, char **error_string) +{ + bool pass = true; + NTSTATUS nt_status; + DATA_BLOB lm_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24); + DATA_BLOB nt_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24); + + bool lm_good; + uint8_t lm_key[8]; + uint8_t nt_hash[16]; + uint8_t lm_hash[16]; + uint8_t nt_key[16]; + uint8_t user_session_key[16]; + uint8_t expected_user_session_key[16]; + uint8_t session_nonce_hash[16]; + uint8_t client_chall[8]; + + struct MD5Context md5_session_nonce_ctx; + HMACMD5Context hmac_ctx; + + ZERO_STRUCT(user_session_key); + ZERO_STRUCT(lm_key); + generate_random_buffer(client_chall, 8); + + MD5Init(&md5_session_nonce_ctx); + MD5Update(&md5_session_nonce_ctx, samlogon_state->chall.data, 8); + MD5Update(&md5_session_nonce_ctx, client_chall, 8); + MD5Final(session_nonce_hash, &md5_session_nonce_ctx); + + E_md4hash(samlogon_state->password, (uint8_t *)nt_hash); + lm_good = E_deshash(samlogon_state->password, (uint8_t *)lm_hash); + SMBsesskeygen_ntv1((const uint8_t *)nt_hash, + nt_key); + + SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data, nt_response.data); + + memcpy(lm_response.data, session_nonce_hash, 8); + memset(lm_response.data + 8, 0, 16); + + hmac_md5_init_rfc2104(nt_key, 16, &hmac_ctx); + hmac_md5_update(samlogon_state->chall.data, 8, &hmac_ctx); + hmac_md5_update(client_chall, 8, &hmac_ctx); + hmac_md5_final(expected_user_session_key, &hmac_ctx); + + nt_status = check_samlogon(samlogon_state, + BREAK_NONE, + samlogon_state->parameter_control, + &samlogon_state->chall, + &lm_response, + &nt_response, + lm_key, + user_session_key, + error_string); + + if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) { + /* for 'old' passwords, we allow the server to be OK or wrong password */ + if (samlogon_state->old_password) { + return true; + } + return false; + } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) { + SAFE_FREE(*error_string); + asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status)); + return false; + } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) { + return true; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + if (lm_good) { + if (memcmp(lm_hash, lm_key, + sizeof(lm_key)) != 0) { + d_printf("LM Key does not match expectations!\n"); + d_printf("lm_key:\n"); + dump_data(1, lm_key, 8); + d_printf("expected:\n"); + dump_data(1, lm_hash, 8); + pass = false; + } + } else { + static const uint8_t zeros[8]; + if (memcmp(zeros, lm_key, + sizeof(lm_key)) != 0) { + d_printf("LM Session Key does not match expectations (zeros)!\n"); + d_printf("lm_key:\n"); + dump_data(1, lm_key, 8); + d_printf("expected:\n"); + dump_data(1, zeros, 8); + pass = false; + } + } + if (memcmp(nt_key, user_session_key, 16) != 0) { + d_printf("NT Session Key does not match expectations (should be NT Key)!\n"); + d_printf("user_session_key:\n"); + dump_data(1, user_session_key, sizeof(user_session_key)); + d_printf("expected:\n"); + dump_data(1, nt_key, sizeof(nt_key)); + pass = false; + } + return pass; +} + +static bool test_plaintext(struct samlogon_state *samlogon_state, enum ntlm_break break_which, char **error_string) +{ + NTSTATUS nt_status; + DATA_BLOB nt_response = data_blob(NULL, 0); + DATA_BLOB lm_response = data_blob(NULL, 0); + char *password; + char *dospw; + void *unicodepw; + + uint8_t user_session_key[16]; + uint8_t lm_key[16]; + uint8_t lm_hash[16]; + static const uint8_t zeros[8]; + DATA_BLOB chall = data_blob_talloc(samlogon_state->mem_ctx, zeros, sizeof(zeros)); + bool lm_good = E_deshash(samlogon_state->password, lm_hash); + + ZERO_STRUCT(user_session_key); + + if ((push_ucs2_talloc(samlogon_state->mem_ctx, + samlogon_state->iconv_convenience, + &unicodepw, samlogon_state->password)) == -1) { + DEBUG(0, ("push_ucs2_allocate failed!\n")); + exit(1); + } + + nt_response = data_blob_talloc(samlogon_state->mem_ctx, unicodepw, strlen_m(samlogon_state->password)*2); + + password = strupper_talloc(samlogon_state->mem_ctx, samlogon_state->password); + + if ((convert_string_talloc(samlogon_state->mem_ctx, + samlogon_state->iconv_convenience, + CH_UNIX, CH_DOS, + password, strlen(password)+1, + (void**)&dospw)) == -1) { + DEBUG(0, ("convert_string_talloc failed!\n")); + exit(1); + } + + lm_response = data_blob_talloc(samlogon_state->mem_ctx, dospw, strlen(dospw)); + + nt_status = check_samlogon(samlogon_state, + break_which, + samlogon_state->parameter_control | MSV1_0_CLEARTEXT_PASSWORD_ALLOWED, + &chall, + &lm_response, + &nt_response, + lm_key, + user_session_key, + error_string); + + if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) { + /* for 'old' passwords, we allow the server to be OK or wrong password */ + if (samlogon_state->old_password) { + return true; + } + /* for 'long' passwords, the LM password is invalid */ + if (break_which == NO_NT && !lm_good) { + return true; + } + return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH)); + } else if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, nt_status) && strchr_m(samlogon_state->account_name, '@')) { + return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH) || (break_which == NO_NT)); + } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) { + SAFE_FREE(*error_string); + asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status)); + return false; + } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) { + return true; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + if (break_which == NO_NT && !lm_good) { + *error_string = strdup("LM password is 'long' (> 14 chars and therefore invalid) but login did not fail!"); + return false; + } + + return true; +} + +static bool test_plaintext_none_broken(struct samlogon_state *samlogon_state, + char **error_string) { + return test_plaintext(samlogon_state, BREAK_NONE, error_string); +} + +static bool test_plaintext_lm_broken(struct samlogon_state *samlogon_state, + char **error_string) { + return test_plaintext(samlogon_state, BREAK_LM, error_string); +} + +static bool test_plaintext_nt_broken(struct samlogon_state *samlogon_state, + char **error_string) { + return test_plaintext(samlogon_state, BREAK_NT, error_string); +} + +static bool test_plaintext_nt_only(struct samlogon_state *samlogon_state, + char **error_string) { + return test_plaintext(samlogon_state, NO_LM, error_string); +} + +static bool test_plaintext_lm_only(struct samlogon_state *samlogon_state, + char **error_string) { + return test_plaintext(samlogon_state, NO_NT, error_string); +} + +/* + Tests: + + - LM only + - NT and LM + - NT + - NT in LM field + - NT in both fields + - NTLMv2 + - NTLMv2 and LMv2 + - LMv2 + - plaintext tests (in challenge-response fields) + + check we get the correct session key in each case + check what values we get for the LM session key + +*/ + +static const struct ntlm_tests { + bool (*fn)(struct samlogon_state *, char **); + const char *name; + bool expect_fail; +} test_table[] = { + {test_lmv2_ntlmv2, "NTLMv2 and LMv2", false}, +#if 0 + {test_lmv2_ntlmv2_no_dom, "NTLMv2 and LMv2 (no domain)", false}, +#endif + {test_lm, "LM", false}, + {test_lm_ntlm, "LM and NTLM", false}, + {test_lm_ntlm_both_broken, "LM and NTLM, both broken", false}, + {test_ntlm, "NTLM", false}, + {test_ntlm_in_lm, "NTLM in LM", false}, + {test_ntlm_in_both, "NTLM in both", false}, + {test_ntlmv2, "NTLMv2", false}, + {test_ntlmv2_no_dom, "NTLMv2 (no domain)", false}, + {test_lmv2, "LMv2", false}, + {test_lmv2_no_dom, "LMv2 (no domain)", false}, + {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken", false}, + {test_ntlmv2_lmv2_broken_no_dom, "NTLMv2 and LMv2, LMv2 broken (no domain)", false}, + {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken", false}, +#if 0 + {test_ntlmv2_ntlmv2_broken_no_dom, "NTLMv2 and LMv2, NTLMv2 broken (no domain)", false}, +#endif + {test_ntlmv2_both_broken, "NTLMv2 and LMv2, both broken", false}, + {test_ntlmv2_both_broken_no_dom, "NTLMv2 and LMv2, both broken (no domain)", false}, + {test_ntlm_lm_broken, "NTLM and LM, LM broken", false}, + {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken", false}, + {test_ntlm2, "NTLM2 (NTLMv2 session security)", false}, + {test_lmv2_ntlm_both_broken, "LMv2 and NTLM, both broken", false}, + {test_lmv2_ntlm_both_broken_no_dom, "LMv2 and NTLM, both broken (no domain)", false}, + {test_lmv2_ntlm_break_ntlm, "LMv2 and NTLM, NTLM broken", false}, + {test_lmv2_ntlm_break_ntlm_no_dom, "LMv2 and NTLM, NTLM broken (no domain)", false}, + {test_lmv2_ntlm_break_lm, "LMv2 and NTLM, LMv2 broken", false}, + {test_lmv2_ntlm_break_lm_no_dom, "LMv2 and NTLM, LMv2 broken (no domain)", false}, + {test_plaintext_none_broken, "Plaintext", false}, + {test_plaintext_lm_broken, "Plaintext LM broken", false}, + {test_plaintext_nt_broken, "Plaintext NT broken", false}, + {test_plaintext_nt_only, "Plaintext NT only", false}, + {test_plaintext_lm_only, "Plaintext LM only", false}, + {NULL, NULL} +}; + +/* + try a netlogon SamLogon +*/ +static bool test_SamLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct torture_context *tctx, + struct creds_CredentialState *creds, + const char *comment, + const char *account_domain, const char *account_name, + const char *plain_pass, uint32_t parameter_control, + NTSTATUS expected_error, bool old_password, + int n_subtests) +{ + TALLOC_CTX *fn_ctx = talloc_named(mem_ctx, 0, "test_SamLogon function-level context"); + int i, v, l, f; + bool ret = true; + int validation_levels[] = {2,3,6}; + int logon_levels[] = { 2, 6 }; + int function_levels[] = { + NDR_NETR_LOGONSAMLOGON, + NDR_NETR_LOGONSAMLOGONEX, + NDR_NETR_LOGONSAMLOGONWITHFLAGS }; + struct samlogon_state samlogon_state; + + d_printf("testing netr_LogonSamLogon and netr_LogonSamLogonWithFlags\n"); + + samlogon_state.comment = comment; + samlogon_state.account_name = account_name; + samlogon_state.account_domain = account_domain; + samlogon_state.password = plain_pass; + samlogon_state.p = p; + samlogon_state.creds = creds; + samlogon_state.expected_error = expected_error; + samlogon_state.chall = data_blob_talloc(fn_ctx, NULL, 8); + samlogon_state.parameter_control = parameter_control; + samlogon_state.old_password = old_password; + samlogon_state.iconv_convenience = lp_iconv_convenience(tctx->lp_ctx); + + generate_random_buffer(samlogon_state.chall.data, 8); + samlogon_state.r_flags.in.server_name = talloc_asprintf(fn_ctx, "\\\\%s", dcerpc_server_name(p)); + samlogon_state.r_flags.in.computer_name = TEST_MACHINE_NAME; + samlogon_state.r_flags.in.credential = &samlogon_state.auth; + samlogon_state.r_flags.in.return_authenticator = &samlogon_state.auth2; + samlogon_state.r_flags.in.flags = 0; + + samlogon_state.r_ex.in.server_name = talloc_asprintf(fn_ctx, "\\\\%s", dcerpc_server_name(p)); + samlogon_state.r_ex.in.computer_name = TEST_MACHINE_NAME; + samlogon_state.r_ex.in.flags = 0; + + samlogon_state.r.in.server_name = talloc_asprintf(fn_ctx, "\\\\%s", dcerpc_server_name(p)); + samlogon_state.r.in.computer_name = TEST_MACHINE_NAME; + samlogon_state.r.in.credential = &samlogon_state.auth; + samlogon_state.r.in.return_authenticator = &samlogon_state.auth2; + + for (f=0;f<ARRAY_SIZE(function_levels);f++) { + for (i=0; test_table[i].fn; i++) { + if (n_subtests && (i > n_subtests)) { + continue; + } + for (v=0;v<ARRAY_SIZE(validation_levels);v++) { + for (l=0;l<ARRAY_SIZE(logon_levels);l++) { + char *error_string = NULL; + TALLOC_CTX *tmp_ctx = talloc_named(fn_ctx, 0, "test_SamLogon inner loop"); + samlogon_state.mem_ctx = tmp_ctx; + samlogon_state.function_level = function_levels[f]; + samlogon_state.r.in.validation_level = validation_levels[v]; + samlogon_state.r.in.logon_level = logon_levels[l]; + samlogon_state.r_ex.in.validation_level = validation_levels[v]; + samlogon_state.r_ex.in.logon_level = logon_levels[l]; + samlogon_state.r_flags.in.validation_level = validation_levels[v]; + samlogon_state.r_flags.in.logon_level = logon_levels[l]; + if (!test_table[i].fn(&samlogon_state, &error_string)) { + d_printf("Testing '%s' [%s]\\[%s] '%s' at validation level %d, logon level %d, function %d: \n", + samlogon_state.comment, + samlogon_state.account_domain, + samlogon_state.account_name, + test_table[i].name, validation_levels[v], + logon_levels[l], function_levels[f]); + + if (test_table[i].expect_fail) { + d_printf(" failed (expected, test incomplete): %s\n", error_string); + } else { + d_printf(" failed: %s\n", error_string); + ret = false; + } + SAFE_FREE(error_string); + } + talloc_free(tmp_ctx); + } + } + } + } + talloc_free(fn_ctx); + return ret; +} + +/* + test an ADS style interactive domain logon +*/ +bool test_InteractiveLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct creds_CredentialState *creds, + const char *comment, + const char *workstation_name, + const char *account_domain, const char *account_name, + const char *plain_pass, uint32_t parameter_control, + NTSTATUS expected_error) +{ + NTSTATUS status; + TALLOC_CTX *fn_ctx = talloc_named(mem_ctx, 0, "test_InteractiveLogon function-level context"); + struct netr_LogonSamLogonWithFlags r; + struct netr_Authenticator a, ra; + struct netr_PasswordInfo pinfo; + + ZERO_STRUCT(a); + ZERO_STRUCT(r); + ZERO_STRUCT(ra); + + creds_client_authenticator(creds, &a); + + r.in.server_name = talloc_asprintf(fn_ctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &a; + r.in.return_authenticator = &ra; + r.in.logon_level = 5; + r.in.logon.password = &pinfo; + r.in.validation_level = 6; + r.in.flags = 0; + + pinfo.identity_info.domain_name.string = account_domain; + pinfo.identity_info.parameter_control = parameter_control; + pinfo.identity_info.logon_id_low = 0; + pinfo.identity_info.logon_id_high = 0; + pinfo.identity_info.account_name.string = account_name; + pinfo.identity_info.workstation.string = workstation_name; + + if (!E_deshash(plain_pass, pinfo.lmpassword.hash)) { + ZERO_STRUCT(pinfo.lmpassword.hash); + } + E_md4hash(plain_pass, pinfo.ntpassword.hash); + + if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { + creds_arcfour_crypt(creds, pinfo.lmpassword.hash, 16); + creds_arcfour_crypt(creds, pinfo.ntpassword.hash, 16); + } else { + creds_des_encrypt(creds, &pinfo.lmpassword); + creds_des_encrypt(creds, &pinfo.ntpassword); + } + + d_printf("Testing netr_LogonSamLogonWithFlags '%s' (Interactive Logon)\n", comment); + + status = dcerpc_netr_LogonSamLogonWithFlags(p, fn_ctx, &r); + if (!r.out.return_authenticator + || !creds_client_check(creds, &r.out.return_authenticator->cred)) { + d_printf("Credential chaining failed\n"); + talloc_free(fn_ctx); + return false; + } + + talloc_free(fn_ctx); + + if (!NT_STATUS_EQUAL(expected_error, status)) { + d_printf("[%s]\\[%s] netr_LogonSamLogonWithFlags - expected %s got %s\n", + account_domain, account_name, nt_errstr(expected_error), nt_errstr(status)); + return false; + } + + return true; +} + + + +bool torture_rpc_samlogon(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + struct dcerpc_binding *b; + struct cli_credentials *machine_credentials; + TALLOC_CTX *mem_ctx = talloc_init("torture_rpc_netlogon"); + bool ret = true; + struct test_join *join_ctx = NULL; + struct test_join *user_ctx = NULL, *user_ctx_wrong_wks = NULL, *user_ctx_wrong_time = NULL; + char *user_password, *user_password_wrong_wks, *user_password_wrong_time; + const char *old_user_password; + char *test_machine_account; + const char *userdomain; + struct samr_SetUserInfo s; + union samr_UserInfo u; + int i; + int ci; + + unsigned int credential_flags[] = { + NETLOGON_NEG_AUTH2_FLAGS, + NETLOGON_NEG_ARCFOUR, + NETLOGON_NEG_ARCFOUR | NETLOGON_NEG_128BIT, + NETLOGON_NEG_AUTH2_ADS_FLAGS, + 0 /* yes, this is a valid flag, causes the use of DES */ + }; + + struct creds_CredentialState *creds; + + test_machine_account = talloc_asprintf(mem_ctx, "%s$", TEST_MACHINE_NAME); + /* We only need to join as a workstation here, and in future, + * if we wish to test against trusted domains, we must be a + * workstation here */ + join_ctx = torture_join_domain(torture, TEST_MACHINE_NAME, ACB_WSTRUST, + &machine_credentials); + if (!join_ctx) { + d_printf("Failed to join as Workstation\n"); + return false; + } + + userdomain = torture_setting_string(torture, "userdomain", lp_workgroup(torture->lp_ctx)); + + user_ctx = torture_create_testuser(torture, + TEST_USER_NAME, + userdomain, + ACB_NORMAL, + (const char **)&user_password); + if (!user_ctx) { + d_printf("Failed to create a test user\n"); + return false; + } + + old_user_password = user_password; + + test_ChangePasswordUser3(torture_join_samr_pipe(user_ctx), mem_ctx, + TEST_USER_NAME, 16 /* > 14 */, &user_password, + NULL, 0, false); + + user_ctx_wrong_wks = torture_create_testuser(torture, + TEST_USER_NAME_WRONG_WKS, + userdomain, + ACB_NORMAL, + (const char **)&user_password_wrong_wks); + if (!user_ctx_wrong_wks) { + d_printf("Failed to create a test user (wrong workstation test)\n"); + return false; + } + + ZERO_STRUCT(u); + s.in.user_handle = torture_join_samr_user_policy(user_ctx_wrong_wks); + s.in.info = &u; + s.in.level = 21; + + u.info21.fields_present = SAMR_FIELD_WORKSTATIONS; + u.info21.workstations.string = "not" TEST_MACHINE_NAME; + + status = dcerpc_samr_SetUserInfo(torture_join_samr_pipe(user_ctx_wrong_wks), mem_ctx, &s); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo (list of workstations) failed - %s\n", nt_errstr(status)); + ret = false; + goto failed; + } + + user_ctx_wrong_time + = torture_create_testuser(torture, TEST_USER_NAME_WRONG_TIME, + userdomain, + ACB_NORMAL, + (const char **)&user_password_wrong_time); + if (!user_ctx_wrong_time) { + d_printf("Failed to create a test user (wrong workstation test)\n"); + return false; + } + + ZERO_STRUCT(u); + s.in.user_handle = torture_join_samr_user_policy(user_ctx_wrong_time); + s.in.info = &u; + s.in.level = 21; + + u.info21.fields_present = SAMR_FIELD_WORKSTATIONS | SAMR_FIELD_LOGON_HOURS; + u.info21.workstations.string = TEST_MACHINE_NAME; + u.info21.logon_hours.units_per_week = 168; + u.info21.logon_hours.bits = talloc_zero_array(mem_ctx, uint8_t, 168); + + status = dcerpc_samr_SetUserInfo(torture_join_samr_pipe(user_ctx_wrong_time), mem_ctx, &s); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo (logon times and list of workstations) failed - %s\n", nt_errstr(status)); + ret = false; + goto failed; + } + + status = torture_rpc_binding(torture, &b); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto failed; + } + + /* We have to use schannel, otherwise the SamLogonEx fails + * with INTERNAL_ERROR */ + + b->flags &= ~DCERPC_AUTH_OPTIONS; + b->flags |= DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128; + + status = dcerpc_pipe_connect_b(mem_ctx, &p, b, + &ndr_table_netlogon, + machine_credentials, torture->ev, torture->lp_ctx); + + if (!NT_STATUS_IS_OK(status)) { + d_printf("RPC pipe connect as domain member failed: %s\n", nt_errstr(status)); + ret = false; + goto failed; + } + + status = dcerpc_schannel_creds(p->conn->security_state.generic_state, mem_ctx, &creds); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto failed; + } + + { + + struct { + const char *comment; + const char *domain; + const char *username; + const char *password; + bool network_login; + NTSTATUS expected_interactive_error; + NTSTATUS expected_network_error; + uint32_t parameter_control; + bool old_password; /* Allow an old password to be accepted or rejected without error, as well as session key bugs */ + } usercreds[] = { + { + .comment = "domain\\user", + .domain = cli_credentials_get_domain(cmdline_credentials), + .username = cli_credentials_get_username(cmdline_credentials), + .password = cli_credentials_get_password(cmdline_credentials), + .network_login = true, + .expected_interactive_error = NT_STATUS_OK, + .expected_network_error = NT_STATUS_OK + }, + { + .comment = "realm\\user", + .domain = cli_credentials_get_realm(cmdline_credentials), + .username = cli_credentials_get_username(cmdline_credentials), + .password = cli_credentials_get_password(cmdline_credentials), + .network_login = true, + .expected_interactive_error = NT_STATUS_OK, + .expected_network_error = NT_STATUS_OK + }, + { + .comment = "user@domain", + .domain = NULL, + .username = talloc_asprintf(mem_ctx, + "%s@%s", + cli_credentials_get_username(cmdline_credentials), + cli_credentials_get_domain(cmdline_credentials) + ), + .password = cli_credentials_get_password(cmdline_credentials), + .network_login = false, /* works for some things, but not NTLMv2. Odd */ + .expected_interactive_error = NT_STATUS_OK, + .expected_network_error = NT_STATUS_OK + }, + { + .comment = "user@realm", + .domain = NULL, + .username = talloc_asprintf(mem_ctx, + "%s@%s", + cli_credentials_get_username(cmdline_credentials), + cli_credentials_get_realm(cmdline_credentials) + ), + .password = cli_credentials_get_password(cmdline_credentials), + .network_login = true, + .expected_interactive_error = NT_STATUS_OK, + .expected_network_error = NT_STATUS_OK + }, + { + .comment = "machine domain\\user", + .domain = cli_credentials_get_domain(machine_credentials), + .username = cli_credentials_get_username(machine_credentials), + .password = cli_credentials_get_password(machine_credentials), + .network_login = true, + .expected_interactive_error = NT_STATUS_NO_SUCH_USER, + .parameter_control = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT + }, + { + .comment = "machine domain\\user", + .domain = cli_credentials_get_domain(machine_credentials), + .username = cli_credentials_get_username(machine_credentials), + .password = cli_credentials_get_password(machine_credentials), + .network_login = true, + .expected_interactive_error = NT_STATUS_NO_SUCH_USER, + .expected_network_error = NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT + }, + { + .comment = "machine realm\\user", + .domain = cli_credentials_get_realm(machine_credentials), + .username = cli_credentials_get_username(machine_credentials), + .password = cli_credentials_get_password(machine_credentials), + .network_login = true, + .expected_interactive_error = NT_STATUS_NO_SUCH_USER, + .parameter_control = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT + }, + { + .comment = "machine user@domain", + .domain = NULL, + .username = talloc_asprintf(mem_ctx, + "%s@%s", + cli_credentials_get_username(machine_credentials), + cli_credentials_get_domain(machine_credentials) + ), + .password = cli_credentials_get_password(machine_credentials), + .network_login = false, /* works for some things, but not NTLMv2. Odd */ + .expected_interactive_error = NT_STATUS_NO_SUCH_USER, + .parameter_control = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT + }, + { + .comment = "machine user@realm", + .domain = NULL, + .username = talloc_asprintf(mem_ctx, + "%s@%s", + cli_credentials_get_username(machine_credentials), + cli_credentials_get_realm(machine_credentials) + ), + .password = cli_credentials_get_password(machine_credentials), + .network_login = true, + .expected_interactive_error = NT_STATUS_NO_SUCH_USER, + .parameter_control = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT + }, + { + .comment = "test user (long pw): domain\\user", + .domain = userdomain, + .username = TEST_USER_NAME, + .password = user_password, + .network_login = true, + .expected_interactive_error = NT_STATUS_OK, + .expected_network_error = NT_STATUS_OK + }, + { + .comment = "test user (long pw): user@realm", + .domain = NULL, + .username = talloc_asprintf(mem_ctx, + "%s@%s", + TEST_USER_NAME, + lp_realm(torture->lp_ctx)), + .password = user_password, + .network_login = true, + .expected_interactive_error = NT_STATUS_OK, + .expected_network_error = NT_STATUS_OK + }, + { + .comment = "test user (long pw): user@domain", + .domain = NULL, + .username = talloc_asprintf(mem_ctx, + "%s@%s", + TEST_USER_NAME, + userdomain), + .password = user_password, + .network_login = false, /* works for some things, but not NTLMv2. Odd */ + .expected_interactive_error = NT_STATUS_OK, + .expected_network_error = NT_STATUS_OK + }, + /* Oddball, can we use the old password ? */ + { + .comment = "test user: user\\domain OLD PASSWORD", + .domain = userdomain, + .username = TEST_USER_NAME, + .password = old_user_password, + .network_login = true, + .expected_interactive_error = NT_STATUS_WRONG_PASSWORD, + .expected_network_error = NT_STATUS_OK, + .old_password = true + }, + { + .comment = "test user (wong workstation): domain\\user", + .domain = userdomain, + .username = TEST_USER_NAME_WRONG_WKS, + .password = user_password_wrong_wks, + .network_login = true, + .expected_interactive_error = NT_STATUS_INVALID_WORKSTATION, + .expected_network_error = NT_STATUS_INVALID_WORKSTATION + } + }; + + /* Try all the tests for different username forms */ + for (ci = 0; ci < ARRAY_SIZE(usercreds); ci++) { + + if (!test_InteractiveLogon(p, mem_ctx, creds, + usercreds[ci].comment, + TEST_MACHINE_NAME, + usercreds[ci].domain, + usercreds[ci].username, + usercreds[ci].password, + usercreds[ci].parameter_control, + usercreds[ci].expected_interactive_error)) { + ret = false; + } + + if (usercreds[ci].network_login) { + if (!test_SamLogon(p, mem_ctx, torture, creds, + usercreds[ci].comment, + usercreds[ci].domain, + usercreds[ci].username, + usercreds[ci].password, + usercreds[ci].parameter_control, + usercreds[ci].expected_network_error, + usercreds[ci].old_password, + 0)) { + ret = false; + } + } + } + + /* Using the first username form, try the different + * credentials flag setups, on only one of the tests (checks + * session key encryption) */ + + for (i=0; i < ARRAY_SIZE(credential_flags); i++) { + /* TODO: Somehow we lost setting up the different credential flags here! */ + + if (!test_InteractiveLogon(p, mem_ctx, creds, + usercreds[0].comment, + TEST_MACHINE_NAME, + usercreds[0].domain, + usercreds[0].username, + usercreds[0].password, + usercreds[0].parameter_control, + usercreds[0].expected_interactive_error)) { + ret = false; + } + + if (usercreds[0].network_login) { + if (!test_SamLogon(p, mem_ctx, torture, creds, + usercreds[0].comment, + usercreds[0].domain, + usercreds[0].username, + usercreds[0].password, + usercreds[0].parameter_control, + usercreds[0].expected_network_error, + usercreds[0].old_password, + 1)) { + ret = false; + } + } + } + + } +failed: + talloc_free(mem_ctx); + + torture_leave_domain(torture, join_ctx); + torture_leave_domain(torture, user_ctx); + torture_leave_domain(torture, user_ctx_wrong_wks); + torture_leave_domain(torture, user_ctx_wrong_time); + return ret; +} diff --git a/source4/torture/rpc/samr.c b/source4/torture/rpc/samr.c new file mode 100644 index 0000000000..6afda6e9b5 --- /dev/null +++ b/source4/torture/rpc/samr.c @@ -0,0 +1,4648 @@ +/* + Unix SMB/CIFS implementation. + test suite for samr rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "system/time.h" +#include "librpc/gen_ndr/lsa.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "lib/crypto/crypto.h" +#include "libcli/auth/libcli_auth.h" +#include "libcli/security/security.h" +#include "torture/rpc/rpc.h" +#include "param/param.h" + +#define TEST_ACCOUNT_NAME "samrtorturetest" +#define TEST_ALIASNAME "samrtorturetestalias" +#define TEST_GROUPNAME "samrtorturetestgroup" +#define TEST_MACHINENAME "samrtestmach$" +#define TEST_DOMAINNAME "samrtestdom$" + +enum torture_samr_choice { + TORTURE_SAMR_PASSWORDS, + TORTURE_SAMR_USER_ATTRIBUTES, + TORTURE_SAMR_OTHER +}; + +static bool test_QueryUserInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle); + +static bool test_QueryUserInfo2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle); + +static bool test_QueryAliasInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle); + +static bool test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + const char *acct_name, + struct policy_handle *domain_handle, char **password); + +static void init_lsa_String(struct lsa_String *string, const char *s) +{ + string->string = s; +} + +bool test_samr_handle_Close(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_Close r; + + r.in.handle = handle; + r.out.handle = handle; + + status = dcerpc_samr_Close(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("Close handle failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + +static bool test_Shutdown(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_Shutdown r; + + if (!torture_setting_bool(tctx, "dangerous", false)) { + printf("samr_Shutdown disabled - enable dangerous tests to use\n"); + return true; + } + + r.in.connect_handle = handle; + + printf("testing samr_Shutdown\n"); + + status = dcerpc_samr_Shutdown(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("samr_Shutdown failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + +static bool test_SetDsrmPassword(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_SetDsrmPassword r; + struct lsa_String string; + struct samr_Password hash; + + if (!torture_setting_bool(tctx, "dangerous", false)) { + printf("samr_SetDsrmPassword disabled - enable dangerous tests to use\n"); + return true; + } + + E_md4hash("TeSTDSRM123", hash.hash); + + init_lsa_String(&string, "Administrator"); + + r.in.name = &string; + r.in.unknown = 0; + r.in.hash = &hash; + + printf("testing samr_SetDsrmPassword\n"); + + status = dcerpc_samr_SetDsrmPassword(p, tctx, &r); + if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + printf("samr_SetDsrmPassword failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + + +static bool test_QuerySecurity(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_QuerySecurity r; + struct samr_SetSecurity s; + + r.in.handle = handle; + r.in.sec_info = 7; + + status = dcerpc_samr_QuerySecurity(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("QuerySecurity failed - %s\n", nt_errstr(status)); + return false; + } + + if (r.out.sdbuf == NULL) { + return false; + } + + s.in.handle = handle; + s.in.sec_info = 7; + s.in.sdbuf = r.out.sdbuf; + + if (torture_setting_bool(tctx, "samba4", false)) { + printf("skipping SetSecurity test against Samba4\n"); + return true; + } + + status = dcerpc_samr_SetSecurity(p, tctx, &s); + if (!NT_STATUS_IS_OK(status)) { + printf("SetSecurity failed - %s\n", nt_errstr(status)); + return false; + } + + status = dcerpc_samr_QuerySecurity(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("QuerySecurity failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + + +static bool test_SetUserInfo(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, uint32_t base_acct_flags, + const char *base_account_name) +{ + NTSTATUS status; + struct samr_SetUserInfo s; + struct samr_SetUserInfo2 s2; + struct samr_QueryUserInfo q; + struct samr_QueryUserInfo q0; + union samr_UserInfo u; + bool ret = true; + const char *test_account_name; + + uint32_t user_extra_flags = 0; + if (base_acct_flags == ACB_NORMAL) { + /* When created, accounts are expired by default */ + user_extra_flags = ACB_PW_EXPIRED; + } + + s.in.user_handle = handle; + s.in.info = &u; + + s2.in.user_handle = handle; + s2.in.info = &u; + + q.in.user_handle = handle; + q.out.info = &u; + q0 = q; + +#define TESTCALL(call, r) \ + status = dcerpc_samr_ ##call(p, tctx, &r); \ + if (!NT_STATUS_IS_OK(status)) { \ + printf(#call " level %u failed - %s (%s)\n", \ + r.in.level, nt_errstr(status), __location__); \ + ret = false; \ + break; \ + } + +#define STRING_EQUAL(s1, s2, field) \ + if ((s1 && !s2) || (s2 && !s1) || strcmp(s1, s2)) { \ + printf("Failed to set %s to '%s' (%s)\n", \ + #field, s2, __location__); \ + ret = false; \ + break; \ + } + +#define INT_EQUAL(i1, i2, field) \ + if (i1 != i2) { \ + printf("Failed to set %s to 0x%llx - got 0x%llx (%s)\n", \ + #field, (unsigned long long)i2, (unsigned long long)i1, __location__); \ + ret = false; \ + break; \ + } + +#define TEST_USERINFO_STRING(lvl1, field1, lvl2, field2, value, fpval) do { \ + printf("field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \ + q.in.level = lvl1; \ + TESTCALL(QueryUserInfo, q) \ + s.in.level = lvl1; \ + s2.in.level = lvl1; \ + u = *q.out.info; \ + if (lvl1 == 21) { \ + ZERO_STRUCT(u.info21); \ + u.info21.fields_present = fpval; \ + } \ + init_lsa_String(&u.info ## lvl1.field1, value); \ + TESTCALL(SetUserInfo, s) \ + TESTCALL(SetUserInfo2, s2) \ + init_lsa_String(&u.info ## lvl1.field1, ""); \ + TESTCALL(QueryUserInfo, q); \ + u = *q.out.info; \ + STRING_EQUAL(u.info ## lvl1.field1.string, value, field1); \ + q.in.level = lvl2; \ + TESTCALL(QueryUserInfo, q) \ + u = *q.out.info; \ + STRING_EQUAL(u.info ## lvl2.field2.string, value, field2); \ + } while (0) + +#define TEST_USERINFO_INT_EXP(lvl1, field1, lvl2, field2, value, exp_value, fpval) do { \ + printf("field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \ + q.in.level = lvl1; \ + TESTCALL(QueryUserInfo, q) \ + s.in.level = lvl1; \ + s2.in.level = lvl1; \ + u = *q.out.info; \ + if (lvl1 == 21) { \ + uint8_t *bits = u.info21.logon_hours.bits; \ + ZERO_STRUCT(u.info21); \ + if (fpval == SAMR_FIELD_LOGON_HOURS) { \ + u.info21.logon_hours.units_per_week = 168; \ + u.info21.logon_hours.bits = bits; \ + } \ + u.info21.fields_present = fpval; \ + } \ + u.info ## lvl1.field1 = value; \ + TESTCALL(SetUserInfo, s) \ + TESTCALL(SetUserInfo2, s2) \ + u.info ## lvl1.field1 = 0; \ + TESTCALL(QueryUserInfo, q); \ + u = *q.out.info; \ + INT_EQUAL(u.info ## lvl1.field1, exp_value, field1); \ + q.in.level = lvl2; \ + TESTCALL(QueryUserInfo, q) \ + u = *q.out.info; \ + INT_EQUAL(u.info ## lvl2.field2, exp_value, field1); \ + } while (0) + +#define TEST_USERINFO_INT(lvl1, field1, lvl2, field2, value, fpval) do { \ + TEST_USERINFO_INT_EXP(lvl1, field1, lvl2, field2, value, value, fpval); \ + } while (0) + + q0.in.level = 12; + do { TESTCALL(QueryUserInfo, q0) } while (0); + + TEST_USERINFO_STRING(2, comment, 1, comment, "xx2-1 comment", 0); + TEST_USERINFO_STRING(2, comment, 21, comment, "xx2-21 comment", 0); + TEST_USERINFO_STRING(21, comment, 21, comment, "xx21-21 comment", + SAMR_FIELD_COMMENT); + + test_account_name = talloc_asprintf(tctx, "%sxx7-1", base_account_name); + TEST_USERINFO_STRING(7, account_name, 1, account_name, base_account_name, 0); + test_account_name = talloc_asprintf(tctx, "%sxx7-3", base_account_name); + TEST_USERINFO_STRING(7, account_name, 3, account_name, base_account_name, 0); + test_account_name = talloc_asprintf(tctx, "%sxx7-5", base_account_name); + TEST_USERINFO_STRING(7, account_name, 5, account_name, base_account_name, 0); + test_account_name = talloc_asprintf(tctx, "%sxx7-6", base_account_name); + TEST_USERINFO_STRING(7, account_name, 6, account_name, base_account_name, 0); + test_account_name = talloc_asprintf(tctx, "%sxx7-7", base_account_name); + TEST_USERINFO_STRING(7, account_name, 7, account_name, base_account_name, 0); + test_account_name = talloc_asprintf(tctx, "%sxx7-21", base_account_name); + TEST_USERINFO_STRING(7, account_name, 21, account_name, base_account_name, 0); + test_account_name = base_account_name; + TEST_USERINFO_STRING(21, account_name, 21, account_name, base_account_name, + SAMR_FIELD_ACCOUNT_NAME); + + TEST_USERINFO_STRING(6, full_name, 1, full_name, "xx6-1 full_name", 0); + TEST_USERINFO_STRING(6, full_name, 3, full_name, "xx6-3 full_name", 0); + TEST_USERINFO_STRING(6, full_name, 5, full_name, "xx6-5 full_name", 0); + TEST_USERINFO_STRING(6, full_name, 6, full_name, "xx6-6 full_name", 0); + TEST_USERINFO_STRING(6, full_name, 8, full_name, "xx6-8 full_name", 0); + TEST_USERINFO_STRING(6, full_name, 21, full_name, "xx6-21 full_name", 0); + TEST_USERINFO_STRING(8, full_name, 21, full_name, "xx8-21 full_name", 0); + TEST_USERINFO_STRING(21, full_name, 21, full_name, "xx21-21 full_name", + SAMR_FIELD_FULL_NAME); + + TEST_USERINFO_STRING(6, full_name, 1, full_name, "", 0); + TEST_USERINFO_STRING(6, full_name, 3, full_name, "", 0); + TEST_USERINFO_STRING(6, full_name, 5, full_name, "", 0); + TEST_USERINFO_STRING(6, full_name, 6, full_name, "", 0); + TEST_USERINFO_STRING(6, full_name, 8, full_name, "", 0); + TEST_USERINFO_STRING(6, full_name, 21, full_name, "", 0); + TEST_USERINFO_STRING(8, full_name, 21, full_name, "", 0); + TEST_USERINFO_STRING(21, full_name, 21, full_name, "", + SAMR_FIELD_FULL_NAME); + + TEST_USERINFO_STRING(11, logon_script, 3, logon_script, "xx11-3 logon_script", 0); + TEST_USERINFO_STRING(11, logon_script, 5, logon_script, "xx11-5 logon_script", 0); + TEST_USERINFO_STRING(11, logon_script, 21, logon_script, "xx11-21 logon_script", 0); + TEST_USERINFO_STRING(21, logon_script, 21, logon_script, "xx21-21 logon_script", + SAMR_FIELD_LOGON_SCRIPT); + + TEST_USERINFO_STRING(12, profile_path, 3, profile_path, "xx12-3 profile_path", 0); + TEST_USERINFO_STRING(12, profile_path, 5, profile_path, "xx12-5 profile_path", 0); + TEST_USERINFO_STRING(12, profile_path, 21, profile_path, "xx12-21 profile_path", 0); + TEST_USERINFO_STRING(21, profile_path, 21, profile_path, "xx21-21 profile_path", + SAMR_FIELD_PROFILE_PATH); + + TEST_USERINFO_STRING(10, home_directory, 3, home_directory, "xx10-3 home_directory", 0); + TEST_USERINFO_STRING(10, home_directory, 5, home_directory, "xx10-5 home_directory", 0); + TEST_USERINFO_STRING(10, home_directory, 21, home_directory, "xx10-21 home_directory", 0); + TEST_USERINFO_STRING(21, home_directory, 21, home_directory, "xx21-21 home_directory", + SAMR_FIELD_HOME_DIRECTORY); + TEST_USERINFO_STRING(21, home_directory, 10, home_directory, "xx21-10 home_directory", + SAMR_FIELD_HOME_DIRECTORY); + + TEST_USERINFO_STRING(10, home_drive, 3, home_drive, "xx10-3 home_drive", 0); + TEST_USERINFO_STRING(10, home_drive, 5, home_drive, "xx10-5 home_drive", 0); + TEST_USERINFO_STRING(10, home_drive, 21, home_drive, "xx10-21 home_drive", 0); + TEST_USERINFO_STRING(21, home_drive, 21, home_drive, "xx21-21 home_drive", + SAMR_FIELD_HOME_DRIVE); + TEST_USERINFO_STRING(21, home_drive, 10, home_drive, "xx21-10 home_drive", + SAMR_FIELD_HOME_DRIVE); + + TEST_USERINFO_STRING(13, description, 1, description, "xx13-1 description", 0); + TEST_USERINFO_STRING(13, description, 5, description, "xx13-5 description", 0); + TEST_USERINFO_STRING(13, description, 21, description, "xx13-21 description", 0); + TEST_USERINFO_STRING(21, description, 21, description, "xx21-21 description", + SAMR_FIELD_DESCRIPTION); + + TEST_USERINFO_STRING(14, workstations, 3, workstations, "14workstation3", 0); + TEST_USERINFO_STRING(14, workstations, 5, workstations, "14workstation4", 0); + TEST_USERINFO_STRING(14, workstations, 21, workstations, "14workstation21", 0); + TEST_USERINFO_STRING(21, workstations, 21, workstations, "21workstation21", + SAMR_FIELD_WORKSTATIONS); + TEST_USERINFO_STRING(21, workstations, 3, workstations, "21workstation3", + SAMR_FIELD_WORKSTATIONS); + TEST_USERINFO_STRING(21, workstations, 5, workstations, "21workstation5", + SAMR_FIELD_WORKSTATIONS); + TEST_USERINFO_STRING(21, workstations, 14, workstations, "21workstation14", + SAMR_FIELD_WORKSTATIONS); + + TEST_USERINFO_STRING(20, parameters, 21, parameters, "xx20-21 parameters", 0); + TEST_USERINFO_STRING(21, parameters, 21, parameters, "xx21-21 parameters", + SAMR_FIELD_PARAMETERS); + TEST_USERINFO_STRING(21, parameters, 20, parameters, "xx21-20 parameters", + SAMR_FIELD_PARAMETERS); + + TEST_USERINFO_INT(2, country_code, 2, country_code, __LINE__, 0); + TEST_USERINFO_INT(2, country_code, 21, country_code, __LINE__, 0); + TEST_USERINFO_INT(21, country_code, 21, country_code, __LINE__, + SAMR_FIELD_COUNTRY_CODE); + TEST_USERINFO_INT(21, country_code, 2, country_code, __LINE__, + SAMR_FIELD_COUNTRY_CODE); + + TEST_USERINFO_INT(2, code_page, 21, code_page, __LINE__, 0); + TEST_USERINFO_INT(21, code_page, 21, code_page, __LINE__, + SAMR_FIELD_CODE_PAGE); + TEST_USERINFO_INT(21, code_page, 2, code_page, __LINE__, + SAMR_FIELD_CODE_PAGE); + + TEST_USERINFO_INT(17, acct_expiry, 21, acct_expiry, __LINE__, 0); + TEST_USERINFO_INT(17, acct_expiry, 5, acct_expiry, __LINE__, 0); + TEST_USERINFO_INT(21, acct_expiry, 21, acct_expiry, __LINE__, + SAMR_FIELD_ACCT_EXPIRY); + TEST_USERINFO_INT(21, acct_expiry, 5, acct_expiry, __LINE__, + SAMR_FIELD_ACCT_EXPIRY); + TEST_USERINFO_INT(21, acct_expiry, 17, acct_expiry, __LINE__, + SAMR_FIELD_ACCT_EXPIRY); + + TEST_USERINFO_INT(4, logon_hours.bits[3], 3, logon_hours.bits[3], 1, 0); + TEST_USERINFO_INT(4, logon_hours.bits[3], 5, logon_hours.bits[3], 2, 0); + TEST_USERINFO_INT(4, logon_hours.bits[3], 21, logon_hours.bits[3], 3, 0); + TEST_USERINFO_INT(21, logon_hours.bits[3], 21, logon_hours.bits[3], 4, + SAMR_FIELD_LOGON_HOURS); + + TEST_USERINFO_INT_EXP(16, acct_flags, 5, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ), + (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ | user_extra_flags), + 0); + TEST_USERINFO_INT_EXP(16, acct_flags, 5, acct_flags, + (base_acct_flags | ACB_DISABLED), + (base_acct_flags | ACB_DISABLED | user_extra_flags), + 0); + + /* Setting PWNOEXP clears the magic ACB_PW_EXPIRED flag */ + TEST_USERINFO_INT_EXP(16, acct_flags, 5, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_PWNOEXP), + (base_acct_flags | ACB_DISABLED | ACB_PWNOEXP), + 0); + TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ), + (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ | user_extra_flags), + 0); + + + /* The 'autolock' flag doesn't stick - check this */ + TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_AUTOLOCK), + (base_acct_flags | ACB_DISABLED | user_extra_flags), + 0); +#if 0 + /* Removing the 'disabled' flag doesn't stick - check this */ + TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, + (base_acct_flags), + (base_acct_flags | ACB_DISABLED | user_extra_flags), + 0); +#endif + /* The 'store plaintext' flag does stick */ + TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_ENC_TXT_PWD_ALLOWED), + (base_acct_flags | ACB_DISABLED | ACB_ENC_TXT_PWD_ALLOWED | user_extra_flags), + 0); + /* The 'use DES' flag does stick */ + TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_USE_DES_KEY_ONLY), + (base_acct_flags | ACB_DISABLED | ACB_USE_DES_KEY_ONLY | user_extra_flags), + 0); + /* The 'don't require kerberos pre-authentication flag does stick */ + TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_DONT_REQUIRE_PREAUTH), + (base_acct_flags | ACB_DISABLED | ACB_DONT_REQUIRE_PREAUTH | user_extra_flags), + 0); + /* The 'no kerberos PAC required' flag sticks */ + TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_NO_AUTH_DATA_REQD), + (base_acct_flags | ACB_DISABLED | ACB_NO_AUTH_DATA_REQD | user_extra_flags), + 0); + + TEST_USERINFO_INT_EXP(21, acct_flags, 21, acct_flags, + (base_acct_flags | ACB_DISABLED), + (base_acct_flags | ACB_DISABLED | user_extra_flags), + SAMR_FIELD_ACCT_FLAGS); + +#if 0 + /* these fail with win2003 - it appears you can't set the primary gid? + the set succeeds, but the gid isn't changed. Very weird! */ + TEST_USERINFO_INT(9, primary_gid, 1, primary_gid, 513); + TEST_USERINFO_INT(9, primary_gid, 3, primary_gid, 513); + TEST_USERINFO_INT(9, primary_gid, 5, primary_gid, 513); + TEST_USERINFO_INT(9, primary_gid, 21, primary_gid, 513); +#endif + + return ret; +} + +/* + generate a random password for password change tests +*/ +static char *samr_rand_pass(TALLOC_CTX *mem_ctx, int min_len) +{ + size_t len = MAX(8, min_len) + (random() % 6); + char *s = generate_random_str(mem_ctx, len); + printf("Generated password '%s'\n", s); + return s; +} + +/* + generate a random password for password change tests (fixed length) +*/ +static char *samr_rand_pass_fixed_len(TALLOC_CTX *mem_ctx, int len) +{ + char *s = generate_random_str(mem_ctx, len); + printf("Generated password '%s'\n", s); + return s; +} + +static bool test_SetUserPass(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, char **password) +{ + NTSTATUS status; + struct samr_SetUserInfo s; + union samr_UserInfo u; + bool ret = true; + DATA_BLOB session_key; + char *newpass; + struct samr_GetUserPwInfo pwp; + int policy_min_pw_len = 0; + pwp.in.user_handle = handle; + + status = dcerpc_samr_GetUserPwInfo(p, mem_ctx, &pwp); + if (NT_STATUS_IS_OK(status)) { + policy_min_pw_len = pwp.out.info.min_password_length; + } + newpass = samr_rand_pass(mem_ctx, policy_min_pw_len); + + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = 24; + + encode_pw_buffer(u.info24.password.data, newpass, STR_UNICODE); + /* w2k3 ignores this length */ + u.info24.pw_len = strlen_m(newpass) * 2; + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + arcfour_crypt_blob(u.info24.password.data, 516, &session_key); + + printf("Testing SetUserInfo level 24 (set password)\n"); + + status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo level %u failed - %s\n", + s.in.level, nt_errstr(status)); + ret = false; + } else { + *password = newpass; + } + + return ret; +} + + +static bool test_SetUserPass_23(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, uint32_t fields_present, + char **password) +{ + NTSTATUS status; + struct samr_SetUserInfo s; + union samr_UserInfo u; + bool ret = true; + DATA_BLOB session_key; + char *newpass; + struct samr_GetUserPwInfo pwp; + int policy_min_pw_len = 0; + pwp.in.user_handle = handle; + + status = dcerpc_samr_GetUserPwInfo(p, mem_ctx, &pwp); + if (NT_STATUS_IS_OK(status)) { + policy_min_pw_len = pwp.out.info.min_password_length; + } + newpass = samr_rand_pass(mem_ctx, policy_min_pw_len); + + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = 23; + + ZERO_STRUCT(u); + + u.info23.info.fields_present = fields_present; + + encode_pw_buffer(u.info23.password.data, newpass, STR_UNICODE); + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + arcfour_crypt_blob(u.info23.password.data, 516, &session_key); + + printf("Testing SetUserInfo level 23 (set password)\n"); + + status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo level %u failed - %s\n", + s.in.level, nt_errstr(status)); + ret = false; + } else { + *password = newpass; + } + + encode_pw_buffer(u.info23.password.data, newpass, STR_UNICODE); + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + /* This should break the key nicely */ + session_key.length--; + arcfour_crypt_blob(u.info23.password.data, 516, &session_key); + + printf("Testing SetUserInfo level 23 (set password) with wrong password\n"); + + status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s); + if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { + printf("SetUserInfo level %u should have failed with WRONG_PASSWORD- %s\n", + s.in.level, nt_errstr(status)); + ret = false; + } + + return ret; +} + + +static bool test_SetUserPassEx(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, bool makeshort, + char **password) +{ + NTSTATUS status; + struct samr_SetUserInfo s; + union samr_UserInfo u; + bool ret = true; + DATA_BLOB session_key; + DATA_BLOB confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16); + uint8_t confounder[16]; + char *newpass; + struct MD5Context ctx; + struct samr_GetUserPwInfo pwp; + int policy_min_pw_len = 0; + pwp.in.user_handle = handle; + + status = dcerpc_samr_GetUserPwInfo(p, mem_ctx, &pwp); + if (NT_STATUS_IS_OK(status)) { + policy_min_pw_len = pwp.out.info.min_password_length; + } + if (makeshort && policy_min_pw_len) { + newpass = samr_rand_pass_fixed_len(mem_ctx, policy_min_pw_len - 1); + } else { + newpass = samr_rand_pass(mem_ctx, policy_min_pw_len); + } + + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = 26; + + encode_pw_buffer(u.info26.password.data, newpass, STR_UNICODE); + u.info26.pw_len = strlen(newpass); + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + generate_random_buffer((uint8_t *)confounder, 16); + + MD5Init(&ctx); + MD5Update(&ctx, confounder, 16); + MD5Update(&ctx, session_key.data, session_key.length); + MD5Final(confounded_session_key.data, &ctx); + + arcfour_crypt_blob(u.info26.password.data, 516, &confounded_session_key); + memcpy(&u.info26.password.data[516], confounder, 16); + + printf("Testing SetUserInfo level 26 (set password ex)\n"); + + status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo level %u failed - %s\n", + s.in.level, nt_errstr(status)); + ret = false; + } else { + *password = newpass; + } + + /* This should break the key nicely */ + confounded_session_key.data[0]++; + + arcfour_crypt_blob(u.info26.password.data, 516, &confounded_session_key); + memcpy(&u.info26.password.data[516], confounder, 16); + + printf("Testing SetUserInfo level 26 (set password ex) with wrong session key\n"); + + status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s); + if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { + printf("SetUserInfo level %u should have failed with WRONG_PASSWORD: %s\n", + s.in.level, nt_errstr(status)); + ret = false; + } else { + *password = newpass; + } + + return ret; +} + +static bool test_SetUserPass_25(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, uint32_t fields_present, + char **password) +{ + NTSTATUS status; + struct samr_SetUserInfo s; + union samr_UserInfo u; + bool ret = true; + DATA_BLOB session_key; + DATA_BLOB confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16); + struct MD5Context ctx; + uint8_t confounder[16]; + char *newpass; + struct samr_GetUserPwInfo pwp; + int policy_min_pw_len = 0; + pwp.in.user_handle = handle; + + status = dcerpc_samr_GetUserPwInfo(p, mem_ctx, &pwp); + if (NT_STATUS_IS_OK(status)) { + policy_min_pw_len = pwp.out.info.min_password_length; + } + newpass = samr_rand_pass(mem_ctx, policy_min_pw_len); + + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = 25; + + ZERO_STRUCT(u); + + u.info25.info.fields_present = fields_present; + + encode_pw_buffer(u.info25.password.data, newpass, STR_UNICODE); + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + generate_random_buffer((uint8_t *)confounder, 16); + + MD5Init(&ctx); + MD5Update(&ctx, confounder, 16); + MD5Update(&ctx, session_key.data, session_key.length); + MD5Final(confounded_session_key.data, &ctx); + + arcfour_crypt_blob(u.info25.password.data, 516, &confounded_session_key); + memcpy(&u.info25.password.data[516], confounder, 16); + + printf("Testing SetUserInfo level 25 (set password ex)\n"); + + status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo level %u failed - %s\n", + s.in.level, nt_errstr(status)); + ret = false; + } else { + *password = newpass; + } + + /* This should break the key nicely */ + confounded_session_key.data[0]++; + + arcfour_crypt_blob(u.info25.password.data, 516, &confounded_session_key); + memcpy(&u.info25.password.data[516], confounder, 16); + + printf("Testing SetUserInfo level 25 (set password ex) with wrong session key\n"); + + status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s); + if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { + printf("SetUserInfo level %u should have failed with WRONG_PASSWORD- %s\n", + s.in.level, nt_errstr(status)); + ret = false; + } + + return ret; +} + +static bool test_SetAliasInfo(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_SetAliasInfo r; + struct samr_QueryAliasInfo q; + uint16_t levels[] = {2, 3}; + int i; + bool ret = true; + + /* Ignoring switch level 1, as that includes the number of members for the alias + * and setting this to a wrong value might have negative consequences + */ + + for (i=0;i<ARRAY_SIZE(levels);i++) { + printf("Testing SetAliasInfo level %u\n", levels[i]); + + r.in.alias_handle = handle; + r.in.level = levels[i]; + r.in.info = talloc(tctx, union samr_AliasInfo); + switch (r.in.level) { + case ALIASINFONAME: init_lsa_String(&r.in.info->name,TEST_ALIASNAME); break; + case ALIASINFODESCRIPTION: init_lsa_String(&r.in.info->description, + "Test Description, should test I18N as well"); break; + case ALIASINFOALL: printf("ALIASINFOALL ignored\n"); break; + } + + status = dcerpc_samr_SetAliasInfo(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("SetAliasInfo level %u failed - %s\n", + levels[i], nt_errstr(status)); + ret = false; + } + + q.in.alias_handle = handle; + q.in.level = levels[i]; + + status = dcerpc_samr_QueryAliasInfo(p, tctx, &q); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryAliasInfo level %u failed - %s\n", + levels[i], nt_errstr(status)); + ret = false; + } + } + + return ret; +} + +static bool test_GetGroupsForUser(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *user_handle) +{ + struct samr_GetGroupsForUser r; + NTSTATUS status; + bool ret = true; + + printf("testing GetGroupsForUser\n"); + + r.in.user_handle = user_handle; + + status = dcerpc_samr_GetGroupsForUser(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("GetGroupsForUser failed - %s\n",nt_errstr(status)); + ret = false; + } + + return ret; + +} + +static bool test_GetDomPwInfo(struct dcerpc_pipe *p, struct torture_context *tctx, + struct lsa_String *domain_name) +{ + NTSTATUS status; + struct samr_GetDomPwInfo r; + bool ret = true; + + r.in.domain_name = domain_name; + printf("Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); + + status = dcerpc_samr_GetDomPwInfo(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("GetDomPwInfo failed - %s\n", nt_errstr(status)); + ret = false; + } + + r.in.domain_name->string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + printf("Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); + + status = dcerpc_samr_GetDomPwInfo(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("GetDomPwInfo failed - %s\n", nt_errstr(status)); + ret = false; + } + + r.in.domain_name->string = "\\\\__NONAME__"; + printf("Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); + + status = dcerpc_samr_GetDomPwInfo(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("GetDomPwInfo failed - %s\n", nt_errstr(status)); + ret = false; + } + + r.in.domain_name->string = "\\\\Builtin"; + printf("Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); + + status = dcerpc_samr_GetDomPwInfo(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("GetDomPwInfo failed - %s\n", nt_errstr(status)); + ret = false; + } + + + return ret; +} + +static bool test_GetUserPwInfo(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_GetUserPwInfo r; + bool ret = true; + + printf("Testing GetUserPwInfo\n"); + + r.in.user_handle = handle; + + status = dcerpc_samr_GetUserPwInfo(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("GetUserPwInfo failed - %s\n", nt_errstr(status)); + ret = false; + } + + return ret; +} + +static NTSTATUS test_LookupName(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *domain_handle, const char *name, + uint32_t *rid) +{ + NTSTATUS status; + struct samr_LookupNames n; + struct lsa_String sname[2]; + + init_lsa_String(&sname[0], name); + + n.in.domain_handle = domain_handle; + n.in.num_names = 1; + n.in.names = sname; + status = dcerpc_samr_LookupNames(p, tctx, &n); + if (NT_STATUS_IS_OK(status)) { + *rid = n.out.rids.ids[0]; + } else { + return status; + } + + init_lsa_String(&sname[1], "xxNONAMExx"); + n.in.num_names = 2; + status = dcerpc_samr_LookupNames(p, tctx, &n); + if (!NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) { + printf("LookupNames[2] failed - %s\n", nt_errstr(status)); + if (NT_STATUS_IS_OK(status)) { + return NT_STATUS_UNSUCCESSFUL; + } + return status; + } + + n.in.num_names = 0; + status = dcerpc_samr_LookupNames(p, tctx, &n); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupNames[0] failed - %s\n", nt_errstr(status)); + return status; + } + + init_lsa_String(&sname[0], "xxNONAMExx"); + n.in.num_names = 1; + status = dcerpc_samr_LookupNames(p, tctx, &n); + if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { + printf("LookupNames[1 bad name] failed - %s\n", nt_errstr(status)); + if (NT_STATUS_IS_OK(status)) { + return NT_STATUS_UNSUCCESSFUL; + } + return status; + } + + init_lsa_String(&sname[0], "xxNONAMExx"); + init_lsa_String(&sname[1], "xxNONAME2xx"); + n.in.num_names = 2; + status = dcerpc_samr_LookupNames(p, tctx, &n); + if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { + printf("LookupNames[2 bad names] failed - %s\n", nt_errstr(status)); + if (NT_STATUS_IS_OK(status)) { + return NT_STATUS_UNSUCCESSFUL; + } + return status; + } + + return NT_STATUS_OK; +} + +static NTSTATUS test_OpenUser_byname(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + const char *name, struct policy_handle *user_handle) +{ + NTSTATUS status; + struct samr_OpenUser r; + uint32_t rid; + + status = test_LookupName(p, mem_ctx, domain_handle, name, &rid); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + r.in.domain_handle = domain_handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.user_handle = user_handle; + status = dcerpc_samr_OpenUser(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenUser_byname(%s -> %d) failed - %s\n", name, rid, nt_errstr(status)); + } + + return status; +} + +#if 0 +static bool test_ChangePasswordNT3(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_ChangePasswordUser r; + bool ret = true; + struct samr_Password hash1, hash2, hash3, hash4, hash5, hash6; + struct policy_handle user_handle; + char *oldpass = "test"; + char *newpass = "test2"; + uint8_t old_nt_hash[16], new_nt_hash[16]; + uint8_t old_lm_hash[16], new_lm_hash[16]; + + status = test_OpenUser_byname(p, mem_ctx, handle, "testuser", &user_handle); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + printf("Testing ChangePasswordUser for user 'testuser'\n"); + + printf("old password: %s\n", oldpass); + printf("new password: %s\n", newpass); + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); + E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); + E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); + E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); + E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); + E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); + + r.in.handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + r.in.nt_cross = &hash5; + r.in.cross2_present = 1; + r.in.lm_cross = &hash6; + + status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("ChangePasswordUser failed - %s\n", nt_errstr(status)); + ret = false; + } + + if (!test_samr_handle_Close(p, mem_ctx, &user_handle)) { + ret = false; + } + + return ret; +} +#endif + +static bool test_ChangePasswordUser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + const char *acct_name, + struct policy_handle *handle, char **password) +{ + NTSTATUS status; + struct samr_ChangePasswordUser r; + bool ret = true; + struct samr_Password hash1, hash2, hash3, hash4, hash5, hash6; + struct policy_handle user_handle; + char *oldpass; + uint8_t old_nt_hash[16], new_nt_hash[16]; + uint8_t old_lm_hash[16], new_lm_hash[16]; + bool changed = true; + + char *newpass; + struct samr_GetUserPwInfo pwp; + int policy_min_pw_len = 0; + + status = test_OpenUser_byname(p, mem_ctx, handle, acct_name, &user_handle); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + pwp.in.user_handle = &user_handle; + + status = dcerpc_samr_GetUserPwInfo(p, mem_ctx, &pwp); + if (NT_STATUS_IS_OK(status)) { + policy_min_pw_len = pwp.out.info.min_password_length; + } + newpass = samr_rand_pass(mem_ctx, policy_min_pw_len); + + printf("Testing ChangePasswordUser\n"); + + if (!*password) { + printf("Failing ChangePasswordUser as old password was NULL. Previous test failed?\n"); + return false; + } + + oldpass = *password; + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); + E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); + E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); + E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); + E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); + E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + /* Break the LM hash */ + hash1.hash[0]++; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + r.in.nt_cross = &hash5; + r.in.cross2_present = 1; + r.in.lm_cross = &hash6; + + status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { + printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the LM hash, got %s\n", nt_errstr(status)); + ret = false; + } + + /* Unbreak the LM hash */ + hash1.hash[0]--; + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + /* Break the NT hash */ + hash3.hash[0]--; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + r.in.nt_cross = &hash5; + r.in.cross2_present = 1; + r.in.lm_cross = &hash6; + + status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { + printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the NT hash, got %s\n", nt_errstr(status)); + ret = false; + } + + /* Unbreak the NT hash */ + hash3.hash[0]--; + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + r.in.nt_cross = &hash5; + r.in.cross2_present = 1; + /* Break the LM cross */ + hash6.hash[0]++; + r.in.lm_cross = &hash6; + + status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { + printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the LM cross-hash, got %s\n", nt_errstr(status)); + ret = false; + } + + /* Unbreak the LM cross */ + hash6.hash[0]--; + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + /* Break the NT cross */ + hash5.hash[0]++; + r.in.nt_cross = &hash5; + r.in.cross2_present = 1; + r.in.lm_cross = &hash6; + + status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { + printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the NT cross-hash, got %s\n", nt_errstr(status)); + ret = false; + } + + /* Unbreak the NT cross */ + hash5.hash[0]--; + + + /* Reset the hashes to not broken values */ + E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); + E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); + E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); + E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); + E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); + E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + r.in.nt_cross = &hash5; + r.in.cross2_present = 0; + r.in.lm_cross = NULL; + + status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r); + if (NT_STATUS_IS_OK(status)) { + changed = true; + *password = newpass; + } else if (!NT_STATUS_EQUAL(NT_STATUS_PASSWORD_RESTRICTION, status)) { + printf("ChangePasswordUser failed: expected NT_STATUS_OK, or at least NT_STATUS_PASSWORD_RESTRICTION, got %s\n", nt_errstr(status)); + ret = false; + } + + oldpass = newpass; + newpass = samr_rand_pass(mem_ctx, policy_min_pw_len); + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + + /* Reset the hashes to not broken values */ + E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); + E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); + E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); + E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); + E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); + E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 0; + r.in.nt_cross = NULL; + r.in.cross2_present = 1; + r.in.lm_cross = &hash6; + + status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r); + if (NT_STATUS_IS_OK(status)) { + changed = true; + *password = newpass; + } else if (!NT_STATUS_EQUAL(NT_STATUS_PASSWORD_RESTRICTION, status)) { + printf("ChangePasswordUser failed: expected NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED, got %s\n", nt_errstr(status)); + ret = false; + } + + oldpass = newpass; + newpass = samr_rand_pass(mem_ctx, policy_min_pw_len); + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + + /* Reset the hashes to not broken values */ + E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); + E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); + E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); + E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); + E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); + E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + r.in.nt_cross = &hash5; + r.in.cross2_present = 1; + r.in.lm_cross = &hash6; + + status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r); + if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) { + printf("ChangePasswordUser returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status)); + } else if (!NT_STATUS_IS_OK(status)) { + printf("ChangePasswordUser failed - %s\n", nt_errstr(status)); + ret = false; + } else { + changed = true; + *password = newpass; + } + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + r.in.nt_cross = &hash5; + r.in.cross2_present = 1; + r.in.lm_cross = &hash6; + + if (changed) { + status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r); + if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) { + printf("ChangePasswordUser returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status)); + } else if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { + printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we already changed the password, got %s\n", nt_errstr(status)); + ret = false; + } + } + + + if (!test_samr_handle_Close(p, mem_ctx, &user_handle)) { + ret = false; + } + + return ret; +} + + +static bool test_OemChangePasswordUser2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + const char *acct_name, + struct policy_handle *handle, char **password) +{ + NTSTATUS status; + struct samr_OemChangePasswordUser2 r; + bool ret = true; + struct samr_Password lm_verifier; + struct samr_CryptPassword lm_pass; + struct lsa_AsciiString server, account, account_bad; + char *oldpass; + char *newpass; + uint8_t old_lm_hash[16], new_lm_hash[16]; + + struct samr_GetDomPwInfo dom_pw_info; + int policy_min_pw_len = 0; + + struct lsa_String domain_name; + + domain_name.string = ""; + dom_pw_info.in.domain_name = &domain_name; + + printf("Testing OemChangePasswordUser2\n"); + + if (!*password) { + printf("Failing OemChangePasswordUser2 as old password was NULL. Previous test failed?\n"); + return false; + } + + oldpass = *password; + + status = dcerpc_samr_GetDomPwInfo(p, mem_ctx, &dom_pw_info); + if (NT_STATUS_IS_OK(status)) { + policy_min_pw_len = dom_pw_info.out.info.min_password_length; + } + + newpass = samr_rand_pass(mem_ctx, policy_min_pw_len); + + server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p)); + account.string = acct_name; + + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + encode_pw_buffer(lm_pass.data, newpass, STR_ASCII); + arcfour_crypt(lm_pass.data, old_lm_hash, 516); + E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.password = &lm_pass; + r.in.hash = &lm_verifier; + + /* Break the verification */ + lm_verifier.hash[0]++; + + status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) + && !NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { + printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalid password verifier - %s\n", + nt_errstr(status)); + ret = false; + } + + encode_pw_buffer(lm_pass.data, newpass, STR_ASCII); + /* Break the old password */ + old_lm_hash[0]++; + arcfour_crypt(lm_pass.data, old_lm_hash, 516); + /* unbreak it for the next operation */ + old_lm_hash[0]--; + E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.password = &lm_pass; + r.in.hash = &lm_verifier; + + status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) + && !NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { + printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalidly encrpted password - %s\n", + nt_errstr(status)); + ret = false; + } + + encode_pw_buffer(lm_pass.data, newpass, STR_ASCII); + arcfour_crypt(lm_pass.data, old_lm_hash, 516); + + r.in.server = &server; + r.in.account = &account; + r.in.password = &lm_pass; + r.in.hash = NULL; + + status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) + && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { + printf("ChangePasswordUser3 failed, should have returned INVALID_PARAMETER (or at least 'PASSWORD_RESTRICTON') for no supplied validation hash - %s\n", + nt_errstr(status)); + ret = false; + } + + /* This shouldn't be a valid name */ + account_bad.string = TEST_ACCOUNT_NAME "XX"; + r.in.account = &account_bad; + + status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { + printf("ChangePasswordUser3 failed, should have returned INVALID_PARAMETER for no supplied validation hash and invalid user - %s\n", + nt_errstr(status)); + ret = false; + } + + /* This shouldn't be a valid name */ + account_bad.string = TEST_ACCOUNT_NAME "XX"; + r.in.account = &account_bad; + r.in.password = &lm_pass; + r.in.hash = &lm_verifier; + + status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { + printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD for invalid user - %s\n", + nt_errstr(status)); + ret = false; + } + + /* This shouldn't be a valid name */ + account_bad.string = TEST_ACCOUNT_NAME "XX"; + r.in.account = &account_bad; + r.in.password = NULL; + r.in.hash = &lm_verifier; + + status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { + printf("ChangePasswordUser3 failed, should have returned INVALID_PARAMETER for no supplied password and invalid user - %s\n", + nt_errstr(status)); + ret = false; + } + + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + encode_pw_buffer(lm_pass.data, newpass, STR_ASCII); + arcfour_crypt(lm_pass.data, old_lm_hash, 516); + E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.password = &lm_pass; + r.in.hash = &lm_verifier; + + status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r); + if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) { + printf("OemChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status)); + } else if (!NT_STATUS_IS_OK(status)) { + printf("OemChangePasswordUser2 failed - %s\n", nt_errstr(status)); + ret = false; + } else { + *password = newpass; + } + + return ret; +} + + +static bool test_ChangePasswordUser2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + const char *acct_name, + char **password, + char *newpass, bool allow_password_restriction) +{ + NTSTATUS status; + struct samr_ChangePasswordUser2 r; + bool ret = true; + struct lsa_String server, account; + struct samr_CryptPassword nt_pass, lm_pass; + struct samr_Password nt_verifier, lm_verifier; + char *oldpass; + uint8_t old_nt_hash[16], new_nt_hash[16]; + uint8_t old_lm_hash[16], new_lm_hash[16]; + + struct samr_GetDomPwInfo dom_pw_info; + + struct lsa_String domain_name; + + domain_name.string = ""; + dom_pw_info.in.domain_name = &domain_name; + + printf("Testing ChangePasswordUser2 on %s\n", acct_name); + + if (!*password) { + printf("Failing ChangePasswordUser3 as old password was NULL. Previous test failed?\n"); + return false; + } + oldpass = *password; + + if (!newpass) { + int policy_min_pw_len = 0; + status = dcerpc_samr_GetDomPwInfo(p, mem_ctx, &dom_pw_info); + if (NT_STATUS_IS_OK(status)) { + policy_min_pw_len = dom_pw_info.out.info.min_password_length; + } + + newpass = samr_rand_pass(mem_ctx, policy_min_pw_len); + } + + server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p)); + init_lsa_String(&account, acct_name); + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + encode_pw_buffer(lm_pass.data, newpass, STR_ASCII|STR_TERMINATE); + arcfour_crypt(lm_pass.data, old_lm_hash, 516); + E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash); + + encode_pw_buffer(nt_pass.data, newpass, STR_UNICODE); + arcfour_crypt(nt_pass.data, old_nt_hash, 516); + E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.nt_password = &nt_pass; + r.in.nt_verifier = &nt_verifier; + r.in.lm_change = 1; + r.in.lm_password = &lm_pass; + r.in.lm_verifier = &lm_verifier; + + status = dcerpc_samr_ChangePasswordUser2(p, mem_ctx, &r); + if (allow_password_restriction && NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) { + printf("ChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status)); + } else if (!NT_STATUS_IS_OK(status)) { + printf("ChangePasswordUser2 failed - %s\n", nt_errstr(status)); + ret = false; + } else { + *password = newpass; + } + + return ret; +} + + +bool test_ChangePasswordUser3(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + const char *account_string, + int policy_min_pw_len, + char **password, + const char *newpass, + NTTIME last_password_change, + bool handle_reject_reason) +{ + NTSTATUS status; + struct samr_ChangePasswordUser3 r; + bool ret = true; + struct lsa_String server, account, account_bad; + struct samr_CryptPassword nt_pass, lm_pass; + struct samr_Password nt_verifier, lm_verifier; + char *oldpass; + uint8_t old_nt_hash[16], new_nt_hash[16]; + uint8_t old_lm_hash[16], new_lm_hash[16]; + NTTIME t; + + printf("Testing ChangePasswordUser3\n"); + + if (newpass == NULL) { + do { + if (policy_min_pw_len == 0) { + newpass = samr_rand_pass(mem_ctx, policy_min_pw_len); + } else { + newpass = samr_rand_pass_fixed_len(mem_ctx, policy_min_pw_len); + } + } while (check_password_quality(newpass) == false); + } else { + printf("Using password '%s'\n", newpass); + } + + if (!*password) { + printf("Failing ChangePasswordUser3 as old password was NULL. Previous test failed?\n"); + return false; + } + + oldpass = *password; + server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p)); + init_lsa_String(&account, account_string); + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + encode_pw_buffer(lm_pass.data, newpass, STR_UNICODE); + arcfour_crypt(lm_pass.data, old_nt_hash, 516); + E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash); + + encode_pw_buffer(nt_pass.data, newpass, STR_UNICODE); + arcfour_crypt(nt_pass.data, old_nt_hash, 516); + E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); + + /* Break the verification */ + nt_verifier.hash[0]++; + + r.in.server = &server; + r.in.account = &account; + r.in.nt_password = &nt_pass; + r.in.nt_verifier = &nt_verifier; + r.in.lm_change = 1; + r.in.lm_password = &lm_pass; + r.in.lm_verifier = &lm_verifier; + r.in.password3 = NULL; + + status = dcerpc_samr_ChangePasswordUser3(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) && + (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD))) { + printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalid password verifier - %s\n", + nt_errstr(status)); + ret = false; + } + + encode_pw_buffer(lm_pass.data, newpass, STR_UNICODE); + arcfour_crypt(lm_pass.data, old_nt_hash, 516); + E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash); + + encode_pw_buffer(nt_pass.data, newpass, STR_UNICODE); + /* Break the NT hash */ + old_nt_hash[0]++; + arcfour_crypt(nt_pass.data, old_nt_hash, 516); + /* Unbreak it again */ + old_nt_hash[0]--; + E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.nt_password = &nt_pass; + r.in.nt_verifier = &nt_verifier; + r.in.lm_change = 1; + r.in.lm_password = &lm_pass; + r.in.lm_verifier = &lm_verifier; + r.in.password3 = NULL; + + status = dcerpc_samr_ChangePasswordUser3(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) && + (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD))) { + printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalidly encrpted password - %s\n", + nt_errstr(status)); + ret = false; + } + + /* This shouldn't be a valid name */ + init_lsa_String(&account_bad, talloc_asprintf(mem_ctx, "%sXX", account_string)); + + r.in.account = &account_bad; + status = dcerpc_samr_ChangePasswordUser3(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { + printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD for invalid username - %s\n", + nt_errstr(status)); + ret = false; + } + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + encode_pw_buffer(lm_pass.data, newpass, STR_UNICODE); + arcfour_crypt(lm_pass.data, old_nt_hash, 516); + E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash); + + encode_pw_buffer(nt_pass.data, newpass, STR_UNICODE); + arcfour_crypt(nt_pass.data, old_nt_hash, 516); + E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.nt_password = &nt_pass; + r.in.nt_verifier = &nt_verifier; + r.in.lm_change = 1; + r.in.lm_password = &lm_pass; + r.in.lm_verifier = &lm_verifier; + r.in.password3 = NULL; + + unix_to_nt_time(&t, time(NULL)); + + status = dcerpc_samr_ChangePasswordUser3(p, mem_ctx, &r); + + if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) + && r.out.dominfo + && r.out.reject + && handle_reject_reason + && (!null_nttime(last_password_change) || !r.out.dominfo->min_password_age)) { + if (r.out.dominfo->password_properties & DOMAIN_REFUSE_PASSWORD_CHANGE ) { + + if (r.out.reject && (r.out.reject->reason != SAMR_REJECT_OTHER)) { + printf("expected SAMR_REJECT_OTHER (%d), got %d\n", + SAMR_REJECT_OTHER, r.out.reject->reason); + return false; + } + } + + /* We tested the order of precendence which is as follows: + + * pwd min_age + * pwd length + * pwd complexity + * pwd history + + Guenther */ + + if ((r.out.dominfo->min_password_age > 0) && !null_nttime(last_password_change) && + (last_password_change + r.out.dominfo->min_password_age > t)) { + + if (r.out.reject->reason != SAMR_REJECT_OTHER) { + printf("expected SAMR_REJECT_OTHER (%d), got %d\n", + SAMR_REJECT_OTHER, r.out.reject->reason); + return false; + } + + } else if ((r.out.dominfo->min_password_length > 0) && + (strlen(newpass) < r.out.dominfo->min_password_length)) { + + if (r.out.reject->reason != SAMR_REJECT_TOO_SHORT) { + printf("expected SAMR_REJECT_TOO_SHORT (%d), got %d\n", + SAMR_REJECT_TOO_SHORT, r.out.reject->reason); + return false; + } + + } else if ((r.out.dominfo->password_history_length > 0) && + strequal(oldpass, newpass)) { + + if (r.out.reject->reason != SAMR_REJECT_IN_HISTORY) { + printf("expected SAMR_REJECT_IN_HISTORY (%d), got %d\n", + SAMR_REJECT_IN_HISTORY, r.out.reject->reason); + return false; + } + } else if (r.out.dominfo->password_properties & DOMAIN_PASSWORD_COMPLEX) { + + if (r.out.reject->reason != SAMR_REJECT_COMPLEXITY) { + printf("expected SAMR_REJECT_COMPLEXITY (%d), got %d\n", + SAMR_REJECT_COMPLEXITY, r.out.reject->reason); + return false; + } + + } + + if (r.out.reject->reason == SAMR_REJECT_TOO_SHORT) { + /* retry with adjusted size */ + return test_ChangePasswordUser3(p, mem_ctx, account_string, + r.out.dominfo->min_password_length, + password, NULL, 0, false); + + } + + } else if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) { + if (r.out.reject && r.out.reject->reason != SAMR_REJECT_OTHER) { + printf("expected SAMR_REJECT_OTHER (%d), got %d\n", + SAMR_REJECT_OTHER, r.out.reject->reason); + return false; + } + /* Perhaps the server has a 'min password age' set? */ + + } else if (!NT_STATUS_IS_OK(status)) { + printf("ChangePasswordUser3 failed - %s\n", nt_errstr(status)); + ret = false; + } else { + *password = talloc_strdup(mem_ctx, newpass); + } + + return ret; +} + + +static bool test_GetMembersInAlias(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *alias_handle) +{ + struct samr_GetMembersInAlias r; + struct lsa_SidArray sids; + NTSTATUS status; + bool ret = true; + + printf("Testing GetMembersInAlias\n"); + + r.in.alias_handle = alias_handle; + r.out.sids = &sids; + + status = dcerpc_samr_GetMembersInAlias(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("GetMembersInAlias failed - %s\n", + nt_errstr(status)); + ret = false; + } + + return ret; +} + +static bool test_AddMemberToAlias(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *alias_handle, + const struct dom_sid *domain_sid) +{ + struct samr_AddAliasMember r; + struct samr_DeleteAliasMember d; + NTSTATUS status; + bool ret = true; + struct dom_sid *sid; + + sid = dom_sid_add_rid(mem_ctx, domain_sid, 512); + + printf("testing AddAliasMember\n"); + r.in.alias_handle = alias_handle; + r.in.sid = sid; + + status = dcerpc_samr_AddAliasMember(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("AddAliasMember failed - %s\n", nt_errstr(status)); + ret = false; + } + + d.in.alias_handle = alias_handle; + d.in.sid = sid; + + status = dcerpc_samr_DeleteAliasMember(p, mem_ctx, &d); + if (!NT_STATUS_IS_OK(status)) { + printf("DelAliasMember failed - %s\n", nt_errstr(status)); + ret = false; + } + + return ret; +} + +static bool test_AddMultipleMembersToAlias(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *alias_handle) +{ + struct samr_AddMultipleMembersToAlias a; + struct samr_RemoveMultipleMembersFromAlias r; + NTSTATUS status; + bool ret = true; + struct lsa_SidArray sids; + + printf("testing AddMultipleMembersToAlias\n"); + a.in.alias_handle = alias_handle; + a.in.sids = &sids; + + sids.num_sids = 3; + sids.sids = talloc_array(mem_ctx, struct lsa_SidPtr, 3); + + sids.sids[0].sid = dom_sid_parse_talloc(mem_ctx, "S-1-5-32-1-2-3-1"); + sids.sids[1].sid = dom_sid_parse_talloc(mem_ctx, "S-1-5-32-1-2-3-2"); + sids.sids[2].sid = dom_sid_parse_talloc(mem_ctx, "S-1-5-32-1-2-3-3"); + + status = dcerpc_samr_AddMultipleMembersToAlias(p, mem_ctx, &a); + if (!NT_STATUS_IS_OK(status)) { + printf("AddMultipleMembersToAlias failed - %s\n", nt_errstr(status)); + ret = false; + } + + + printf("testing RemoveMultipleMembersFromAlias\n"); + r.in.alias_handle = alias_handle; + r.in.sids = &sids; + + status = dcerpc_samr_RemoveMultipleMembersFromAlias(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("RemoveMultipleMembersFromAlias failed - %s\n", nt_errstr(status)); + ret = false; + } + + /* strange! removing twice doesn't give any error */ + status = dcerpc_samr_RemoveMultipleMembersFromAlias(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("RemoveMultipleMembersFromAlias failed - %s\n", nt_errstr(status)); + ret = false; + } + + /* but removing an alias that isn't there does */ + sids.sids[2].sid = dom_sid_parse_talloc(mem_ctx, "S-1-5-32-1-2-3-4"); + + status = dcerpc_samr_RemoveMultipleMembersFromAlias(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) { + printf("RemoveMultipleMembersFromAlias failed - %s\n", nt_errstr(status)); + ret = false; + } + + return ret; +} + +static bool test_TestPrivateFunctionsUser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *user_handle) +{ + struct samr_TestPrivateFunctionsUser r; + NTSTATUS status; + bool ret = true; + + printf("Testing TestPrivateFunctionsUser\n"); + + r.in.user_handle = user_handle; + + status = dcerpc_samr_TestPrivateFunctionsUser(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(NT_STATUS_NOT_IMPLEMENTED, status)) { + printf("TestPrivateFunctionsUser failed - %s\n", nt_errstr(status)); + ret = false; + } + + return ret; +} + + +static bool test_user_ops(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *user_handle, + struct policy_handle *domain_handle, + uint32_t base_acct_flags, + const char *base_acct_name, enum torture_samr_choice which_ops) +{ + char *password = NULL; + struct samr_QueryUserInfo q; + NTSTATUS status; + + bool ret = true; + int i; + uint32_t rid; + const uint32_t password_fields[] = { + SAMR_FIELD_PASSWORD, + SAMR_FIELD_PASSWORD2, + SAMR_FIELD_PASSWORD | SAMR_FIELD_PASSWORD2, + 0 + }; + + status = test_LookupName(p, tctx, domain_handle, base_acct_name, &rid); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + } + + switch (which_ops) { + case TORTURE_SAMR_USER_ATTRIBUTES: + if (!test_QuerySecurity(p, tctx, user_handle)) { + ret = false; + } + + if (!test_QueryUserInfo(p, tctx, user_handle)) { + ret = false; + } + + if (!test_QueryUserInfo2(p, tctx, user_handle)) { + ret = false; + } + + if (!test_SetUserInfo(p, tctx, user_handle, base_acct_flags, + base_acct_name)) { + ret = false; + } + + if (!test_GetUserPwInfo(p, tctx, user_handle)) { + ret = false; + } + + if (!test_TestPrivateFunctionsUser(p, tctx, user_handle)) { + ret = false; + } + + if (!test_SetUserPass(p, tctx, user_handle, &password)) { + ret = false; + } + break; + case TORTURE_SAMR_PASSWORDS: + if (base_acct_flags & (ACB_WSTRUST|ACB_DOMTRUST|ACB_SVRTRUST)) { + char simple_pass[9]; + char *v = generate_random_str(tctx, 1); + + ZERO_STRUCT(simple_pass); + memset(simple_pass, *v, sizeof(simple_pass) - 1); + + printf("Testing machine account password policy rules\n"); + + /* Workstation trust accounts don't seem to need to honour password quality policy */ + if (!test_SetUserPassEx(p, tctx, user_handle, true, &password)) { + ret = false; + } + + if (!test_ChangePasswordUser2(p, tctx, base_acct_name, &password, simple_pass, false)) { + ret = false; + } + + /* reset again, to allow another 'user' password change */ + if (!test_SetUserPassEx(p, tctx, user_handle, true, &password)) { + ret = false; + } + + /* Try a 'short' password */ + if (!test_ChangePasswordUser2(p, tctx, base_acct_name, &password, samr_rand_pass(tctx, 4), false)) { + ret = false; + } + + } + + for (i = 0; password_fields[i]; i++) { + if (!test_SetUserPass_23(p, tctx, user_handle, password_fields[i], &password)) { + ret = false; + } + + /* check it was set right */ + if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) { + ret = false; + } + } + + for (i = 0; password_fields[i]; i++) { + if (!test_SetUserPass_25(p, tctx, user_handle, password_fields[i], &password)) { + ret = false; + } + + /* check it was set right */ + if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) { + ret = false; + } + } + + if (!test_SetUserPassEx(p, tctx, user_handle, false, &password)) { + ret = false; + } + + if (!test_ChangePassword(p, tctx, base_acct_name, domain_handle, &password)) { + ret = false; + } + + q.in.user_handle = user_handle; + q.in.level = 5; + + status = dcerpc_samr_QueryUserInfo(p, tctx, &q); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryUserInfo level %u failed - %s\n", + q.in.level, nt_errstr(status)); + ret = false; + } else { + uint32_t expected_flags = (base_acct_flags | ACB_PWNOTREQ | ACB_DISABLED); + if ((q.out.info->info5.acct_flags) != expected_flags) { + printf("QuerUserInfo level 5 failed, it returned 0x%08x when we expected flags of 0x%08x\n", + q.out.info->info5.acct_flags, + expected_flags); + ret = false; + } + if (q.out.info->info5.rid != rid) { + printf("QuerUserInfo level 5 failed, it returned %u when we expected rid of %u\n", + q.out.info->info5.rid, rid); + + } + } + + break; + case TORTURE_SAMR_OTHER: + /* We just need the account to exist */ + break; + } + return ret; +} + +static bool test_alias_ops(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *alias_handle, + const struct dom_sid *domain_sid) +{ + bool ret = true; + + if (!test_QuerySecurity(p, tctx, alias_handle)) { + ret = false; + } + + if (!test_QueryAliasInfo(p, tctx, alias_handle)) { + ret = false; + } + + if (!test_SetAliasInfo(p, tctx, alias_handle)) { + ret = false; + } + + if (!test_AddMemberToAlias(p, tctx, alias_handle, domain_sid)) { + ret = false; + } + + if (torture_setting_bool(tctx, "samba4", false)) { + printf("skipping MultipleMembers Alias tests against Samba4\n"); + return ret; + } + + if (!test_AddMultipleMembersToAlias(p, tctx, alias_handle)) { + ret = false; + } + + return ret; +} + + +static bool test_DeleteUser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *user_handle) +{ + struct samr_DeleteUser d; + NTSTATUS status; + bool ret = true; + printf("Testing DeleteUser\n"); + + d.in.user_handle = user_handle; + d.out.user_handle = user_handle; + + status = dcerpc_samr_DeleteUser(p, mem_ctx, &d); + if (!NT_STATUS_IS_OK(status)) { + printf("DeleteUser failed - %s\n", nt_errstr(status)); + ret = false; + } + + return ret; +} + +bool test_DeleteUser_byname(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, const char *name) +{ + NTSTATUS status; + struct samr_DeleteUser d; + struct policy_handle user_handle; + uint32_t rid; + + status = test_LookupName(p, mem_ctx, handle, name, &rid); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + status = test_OpenUser_byname(p, mem_ctx, handle, name, &user_handle); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + d.in.user_handle = &user_handle; + d.out.user_handle = &user_handle; + status = dcerpc_samr_DeleteUser(p, mem_ctx, &d); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + return true; + +failed: + printf("DeleteUser_byname(%s) failed - %s\n", name, nt_errstr(status)); + return false; +} + + +static bool test_DeleteGroup_byname(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, const char *name) +{ + NTSTATUS status; + struct samr_OpenGroup r; + struct samr_DeleteDomainGroup d; + struct policy_handle group_handle; + uint32_t rid; + + status = test_LookupName(p, mem_ctx, handle, name, &rid); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + r.in.domain_handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.group_handle = &group_handle; + status = dcerpc_samr_OpenGroup(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + d.in.group_handle = &group_handle; + d.out.group_handle = &group_handle; + status = dcerpc_samr_DeleteDomainGroup(p, mem_ctx, &d); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + return true; + +failed: + printf("DeleteGroup_byname(%s) failed - %s\n", name, nt_errstr(status)); + return false; +} + + +static bool test_DeleteAlias_byname(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, const char *name) +{ + NTSTATUS status; + struct samr_OpenAlias r; + struct samr_DeleteDomAlias d; + struct policy_handle alias_handle; + uint32_t rid; + + printf("testing DeleteAlias_byname\n"); + + status = test_LookupName(p, mem_ctx, domain_handle, name, &rid); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + r.in.domain_handle = domain_handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.alias_handle = &alias_handle; + status = dcerpc_samr_OpenAlias(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + d.in.alias_handle = &alias_handle; + d.out.alias_handle = &alias_handle; + status = dcerpc_samr_DeleteDomAlias(p, mem_ctx, &d); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + return true; + +failed: + printf("DeleteAlias_byname(%s) failed - %s\n", name, nt_errstr(status)); + return false; +} + +static bool test_DeleteAlias(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *alias_handle) +{ + struct samr_DeleteDomAlias d; + NTSTATUS status; + bool ret = true; + printf("Testing DeleteAlias\n"); + + d.in.alias_handle = alias_handle; + d.out.alias_handle = alias_handle; + + status = dcerpc_samr_DeleteDomAlias(p, mem_ctx, &d); + if (!NT_STATUS_IS_OK(status)) { + printf("DeleteAlias failed - %s\n", nt_errstr(status)); + ret = false; + } + + return ret; +} + +static bool test_CreateAlias(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *domain_handle, + struct policy_handle *alias_handle, + const struct dom_sid *domain_sid) +{ + NTSTATUS status; + struct samr_CreateDomAlias r; + struct lsa_String name; + uint32_t rid; + bool ret = true; + + init_lsa_String(&name, TEST_ALIASNAME); + r.in.domain_handle = domain_handle; + r.in.alias_name = &name; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.alias_handle = alias_handle; + r.out.rid = &rid; + + printf("Testing CreateAlias (%s)\n", r.in.alias_name->string); + + status = dcerpc_samr_CreateDomAlias(p, tctx, &r); + + if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) { + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + printf("Server correctly refused create of '%s'\n", r.in.alias_name->string); + return true; + } else { + printf("Server should have refused create of '%s', got %s instead\n", r.in.alias_name->string, + nt_errstr(status)); + return false; + } + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_ALIAS_EXISTS)) { + if (!test_DeleteAlias_byname(p, tctx, domain_handle, r.in.alias_name->string)) { + return false; + } + status = dcerpc_samr_CreateDomAlias(p, tctx, &r); + } + + if (!NT_STATUS_IS_OK(status)) { + printf("CreateAlias failed - %s\n", nt_errstr(status)); + return false; + } + + if (!test_alias_ops(p, tctx, alias_handle, domain_sid)) { + ret = false; + } + + return ret; +} + +static bool test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + const char *acct_name, + struct policy_handle *domain_handle, char **password) +{ + bool ret = true; + + if (!*password) { + return false; + } + + if (!test_ChangePasswordUser(p, mem_ctx, acct_name, domain_handle, password)) { + ret = false; + } + + if (!test_ChangePasswordUser2(p, mem_ctx, acct_name, password, 0, true)) { + ret = false; + } + + if (!test_OemChangePasswordUser2(p, mem_ctx, acct_name, domain_handle, password)) { + ret = false; + } + + /* test what happens when setting the old password again */ + if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, *password, 0, true)) { + ret = false; + } + + { + char simple_pass[9]; + char *v = generate_random_str(mem_ctx, 1); + + ZERO_STRUCT(simple_pass); + memset(simple_pass, *v, sizeof(simple_pass) - 1); + + /* test what happens when picking a simple password */ + if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, simple_pass, 0, true)) { + ret = false; + } + } + + /* set samr_SetDomainInfo level 1 with min_length 5 */ + { + struct samr_QueryDomainInfo r; + struct samr_SetDomainInfo s; + uint16_t len_old, len; + uint32_t pwd_prop_old; + int64_t min_pwd_age_old; + NTSTATUS status; + + len = 5; + + r.in.domain_handle = domain_handle; + r.in.level = 1; + + printf("testing samr_QueryDomainInfo level 1\n"); + status = dcerpc_samr_QueryDomainInfo(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + s.in.domain_handle = domain_handle; + s.in.level = 1; + s.in.info = r.out.info; + + /* remember the old min length, so we can reset it */ + len_old = s.in.info->info1.min_password_length; + s.in.info->info1.min_password_length = len; + pwd_prop_old = s.in.info->info1.password_properties; + /* turn off password complexity checks for this test */ + s.in.info->info1.password_properties &= ~DOMAIN_PASSWORD_COMPLEX; + + min_pwd_age_old = s.in.info->info1.min_password_age; + s.in.info->info1.min_password_age = 0; + + printf("testing samr_SetDomainInfo level 1\n"); + status = dcerpc_samr_SetDomainInfo(p, mem_ctx, &s); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + printf("calling test_ChangePasswordUser3 with too short password\n"); + + if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, len - 1, password, NULL, 0, true)) { + ret = false; + } + + s.in.info->info1.min_password_length = len_old; + s.in.info->info1.password_properties = pwd_prop_old; + s.in.info->info1.min_password_age = min_pwd_age_old; + + printf("testing samr_SetDomainInfo level 1\n"); + status = dcerpc_samr_SetDomainInfo(p, mem_ctx, &s); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + } + + { + NTSTATUS status; + struct samr_OpenUser r; + struct samr_QueryUserInfo q; + struct samr_LookupNames n; + struct policy_handle user_handle; + + n.in.domain_handle = domain_handle; + n.in.num_names = 1; + n.in.names = talloc_array(mem_ctx, struct lsa_String, 1); + n.in.names[0].string = acct_name; + + status = dcerpc_samr_LookupNames(p, mem_ctx, &n); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupNames failed - %s\n", nt_errstr(status)); + return false; + } + + r.in.domain_handle = domain_handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = n.out.rids.ids[0]; + r.out.user_handle = &user_handle; + + status = dcerpc_samr_OpenUser(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenUser(%u) failed - %s\n", n.out.rids.ids[0], nt_errstr(status)); + return false; + } + + q.in.user_handle = &user_handle; + q.in.level = 5; + + status = dcerpc_samr_QueryUserInfo(p, mem_ctx, &q); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryUserInfo failed - %s\n", nt_errstr(status)); + return false; + } + + printf("calling test_ChangePasswordUser3 with too early password change\n"); + + if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, NULL, + q.out.info->info5.last_password_change, true)) { + ret = false; + } + } + + /* we change passwords twice - this has the effect of verifying + they were changed correctly for the final call */ + if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, NULL, 0, true)) { + ret = false; + } + + if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, NULL, 0, true)) { + ret = false; + } + + return ret; +} + +static bool test_CreateUser(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *domain_handle, + struct policy_handle *user_handle_out, + struct dom_sid *domain_sid, + enum torture_samr_choice which_ops) +{ + + TALLOC_CTX *user_ctx; + + NTSTATUS status; + struct samr_CreateUser r; + struct samr_QueryUserInfo q; + struct samr_DeleteUser d; + uint32_t rid; + + /* This call creates a 'normal' account - check that it really does */ + const uint32_t acct_flags = ACB_NORMAL; + struct lsa_String name; + bool ret = true; + + struct policy_handle user_handle; + user_ctx = talloc_named(tctx, 0, "test_CreateUser2 per-user context"); + init_lsa_String(&name, TEST_ACCOUNT_NAME); + + r.in.domain_handle = domain_handle; + r.in.account_name = &name; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.user_handle = &user_handle; + r.out.rid = &rid; + + printf("Testing CreateUser(%s)\n", r.in.account_name->string); + + status = dcerpc_samr_CreateUser(p, user_ctx, &r); + + if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) { + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + printf("Server correctly refused create of '%s'\n", r.in.account_name->string); + return true; + } else { + printf("Server should have refused create of '%s', got %s instead\n", r.in.account_name->string, + nt_errstr(status)); + return false; + } + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) { + if (!test_DeleteUser_byname(p, user_ctx, domain_handle, r.in.account_name->string)) { + talloc_free(user_ctx); + return false; + } + status = dcerpc_samr_CreateUser(p, user_ctx, &r); + } + if (!NT_STATUS_IS_OK(status)) { + talloc_free(user_ctx); + printf("CreateUser failed - %s\n", nt_errstr(status)); + return false; + } else { + q.in.user_handle = &user_handle; + q.in.level = 16; + + status = dcerpc_samr_QueryUserInfo(p, user_ctx, &q); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryUserInfo level %u failed - %s\n", + q.in.level, nt_errstr(status)); + ret = false; + } else { + if ((q.out.info->info16.acct_flags & acct_flags) != acct_flags) { + printf("QuerUserInfo level 16 failed, it returned 0x%08x when we expected flags of 0x%08x\n", + q.out.info->info16.acct_flags, + acct_flags); + ret = false; + } + } + + if (!test_user_ops(p, tctx, &user_handle, domain_handle, + acct_flags, name.string, which_ops)) { + ret = false; + } + + if (user_handle_out) { + *user_handle_out = user_handle; + } else { + printf("Testing DeleteUser (createuser test)\n"); + + d.in.user_handle = &user_handle; + d.out.user_handle = &user_handle; + + status = dcerpc_samr_DeleteUser(p, user_ctx, &d); + if (!NT_STATUS_IS_OK(status)) { + printf("DeleteUser failed - %s\n", nt_errstr(status)); + ret = false; + } + } + + } + + talloc_free(user_ctx); + + return ret; +} + + +static bool test_CreateUser2(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *domain_handle, + struct dom_sid *domain_sid, + enum torture_samr_choice which_ops) +{ + NTSTATUS status; + struct samr_CreateUser2 r; + struct samr_QueryUserInfo q; + struct samr_DeleteUser d; + struct policy_handle user_handle; + uint32_t rid; + struct lsa_String name; + bool ret = true; + int i; + + struct { + uint32_t acct_flags; + const char *account_name; + NTSTATUS nt_status; + } account_types[] = { + { ACB_NORMAL, TEST_ACCOUNT_NAME, NT_STATUS_OK }, + { ACB_NORMAL | ACB_DISABLED, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_NORMAL | ACB_PWNOEXP, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_WSTRUST, TEST_MACHINENAME, NT_STATUS_OK }, + { ACB_WSTRUST | ACB_DISABLED, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_WSTRUST | ACB_PWNOEXP, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_SVRTRUST, TEST_MACHINENAME, NT_STATUS_OK }, + { ACB_SVRTRUST | ACB_DISABLED, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_SVRTRUST | ACB_PWNOEXP, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_DOMTRUST, TEST_DOMAINNAME, NT_STATUS_OK }, + { ACB_DOMTRUST | ACB_DISABLED, TEST_DOMAINNAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_DOMTRUST | ACB_PWNOEXP, TEST_DOMAINNAME, NT_STATUS_INVALID_PARAMETER }, + { 0, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_DISABLED, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER }, + { 0, NULL, NT_STATUS_INVALID_PARAMETER } + }; + + for (i = 0; account_types[i].account_name; i++) { + TALLOC_CTX *user_ctx; + uint32_t acct_flags = account_types[i].acct_flags; + uint32_t access_granted; + user_ctx = talloc_named(tctx, 0, "test_CreateUser2 per-user context"); + init_lsa_String(&name, account_types[i].account_name); + + r.in.domain_handle = domain_handle; + r.in.account_name = &name; + r.in.acct_flags = acct_flags; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.user_handle = &user_handle; + r.out.access_granted = &access_granted; + r.out.rid = &rid; + + printf("Testing CreateUser2(%s, 0x%x)\n", r.in.account_name->string, acct_flags); + + status = dcerpc_samr_CreateUser2(p, user_ctx, &r); + + if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) { + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + printf("Server correctly refused create of '%s'\n", r.in.account_name->string); + continue; + } else { + printf("Server should have refused create of '%s', got %s instead\n", r.in.account_name->string, + nt_errstr(status)); + ret = false; + continue; + } + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) { + if (!test_DeleteUser_byname(p, user_ctx, domain_handle, r.in.account_name->string)) { + talloc_free(user_ctx); + ret = false; + continue; + } + status = dcerpc_samr_CreateUser2(p, user_ctx, &r); + + } + if (!NT_STATUS_EQUAL(status, account_types[i].nt_status)) { + printf("CreateUser2 failed gave incorrect error return - %s (should be %s)\n", + nt_errstr(status), nt_errstr(account_types[i].nt_status)); + ret = false; + } + + if (NT_STATUS_IS_OK(status)) { + q.in.user_handle = &user_handle; + q.in.level = 5; + + status = dcerpc_samr_QueryUserInfo(p, user_ctx, &q); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryUserInfo level %u failed - %s\n", + q.in.level, nt_errstr(status)); + ret = false; + } else { + uint32_t expected_flags = (acct_flags | ACB_PWNOTREQ | ACB_DISABLED); + if (acct_flags == ACB_NORMAL) { + expected_flags |= ACB_PW_EXPIRED; + } + if ((q.out.info->info5.acct_flags) != expected_flags) { + printf("QuerUserInfo level 5 failed, it returned 0x%08x when we expected flags of 0x%08x\n", + q.out.info->info5.acct_flags, + expected_flags); + ret = false; + } + switch (acct_flags) { + case ACB_SVRTRUST: + if (q.out.info->info5.primary_gid != DOMAIN_RID_DCS) { + printf("QuerUserInfo level 5: DC should have had Primary Group %d, got %d\n", + DOMAIN_RID_DCS, q.out.info->info5.primary_gid); + ret = false; + } + break; + case ACB_WSTRUST: + if (q.out.info->info5.primary_gid != DOMAIN_RID_DOMAIN_MEMBERS) { + printf("QuerUserInfo level 5: Domain Member should have had Primary Group %d, got %d\n", + DOMAIN_RID_DOMAIN_MEMBERS, q.out.info->info5.primary_gid); + ret = false; + } + break; + case ACB_NORMAL: + if (q.out.info->info5.primary_gid != DOMAIN_RID_USERS) { + printf("QuerUserInfo level 5: Users should have had Primary Group %d, got %d\n", + DOMAIN_RID_USERS, q.out.info->info5.primary_gid); + ret = false; + } + break; + } + } + + if (!test_user_ops(p, tctx, &user_handle, domain_handle, + acct_flags, name.string, which_ops)) { + ret = false; + } + + printf("Testing DeleteUser (createuser2 test)\n"); + + d.in.user_handle = &user_handle; + d.out.user_handle = &user_handle; + + status = dcerpc_samr_DeleteUser(p, user_ctx, &d); + if (!NT_STATUS_IS_OK(status)) { + printf("DeleteUser failed - %s\n", nt_errstr(status)); + ret = false; + } + } + talloc_free(user_ctx); + } + + return ret; +} + +static bool test_QueryAliasInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_QueryAliasInfo r; + uint16_t levels[] = {1, 2, 3}; + int i; + bool ret = true; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + printf("Testing QueryAliasInfo level %u\n", levels[i]); + + r.in.alias_handle = handle; + r.in.level = levels[i]; + + status = dcerpc_samr_QueryAliasInfo(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryAliasInfo level %u failed - %s\n", + levels[i], nt_errstr(status)); + ret = false; + } + } + + return ret; +} + +static bool test_QueryGroupInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_QueryGroupInfo r; + uint16_t levels[] = {1, 2, 3, 4, 5}; + int i; + bool ret = true; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + printf("Testing QueryGroupInfo level %u\n", levels[i]); + + r.in.group_handle = handle; + r.in.level = levels[i]; + + status = dcerpc_samr_QueryGroupInfo(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryGroupInfo level %u failed - %s\n", + levels[i], nt_errstr(status)); + ret = false; + } + } + + return ret; +} + +static bool test_QueryGroupMember(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_QueryGroupMember r; + bool ret = true; + + printf("Testing QueryGroupMember\n"); + + r.in.group_handle = handle; + + status = dcerpc_samr_QueryGroupMember(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryGroupInfo failed - %s\n", nt_errstr(status)); + ret = false; + } + + return ret; +} + + +static bool test_SetGroupInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_QueryGroupInfo r; + struct samr_SetGroupInfo s; + uint16_t levels[] = {1, 2, 3, 4}; + uint16_t set_ok[] = {0, 1, 1, 1}; + int i; + bool ret = true; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + printf("Testing QueryGroupInfo level %u\n", levels[i]); + + r.in.group_handle = handle; + r.in.level = levels[i]; + + status = dcerpc_samr_QueryGroupInfo(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryGroupInfo level %u failed - %s\n", + levels[i], nt_errstr(status)); + ret = false; + } + + printf("Testing SetGroupInfo level %u\n", levels[i]); + + s.in.group_handle = handle; + s.in.level = levels[i]; + s.in.info = r.out.info; + +#if 0 + /* disabled this, as it changes the name only from the point of view of samr, + but leaves the name from the point of view of w2k3 internals (and ldap). This means + the name is still reserved, so creating the old name fails, but deleting by the old name + also fails */ + if (s.in.level == 2) { + init_lsa_String(&s.in.info->string, "NewName"); + } +#endif + + if (s.in.level == 4) { + init_lsa_String(&s.in.info->description, "test description"); + } + + status = dcerpc_samr_SetGroupInfo(p, mem_ctx, &s); + if (set_ok[i]) { + if (!NT_STATUS_IS_OK(status)) { + printf("SetGroupInfo level %u failed - %s\n", + r.in.level, nt_errstr(status)); + ret = false; + continue; + } + } else { + if (!NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, status)) { + printf("SetGroupInfo level %u gave %s - should have been NT_STATUS_INVALID_INFO_CLASS\n", + r.in.level, nt_errstr(status)); + ret = false; + continue; + } + } + } + + return ret; +} + +static bool test_QueryUserInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_QueryUserInfo r; + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 16, 17, 20, 21}; + int i; + bool ret = true; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + printf("Testing QueryUserInfo level %u\n", levels[i]); + + r.in.user_handle = handle; + r.in.level = levels[i]; + + status = dcerpc_samr_QueryUserInfo(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryUserInfo level %u failed - %s\n", + levels[i], nt_errstr(status)); + ret = false; + } + } + + return ret; +} + +static bool test_QueryUserInfo2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_QueryUserInfo2 r; + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 16, 17, 20, 21}; + int i; + bool ret = true; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + printf("Testing QueryUserInfo2 level %u\n", levels[i]); + + r.in.user_handle = handle; + r.in.level = levels[i]; + + status = dcerpc_samr_QueryUserInfo2(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryUserInfo2 level %u failed - %s\n", + levels[i], nt_errstr(status)); + ret = false; + } + } + + return ret; +} + +static bool test_OpenUser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, uint32_t rid) +{ + NTSTATUS status; + struct samr_OpenUser r; + struct policy_handle user_handle; + bool ret = true; + + printf("Testing OpenUser(%u)\n", rid); + + r.in.domain_handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.user_handle = &user_handle; + + status = dcerpc_samr_OpenUser(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(status)); + return false; + } + + if (!test_QuerySecurity(p, mem_ctx, &user_handle)) { + ret = false; + } + + if (!test_QueryUserInfo(p, mem_ctx, &user_handle)) { + ret = false; + } + + if (!test_QueryUserInfo2(p, mem_ctx, &user_handle)) { + ret = false; + } + + if (!test_GetUserPwInfo(p, mem_ctx, &user_handle)) { + ret = false; + } + + if (!test_GetGroupsForUser(p,mem_ctx, &user_handle)) { + ret = false; + } + + if (!test_samr_handle_Close(p, mem_ctx, &user_handle)) { + ret = false; + } + + return ret; +} + +static bool test_OpenGroup(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, uint32_t rid) +{ + NTSTATUS status; + struct samr_OpenGroup r; + struct policy_handle group_handle; + bool ret = true; + + printf("Testing OpenGroup(%u)\n", rid); + + r.in.domain_handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.group_handle = &group_handle; + + status = dcerpc_samr_OpenGroup(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenGroup(%u) failed - %s\n", rid, nt_errstr(status)); + return false; + } + + if (!test_QuerySecurity(p, mem_ctx, &group_handle)) { + ret = false; + } + + if (!test_QueryGroupInfo(p, mem_ctx, &group_handle)) { + ret = false; + } + + if (!test_QueryGroupMember(p, mem_ctx, &group_handle)) { + ret = false; + } + + if (!test_samr_handle_Close(p, mem_ctx, &group_handle)) { + ret = false; + } + + return ret; +} + +static bool test_OpenAlias(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, uint32_t rid) +{ + NTSTATUS status; + struct samr_OpenAlias r; + struct policy_handle alias_handle; + bool ret = true; + + printf("Testing OpenAlias(%u)\n", rid); + + r.in.domain_handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.alias_handle = &alias_handle; + + status = dcerpc_samr_OpenAlias(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenAlias(%u) failed - %s\n", rid, nt_errstr(status)); + return false; + } + + if (!test_QuerySecurity(p, mem_ctx, &alias_handle)) { + ret = false; + } + + if (!test_QueryAliasInfo(p, mem_ctx, &alias_handle)) { + ret = false; + } + + if (!test_GetMembersInAlias(p, mem_ctx, &alias_handle)) { + ret = false; + } + + if (!test_samr_handle_Close(p, mem_ctx, &alias_handle)) { + ret = false; + } + + return ret; +} + +static bool check_mask(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, uint32_t rid, + uint32_t acct_flag_mask) +{ + NTSTATUS status; + struct samr_OpenUser r; + struct samr_QueryUserInfo q; + struct policy_handle user_handle; + bool ret = true; + + printf("Testing OpenUser(%u)\n", rid); + + r.in.domain_handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.user_handle = &user_handle; + + status = dcerpc_samr_OpenUser(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(status)); + return false; + } + + q.in.user_handle = &user_handle; + q.in.level = 16; + + status = dcerpc_samr_QueryUserInfo(p, mem_ctx, &q); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryUserInfo level 16 failed - %s\n", + nt_errstr(status)); + ret = false; + } else { + if ((acct_flag_mask & q.out.info->info16.acct_flags) == 0) { + printf("Server failed to filter for 0x%x, allowed 0x%x (%d) on EnumDomainUsers\n", + acct_flag_mask, q.out.info->info16.acct_flags, rid); + ret = false; + } + } + + if (!test_samr_handle_Close(p, mem_ctx, &user_handle)) { + ret = false; + } + + return ret; +} + +static bool test_EnumDomainUsers(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status = STATUS_MORE_ENTRIES; + struct samr_EnumDomainUsers r; + uint32_t mask, resume_handle=0; + int i, mask_idx; + bool ret = true; + struct samr_LookupNames n; + struct samr_LookupRids lr ; + uint32_t masks[] = {ACB_NORMAL, ACB_DOMTRUST, ACB_WSTRUST, + ACB_DISABLED, ACB_NORMAL | ACB_DISABLED, + ACB_SVRTRUST | ACB_DOMTRUST | ACB_WSTRUST, + ACB_PWNOEXP, 0}; + + printf("Testing EnumDomainUsers\n"); + + for (mask_idx=0;mask_idx<ARRAY_SIZE(masks);mask_idx++) { + r.in.domain_handle = handle; + r.in.resume_handle = &resume_handle; + r.in.acct_flags = mask = masks[mask_idx]; + r.in.max_size = (uint32_t)-1; + r.out.resume_handle = &resume_handle; + + status = dcerpc_samr_EnumDomainUsers(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) && + !NT_STATUS_IS_OK(status)) { + printf("EnumDomainUsers failed - %s\n", nt_errstr(status)); + return false; + } + + if (!r.out.sam) { + printf("EnumDomainUsers failed: r.out.sam unexpectedly NULL\n"); + return false; + } + + if (r.out.sam->count == 0) { + continue; + } + + for (i=0;i<r.out.sam->count;i++) { + if (mask) { + if (!check_mask(p, mem_ctx, handle, r.out.sam->entries[i].idx, mask)) { + ret = false; + } + } else if (!test_OpenUser(p, mem_ctx, handle, r.out.sam->entries[i].idx)) { + ret = false; + } + } + } + + printf("Testing LookupNames\n"); + n.in.domain_handle = handle; + n.in.num_names = r.out.sam->count; + n.in.names = talloc_array(mem_ctx, struct lsa_String, r.out.sam->count); + for (i=0;i<r.out.sam->count;i++) { + n.in.names[i].string = r.out.sam->entries[i].name.string; + } + status = dcerpc_samr_LookupNames(p, mem_ctx, &n); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupNames failed - %s\n", nt_errstr(status)); + ret = false; + } + + + printf("Testing LookupRids\n"); + lr.in.domain_handle = handle; + lr.in.num_rids = r.out.sam->count; + lr.in.rids = talloc_array(mem_ctx, uint32_t, r.out.sam->count); + for (i=0;i<r.out.sam->count;i++) { + lr.in.rids[i] = r.out.sam->entries[i].idx; + } + status = dcerpc_samr_LookupRids(p, mem_ctx, &lr); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupRids failed - %s\n", nt_errstr(status)); + ret = false; + } + + return ret; +} + +/* + try blasting the server with a bunch of sync requests +*/ +static bool test_EnumDomainUsers_async(struct dcerpc_pipe *p, TALLOC_CTX *tctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_EnumDomainUsers r; + uint32_t resume_handle=0; + int i; +#define ASYNC_COUNT 100 + struct rpc_request *req[ASYNC_COUNT]; + + if (!torture_setting_bool(tctx, "dangerous", false)) { + printf("samr async test disabled - enable dangerous tests to use\n"); + return true; + } + + printf("Testing EnumDomainUsers_async\n"); + + r.in.domain_handle = handle; + r.in.resume_handle = &resume_handle; + r.in.acct_flags = 0; + r.in.max_size = (uint32_t)-1; + r.out.resume_handle = &resume_handle; + + for (i=0;i<ASYNC_COUNT;i++) { + req[i] = dcerpc_samr_EnumDomainUsers_send(p, tctx, &r); + } + + for (i=0;i<ASYNC_COUNT;i++) { + status = dcerpc_ndr_request_recv(req[i]); + if (!NT_STATUS_IS_OK(status)) { + printf("EnumDomainUsers[%d] failed - %s\n", + i, nt_errstr(status)); + return false; + } + } + + printf("%d async requests OK\n", i); + + return true; +} + +static bool test_EnumDomainGroups(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_EnumDomainGroups r; + uint32_t resume_handle=0; + int i; + bool ret = true; + + printf("Testing EnumDomainGroups\n"); + + r.in.domain_handle = handle; + r.in.resume_handle = &resume_handle; + r.in.max_size = (uint32_t)-1; + r.out.resume_handle = &resume_handle; + + status = dcerpc_samr_EnumDomainGroups(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("EnumDomainGroups failed - %s\n", nt_errstr(status)); + return false; + } + + if (!r.out.sam) { + return false; + } + + for (i=0;i<r.out.sam->count;i++) { + if (!test_OpenGroup(p, mem_ctx, handle, r.out.sam->entries[i].idx)) { + ret = false; + } + } + + return ret; +} + +static bool test_EnumDomainAliases(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_EnumDomainAliases r; + uint32_t resume_handle=0; + int i; + bool ret = true; + + printf("Testing EnumDomainAliases\n"); + + r.in.domain_handle = handle; + r.in.resume_handle = &resume_handle; + r.in.acct_flags = (uint32_t)-1; + r.out.resume_handle = &resume_handle; + + status = dcerpc_samr_EnumDomainAliases(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("EnumDomainAliases failed - %s\n", nt_errstr(status)); + return false; + } + + if (!r.out.sam) { + return false; + } + + for (i=0;i<r.out.sam->count;i++) { + if (!test_OpenAlias(p, mem_ctx, handle, r.out.sam->entries[i].idx)) { + ret = false; + } + } + + return ret; +} + +static bool test_GetDisplayEnumerationIndex(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_GetDisplayEnumerationIndex r; + bool ret = true; + uint16_t levels[] = {1, 2, 3, 4, 5}; + uint16_t ok_lvl[] = {1, 1, 1, 0, 0}; + int i; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + printf("Testing GetDisplayEnumerationIndex level %u\n", levels[i]); + + r.in.domain_handle = handle; + r.in.level = levels[i]; + init_lsa_String(&r.in.name, TEST_ACCOUNT_NAME); + + status = dcerpc_samr_GetDisplayEnumerationIndex(p, mem_ctx, &r); + + if (ok_lvl[i] && + !NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, status)) { + printf("GetDisplayEnumerationIndex level %u failed - %s\n", + levels[i], nt_errstr(status)); + ret = false; + } + + init_lsa_String(&r.in.name, "zzzzzzzz"); + + status = dcerpc_samr_GetDisplayEnumerationIndex(p, mem_ctx, &r); + + if (ok_lvl[i] && !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, status)) { + printf("GetDisplayEnumerationIndex level %u failed - %s\n", + levels[i], nt_errstr(status)); + ret = false; + } + } + + return ret; +} + +static bool test_GetDisplayEnumerationIndex2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_GetDisplayEnumerationIndex2 r; + bool ret = true; + uint16_t levels[] = {1, 2, 3, 4, 5}; + uint16_t ok_lvl[] = {1, 1, 1, 0, 0}; + int i; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + printf("Testing GetDisplayEnumerationIndex2 level %u\n", levels[i]); + + r.in.domain_handle = handle; + r.in.level = levels[i]; + init_lsa_String(&r.in.name, TEST_ACCOUNT_NAME); + + status = dcerpc_samr_GetDisplayEnumerationIndex2(p, mem_ctx, &r); + if (ok_lvl[i] && + !NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, status)) { + printf("GetDisplayEnumerationIndex2 level %u failed - %s\n", + levels[i], nt_errstr(status)); + ret = false; + } + + init_lsa_String(&r.in.name, "zzzzzzzz"); + + status = dcerpc_samr_GetDisplayEnumerationIndex2(p, mem_ctx, &r); + if (ok_lvl[i] && !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, status)) { + printf("GetDisplayEnumerationIndex2 level %u failed - %s\n", + levels[i], nt_errstr(status)); + ret = false; + } + } + + return ret; +} + +#define STRING_EQUAL_QUERY(s1, s2, user) \ + if (s1.string == NULL && s2.string != NULL && s2.string[0] == '\0') { \ + /* odd, but valid */ \ + } else if ((s1.string && !s2.string) || (s2.string && !s1.string) || strcmp(s1.string, s2.string)) { \ + printf("%s mismatch for %s: %s != %s (%s)\n", \ + #s1, user.string, s1.string, s2.string, __location__); \ + ret = false; \ + } +#define INT_EQUAL_QUERY(s1, s2, user) \ + if (s1 != s2) { \ + printf("%s mismatch for %s: 0x%llx != 0x%llx (%s)\n", \ + #s1, user.string, (unsigned long long)s1, (unsigned long long)s2, __location__); \ + ret = false; \ + } + +static bool test_each_DisplayInfo_user(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct samr_QueryDisplayInfo *querydisplayinfo, + bool *seen_testuser) +{ + struct samr_OpenUser r; + struct samr_QueryUserInfo q; + struct policy_handle user_handle; + int i, ret = true; + NTSTATUS status; + r.in.domain_handle = querydisplayinfo->in.domain_handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + for (i = 0; ; i++) { + switch (querydisplayinfo->in.level) { + case 1: + if (i >= querydisplayinfo->out.info.info1.count) { + return ret; + } + r.in.rid = querydisplayinfo->out.info.info1.entries[i].rid; + break; + case 2: + if (i >= querydisplayinfo->out.info.info2.count) { + return ret; + } + r.in.rid = querydisplayinfo->out.info.info2.entries[i].rid; + break; + case 3: + /* Groups */ + case 4: + case 5: + /* Not interested in validating just the account name */ + return true; + } + + r.out.user_handle = &user_handle; + + switch (querydisplayinfo->in.level) { + case 1: + case 2: + status = dcerpc_samr_OpenUser(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenUser(%u) failed - %s\n", r.in.rid, nt_errstr(status)); + return false; + } + } + + q.in.user_handle = &user_handle; + q.in.level = 21; + status = dcerpc_samr_QueryUserInfo(p, mem_ctx, &q); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryUserInfo(%u) failed - %s\n", r.in.rid, nt_errstr(status)); + return false; + } + + switch (querydisplayinfo->in.level) { + case 1: + if (seen_testuser && strcmp(q.out.info->info21.account_name.string, TEST_ACCOUNT_NAME) == 0) { + *seen_testuser = true; + } + STRING_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].full_name, + q.out.info->info21.full_name, q.out.info->info21.account_name); + STRING_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].account_name, + q.out.info->info21.account_name, q.out.info->info21.account_name); + STRING_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].description, + q.out.info->info21.description, q.out.info->info21.account_name); + INT_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].rid, + q.out.info->info21.rid, q.out.info->info21.account_name); + INT_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].acct_flags, + q.out.info->info21.acct_flags, q.out.info->info21.account_name); + + break; + case 2: + STRING_EQUAL_QUERY(querydisplayinfo->out.info.info2.entries[i].account_name, + q.out.info->info21.account_name, q.out.info->info21.account_name); + STRING_EQUAL_QUERY(querydisplayinfo->out.info.info2.entries[i].description, + q.out.info->info21.description, q.out.info->info21.account_name); + INT_EQUAL_QUERY(querydisplayinfo->out.info.info2.entries[i].rid, + q.out.info->info21.rid, q.out.info->info21.account_name); + INT_EQUAL_QUERY((querydisplayinfo->out.info.info2.entries[i].acct_flags & ~ACB_NORMAL), + q.out.info->info21.acct_flags, q.out.info->info21.account_name); + + if (!(querydisplayinfo->out.info.info2.entries[i].acct_flags & ACB_NORMAL)) { + printf("Missing ACB_NORMAL in querydisplayinfo->out.info.info2.entries[i].acct_flags on %s\n", + q.out.info->info21.account_name.string); + } + + if (!(q.out.info->info21.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST))) { + printf("Found non-trust account %s in trust account listing: 0x%x 0x%x\n", + q.out.info->info21.account_name.string, + querydisplayinfo->out.info.info2.entries[i].acct_flags, + q.out.info->info21.acct_flags); + return false; + } + + break; + } + + if (!test_samr_handle_Close(p, mem_ctx, &user_handle)) { + return false; + } + } + return ret; +} + +static bool test_QueryDisplayInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_QueryDisplayInfo r; + struct samr_QueryDomainInfo dom_info; + bool ret = true; + uint16_t levels[] = {1, 2, 3, 4, 5}; + int i; + bool seen_testuser = false; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + printf("Testing QueryDisplayInfo level %u\n", levels[i]); + + r.in.start_idx = 0; + status = STATUS_MORE_ENTRIES; + while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + r.in.domain_handle = handle; + r.in.level = levels[i]; + r.in.max_entries = 2; + r.in.buf_size = (uint32_t)-1; + + status = dcerpc_samr_QueryDisplayInfo(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) && !NT_STATUS_IS_OK(status)) { + printf("QueryDisplayInfo level %u failed - %s\n", + levels[i], nt_errstr(status)); + ret = false; + } + switch (r.in.level) { + case 1: + if (!test_each_DisplayInfo_user(p, mem_ctx, &r, &seen_testuser)) { + ret = false; + } + r.in.start_idx += r.out.info.info1.count; + break; + case 2: + if (!test_each_DisplayInfo_user(p, mem_ctx, &r, NULL)) { + ret = false; + } + r.in.start_idx += r.out.info.info2.count; + break; + case 3: + r.in.start_idx += r.out.info.info3.count; + break; + case 4: + r.in.start_idx += r.out.info.info4.count; + break; + case 5: + r.in.start_idx += r.out.info.info5.count; + break; + } + } + dom_info.in.domain_handle = handle; + dom_info.in.level = 2; + /* Check number of users returned is correct */ + status = dcerpc_samr_QueryDomainInfo(p, mem_ctx, &dom_info); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryDomainInfo level %u failed - %s\n", + r.in.level, nt_errstr(status)); + ret = false; + break; + } + switch (r.in.level) { + case 1: + case 4: + if (dom_info.out.info->general.num_users < r.in.start_idx) { + printf("QueryDomainInfo indicates that QueryDisplayInfo returned more users (%d/%d) than the domain %s is said to contain!\n", + r.in.start_idx, dom_info.out.info->general.num_groups, + dom_info.out.info->general.domain_name.string); + ret = false; + } + if (!seen_testuser) { + struct policy_handle user_handle; + if (NT_STATUS_IS_OK(test_OpenUser_byname(p, mem_ctx, handle, TEST_ACCOUNT_NAME, &user_handle))) { + printf("Didn't find test user " TEST_ACCOUNT_NAME " in enumeration of %s\n", + dom_info.out.info->general.domain_name.string); + ret = false; + test_samr_handle_Close(p, mem_ctx, &user_handle); + } + } + break; + case 3: + case 5: + if (dom_info.out.info->general.num_groups != r.in.start_idx) { + printf("QueryDomainInfo indicates that QueryDisplayInfo didn't return all (%d/%d) the groups in %s\n", + r.in.start_idx, dom_info.out.info->general.num_groups, + dom_info.out.info->general.domain_name.string); + ret = false; + } + + break; + } + + } + + return ret; +} + +static bool test_QueryDisplayInfo2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_QueryDisplayInfo2 r; + bool ret = true; + uint16_t levels[] = {1, 2, 3, 4, 5}; + int i; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + printf("Testing QueryDisplayInfo2 level %u\n", levels[i]); + + r.in.domain_handle = handle; + r.in.level = levels[i]; + r.in.start_idx = 0; + r.in.max_entries = 1000; + r.in.buf_size = (uint32_t)-1; + + status = dcerpc_samr_QueryDisplayInfo2(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryDisplayInfo2 level %u failed - %s\n", + levels[i], nt_errstr(status)); + ret = false; + } + } + + return ret; +} + +static bool test_QueryDisplayInfo3(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_QueryDisplayInfo3 r; + bool ret = true; + uint16_t levels[] = {1, 2, 3, 4, 5}; + int i; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + printf("Testing QueryDisplayInfo3 level %u\n", levels[i]); + + r.in.domain_handle = handle; + r.in.level = levels[i]; + r.in.start_idx = 0; + r.in.max_entries = 1000; + r.in.buf_size = (uint32_t)-1; + + status = dcerpc_samr_QueryDisplayInfo3(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryDisplayInfo3 level %u failed - %s\n", + levels[i], nt_errstr(status)); + ret = false; + } + } + + return ret; +} + + +static bool test_QueryDisplayInfo_continue(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_QueryDisplayInfo r; + bool ret = true; + + printf("Testing QueryDisplayInfo continuation\n"); + + r.in.domain_handle = handle; + r.in.level = 1; + r.in.start_idx = 0; + r.in.max_entries = 1; + r.in.buf_size = (uint32_t)-1; + + do { + status = dcerpc_samr_QueryDisplayInfo(p, mem_ctx, &r); + if (NT_STATUS_IS_OK(status) && r.out.returned_size != 0) { + if (r.out.info.info1.entries[0].idx != r.in.start_idx + 1) { + printf("expected idx %d but got %d\n", + r.in.start_idx + 1, + r.out.info.info1.entries[0].idx); + break; + } + } + if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) && + !NT_STATUS_IS_OK(status)) { + printf("QueryDisplayInfo level %u failed - %s\n", + r.in.level, nt_errstr(status)); + ret = false; + break; + } + r.in.start_idx++; + } while ((NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) || + NT_STATUS_IS_OK(status)) && + r.out.returned_size != 0); + + return ret; +} + +static bool test_QueryDomainInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_QueryDomainInfo r; + struct samr_SetDomainInfo s; + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13}; + uint16_t set_ok[] = {1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0}; + int i; + bool ret = true; + const char *domain_comment = talloc_asprintf(mem_ctx, + "Tortured by Samba4 RPC-SAMR: %s", + timestring(mem_ctx, time(NULL))); + + s.in.domain_handle = handle; + s.in.level = 4; + s.in.info = talloc(mem_ctx, union samr_DomainInfo); + + s.in.info->oem.oem_information.string = domain_comment; + status = dcerpc_samr_SetDomainInfo(p, mem_ctx, &s); + if (!NT_STATUS_IS_OK(status)) { + printf("SetDomainInfo level %u (set comment) failed - %s\n", + r.in.level, nt_errstr(status)); + return false; + } + + for (i=0;i<ARRAY_SIZE(levels);i++) { + printf("Testing QueryDomainInfo level %u\n", levels[i]); + + r.in.domain_handle = handle; + r.in.level = levels[i]; + + status = dcerpc_samr_QueryDomainInfo(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryDomainInfo level %u failed - %s\n", + r.in.level, nt_errstr(status)); + ret = false; + continue; + } + + switch (levels[i]) { + case 2: + if (strcmp(r.out.info->general.oem_information.string, domain_comment) != 0) { + printf("QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n", + levels[i], r.out.info->general.oem_information.string, domain_comment); + ret = false; + } + if (!r.out.info->general.primary.string) { + printf("QueryDomainInfo level %u returned no PDC name\n", + levels[i]); + ret = false; + } else if (r.out.info->general.role == SAMR_ROLE_DOMAIN_PDC) { + if (dcerpc_server_name(p) && strcasecmp_m(dcerpc_server_name(p), r.out.info->general.primary.string) != 0) { + printf("QueryDomainInfo level %u returned different PDC name (%s) compared to server name (%s), despite claiming to be the PDC\n", + levels[i], r.out.info->general.primary.string, dcerpc_server_name(p)); + } + } + break; + case 4: + if (strcmp(r.out.info->oem.oem_information.string, domain_comment) != 0) { + printf("QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n", + levels[i], r.out.info->oem.oem_information.string, domain_comment); + ret = false; + } + break; + case 6: + if (!r.out.info->info6.primary.string) { + printf("QueryDomainInfo level %u returned no PDC name\n", + levels[i]); + ret = false; + } + break; + case 11: + if (strcmp(r.out.info->general2.general.oem_information.string, domain_comment) != 0) { + printf("QueryDomainInfo level %u returned different comment (%s, expected %s)\n", + levels[i], r.out.info->general2.general.oem_information.string, domain_comment); + ret = false; + } + break; + } + + printf("Testing SetDomainInfo level %u\n", levels[i]); + + s.in.domain_handle = handle; + s.in.level = levels[i]; + s.in.info = r.out.info; + + status = dcerpc_samr_SetDomainInfo(p, mem_ctx, &s); + if (set_ok[i]) { + if (!NT_STATUS_IS_OK(status)) { + printf("SetDomainInfo level %u failed - %s\n", + r.in.level, nt_errstr(status)); + ret = false; + continue; + } + } else { + if (!NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, status)) { + printf("SetDomainInfo level %u gave %s - should have been NT_STATUS_INVALID_INFO_CLASS\n", + r.in.level, nt_errstr(status)); + ret = false; + continue; + } + } + + status = dcerpc_samr_QueryDomainInfo(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryDomainInfo level %u failed - %s\n", + r.in.level, nt_errstr(status)); + ret = false; + continue; + } + } + + return ret; +} + + +static bool test_QueryDomainInfo2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_QueryDomainInfo2 r; + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13}; + int i; + bool ret = true; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + printf("Testing QueryDomainInfo2 level %u\n", levels[i]); + + r.in.domain_handle = handle; + r.in.level = levels[i]; + + status = dcerpc_samr_QueryDomainInfo2(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryDomainInfo2 level %u failed - %s\n", + r.in.level, nt_errstr(status)); + ret = false; + continue; + } + } + + return true; +} + +/* Test whether querydispinfo level 5 and enumdomgroups return the same + set of group names. */ +static bool test_GroupList(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + struct samr_EnumDomainGroups q1; + struct samr_QueryDisplayInfo q2; + NTSTATUS status; + uint32_t resume_handle=0; + int i; + bool ret = true; + + int num_names = 0; + const char **names = NULL; + + printf("Testing coherency of querydispinfo vs enumdomgroups\n"); + + q1.in.domain_handle = handle; + q1.in.resume_handle = &resume_handle; + q1.in.max_size = 5; + q1.out.resume_handle = &resume_handle; + + status = STATUS_MORE_ENTRIES; + while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + status = dcerpc_samr_EnumDomainGroups(p, mem_ctx, &q1); + + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) + break; + + for (i=0; i<q1.out.num_entries; i++) { + add_string_to_array(mem_ctx, + q1.out.sam->entries[i].name.string, + &names, &num_names); + } + } + + if (!NT_STATUS_IS_OK(status)) { + printf("EnumDomainGroups failed - %s\n", nt_errstr(status)); + return false; + } + + if (!q1.out.sam) { + printf("EnumDomainGroups failed to return q1.out.sam\n"); + return false; + } + + q2.in.domain_handle = handle; + q2.in.level = 5; + q2.in.start_idx = 0; + q2.in.max_entries = 5; + q2.in.buf_size = (uint32_t)-1; + + status = STATUS_MORE_ENTRIES; + while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + status = dcerpc_samr_QueryDisplayInfo(p, mem_ctx, &q2); + + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) + break; + + for (i=0; i<q2.out.info.info5.count; i++) { + int j; + const char *name = q2.out.info.info5.entries[i].account_name.string; + bool found = false; + for (j=0; j<num_names; j++) { + if (names[j] == NULL) + continue; + if (strequal(names[j], name)) { + names[j] = NULL; + found = true; + break; + } + } + + if (!found) { + printf("QueryDisplayInfo gave name [%s] that EnumDomainGroups did not\n", + name); + ret = false; + } + } + q2.in.start_idx += q2.out.info.info5.count; + } + + if (!NT_STATUS_IS_OK(status)) { + printf("QueryDisplayInfo level 5 failed - %s\n", + nt_errstr(status)); + ret = false; + } + + for (i=0; i<num_names; i++) { + if (names[i] != NULL) { + printf("EnumDomainGroups gave name [%s] that QueryDisplayInfo did not\n", + names[i]); + ret = false; + } + } + + return ret; +} + +static bool test_DeleteDomainGroup(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *group_handle) +{ + struct samr_DeleteDomainGroup d; + NTSTATUS status; + bool ret = true; + + printf("Testing DeleteDomainGroup\n"); + + d.in.group_handle = group_handle; + d.out.group_handle = group_handle; + + status = dcerpc_samr_DeleteDomainGroup(p, mem_ctx, &d); + if (!NT_STATUS_IS_OK(status)) { + printf("DeleteDomainGroup failed - %s\n", nt_errstr(status)); + ret = false; + } + + return ret; +} + +static bool test_TestPrivateFunctionsDomain(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle) +{ + struct samr_TestPrivateFunctionsDomain r; + NTSTATUS status; + bool ret = true; + + printf("Testing TestPrivateFunctionsDomain\n"); + + r.in.domain_handle = domain_handle; + + status = dcerpc_samr_TestPrivateFunctionsDomain(p, mem_ctx, &r); + if (!NT_STATUS_EQUAL(NT_STATUS_NOT_IMPLEMENTED, status)) { + printf("TestPrivateFunctionsDomain failed - %s\n", nt_errstr(status)); + ret = false; + } + + return ret; +} + +static bool test_RidToSid(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct dom_sid *domain_sid, + struct policy_handle *domain_handle) +{ + struct samr_RidToSid r; + NTSTATUS status; + bool ret = true; + struct dom_sid *calc_sid; + int rids[] = { 0, 42, 512, 10200 }; + int i; + + for (i=0;i<ARRAY_SIZE(rids);i++) { + + printf("Testing RidToSid\n"); + + calc_sid = dom_sid_dup(mem_ctx, domain_sid); + r.in.domain_handle = domain_handle; + r.in.rid = rids[i]; + + status = dcerpc_samr_RidToSid(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("RidToSid for %d failed - %s\n", rids[i], nt_errstr(status)); + ret = false; + } else { + calc_sid = dom_sid_add_rid(calc_sid, calc_sid, rids[i]); + + if (!dom_sid_equal(calc_sid, r.out.sid)) { + printf("RidToSid for %d failed - got %s, expected %s\n", rids[i], + dom_sid_string(mem_ctx, r.out.sid), + dom_sid_string(mem_ctx, calc_sid)); + ret = false; + } + } + } + + return ret; +} + +static bool test_GetBootKeyInformation(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle) +{ + struct samr_GetBootKeyInformation r; + NTSTATUS status; + bool ret = true; + + printf("Testing GetBootKeyInformation\n"); + + r.in.domain_handle = domain_handle; + + status = dcerpc_samr_GetBootKeyInformation(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + /* w2k3 seems to fail this sometimes and pass it sometimes */ + printf("GetBootKeyInformation (ignored) - %s\n", nt_errstr(status)); + } + + return ret; +} + +static bool test_AddGroupMember(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *domain_handle, + struct policy_handle *group_handle) +{ + NTSTATUS status; + struct samr_AddGroupMember r; + struct samr_DeleteGroupMember d; + struct samr_QueryGroupMember q; + struct samr_SetMemberAttributesOfGroup s; + bool ret = true; + uint32_t rid; + + status = test_LookupName(p, tctx, domain_handle, TEST_ACCOUNT_NAME, &rid); + if (!NT_STATUS_IS_OK(status)) { + printf("test_AddGroupMember looking up name " TEST_ACCOUNT_NAME " failed - %s\n", nt_errstr(status)); + return false; + } + + r.in.group_handle = group_handle; + r.in.rid = rid; + r.in.flags = 0; /* ??? */ + + printf("Testing AddGroupMember and DeleteGroupMember\n"); + + d.in.group_handle = group_handle; + d.in.rid = rid; + + status = dcerpc_samr_DeleteGroupMember(p, tctx, &d); + if (!NT_STATUS_EQUAL(NT_STATUS_MEMBER_NOT_IN_GROUP, status)) { + printf("DeleteGroupMember gave %s - should be NT_STATUS_MEMBER_NOT_IN_GROUP\n", + nt_errstr(status)); + return false; + } + + status = dcerpc_samr_AddGroupMember(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("AddGroupMember failed - %s\n", nt_errstr(status)); + return false; + } + + status = dcerpc_samr_AddGroupMember(p, tctx, &r); + if (!NT_STATUS_EQUAL(NT_STATUS_MEMBER_IN_GROUP, status)) { + printf("AddGroupMember gave %s - should be NT_STATUS_MEMBER_IN_GROUP\n", + nt_errstr(status)); + return false; + } + + if (torture_setting_bool(tctx, "samba4", false)) { + printf("skipping SetMemberAttributesOfGroup test against Samba4\n"); + } else { + /* this one is quite strange. I am using random inputs in the + hope of triggering an error that might give us a clue */ + + s.in.group_handle = group_handle; + s.in.unknown1 = random(); + s.in.unknown2 = random(); + + status = dcerpc_samr_SetMemberAttributesOfGroup(p, tctx, &s); + if (!NT_STATUS_IS_OK(status)) { + printf("SetMemberAttributesOfGroup failed - %s\n", nt_errstr(status)); + return false; + } + } + + q.in.group_handle = group_handle; + + status = dcerpc_samr_QueryGroupMember(p, tctx, &q); + if (!NT_STATUS_IS_OK(status)) { + printf("QueryGroupMember failed - %s\n", nt_errstr(status)); + return false; + } + + status = dcerpc_samr_DeleteGroupMember(p, tctx, &d); + if (!NT_STATUS_IS_OK(status)) { + printf("DeleteGroupMember failed - %s\n", nt_errstr(status)); + return false; + } + + status = dcerpc_samr_AddGroupMember(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("AddGroupMember failed - %s\n", nt_errstr(status)); + return false; + } + + return ret; +} + + +static bool test_CreateDomainGroup(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + struct policy_handle *group_handle, + struct dom_sid *domain_sid) +{ + NTSTATUS status; + struct samr_CreateDomainGroup r; + uint32_t rid; + struct lsa_String name; + bool ret = true; + + init_lsa_String(&name, TEST_GROUPNAME); + + r.in.domain_handle = domain_handle; + r.in.name = &name; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.group_handle = group_handle; + r.out.rid = &rid; + + printf("Testing CreateDomainGroup(%s)\n", r.in.name->string); + + status = dcerpc_samr_CreateDomainGroup(p, mem_ctx, &r); + + if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(mem_ctx, SID_BUILTIN))) { + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + printf("Server correctly refused create of '%s'\n", r.in.name->string); + return true; + } else { + printf("Server should have refused create of '%s', got %s instead\n", r.in.name->string, + nt_errstr(status)); + return false; + } + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_GROUP_EXISTS)) { + if (!test_DeleteGroup_byname(p, mem_ctx, domain_handle, r.in.name->string)) { + printf("CreateDomainGroup failed: Could not delete domain group %s - %s\n", r.in.name->string, + nt_errstr(status)); + return false; + } + status = dcerpc_samr_CreateDomainGroup(p, mem_ctx, &r); + } + if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) { + if (!test_DeleteUser_byname(p, mem_ctx, domain_handle, r.in.name->string)) { + + printf("CreateDomainGroup failed: Could not delete user %s - %s\n", r.in.name->string, + nt_errstr(status)); + return false; + } + status = dcerpc_samr_CreateDomainGroup(p, mem_ctx, &r); + } + if (!NT_STATUS_IS_OK(status)) { + printf("CreateDomainGroup failed - %s\n", nt_errstr(status)); + return false; + } + + if (!test_AddGroupMember(p, mem_ctx, domain_handle, group_handle)) { + printf("CreateDomainGroup failed - %s\n", nt_errstr(status)); + ret = false; + } + + if (!test_SetGroupInfo(p, mem_ctx, group_handle)) { + ret = false; + } + + return ret; +} + + +/* + its not totally clear what this does. It seems to accept any sid you like. +*/ +static bool test_RemoveMemberFromForeignDomain(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle) +{ + NTSTATUS status; + struct samr_RemoveMemberFromForeignDomain r; + + r.in.domain_handle = domain_handle; + r.in.sid = dom_sid_parse_talloc(mem_ctx, "S-1-5-32-12-34-56-78"); + + status = dcerpc_samr_RemoveMemberFromForeignDomain(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("RemoveMemberFromForeignDomain failed - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + + + +static bool test_Connect(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle); + +static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, struct dom_sid *sid, + enum torture_samr_choice which_ops) +{ + NTSTATUS status; + struct samr_OpenDomain r; + struct policy_handle domain_handle; + struct policy_handle alias_handle; + struct policy_handle user_handle; + struct policy_handle group_handle; + bool ret = true; + + ZERO_STRUCT(alias_handle); + ZERO_STRUCT(user_handle); + ZERO_STRUCT(group_handle); + ZERO_STRUCT(domain_handle); + + printf("Testing OpenDomain of %s\n", dom_sid_string(tctx, sid)); + + r.in.connect_handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.sid = sid; + r.out.domain_handle = &domain_handle; + + status = dcerpc_samr_OpenDomain(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenDomain failed - %s\n", nt_errstr(status)); + return false; + } + + /* run the domain tests with the main handle closed - this tests + the servers reference counting */ + ret &= test_samr_handle_Close(p, tctx, handle); + + switch (which_ops) { + case TORTURE_SAMR_USER_ATTRIBUTES: + case TORTURE_SAMR_PASSWORDS: + ret &= test_CreateUser2(p, tctx, &domain_handle, sid, which_ops); + ret &= test_CreateUser(p, tctx, &domain_handle, &user_handle, sid, which_ops); + /* This test needs 'complex' users to validate */ + ret &= test_QueryDisplayInfo(p, tctx, &domain_handle); + if (!ret) { + printf("Testing PASSWORDS or ATTRIBUTES on domain %s failed!\n", dom_sid_string(tctx, sid)); + } + break; + case TORTURE_SAMR_OTHER: + ret &= test_CreateUser(p, tctx, &domain_handle, &user_handle, sid, which_ops); + if (!ret) { + printf("Failed to CreateUser in SAMR-OTHER on domain %s!\n", dom_sid_string(tctx, sid)); + } + ret &= test_QuerySecurity(p, tctx, &domain_handle); + ret &= test_RemoveMemberFromForeignDomain(p, tctx, &domain_handle); + ret &= test_CreateAlias(p, tctx, &domain_handle, &alias_handle, sid); + ret &= test_CreateDomainGroup(p, tctx, &domain_handle, &group_handle, sid); + ret &= test_QueryDomainInfo(p, tctx, &domain_handle); + ret &= test_QueryDomainInfo2(p, tctx, &domain_handle); + ret &= test_EnumDomainUsers(p, tctx, &domain_handle); + ret &= test_EnumDomainUsers_async(p, tctx, &domain_handle); + ret &= test_EnumDomainGroups(p, tctx, &domain_handle); + ret &= test_EnumDomainAliases(p, tctx, &domain_handle); + ret &= test_QueryDisplayInfo2(p, tctx, &domain_handle); + ret &= test_QueryDisplayInfo3(p, tctx, &domain_handle); + ret &= test_QueryDisplayInfo_continue(p, tctx, &domain_handle); + + if (torture_setting_bool(tctx, "samba4", false)) { + printf("skipping GetDisplayEnumerationIndex test against Samba4\n"); + } else { + ret &= test_GetDisplayEnumerationIndex(p, tctx, &domain_handle); + ret &= test_GetDisplayEnumerationIndex2(p, tctx, &domain_handle); + } + ret &= test_GroupList(p, tctx, &domain_handle); + ret &= test_TestPrivateFunctionsDomain(p, tctx, &domain_handle); + ret &= test_RidToSid(p, tctx, sid, &domain_handle); + ret &= test_GetBootKeyInformation(p, tctx, &domain_handle); + if (!ret) { + printf("Testing SAMR-OTHER on domain %s failed!\n", dom_sid_string(tctx, sid)); + } + break; + } + + if (!policy_handle_empty(&user_handle) && + !test_DeleteUser(p, tctx, &user_handle)) { + ret = false; + } + + if (!policy_handle_empty(&alias_handle) && + !test_DeleteAlias(p, tctx, &alias_handle)) { + ret = false; + } + + if (!policy_handle_empty(&group_handle) && + !test_DeleteDomainGroup(p, tctx, &group_handle)) { + ret = false; + } + + ret &= test_samr_handle_Close(p, tctx, &domain_handle); + + /* reconnect the main handle */ + ret &= test_Connect(p, tctx, handle); + + if (!ret) { + printf("Testing domain %s failed!\n", dom_sid_string(tctx, sid)); + } + + return ret; +} + +static bool test_LookupDomain(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, const char *domain, + enum torture_samr_choice which_ops) +{ + NTSTATUS status; + struct samr_LookupDomain r; + struct lsa_String n1; + struct lsa_String n2; + bool ret = true; + + printf("Testing LookupDomain(%s)\n", domain); + + /* check for correct error codes */ + r.in.connect_handle = handle; + r.in.domain_name = &n2; + n2.string = NULL; + + status = dcerpc_samr_LookupDomain(p, tctx, &r); + if (!NT_STATUS_EQUAL(NT_STATUS_INVALID_PARAMETER, status)) { + printf("failed: LookupDomain expected NT_STATUS_INVALID_PARAMETER - %s\n", nt_errstr(status)); + ret = false; + } + + init_lsa_String(&n2, "xxNODOMAINxx"); + + status = dcerpc_samr_LookupDomain(p, tctx, &r); + if (!NT_STATUS_EQUAL(NT_STATUS_NO_SUCH_DOMAIN, status)) { + printf("failed: LookupDomain expected NT_STATUS_NO_SUCH_DOMAIN - %s\n", nt_errstr(status)); + ret = false; + } + + r.in.connect_handle = handle; + + init_lsa_String(&n1, domain); + r.in.domain_name = &n1; + + status = dcerpc_samr_LookupDomain(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupDomain failed - %s\n", nt_errstr(status)); + ret = false; + } + + if (!test_GetDomPwInfo(p, tctx, &n1)) { + ret = false; + } + + if (!test_OpenDomain(p, tctx, handle, r.out.sid, which_ops)) { + ret = false; + } + + return ret; +} + + +static bool test_EnumDomains(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, enum torture_samr_choice which_ops) +{ + NTSTATUS status; + struct samr_EnumDomains r; + uint32_t resume_handle = 0; + int i; + bool ret = true; + + r.in.connect_handle = handle; + r.in.resume_handle = &resume_handle; + r.in.buf_size = (uint32_t)-1; + r.out.resume_handle = &resume_handle; + + status = dcerpc_samr_EnumDomains(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("EnumDomains failed - %s\n", nt_errstr(status)); + return false; + } + + if (!r.out.sam) { + return false; + } + + for (i=0;i<r.out.sam->count;i++) { + if (!test_LookupDomain(p, tctx, handle, + r.out.sam->entries[i].name.string, which_ops)) { + ret = false; + } + } + + status = dcerpc_samr_EnumDomains(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("EnumDomains failed - %s\n", nt_errstr(status)); + return false; + } + + return ret; +} + + +static bool test_Connect(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_Connect r; + struct samr_Connect2 r2; + struct samr_Connect3 r3; + struct samr_Connect4 r4; + struct samr_Connect5 r5; + union samr_ConnectInfo info; + struct policy_handle h; + bool ret = true, got_handle = false; + + printf("testing samr_Connect\n"); + + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.connect_handle = &h; + + status = dcerpc_samr_Connect(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect failed - %s\n", nt_errstr(status)); + ret = false; + } else { + got_handle = true; + *handle = h; + } + + printf("testing samr_Connect2\n"); + + r2.in.system_name = NULL; + r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r2.out.connect_handle = &h; + + status = dcerpc_samr_Connect2(p, mem_ctx, &r2); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect2 failed - %s\n", nt_errstr(status)); + ret = false; + } else { + if (got_handle) { + test_samr_handle_Close(p, mem_ctx, handle); + } + got_handle = true; + *handle = h; + } + + printf("testing samr_Connect3\n"); + + r3.in.system_name = NULL; + r3.in.unknown = 0; + r3.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r3.out.connect_handle = &h; + + status = dcerpc_samr_Connect3(p, mem_ctx, &r3); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect3 failed - %s\n", nt_errstr(status)); + ret = false; + } else { + if (got_handle) { + test_samr_handle_Close(p, mem_ctx, handle); + } + got_handle = true; + *handle = h; + } + + printf("testing samr_Connect4\n"); + + r4.in.system_name = ""; + r4.in.unknown = 0; + r4.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r4.out.connect_handle = &h; + + status = dcerpc_samr_Connect4(p, mem_ctx, &r4); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect4 failed - %s\n", nt_errstr(status)); + ret = false; + } else { + if (got_handle) { + test_samr_handle_Close(p, mem_ctx, handle); + } + got_handle = true; + *handle = h; + } + + printf("testing samr_Connect5\n"); + + info.info1.unknown1 = 0; + info.info1.unknown2 = 0; + + r5.in.system_name = ""; + r5.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r5.in.level = 1; + r5.in.info = &info; + r5.out.info = &info; + r5.out.connect_handle = &h; + + status = dcerpc_samr_Connect5(p, mem_ctx, &r5); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + ret = false; + } else { + if (got_handle) { + test_samr_handle_Close(p, mem_ctx, handle); + } + got_handle = true; + *handle = h; + } + + return ret; +} + + +bool torture_rpc_samr(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct policy_handle handle; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ret &= test_Connect(p, torture, &handle); + + ret &= test_QuerySecurity(p, torture, &handle); + + ret &= test_EnumDomains(p, torture, &handle, TORTURE_SAMR_OTHER); + + ret &= test_SetDsrmPassword(p, torture, &handle); + + ret &= test_Shutdown(p, torture, &handle); + + ret &= test_samr_handle_Close(p, torture, &handle); + + return ret; +} + + +bool torture_rpc_samr_users(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct policy_handle handle; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ret &= test_Connect(p, torture, &handle); + + ret &= test_QuerySecurity(p, torture, &handle); + + ret &= test_EnumDomains(p, torture, &handle, TORTURE_SAMR_USER_ATTRIBUTES); + + ret &= test_SetDsrmPassword(p, torture, &handle); + + ret &= test_Shutdown(p, torture, &handle); + + ret &= test_samr_handle_Close(p, torture, &handle); + + return ret; +} + + +bool torture_rpc_samr_passwords(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct policy_handle handle; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ret &= test_Connect(p, torture, &handle); + + ret &= test_EnumDomains(p, torture, &handle, TORTURE_SAMR_PASSWORDS); + + ret &= test_samr_handle_Close(p, torture, &handle); + + return ret; +} + diff --git a/source4/torture/rpc/samr_accessmask.c b/source4/torture/rpc/samr_accessmask.c new file mode 100644 index 0000000000..f496f6300c --- /dev/null +++ b/source4/torture/rpc/samr_accessmask.c @@ -0,0 +1,658 @@ +/* + Unix SMB/CIFS implementation. + test suite for accessmasks on the SAMR pipe + + Copyright (C) Ronnie Sahlberg 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "torture/rpc/rpc.h" +#include "param/param.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_security.h" + + +/* test user created to test the ACLs associated to SAMR objects */ +#define TEST_USER_NAME "samr_testuser" + + +static NTSTATUS torture_samr_Close(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *h) +{ + NTSTATUS status; + struct samr_Close cl; + + cl.in.handle = h; + cl.out.handle = h; + status = dcerpc_samr_Close(p, tctx, &cl); + + return status; +} + +static NTSTATUS torture_samr_Connect5(struct torture_context *tctx, + struct dcerpc_pipe *p, + uint32_t mask, struct policy_handle *h) +{ + NTSTATUS status; + struct samr_Connect5 r5; + union samr_ConnectInfo info; + + info.info1.unknown1 = 0; + info.info1.unknown2 = 0; + r5.in.system_name = ""; + r5.in.level = 1; + r5.in.info = &info; + r5.out.info = &info; + r5.out.connect_handle = h; + r5.in.access_mask = mask; + + status = dcerpc_samr_Connect5(p, tctx, &r5); + + return status; +} + +/* check which bits in accessmask allows us to connect to the server */ +static bool test_samr_accessmask_Connect5(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct policy_handle h; + int i; + uint32_t mask; + + printf("testing which bits in accessmask allows us to connect\n"); + mask = 1; + for (i=0;i<33;i++) { + printf("testing Connect5 with access mask 0x%08x", mask); + status = torture_samr_Connect5(tctx, p, mask, &h); + mask <<= 1; + + switch (i) { + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 20: + case 21: + case 22: + case 23: + case 26: + case 27: + printf(" expecting to fail"); + /* of only one of these bits are set we expect to + fail by default + */ + if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + break; + default: + /* these bits set are expected to succeed by default */ + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + status = torture_samr_Close(tctx, p, &h); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + } + printf(" OK\n"); + } + + return true; +} + +/* check which bits in accessmask allows us to EnumDomains() + by default we must specify at least one of : + SAMR/EnumDomains + Maximum + GenericAll + GenericRead + in the access mask to Connect5() in order to be allowed to perform + EnumDomains() on the policy handle returned from Connect5() +*/ +static bool test_samr_accessmask_EnumDomains(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct samr_EnumDomains ed; + struct policy_handle ch; + int i; + uint32_t mask; + uint32_t resume_handle = 0; + + printf("testing which bits in Connect5 accessmask allows us to EnumDomains\n"); + mask = 1; + for (i=0;i<33;i++) { + printf("testing Connect5/EnumDomains with access mask 0x%08x", mask); + status = torture_samr_Connect5(tctx, p, mask, &ch); + mask <<= 1; + + switch (i) { + case 4: /* SAMR/EnumDomains */ + case 25: /* Maximum */ + case 28: /* GenericAll */ + case 31: /* GenericRead */ + /* these bits set are expected to succeed by default */ + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + ed.in.connect_handle = &ch; + ed.in.resume_handle = &resume_handle; + ed.in.buf_size = (uint32_t)-1; + ed.out.resume_handle = &resume_handle; + + status = dcerpc_samr_EnumDomains(p, tctx, &ed); + if (!NT_STATUS_IS_OK(status)) { + printf("EnumDomains failed - %s\n", nt_errstr(status)); + return false; + } + + status = torture_samr_Close(tctx, p, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + default: + printf(" expecting to fail"); + + if (!NT_STATUS_IS_OK(status)) { + printf(" OK\n"); + continue; + } + + ed.in.connect_handle = &ch; + ed.in.resume_handle = &resume_handle; + ed.in.buf_size = (uint32_t)-1; + ed.out.resume_handle = &resume_handle; + + status = dcerpc_samr_EnumDomains(p, tctx, &ed); + if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) { + printf("EnumDomains failed - %s\n", nt_errstr(status)); + return false; + } + + status = torture_samr_Close(tctx, p, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + } + printf(" OK\n"); + } + + return true; +} + + +/* + * test how ACLs affect how/if a user can connect to the SAMR service + * + * samr_SetSecurity() returns SUCCESS when changing the ACL for + * a policy handle got from Connect5() but the ACL is not changed on + * the server + */ +static bool test_samr_connect_user_acl(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *test_credentials, + const struct dom_sid *test_sid) + +{ + NTSTATUS status; + struct policy_handle ch; + struct policy_handle uch; + struct samr_QuerySecurity qs; + struct samr_SetSecurity ss; + struct security_ace ace; + struct security_descriptor *sd; + struct sec_desc_buf sdb; + bool ret = true; + int sd_size; + struct dcerpc_pipe *test_p; + const char *binding = torture_setting_string(tctx, "binding", NULL); + + printf("testing ACLs to allow/prevent users to connect to SAMR"); + + /* connect to SAMR */ + status = torture_samr_Connect5(tctx, p, SEC_FLAG_MAXIMUM_ALLOWED, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + + /* get the current ACL for the SAMR policy handle */ + qs.in.handle = &ch; + qs.in.sec_info = SECINFO_DACL; + status = dcerpc_samr_QuerySecurity(p, tctx, &qs); + if (!NT_STATUS_IS_OK(status)) { + printf("QuerySecurity failed - %s\n", nt_errstr(status)); + ret = false; + } + + /* how big is the security descriptor? */ + sd_size = qs.out.sdbuf->sd_size; + + + /* add an ACE to the security descriptor to deny the user the + * 'connect to server' right + */ + sd = qs.out.sdbuf->sd; + ace.type = SEC_ACE_TYPE_ACCESS_DENIED; + ace.flags = 0; + ace.access_mask = SAMR_ACCESS_CONNECT_TO_SERVER; + ace.trustee = *test_sid; + status = security_descriptor_dacl_add(sd, &ace); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to add ACE to security descriptor\n"); + ret = false; + } + ss.in.handle = &ch; + ss.in.sec_info = SECINFO_DACL; + ss.in.sdbuf = &sdb; + sdb.sd = sd; + status = dcerpc_samr_SetSecurity(p, tctx, &ss); + if (!NT_STATUS_IS_OK(status)) { + printf("SetSecurity failed - %s\n", nt_errstr(status)); + ret = false; + } + + + /* Try to connect as the test user */ + status = dcerpc_pipe_connect(tctx, + &test_p, binding, &ndr_table_samr, + test_credentials, NULL, tctx->lp_ctx); + /* connect to SAMR as the user */ + status = torture_samr_Connect5(tctx, test_p, SEC_FLAG_MAXIMUM_ALLOWED, &uch); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + /* disconnec the user */ + talloc_free(test_p); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + + /* read the sequrity descriptor back. it should not have changed + * eventhough samr_SetSecurity returned SUCCESS + */ + status = dcerpc_samr_QuerySecurity(p, tctx, &qs); + if (!NT_STATUS_IS_OK(status)) { + printf("QuerySecurity failed - %s\n", nt_errstr(status)); + ret = false; + } + if (sd_size != qs.out.sdbuf->sd_size) { + printf("security descriptor changed\n"); + ret = false; + } + + + status = torture_samr_Close(tctx, p, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + ret = false; + } + + if (ret == true) { + printf(" OK\n"); + } + return ret; +} + +/* + * test if the ACLs are enforced for users. + * a normal testuser only gets the rights provided in hte ACL for + * Everyone which does not include the SAMR_ACCESS_SHUTDOWN_SERVER + * right. If the ACLs are checked when a user connects + * a testuser that requests the accessmask with only this bit set + * the connect should fail. + */ +static bool test_samr_connect_user_acl_enforced(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *test_credentials, + const struct dom_sid *test_sid) + +{ + NTSTATUS status; + struct policy_handle uch; + bool ret = true; + struct dcerpc_pipe *test_p; + const char *binding = torture_setting_string(tctx, "binding", NULL); + + printf("testing if ACLs are enforced for non domain admin users when connecting to SAMR"); + + + status = dcerpc_pipe_connect(tctx, + &test_p, binding, &ndr_table_samr, + test_credentials, NULL, tctx->lp_ctx); + + /* connect to SAMR as the user */ + status = torture_samr_Connect5(tctx, test_p, SAMR_ACCESS_SHUTDOWN_SERVER, &uch); + if (NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + printf(" OK\n"); + + /* disconnec the user */ + talloc_free(test_p); + + return ret; +} + +/* check which bits in accessmask allows us to LookupDomain() + by default we must specify at least one of : + in the access mask to Connect5() in order to be allowed to perform + case 5: samr/opendomain + case 25: Maximum + case 28: GenericAll + case 29: GenericExecute + LookupDomain() on the policy handle returned from Connect5() +*/ +static bool test_samr_accessmask_LookupDomain(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct samr_LookupDomain ld; + struct policy_handle ch; + struct lsa_String dn; + int i; + uint32_t mask; + + printf("testing which bits in Connect5 accessmask allows us to LookupDomain\n"); + mask = 1; + for (i=0;i<33;i++) { + printf("testing Connect5/LookupDomain with access mask 0x%08x", mask); + status = torture_samr_Connect5(tctx, p, mask, &ch); + mask <<= 1; + + switch (i) { + case 5: + case 25: /* Maximum */ + case 28: /* GenericAll */ + case 29: /* GenericExecute */ + /* these bits set are expected to succeed by default */ + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + ld.in.connect_handle = &ch; + ld.in.domain_name = &dn; + dn.string = lp_workgroup(tctx->lp_ctx); + + status = dcerpc_samr_LookupDomain(p, tctx, &ld); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupDomain failed - %s\n", nt_errstr(status)); + return false; + } + + status = torture_samr_Close(tctx, p, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + default: + printf(" expecting to fail"); + + if (!NT_STATUS_IS_OK(status)) { + printf(" OK\n"); + continue; + } + + ld.in.connect_handle = &ch; + ld.in.domain_name = &dn; + dn.string = lp_workgroup(tctx->lp_ctx); + + status = dcerpc_samr_LookupDomain(p, tctx, &ld); + if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) { + printf("LookupDomain failed - %s\n", nt_errstr(status)); + return false; + } + + status = torture_samr_Close(tctx, p, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + } + printf(" OK\n"); + } + + return true; +} + +/* check which bits in accessmask allows us to OpenDomain() + by default we must specify at least one of : + samr/opendomain + Maximum + GenericAll + GenericExecute + in the access mask to Connect5() in order to be allowed to perform + OpenDomain() on the policy handle returned from Connect5() +*/ +static bool test_samr_accessmask_OpenDomain(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct samr_LookupDomain ld; + struct samr_OpenDomain od; + struct policy_handle ch; + struct policy_handle dh; + struct lsa_String dn; + int i; + uint32_t mask; + + + /* first we must grab the sid of the domain */ + status = torture_samr_Connect5(tctx, p, SEC_FLAG_MAXIMUM_ALLOWED, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + ld.in.connect_handle = &ch; + ld.in.domain_name = &dn; + dn.string = lp_workgroup(tctx->lp_ctx); + status = dcerpc_samr_LookupDomain(p, tctx, &ld); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupDomain failed - %s\n", nt_errstr(status)); + return false; + } + + + + printf("testing which bits in Connect5 accessmask allows us to OpenDomain\n"); + mask = 1; + for (i=0;i<33;i++) { + printf("testing Connect5/OpenDomain with access mask 0x%08x", mask); + status = torture_samr_Connect5(tctx, p, mask, &ch); + mask <<= 1; + + switch (i) { + case 5: + case 25: /* Maximum */ + case 28: /* GenericAll */ + case 29: /* GenericExecute */ + /* these bits set are expected to succeed by default */ + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + od.in.connect_handle = &ch; + od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + od.in.sid = ld.out.sid; + od.out.domain_handle = &dh; + + status = dcerpc_samr_OpenDomain(p, tctx, &od); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenDomain failed - %s\n", nt_errstr(status)); + return false; + } + + status = torture_samr_Close(tctx, p, &dh); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + + status = torture_samr_Close(tctx, p, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + default: + printf(" expecting to fail"); + + if (!NT_STATUS_IS_OK(status)) { + printf(" OK\n"); + continue; + } + + status = torture_samr_Close(tctx, p, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + } + printf(" OK\n"); + } + + return true; +} + +static bool test_samr_connect(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + void *testuser; + const char *testuser_passwd; + struct cli_credentials *test_credentials; + bool ret = true; + const struct dom_sid *test_sid; + + /* create a test user */ + testuser = torture_create_testuser(tctx, TEST_USER_NAME, lp_workgroup(tctx->lp_ctx), + ACB_NORMAL, &testuser_passwd); + if (!testuser) { + printf("Failed to create test user\n"); + return false; + } + test_credentials = cli_credentials_init(tctx); + cli_credentials_set_workstation(test_credentials, "localhost", CRED_SPECIFIED); + cli_credentials_set_domain(test_credentials, lp_workgroup(tctx->lp_ctx), + CRED_SPECIFIED); + cli_credentials_set_username(test_credentials, TEST_USER_NAME, CRED_SPECIFIED); + cli_credentials_set_password(test_credentials, testuser_passwd, CRED_SPECIFIED); + test_sid = torture_join_user_sid(testuser); + + + /* test which bits in the accessmask to Connect5 + will allow us to connect to the server + */ + if (!test_samr_accessmask_Connect5(tctx, p)) { + ret = false; + } + + + /* test which bits in the accessmask to Connect5 will allow + * us to call EnumDomains() + */ + if (!test_samr_accessmask_EnumDomains(tctx, p)) { + ret = false; + } + + /* test which bits in the accessmask to Connect5 will allow + * us to call LookupDomain() + */ + if (!test_samr_accessmask_LookupDomain(tctx, p)) { + ret = false; + } + + + /* test which bits in the accessmask to Connect5 will allow + * us to call OpenDomain() + */ + if (!test_samr_accessmask_OpenDomain(tctx, p)) { + ret = false; + } + + + /* test if ACLs can be changed for the policy handle + * returned by Connect5 + */ + if (!test_samr_connect_user_acl(tctx, p, test_credentials, test_sid)) { + ret = false; + } + + /* test if the ACLs that are reported from the Connect5 + * policy handle is enforced. + * i.e. an ordinary user only has the same rights as Everybody + * ReadControl + * Samr/OpenDomain + * Samr/EnumDomains + * Samr/ConnectToServer + * is granted and should therefore not be able to connect when + * requesting SAMR_ACCESS_SHUTDOWN_SERVER + */ + if (!test_samr_connect_user_acl_enforced(tctx, p, test_credentials, test_sid)) { + ret = false; + } + + + + /* remove the test user */ + torture_leave_domain(tctx, testuser); + + return ret; +} + +struct torture_suite *torture_rpc_samr_accessmask(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "SAMR_ACCESSMASK"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "samr", + &ndr_table_samr); + + torture_rpc_tcase_add_test(tcase, "CONNECT", test_samr_connect); + + return suite; +} diff --git a/source4/torture/rpc/samsync.c b/source4/torture/rpc/samsync.c new file mode 100644 index 0000000000..1e76de1dd2 --- /dev/null +++ b/source4/torture/rpc/samsync.c @@ -0,0 +1,1639 @@ +/* + Unix SMB/CIFS implementation. + + test suite for netlogon rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004 + Copyright (C) Tim Potter 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "auth/auth.h" +#include "lib/util/dlinklist.h" +#include "lib/crypto/crypto.h" +#include "system/time.h" +#include "torture/rpc/rpc.h" +#include "auth/gensec/schannel_proto.h" +#include "auth/gensec/gensec.h" +#include "libcli/auth/libcli_auth.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_netlogon.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "param/param.h" + +#define TEST_MACHINE_NAME "samsynctest" +#define TEST_WKSTA_MACHINE_NAME "samsynctest2" +#define TEST_USER_NAME "samsynctestuser" + +/* + try a netlogon SamLogon +*/ +static NTSTATUS test_SamLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct creds_CredentialState *creds, + const char *domain, const char *account_name, + const char *workstation, + struct samr_Password *lm_hash, + struct samr_Password *nt_hash, + struct netr_SamInfo3 **info3) +{ + NTSTATUS status; + struct netr_LogonSamLogon r; + struct netr_Authenticator auth, auth2; + struct netr_NetworkInfo ninfo; + + ninfo.identity_info.domain_name.string = domain; + ninfo.identity_info.parameter_control = 0; + ninfo.identity_info.logon_id_low = 0; + ninfo.identity_info.logon_id_high = 0; + ninfo.identity_info.account_name.string = account_name; + ninfo.identity_info.workstation.string = workstation; + generate_random_buffer(ninfo.challenge, + sizeof(ninfo.challenge)); + if (nt_hash) { + ninfo.nt.length = 24; + ninfo.nt.data = talloc_array(mem_ctx, uint8_t, 24); + SMBOWFencrypt(nt_hash->hash, ninfo.challenge, ninfo.nt.data); + } else { + ninfo.nt.length = 0; + ninfo.nt.data = NULL; + } + + if (lm_hash) { + ninfo.lm.length = 24; + ninfo.lm.data = talloc_array(mem_ctx, uint8_t, 24); + SMBOWFencrypt(lm_hash->hash, ninfo.challenge, ninfo.lm.data); + } else { + ninfo.lm.length = 0; + ninfo.lm.data = NULL; + } + + r.in.server_name = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = workstation; + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = 2; + r.in.logon.network = &ninfo; + + ZERO_STRUCT(auth2); + creds_client_authenticator(creds, &auth); + + r.in.validation_level = 3; + + status = dcerpc_netr_LogonSamLogon(p, mem_ctx, &r); + + if (!creds_client_check(creds, &r.out.return_authenticator->cred)) { + printf("Credential chaining failed\n"); + } + + if (info3) { + *info3 = r.out.validation.sam3; + } + + return status; +} + +struct samsync_state { +/* we remember the sequence numbers so we can easily do a DatabaseDelta */ + uint64_t seq_num[3]; + const char *domain_name[2]; + struct samsync_secret *secrets; + struct samsync_trusted_domain *trusted_domains; + struct creds_CredentialState *creds; + struct creds_CredentialState *creds_netlogon_wksta; + struct policy_handle *connect_handle; + struct policy_handle *domain_handle[2]; + struct dom_sid *sid[2]; + struct dcerpc_pipe *p; + struct dcerpc_pipe *p_netlogon_wksta; + struct dcerpc_pipe *p_samr; + struct dcerpc_pipe *p_lsa; + struct policy_handle *lsa_handle; +}; + +struct samsync_secret { + struct samsync_secret *prev, *next; + DATA_BLOB secret; + const char *name; + NTTIME mtime; +}; + +struct samsync_trusted_domain { + struct samsync_trusted_domain *prev, *next; + struct dom_sid *sid; + const char *name; +}; + +static struct policy_handle *samsync_open_domain(TALLOC_CTX *mem_ctx, + struct samsync_state *samsync_state, + const char *domain, + struct dom_sid **sid) +{ + struct lsa_String name; + struct samr_OpenDomain o; + struct samr_LookupDomain l; + struct policy_handle *domain_handle = talloc(mem_ctx, struct policy_handle); + NTSTATUS nt_status; + + name.string = domain; + l.in.connect_handle = samsync_state->connect_handle; + l.in.domain_name = &name; + + nt_status = dcerpc_samr_LookupDomain(samsync_state->p_samr, mem_ctx, &l); + if (!NT_STATUS_IS_OK(nt_status)) { + printf("LookupDomain failed - %s\n", nt_errstr(nt_status)); + return NULL; + } + + o.in.connect_handle = samsync_state->connect_handle; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.in.sid = l.out.sid; + o.out.domain_handle = domain_handle; + + if (sid) { + *sid = l.out.sid; + } + + nt_status = dcerpc_samr_OpenDomain(samsync_state->p_samr, mem_ctx, &o); + if (!NT_STATUS_IS_OK(nt_status)) { + printf("OpenDomain failed - %s\n", nt_errstr(nt_status)); + return NULL; + } + + return domain_handle; +} + +static struct sec_desc_buf *samsync_query_samr_sec_desc(TALLOC_CTX *mem_ctx, + struct samsync_state *samsync_state, + struct policy_handle *handle) +{ + struct samr_QuerySecurity r; + NTSTATUS status; + + r.in.handle = handle; + r.in.sec_info = 0x7; + + status = dcerpc_samr_QuerySecurity(samsync_state->p_samr, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("SAMR QuerySecurity failed - %s\n", nt_errstr(status)); + return NULL; + } + + return r.out.sdbuf; +} + +static struct sec_desc_buf *samsync_query_lsa_sec_desc(TALLOC_CTX *mem_ctx, + struct samsync_state *samsync_state, + struct policy_handle *handle) +{ + struct lsa_QuerySecurity r; + NTSTATUS status; + + r.in.handle = handle; + r.in.sec_info = 0x7; + + status = dcerpc_lsa_QuerySecurity(samsync_state->p_lsa, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("LSA QuerySecurity failed - %s\n", nt_errstr(status)); + return NULL; + } + + return r.out.sdbuf; +} + +#define TEST_UINT64_EQUAL(i1, i2) do {\ + if (i1 != i2) {\ + printf("%s: uint64 mismatch: " #i1 ": 0x%016llx (%lld) != " #i2 ": 0x%016llx (%lld)\n", \ + __location__, \ + (long long)i1, (long long)i1, \ + (long long)i2, (long long)i2);\ + ret = false;\ + } \ +} while (0) +#define TEST_INT_EQUAL(i1, i2) do {\ + if (i1 != i2) {\ + printf("%s: integer mismatch: " #i1 ": 0x%08x (%d) != " #i2 ": 0x%08x (%d)\n", \ + __location__, i1, i1, i2, i2); \ + ret = false;\ + } \ +} while (0) +#define TEST_TIME_EQUAL(t1, t2) do {\ + if (t1 != t2) {\ + printf("%s: NTTIME mismatch: " #t1 ":%s != " #t2 ": %s\n", \ + __location__, nt_time_string(mem_ctx, t1), nt_time_string(mem_ctx, t2));\ + ret = false;\ + } \ +} while (0) + +#define TEST_STRING_EQUAL(s1, s2) do {\ + if (!((!s1.string || s1.string[0]=='\0') && (!s2.string || s2.string[0]=='\0')) \ + && strcmp_safe(s1.string, s2.string) != 0) {\ + printf("%s: string mismatch: " #s1 ":%s != " #s2 ": %s\n", \ + __location__, s1.string, s2.string);\ + ret = false;\ + } \ +} while (0) + +#define TEST_SID_EQUAL(s1, s2) do {\ + if (!dom_sid_equal(s1, s2)) {\ + printf("%s: dom_sid mismatch: " #s1 ":%s != " #s2 ": %s\n", \ + __location__, dom_sid_string(mem_ctx, s1), dom_sid_string(mem_ctx, s2));\ + ret = false;\ + } \ +} while (0) + +/* The ~SEC_DESC_SACL_PRESENT is because we don't, as administrator, + * get back the SACL part of the SD when we ask over SAMR */ + +#define TEST_SEC_DESC_EQUAL(sd1, pipe, handle) do {\ + struct sec_desc_buf *sdbuf = samsync_query_ ##pipe## _sec_desc(mem_ctx, samsync_state, \ + handle); \ + if (!sdbuf || !sdbuf->sd) { \ + printf("Could not obtain security descriptor to match " #sd1 "\n");\ + ret = false; \ + } else {\ + if (!security_descriptor_mask_equal(sd1.sd, sdbuf->sd, \ + ~SEC_DESC_SACL_PRESENT)) {\ + printf("Security Descriptor Mismatch for %s:\n", #sd1);\ + ndr_print_debug((ndr_print_fn_t)ndr_print_security_descriptor, "SamSync", sd1.sd);\ + ndr_print_debug((ndr_print_fn_t)ndr_print_security_descriptor, "SamR", sdbuf->sd);\ + ret = false;\ + }\ + }\ +} while (0) + +static bool samsync_handle_domain(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain; + struct dom_sid *dom_sid; + struct samr_QueryDomainInfo q[14]; /* q[0] will be unused simple for clarity */ + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13}; + NTSTATUS nt_status; + int i; + bool ret = true; + + samsync_state->seq_num[database_id] = + domain->sequence_num; + switch (database_id) { + case SAM_DATABASE_DOMAIN: + break; + case SAM_DATABASE_BUILTIN: + if (strcasecmp_m("BUILTIN", domain->domain_name.string) != 0) { + printf("BUILTIN domain has different name: %s\n", domain->domain_name.string); + } + break; + case SAM_DATABASE_PRIVS: + printf("DOMAIN entry on privs DB!\n"); + return false; + break; + } + + if (!samsync_state->domain_name[database_id]) { + samsync_state->domain_name[database_id] = + talloc_reference(samsync_state, domain->domain_name.string); + } else { + if (strcasecmp_m(samsync_state->domain_name[database_id], domain->domain_name.string) != 0) { + printf("Domain has name varies!: %s != %s\n", samsync_state->domain_name[database_id], + domain->domain_name.string); + return false; + } + } + + if (!samsync_state->domain_handle[database_id]) { + samsync_state->domain_handle[database_id] + = talloc_reference(samsync_state, + samsync_open_domain(mem_ctx, samsync_state, samsync_state->domain_name[database_id], + &dom_sid)); + } + if (samsync_state->domain_handle[database_id]) { + samsync_state->sid[database_id] = talloc_reference(samsync_state, dom_sid); + } + + printf("\tsequence_nums[%d/%s]=%llu\n", + database_id, domain->domain_name.string, + (long long)samsync_state->seq_num[database_id]); + + for (i=0;i<ARRAY_SIZE(levels);i++) { + q[levels[i]].in.domain_handle = samsync_state->domain_handle[database_id]; + q[levels[i]].in.level = levels[i]; + + nt_status = dcerpc_samr_QueryDomainInfo(samsync_state->p_samr, mem_ctx, &q[levels[i]]); + + if (!NT_STATUS_IS_OK(nt_status)) { + printf("QueryDomainInfo level %u failed - %s\n", + q[levels[i]].in.level, nt_errstr(nt_status)); + return false; + } + } + + TEST_STRING_EQUAL(q[5].out.info->info5.domain_name, domain->domain_name); + + TEST_STRING_EQUAL(q[2].out.info->general.oem_information, domain->oem_information); + TEST_STRING_EQUAL(q[4].out.info->oem.oem_information, domain->oem_information); + TEST_TIME_EQUAL(q[2].out.info->general.force_logoff_time, domain->force_logoff_time); + TEST_TIME_EQUAL(q[3].out.info->info3.force_logoff_time, domain->force_logoff_time); + + TEST_TIME_EQUAL(q[1].out.info->info1.min_password_length, domain->min_password_length); + TEST_TIME_EQUAL(q[1].out.info->info1.password_history_length, domain->password_history_length); + TEST_TIME_EQUAL(q[1].out.info->info1.max_password_age, domain->max_password_age); + TEST_TIME_EQUAL(q[1].out.info->info1.min_password_age, domain->min_password_age); + + TEST_UINT64_EQUAL(q[8].out.info->info8.sequence_num, + domain->sequence_num); + TEST_TIME_EQUAL(q[8].out.info->info8.domain_create_time, + domain->domain_create_time); + TEST_TIME_EQUAL(q[13].out.info->info13.domain_create_time, + domain->domain_create_time); + + TEST_SEC_DESC_EQUAL(domain->sdbuf, samr, samsync_state->domain_handle[database_id]); + + return ret; +} + +static bool samsync_handle_policy(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + struct netr_DELTA_POLICY *policy = delta->delta_union.policy; + + samsync_state->seq_num[database_id] = + policy->sequence_num; + + if (!samsync_state->domain_name[SAM_DATABASE_DOMAIN]) { + samsync_state->domain_name[SAM_DATABASE_DOMAIN] = + talloc_reference(samsync_state, policy->primary_domain_name.string); + } else { + if (strcasecmp_m(samsync_state->domain_name[SAM_DATABASE_DOMAIN], policy->primary_domain_name.string) != 0) { + printf("PRIMARY domain has name varies between DOMAIN and POLICY!: %s != %s\n", samsync_state->domain_name[SAM_DATABASE_DOMAIN], + policy->primary_domain_name.string); + return false; + } + } + + if (!dom_sid_equal(samsync_state->sid[SAM_DATABASE_DOMAIN], policy->sid)) { + printf("Domain SID from POLICY (%s) does not match domain sid from SAMR (%s)\n", + dom_sid_string(mem_ctx, policy->sid), dom_sid_string(mem_ctx, samsync_state->sid[SAM_DATABASE_DOMAIN])); + return false; + } + + printf("\tsequence_nums[%d/PRIVS]=%llu\n", + database_id, + (long long)samsync_state->seq_num[database_id]); + return true; +} + +static bool samsync_handle_user(struct torture_context *tctx, TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + uint32_t rid = delta->delta_id_union.rid; + struct netr_DELTA_USER *user = delta->delta_union.user; + struct netr_SamInfo3 *info3; + struct samr_Password lm_hash; + struct samr_Password nt_hash; + struct samr_Password *lm_hash_p = NULL; + struct samr_Password *nt_hash_p = NULL; + const char *domain = samsync_state->domain_name[database_id]; + const char *username = user->account_name.string; + NTSTATUS nt_status; + bool ret = true; + + struct samr_OpenUser r; + struct samr_QueryUserInfo q; + struct policy_handle user_handle; + + struct samr_GetGroupsForUser getgroups; + if (!samsync_state->domain_name || !samsync_state->domain_handle[database_id]) { + printf("SamSync needs domain information before the users\n"); + return false; + } + + r.in.domain_handle = samsync_state->domain_handle[database_id]; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.user_handle = &user_handle; + + nt_status = dcerpc_samr_OpenUser(samsync_state->p_samr, mem_ctx, &r); + if (!NT_STATUS_IS_OK(nt_status)) { + printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(nt_status)); + return false; + } + + q.in.user_handle = &user_handle; + q.in.level = 21; + + TEST_SEC_DESC_EQUAL(user->sdbuf, samr, &user_handle); + + nt_status = dcerpc_samr_QueryUserInfo(samsync_state->p_samr, mem_ctx, &q); + if (!NT_STATUS_IS_OK(nt_status)) { + printf("QueryUserInfo level %u failed - %s\n", + q.in.level, nt_errstr(nt_status)); + ret = false; + } + + getgroups.in.user_handle = &user_handle; + + nt_status = dcerpc_samr_GetGroupsForUser(samsync_state->p_samr, mem_ctx, &getgroups); + if (!NT_STATUS_IS_OK(nt_status)) { + printf("GetGroupsForUser failed - %s\n", + nt_errstr(nt_status)); + ret = false; + } + + if (!test_samr_handle_Close(samsync_state->p_samr, mem_ctx, &user_handle)) { + printf("samr_handle_Close failed - %s\n", + nt_errstr(nt_status)); + ret = false; + } + if (!ret) { + return false; + } + + if (!NT_STATUS_IS_OK(nt_status)) { + printf("QueryUserInfo level %u failed - %s\n", + q.in.level, nt_errstr(nt_status)); + return false; + } + + TEST_STRING_EQUAL(q.out.info->info21.account_name, user->account_name); + TEST_STRING_EQUAL(q.out.info->info21.full_name, user->full_name); + TEST_INT_EQUAL(q.out.info->info21.rid, user->rid); + TEST_INT_EQUAL(q.out.info->info21.primary_gid, user->primary_gid); + TEST_STRING_EQUAL(q.out.info->info21.home_directory, user->home_directory); + TEST_STRING_EQUAL(q.out.info->info21.home_drive, user->home_drive); + TEST_STRING_EQUAL(q.out.info->info21.logon_script, user->logon_script); + TEST_STRING_EQUAL(q.out.info->info21.description, user->description); + TEST_STRING_EQUAL(q.out.info->info21.workstations, user->workstations); + + TEST_TIME_EQUAL(q.out.info->info21.last_logon, user->last_logon); + TEST_TIME_EQUAL(q.out.info->info21.last_logoff, user->last_logoff); + + + TEST_INT_EQUAL(q.out.info->info21.logon_hours.units_per_week, + user->logon_hours.units_per_week); + if (ret) { + if (memcmp(q.out.info->info21.logon_hours.bits, user->logon_hours.bits, + q.out.info->info21.logon_hours.units_per_week/8) != 0) { + printf("Logon hours mismatch\n"); + ret = false; + } + } + + TEST_INT_EQUAL(q.out.info->info21.bad_password_count, + user->bad_password_count); + TEST_INT_EQUAL(q.out.info->info21.logon_count, + user->logon_count); + + TEST_TIME_EQUAL(q.out.info->info21.last_password_change, + user->last_password_change); + TEST_TIME_EQUAL(q.out.info->info21.acct_expiry, + user->acct_expiry); + + TEST_INT_EQUAL((q.out.info->info21.acct_flags & ~ACB_PW_EXPIRED), user->acct_flags); + if (user->acct_flags & ACB_PWNOEXP) { + if (q.out.info->info21.acct_flags & ACB_PW_EXPIRED) { + printf("ACB flags mismatch: both expired and no expiry!\n"); + ret = false; + } + if (q.out.info->info21.force_password_change != (NTTIME)0x7FFFFFFFFFFFFFFFULL) { + printf("ACB flags mismatch: no password expiry, but force password change 0x%016llx (%lld) != 0x%016llx (%lld)\n", + (unsigned long long)q.out.info->info21.force_password_change, + (unsigned long long)q.out.info->info21.force_password_change, + (unsigned long long)0x7FFFFFFFFFFFFFFFULL, (unsigned long long)0x7FFFFFFFFFFFFFFFULL + ); + ret = false; + } + } + + TEST_INT_EQUAL(q.out.info->info21.nt_password_set, user->nt_password_present); + TEST_INT_EQUAL(q.out.info->info21.lm_password_set, user->lm_password_present); + TEST_INT_EQUAL(q.out.info->info21.password_expired, user->password_expired); + + TEST_STRING_EQUAL(q.out.info->info21.comment, user->comment); + TEST_STRING_EQUAL(q.out.info->info21.parameters, user->parameters); + + TEST_INT_EQUAL(q.out.info->info21.country_code, user->country_code); + TEST_INT_EQUAL(q.out.info->info21.code_page, user->code_page); + + TEST_STRING_EQUAL(q.out.info->info21.profile_path, user->profile_path); + + if (user->lm_password_present) { + sam_rid_crypt(rid, user->lmpassword.hash, lm_hash.hash, 0); + lm_hash_p = &lm_hash; + } + if (user->nt_password_present) { + sam_rid_crypt(rid, user->ntpassword.hash, nt_hash.hash, 0); + nt_hash_p = &nt_hash; + } + + if (user->user_private_info.SensitiveData) { + DATA_BLOB data; + struct netr_USER_KEYS keys; + enum ndr_err_code ndr_err; + data.data = user->user_private_info.SensitiveData; + data.length = user->user_private_info.DataLength; + creds_arcfour_crypt(samsync_state->creds, data.data, data.length); + ndr_err = ndr_pull_struct_blob(&data, mem_ctx, lp_iconv_convenience(tctx->lp_ctx), &keys, (ndr_pull_flags_fn_t)ndr_pull_netr_USER_KEYS); + if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + if (keys.keys.keys2.lmpassword.length == 16) { + sam_rid_crypt(rid, keys.keys.keys2.lmpassword.pwd.hash, lm_hash.hash, 0); + lm_hash_p = &lm_hash; + } + if (keys.keys.keys2.ntpassword.length == 16) { + sam_rid_crypt(rid, keys.keys.keys2.ntpassword.pwd.hash, nt_hash.hash, 0); + nt_hash_p = &nt_hash; + } + } else { + printf("Failed to parse Sensitive Data for %s:\n", username); +#if 0 + dump_data(0, data.data, data.length); +#endif + return false; + } + } + + if (nt_hash_p) { + DATA_BLOB nt_hash_blob = data_blob_const(nt_hash_p, 16); + DEBUG(100,("ACCOUNT [%s\\%-25s] NTHASH %s\n", samsync_state->domain_name[0], username, data_blob_hex_string(mem_ctx, &nt_hash_blob))); + } + if (lm_hash_p) { + DATA_BLOB lm_hash_blob = data_blob_const(lm_hash_p, 16); + DEBUG(100,("ACCOUNT [%s\\%-25s] LMHASH %s\n", samsync_state->domain_name[0], username, data_blob_hex_string(mem_ctx, &lm_hash_blob))); + } + + nt_status = test_SamLogon(samsync_state->p_netlogon_wksta, mem_ctx, samsync_state->creds_netlogon_wksta, + domain, + username, + TEST_WKSTA_MACHINE_NAME, + lm_hash_p, + nt_hash_p, + &info3); + + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_DISABLED)) { + if (user->acct_flags & ACB_DISABLED) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT)) { + if (user->acct_flags & ACB_WSTRUST) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT)) { + if (user->acct_flags & ACB_SVRTRUST) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) { + if (user->acct_flags & ACB_DOMTRUST) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) { + if (user->acct_flags & ACB_DOMTRUST) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_LOCKED_OUT)) { + if (user->acct_flags & ACB_AUTOLOCK) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_EXPIRED)) { + if (q.out.info->info21.acct_flags & ACB_PW_EXPIRED) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) { + if (!lm_hash_p && !nt_hash_p) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_MUST_CHANGE)) { + /* We would need to know the server's current time to test this properly */ + return true; + } else if (NT_STATUS_IS_OK(nt_status)) { + TEST_INT_EQUAL(user->rid, info3->base.rid); + TEST_INT_EQUAL(user->primary_gid, info3->base.primary_gid); + /* this is 0x0 from NT4 sp6 */ + if (info3->base.acct_flags) { + TEST_INT_EQUAL(user->acct_flags, info3->base.acct_flags); + } + /* this is NULL from NT4 sp6 */ + if (info3->base.account_name.string) { + TEST_STRING_EQUAL(user->account_name, info3->base.account_name); + } + /* this is NULL from Win2k3 */ + if (info3->base.full_name.string) { + TEST_STRING_EQUAL(user->full_name, info3->base.full_name); + } + TEST_STRING_EQUAL(user->logon_script, info3->base.logon_script); + TEST_STRING_EQUAL(user->profile_path, info3->base.profile_path); + TEST_STRING_EQUAL(user->home_directory, info3->base.home_directory); + TEST_STRING_EQUAL(user->home_drive, info3->base.home_drive); + TEST_STRING_EQUAL(user->logon_script, info3->base.logon_script); + + + TEST_TIME_EQUAL(user->last_logon, info3->base.last_logon); + TEST_TIME_EQUAL(user->acct_expiry, info3->base.acct_expiry); + TEST_TIME_EQUAL(user->last_password_change, info3->base.last_password_change); + TEST_TIME_EQUAL(q.out.info->info21.force_password_change, info3->base.force_password_change); + + /* Does the concept of a logoff time ever really + * exist? (not in any sensible way, according to the + * doco I read -- abartlet) */ + + /* This copes with the two different versions of 0 I see */ + /* with NT4 sp6 we have the || case */ + if (!((user->last_logoff == 0) + || (info3->base.last_logoff == 0x7fffffffffffffffLL))) { + TEST_TIME_EQUAL(user->last_logoff, info3->base.last_logoff); + } + + TEST_INT_EQUAL(getgroups.out.rids->count, info3->base.groups.count); + if (getgroups.out.rids->count == info3->base.groups.count) { + int i, j; + int count = getgroups.out.rids->count; + bool *matched = talloc_zero_array(mem_ctx, bool, getgroups.out.rids->count); + + for (i = 0; i < count; i++) { + for (j = 0; j < count; j++) { + if ((getgroups.out.rids->rids[i].rid == + info3->base.groups.rids[j].rid) + && (getgroups.out.rids->rids[i].attributes == + info3->base.groups.rids[j].attributes)) { + matched[i] = true; + } + } + } + + for (i = 0; i < getgroups.out.rids->count; i++) { + if (matched[i] == false) { + ret = false; + printf("Could not find group RID %u found in getgroups in NETLOGON reply\n", + getgroups.out.rids->rids[i].rid); + } + } + } + return ret; + } else { + printf("Could not validate password for user %s\\%s: %s\n", + domain, username, nt_errstr(nt_status)); + return false; + } + return false; +} + +static bool samsync_handle_alias(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + uint32_t rid = delta->delta_id_union.rid; + struct netr_DELTA_ALIAS *alias = delta->delta_union.alias; + NTSTATUS nt_status; + bool ret = true; + + struct samr_OpenAlias r; + struct samr_QueryAliasInfo q; + struct policy_handle alias_handle; + + if (!samsync_state->domain_name || !samsync_state->domain_handle[database_id]) { + printf("SamSync needs domain information before the users\n"); + return false; + } + + r.in.domain_handle = samsync_state->domain_handle[database_id]; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.alias_handle = &alias_handle; + + nt_status = dcerpc_samr_OpenAlias(samsync_state->p_samr, mem_ctx, &r); + if (!NT_STATUS_IS_OK(nt_status)) { + printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(nt_status)); + return false; + } + + q.in.alias_handle = &alias_handle; + q.in.level = 1; + + TEST_SEC_DESC_EQUAL(alias->sdbuf, samr, &alias_handle); + + nt_status = dcerpc_samr_QueryAliasInfo(samsync_state->p_samr, mem_ctx, &q); + if (!test_samr_handle_Close(samsync_state->p_samr, mem_ctx, &alias_handle)) { + return false; + } + + if (!NT_STATUS_IS_OK(nt_status)) { + printf("QueryAliasInfo level %u failed - %s\n", + q.in.level, nt_errstr(nt_status)); + return false; + } + + TEST_STRING_EQUAL(q.out.info->all.name, alias->alias_name); + TEST_STRING_EQUAL(q.out.info->all.description, alias->description); + return ret; +} + +static bool samsync_handle_group(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + uint32_t rid = delta->delta_id_union.rid; + struct netr_DELTA_GROUP *group = delta->delta_union.group; + NTSTATUS nt_status; + bool ret = true; + + struct samr_OpenGroup r; + struct samr_QueryGroupInfo q; + struct policy_handle group_handle; + + if (!samsync_state->domain_name || !samsync_state->domain_handle[database_id]) { + printf("SamSync needs domain information before the users\n"); + return false; + } + + r.in.domain_handle = samsync_state->domain_handle[database_id]; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.group_handle = &group_handle; + + nt_status = dcerpc_samr_OpenGroup(samsync_state->p_samr, mem_ctx, &r); + if (!NT_STATUS_IS_OK(nt_status)) { + printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(nt_status)); + return false; + } + + q.in.group_handle = &group_handle; + q.in.level = 1; + + TEST_SEC_DESC_EQUAL(group->sdbuf, samr, &group_handle); + + nt_status = dcerpc_samr_QueryGroupInfo(samsync_state->p_samr, mem_ctx, &q); + if (!test_samr_handle_Close(samsync_state->p_samr, mem_ctx, &group_handle)) { + return false; + } + + if (!NT_STATUS_IS_OK(nt_status)) { + printf("QueryGroupInfo level %u failed - %s\n", + q.in.level, nt_errstr(nt_status)); + return false; + } + + TEST_STRING_EQUAL(q.out.info->all.name, group->group_name); + TEST_INT_EQUAL(q.out.info->all.attributes, group->attributes); + TEST_STRING_EQUAL(q.out.info->all.description, group->description); + return ret; +} + +static bool samsync_handle_secret(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + struct netr_DELTA_SECRET *secret = delta->delta_union.secret; + const char *name = delta->delta_id_union.name; + struct samsync_secret *new = talloc(samsync_state, struct samsync_secret); + struct samsync_secret *old = talloc(mem_ctx, struct samsync_secret); + struct lsa_QuerySecret q; + struct lsa_OpenSecret o; + struct policy_handle sec_handle; + struct lsa_DATA_BUF_PTR bufp1; + struct lsa_DATA_BUF_PTR bufp2; + NTTIME new_mtime; + NTTIME old_mtime; + bool ret = true; + DATA_BLOB lsa_blob1, lsa_blob_out, session_key; + NTSTATUS status; + + creds_arcfour_crypt(samsync_state->creds, secret->current_cipher.cipher_data, + secret->current_cipher.maxlen); + + creds_arcfour_crypt(samsync_state->creds, secret->old_cipher.cipher_data, + secret->old_cipher.maxlen); + + new->name = talloc_reference(new, name); + new->secret = data_blob_talloc(new, secret->current_cipher.cipher_data, secret->current_cipher.maxlen); + new->mtime = secret->current_cipher_set_time; + + new = talloc_reference(samsync_state, new); + DLIST_ADD(samsync_state->secrets, new); + + old->name = talloc_reference(old, name); + old->secret = data_blob_const(secret->old_cipher.cipher_data, secret->old_cipher.maxlen); + old->mtime = secret->old_cipher_set_time; + + o.in.handle = samsync_state->lsa_handle; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.in.name.string = name; + o.out.sec_handle = &sec_handle; + + status = dcerpc_lsa_OpenSecret(samsync_state->p_lsa, mem_ctx, &o); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenSecret failed - %s\n", nt_errstr(status)); + return false; + } + +/* + We would like to do this, but it is NOT_SUPPORTED on win2k3 + TEST_SEC_DESC_EQUAL(secret->sdbuf, lsa, &sec_handle); +*/ + status = dcerpc_fetch_session_key(samsync_state->p_lsa, &session_key); + if (!NT_STATUS_IS_OK(status)) { + printf("dcerpc_fetch_session_key failed - %s\n", nt_errstr(status)); + return false; + } + + + ZERO_STRUCT(new_mtime); + ZERO_STRUCT(old_mtime); + + /* fetch the secret back again */ + q.in.sec_handle = &sec_handle; + q.in.new_val = &bufp1; + q.in.new_mtime = &new_mtime; + q.in.old_val = &bufp2; + q.in.old_mtime = &old_mtime; + + bufp1.buf = NULL; + bufp2.buf = NULL; + + status = dcerpc_lsa_QuerySecret(samsync_state->p_lsa, mem_ctx, &q); + if (NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) { + /* some things are just off limits */ + return true; + } else if (!NT_STATUS_IS_OK(status)) { + printf("QuerySecret failed - %s\n", nt_errstr(status)); + return false; + } + + if (q.out.old_val->buf == NULL) { + /* probably just not available due to ACLs */ + } else { + lsa_blob1.data = q.out.old_val->buf->data; + lsa_blob1.length = q.out.old_val->buf->length; + + status = sess_decrypt_blob(mem_ctx, &lsa_blob1, &session_key, &lsa_blob_out); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to decrypt secrets OLD blob: %s\n", nt_errstr(status)); + return false; + } + + if (!q.out.old_mtime) { + printf("OLD mtime not available on LSA for secret %s\n", old->name); + ret = false; + } + if (old->mtime != *q.out.old_mtime) { + printf("OLD mtime on secret %s does not match between SAMSYNC (%s) and LSA (%s)\n", + old->name, nt_time_string(mem_ctx, old->mtime), + nt_time_string(mem_ctx, *q.out.old_mtime)); + ret = false; + } + + if (old->secret.length != lsa_blob_out.length) { + printf("Returned secret %s doesn't match: %d != %d\n", + old->name, (int)old->secret.length, (int)lsa_blob_out.length); + ret = false; + } else if (memcmp(lsa_blob_out.data, + old->secret.data, old->secret.length) != 0) { + printf("Returned secret %s doesn't match: \n", + old->name); + DEBUG(1, ("SamSync Secret:\n")); + dump_data(1, old->secret.data, old->secret.length); + DEBUG(1, ("LSA Secret:\n")); + dump_data(1, lsa_blob_out.data, lsa_blob_out.length); + ret = false; + } + + } + + if (q.out.new_val->buf == NULL) { + /* probably just not available due to ACLs */ + } else { + lsa_blob1.data = q.out.new_val->buf->data; + lsa_blob1.length = q.out.new_val->buf->length; + + status = sess_decrypt_blob(mem_ctx, &lsa_blob1, &session_key, &lsa_blob_out); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to decrypt secrets OLD blob\n"); + return false; + } + + if (!q.out.new_mtime) { + printf("NEW mtime not available on LSA for secret %s\n", new->name); + ret = false; + } + if (new->mtime != *q.out.new_mtime) { + printf("NEW mtime on secret %s does not match between SAMSYNC (%s) and LSA (%s)\n", + new->name, nt_time_string(mem_ctx, new->mtime), + nt_time_string(mem_ctx, *q.out.new_mtime)); + ret = false; + } + + if (new->secret.length != lsa_blob_out.length) { + printf("Returned secret %s doesn't match: %d != %d\n", + new->name, (int)new->secret.length, (int)lsa_blob_out.length); + ret = false; + } else if (memcmp(lsa_blob_out.data, + new->secret.data, new->secret.length) != 0) { + printf("Returned secret %s doesn't match: \n", + new->name); + DEBUG(1, ("SamSync Secret:\n")); + dump_data(1, new->secret.data, new->secret.length); + DEBUG(1, ("LSA Secret:\n")); + dump_data(1, lsa_blob_out.data, lsa_blob_out.length); + ret = false; + } + } + + return ret; +} + +static bool samsync_handle_trusted_domain(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + NTSTATUS status; + bool ret = true; + struct netr_DELTA_TRUSTED_DOMAIN *trusted_domain = delta->delta_union.trusted_domain; + struct dom_sid *dom_sid = delta->delta_id_union.sid; + + struct samsync_trusted_domain *new = talloc(samsync_state, struct samsync_trusted_domain); + struct lsa_OpenTrustedDomain t; + struct policy_handle trustdom_handle; + struct lsa_QueryTrustedDomainInfo q; + union lsa_TrustedDomainInfo *info[9]; + int levels [] = {1, 3, 8}; + int i; + + new->name = talloc_reference(new, trusted_domain->domain_name.string); + new->sid = talloc_reference(new, dom_sid); + + t.in.handle = samsync_state->lsa_handle; + t.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + t.in.sid = dom_sid; + t.out.trustdom_handle = &trustdom_handle; + + status = dcerpc_lsa_OpenTrustedDomain(samsync_state->p_lsa, mem_ctx, &t); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenTrustedDomain failed - %s\n", nt_errstr(status)); + return false; + } + + for (i=0; i< ARRAY_SIZE(levels); i++) { + q.in.trustdom_handle = &trustdom_handle; + q.in.level = levels[i]; + status = dcerpc_lsa_QueryTrustedDomainInfo(samsync_state->p_lsa, mem_ctx, &q); + if (!NT_STATUS_IS_OK(status)) { + if (q.in.level == 8 && NT_STATUS_EQUAL(status,NT_STATUS_INVALID_PARAMETER)) { + info[levels[i]] = NULL; + continue; + } + printf("QueryInfoTrustedDomain level %d failed - %s\n", + levels[i], nt_errstr(status)); + return false; + } + info[levels[i]] = q.out.info; + } + + if (info[8]) { + TEST_SID_EQUAL(info[8]->full_info.info_ex.sid, dom_sid); + TEST_STRING_EQUAL(info[8]->full_info.info_ex.netbios_name, trusted_domain->domain_name); + } + TEST_STRING_EQUAL(info[1]->name.netbios_name, trusted_domain->domain_name); + TEST_INT_EQUAL(info[3]->posix_offset.posix_offset, trusted_domain->posix_offset); +/* + We would like to do this, but it is NOT_SUPPORTED on win2k3 + TEST_SEC_DESC_EQUAL(trusted_domain->sdbuf, lsa, &trustdom_handle); +*/ + new = talloc_reference(samsync_state, new); + DLIST_ADD(samsync_state->trusted_domains, new); + + return ret; +} + +static bool samsync_handle_account(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + NTSTATUS status; + bool ret = true; + struct netr_DELTA_ACCOUNT *account = delta->delta_union.account; + struct dom_sid *dom_sid = delta->delta_id_union.sid; + + struct lsa_OpenAccount a; + struct policy_handle acct_handle; + struct lsa_EnumPrivsAccount e; + struct lsa_LookupPrivName r; + + int i, j; + + bool *found_priv_in_lsa; + + a.in.handle = samsync_state->lsa_handle; + a.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + a.in.sid = dom_sid; + a.out.acct_handle = &acct_handle; + + status = dcerpc_lsa_OpenAccount(samsync_state->p_lsa, mem_ctx, &a); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenTrustedDomain failed - %s\n", nt_errstr(status)); + return false; + } + + TEST_SEC_DESC_EQUAL(account->sdbuf, lsa, &acct_handle); + + found_priv_in_lsa = talloc_zero_array(mem_ctx, bool, account->privilege_entries); + + e.in.handle = &acct_handle; + + status = dcerpc_lsa_EnumPrivsAccount(samsync_state->p_lsa, mem_ctx, &e); + if (!NT_STATUS_IS_OK(status)) { + printf("EnumPrivsAccount failed - %s\n", nt_errstr(status)); + return false; + } + + if ((account->privilege_entries && !e.out.privs)) { + printf("Account %s has privileges in SamSync, but not LSA\n", + dom_sid_string(mem_ctx, dom_sid)); + return false; + } + + if (!account->privilege_entries && e.out.privs && e.out.privs->count) { + printf("Account %s has privileges in LSA, but not SamSync\n", + dom_sid_string(mem_ctx, dom_sid)); + return false; + } + + TEST_INT_EQUAL(account->privilege_entries, e.out.privs->count); + + for (i=0;i< e.out.privs->count; i++) { + r.in.handle = samsync_state->lsa_handle; + r.in.luid = &e.out.privs->set[i].luid; + + status = dcerpc_lsa_LookupPrivName(samsync_state->p_lsa, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("\nLookupPrivName failed - %s\n", nt_errstr(status)); + return false; + } + + if (!r.out.name) { + printf("\nLookupPrivName failed to return a name\n"); + return false; + } + for (j=0;j<account->privilege_entries; j++) { + if (strcmp(r.out.name->string, account->privilege_name[j].string) == 0) { + found_priv_in_lsa[j] = true; + break; + } + } + } + for (j=0;j<account->privilege_entries; j++) { + if (!found_priv_in_lsa[j]) { + printf("Privilage %s on account %s not found in LSA\n", account->privilege_name[j].string, + dom_sid_string(mem_ctx, dom_sid)); + ret = false; + } + } + return ret; +} + +/* + try a netlogon DatabaseSync +*/ +static bool test_DatabaseSync(struct torture_context *tctx, + struct samsync_state *samsync_state, + TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + TALLOC_CTX *loop_ctx, *delta_ctx, *trustdom_ctx; + struct netr_DatabaseSync r; + const enum netr_SamDatabaseID database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS}; + int i, d; + bool ret = true; + struct samsync_trusted_domain *t; + struct samsync_secret *s; + + const char *domain, *username; + + r.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(samsync_state->p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.preferredmaximumlength = (uint32_t)-1; + ZERO_STRUCT(r.in.return_authenticator); + + for (i=0;i<ARRAY_SIZE(database_ids);i++) { + r.in.sync_context = 0; + r.in.database_id = database_ids[i]; + + printf("Testing DatabaseSync of id %d\n", r.in.database_id); + + do { + loop_ctx = talloc_named(mem_ctx, 0, "DatabaseSync loop context"); + creds_client_authenticator(samsync_state->creds, &r.in.credential); + + status = dcerpc_netr_DatabaseSync(samsync_state->p, loop_ctx, &r); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + printf("DatabaseSync - %s\n", nt_errstr(status)); + ret = false; + break; + } + + if (!creds_client_check(samsync_state->creds, &r.out.return_authenticator.cred)) { + printf("Credential chaining failed\n"); + } + + r.in.sync_context = r.out.sync_context; + + for (d=0; d < r.out.delta_enum_array->num_deltas; d++) { + delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context"); + switch (r.out.delta_enum_array->delta_enum[d].delta_type) { + case NETR_DELTA_DOMAIN: + if (!samsync_handle_domain(delta_ctx, samsync_state, + r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) { + printf("Failed to handle DELTA_DOMAIN\n"); + ret = false; + } + break; + case NETR_DELTA_GROUP: + if (!samsync_handle_group(delta_ctx, samsync_state, + r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) { + printf("Failed to handle DELTA_USER\n"); + ret = false; + } + break; + case NETR_DELTA_USER: + if (!samsync_handle_user(tctx, delta_ctx, samsync_state, + r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) { + printf("Failed to handle DELTA_USER\n"); + ret = false; + } + break; + case NETR_DELTA_ALIAS: + if (!samsync_handle_alias(delta_ctx, samsync_state, + r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) { + printf("Failed to handle DELTA_ALIAS\n"); + ret = false; + } + break; + case NETR_DELTA_POLICY: + if (!samsync_handle_policy(delta_ctx, samsync_state, + r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) { + printf("Failed to handle DELTA_POLICY\n"); + ret = false; + } + break; + case NETR_DELTA_TRUSTED_DOMAIN: + if (!samsync_handle_trusted_domain(delta_ctx, samsync_state, + r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) { + printf("Failed to handle DELTA_TRUSTED_DOMAIN\n"); + ret = false; + } + break; + case NETR_DELTA_ACCOUNT: + if (!samsync_handle_account(delta_ctx, samsync_state, + r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) { + printf("Failed to handle DELTA_ACCOUNT\n"); + ret = false; + } + break; + case NETR_DELTA_SECRET: + if (!samsync_handle_secret(delta_ctx, samsync_state, + r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) { + printf("Failed to handle DELTA_SECRET\n"); + ret = false; + } + break; + case NETR_DELTA_GROUP_MEMBER: + case NETR_DELTA_ALIAS_MEMBER: + /* These are harder to cross-check, and we expect them */ + break; + case NETR_DELTA_DELETE_GROUP: + case NETR_DELTA_RENAME_GROUP: + case NETR_DELTA_DELETE_USER: + case NETR_DELTA_RENAME_USER: + case NETR_DELTA_DELETE_ALIAS: + case NETR_DELTA_RENAME_ALIAS: + case NETR_DELTA_DELETE_TRUST: + case NETR_DELTA_DELETE_ACCOUNT: + case NETR_DELTA_DELETE_SECRET: + case NETR_DELTA_DELETE_GROUP2: + case NETR_DELTA_DELETE_USER2: + case NETR_DELTA_MODIFY_COUNT: + default: + printf("Uxpected delta type %d\n", r.out.delta_enum_array->delta_enum[d].delta_type); + ret = false; + break; + } + talloc_free(delta_ctx); + } + talloc_free(loop_ctx); + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + + } + + domain = samsync_state->domain_name[SAM_DATABASE_DOMAIN]; + if (!domain) { + printf("Never got a DOMAIN object in samsync!\n"); + return false; + } + + trustdom_ctx = talloc_named(mem_ctx, 0, "test_DatabaseSync Trusted domains context"); + + username = talloc_asprintf(trustdom_ctx, "%s$", domain); + for (t=samsync_state->trusted_domains; t; t=t->next) { + char *secret_name = talloc_asprintf(trustdom_ctx, "G$$%s", t->name); + for (s=samsync_state->secrets; s; s=s->next) { + if (strcasecmp_m(s->name, secret_name) == 0) { + NTSTATUS nt_status; + struct samr_Password nt_hash; + mdfour(nt_hash.hash, s->secret.data, s->secret.length); + + printf("Checking password for %s\\%s\n", t->name, username); + nt_status = test_SamLogon(samsync_state->p_netlogon_wksta, trustdom_ctx, samsync_state->creds_netlogon_wksta, + t->name, + username, + TEST_WKSTA_MACHINE_NAME, + NULL, + &nt_hash, + NULL); + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_LOGON_SERVERS)) { + printf("Verifiction of trust password to %s failed: %s (the trusted domain is not available)\n", + t->name, nt_errstr(nt_status)); + + break; + } + if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) { + printf("Verifiction of trust password to %s: should have failed (nologon interdomain trust account), instead: %s\n", + t->name, nt_errstr(nt_status)); + ret = false; + } + + /* break it */ + nt_hash.hash[0]++; + nt_status = test_SamLogon(samsync_state->p_netlogon_wksta, trustdom_ctx, samsync_state->creds_netlogon_wksta, + t->name, + username, + TEST_WKSTA_MACHINE_NAME, + NULL, + &nt_hash, + NULL); + + if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) { + printf("Verifiction of trust password to %s: should have failed (wrong password), instead: %s\n", + t->name, nt_errstr(nt_status)); + ret = false; + } + + break; + } + } + } + talloc_free(trustdom_ctx); + return ret; +} + + +/* + try a netlogon DatabaseDeltas +*/ +static bool test_DatabaseDeltas(struct samsync_state *samsync_state, TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + TALLOC_CTX *loop_ctx; + struct netr_DatabaseDeltas r; + const uint32_t database_ids[] = {0, 1, 2}; + int i; + bool ret = true; + + r.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(samsync_state->p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.preferredmaximumlength = (uint32_t)-1; + ZERO_STRUCT(r.in.return_authenticator); + + for (i=0;i<ARRAY_SIZE(database_ids);i++) { + r.in.database_id = database_ids[i]; + r.in.sequence_num = samsync_state->seq_num[i]; + + if (r.in.sequence_num == 0) continue; + + /* this shows that the bdc doesn't need to do a single call for + * each seqnumber, and the pdc doesn't need to know about old values + * -- metze + */ + r.in.sequence_num -= 10; + + + printf("Testing DatabaseDeltas of id %d at %llu\n", + r.in.database_id, (long long)r.in.sequence_num); + + do { + loop_ctx = talloc_named(mem_ctx, 0, "test_DatabaseDeltas loop context"); + creds_client_authenticator(samsync_state->creds, &r.in.credential); + + status = dcerpc_netr_DatabaseDeltas(samsync_state->p, loop_ctx, &r); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) && + !NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED)) { + printf("DatabaseDeltas - %s\n", nt_errstr(status)); + ret = false; + } + + if (!creds_client_check(samsync_state->creds, &r.out.return_authenticator.cred)) { + printf("Credential chaining failed\n"); + } + + r.in.sequence_num++; + talloc_free(loop_ctx); + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + } + + return ret; +} + + +/* + try a netlogon DatabaseSync2 +*/ +static bool test_DatabaseSync2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct creds_CredentialState *creds) +{ + NTSTATUS status; + TALLOC_CTX *loop_ctx; + struct netr_DatabaseSync2 r; + const uint32_t database_ids[] = {0, 1, 2}; + int i; + bool ret = true; + + r.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.preferredmaximumlength = (uint32_t)-1; + ZERO_STRUCT(r.in.return_authenticator); + + for (i=0;i<ARRAY_SIZE(database_ids);i++) { + r.in.sync_context = 0; + r.in.database_id = database_ids[i]; + r.in.restart_state = 0; + + printf("Testing DatabaseSync2 of id %d\n", r.in.database_id); + + do { + loop_ctx = talloc_named(mem_ctx, 0, "test_DatabaseSync2 loop context"); + creds_client_authenticator(creds, &r.in.credential); + + status = dcerpc_netr_DatabaseSync2(p, loop_ctx, &r); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + printf("DatabaseSync2 - %s\n", nt_errstr(status)); + ret = false; + } + + if (!creds_client_check(creds, &r.out.return_authenticator.cred)) { + printf("Credential chaining failed\n"); + } + + r.in.sync_context = r.out.sync_context; + talloc_free(loop_ctx); + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + } + + return ret; +} + + + +bool torture_rpc_samsync(struct torture_context *torture) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct test_join *join_ctx; + struct test_join *join_ctx2; + struct test_join *user_ctx; + const char *machine_password; + const char *wksta_machine_password; + struct dcerpc_binding *b; + struct dcerpc_binding *b_netlogon_wksta; + struct samr_Connect c; + struct samr_SetDomainInfo s; + struct policy_handle *domain_policy; + + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy2 r; + struct cli_credentials *credentials; + struct cli_credentials *credentials_wksta; + + struct samsync_state *samsync_state; + + char *test_machine_account; + + char *test_wksta_machine_account; + + mem_ctx = talloc_init("torture_rpc_netlogon"); + + test_machine_account = talloc_asprintf(mem_ctx, "%s$", TEST_MACHINE_NAME); + join_ctx = torture_create_testuser(torture, test_machine_account, + lp_workgroup(torture->lp_ctx), ACB_SVRTRUST, + &machine_password); + if (!join_ctx) { + talloc_free(mem_ctx); + printf("Failed to join as BDC\n"); + return false; + } + + test_wksta_machine_account = talloc_asprintf(mem_ctx, "%s$", TEST_WKSTA_MACHINE_NAME); + join_ctx2 = torture_create_testuser(torture, test_wksta_machine_account, lp_workgroup(torture->lp_ctx), ACB_WSTRUST, &wksta_machine_password); + if (!join_ctx2) { + talloc_free(mem_ctx); + printf("Failed to join as member\n"); + return false; + } + + user_ctx = torture_create_testuser(torture, TEST_USER_NAME, + lp_workgroup(torture->lp_ctx), + ACB_NORMAL, NULL); + if (!user_ctx) { + talloc_free(mem_ctx); + printf("Failed to create test account\n"); + return false; + } + + samsync_state = talloc_zero(mem_ctx, struct samsync_state); + + samsync_state->p_samr = torture_join_samr_pipe(join_ctx); + samsync_state->connect_handle = talloc_zero(samsync_state, struct policy_handle); + samsync_state->lsa_handle = talloc_zero(samsync_state, struct policy_handle); + c.in.system_name = NULL; + c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + c.out.connect_handle = samsync_state->connect_handle; + + status = dcerpc_samr_Connect(samsync_state->p_samr, mem_ctx, &c); + if (!NT_STATUS_IS_OK(status)) { + printf("samr_Connect failed\n"); + ret = false; + goto failed; + } + + domain_policy = samsync_open_domain(mem_ctx, samsync_state, lp_workgroup(torture->lp_ctx), NULL); + if (!domain_policy) { + printf("samrsync_open_domain failed\n"); + ret = false; + goto failed; + } + + s.in.domain_handle = domain_policy; + s.in.level = 4; + s.in.info = talloc(mem_ctx, union samr_DomainInfo); + + s.in.info->oem.oem_information.string + = talloc_asprintf(mem_ctx, + "Tortured by Samba4: %s", + timestring(mem_ctx, time(NULL))); + status = dcerpc_samr_SetDomainInfo(samsync_state->p_samr, mem_ctx, &s); + + if (!test_samr_handle_Close(samsync_state->p_samr, mem_ctx, domain_policy)) { + ret = false; + goto failed; + } + + if (!NT_STATUS_IS_OK(status)) { + printf("SetDomainInfo level %u failed - %s\n", + s.in.level, nt_errstr(status)); + ret = false; + goto failed; + } + + + status = torture_rpc_connection(torture, + &samsync_state->p_lsa, + &ndr_table_lsarpc); + + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto failed; + } + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = "\\"; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = samsync_state->lsa_handle; + + status = dcerpc_lsa_OpenPolicy2(samsync_state->p_lsa, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenPolicy2 failed - %s\n", nt_errstr(status)); + ret = false; + goto failed; + } + + status = torture_rpc_binding(torture, &b); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto failed; + } + + b->flags &= ~DCERPC_AUTH_OPTIONS; + b->flags |= DCERPC_SCHANNEL | DCERPC_SIGN; + + credentials = cli_credentials_init(mem_ctx); + + cli_credentials_set_workstation(credentials, TEST_MACHINE_NAME, CRED_SPECIFIED); + cli_credentials_set_domain(credentials, lp_workgroup(torture->lp_ctx), CRED_SPECIFIED); + cli_credentials_set_username(credentials, test_machine_account, CRED_SPECIFIED); + cli_credentials_set_password(credentials, machine_password, CRED_SPECIFIED); + cli_credentials_set_secure_channel_type(credentials, + SEC_CHAN_BDC); + + status = dcerpc_pipe_connect_b(samsync_state, + &samsync_state->p, b, + &ndr_table_netlogon, + credentials, torture->ev, torture->lp_ctx); + + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to connect to server as a BDC: %s\n", nt_errstr(status)); + ret = false; + goto failed; + } + + status = dcerpc_schannel_creds(samsync_state->p->conn->security_state.generic_state, + samsync_state, &samsync_state->creds); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + } + + + + status = torture_rpc_binding(torture, &b_netlogon_wksta); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto failed; + } + + b_netlogon_wksta->flags &= ~DCERPC_AUTH_OPTIONS; + b_netlogon_wksta->flags |= DCERPC_SCHANNEL | DCERPC_SIGN; + + credentials_wksta = cli_credentials_init(mem_ctx); + + cli_credentials_set_workstation(credentials_wksta, TEST_WKSTA_MACHINE_NAME, CRED_SPECIFIED); + cli_credentials_set_domain(credentials_wksta, lp_workgroup(torture->lp_ctx), CRED_SPECIFIED); + cli_credentials_set_username(credentials_wksta, test_wksta_machine_account, CRED_SPECIFIED); + cli_credentials_set_password(credentials_wksta, wksta_machine_password, CRED_SPECIFIED); + cli_credentials_set_secure_channel_type(credentials_wksta, + SEC_CHAN_WKSTA); + + status = dcerpc_pipe_connect_b(samsync_state, + &samsync_state->p_netlogon_wksta, + b_netlogon_wksta, + &ndr_table_netlogon, + credentials_wksta, torture->ev, torture->lp_ctx); + + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to connect to server as a Workstation: %s\n", nt_errstr(status)); + ret = false; + goto failed; + } + + status = dcerpc_schannel_creds(samsync_state->p_netlogon_wksta->conn->security_state.generic_state, + samsync_state, &samsync_state->creds_netlogon_wksta); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to obtail schanel creds!\n"); + ret = false; + } + + if (!test_DatabaseSync(torture, samsync_state, mem_ctx)) { + printf("DatabaseSync failed\n"); + ret = false; + } + + if (!test_DatabaseDeltas(samsync_state, mem_ctx)) { + printf("DatabaseDeltas failed\n"); + ret = false; + } + + if (!test_DatabaseSync2(samsync_state->p, mem_ctx, samsync_state->creds)) { + printf("DatabaseSync2 failed\n"); + ret = false; + } +failed: + + torture_leave_domain(torture, join_ctx); + torture_leave_domain(torture, join_ctx2); + torture_leave_domain(torture, user_ctx); + + talloc_free(mem_ctx); + + return ret; +} diff --git a/source4/torture/rpc/scanner.c b/source4/torture/rpc/scanner.c new file mode 100644 index 0000000000..b761f406ec --- /dev/null +++ b/source4/torture/rpc/scanner.c @@ -0,0 +1,151 @@ +/* + Unix SMB/CIFS implementation. + + scanner for rpc calls + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_mgmt_c.h" +#include "librpc/ndr/ndr_table.h" +#include "torture/rpc/rpc.h" +#include "param/param.h" +#include "librpc/rpc/dcerpc_proto.h" + +/* + work out how many calls there are for an interface + */ +static bool test_num_calls(struct torture_context *tctx, + const struct ndr_interface_table *iface, + TALLOC_CTX *mem_ctx, + struct ndr_syntax_id *id) +{ + struct dcerpc_pipe *p; + NTSTATUS status; + int i; + DATA_BLOB stub_in, stub_out; + int idl_calls; + struct ndr_interface_table tbl; + + /* FIXME: This should be fixed when torture_rpc_connection + * takes a ndr_syntax_id */ + tbl.name = iface->name; + tbl.syntax_id = *id; + + status = torture_rpc_connection(tctx, &p, iface); + if (!NT_STATUS_IS_OK(status)) { + char *uuid_str = GUID_string(mem_ctx, &id->uuid); + printf("Failed to connect to '%s' on '%s' - %s\n", + uuid_str, iface->name, nt_errstr(status)); + talloc_free(uuid_str); + return true; + } + + /* make null calls */ + stub_in = data_blob(NULL, 1000); + memset(stub_in.data, 0xFF, stub_in.length); + + for (i=0;i<200;i++) { + status = dcerpc_request(p, NULL, i, false, mem_ctx, &stub_in, &stub_out); + if (!NT_STATUS_IS_OK(status) && + p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) { + break; + } + + if (!NT_STATUS_IS_OK(status) && p->last_fault_code == 5) { + printf("\tpipe disconnected at %d\n", i); + goto done; + } + + if (!NT_STATUS_IS_OK(status) && p->last_fault_code == 0x80010111) { + printf("\terr 0x80010111 at %d\n", i); + goto done; + } + } + + printf("\t%d calls available\n", i); + idl_calls = ndr_interface_num_calls(&id->uuid, id->if_version); + if (idl_calls == -1) { + printf("\tinterface not known in local IDL\n"); + } else if (i != idl_calls) { + printf("\tWARNING: local IDL defines %u calls\n", idl_calls); + } else { + printf("\tOK: matches num_calls in local IDL\n"); + } + +done: + talloc_free(p); + return true; +} + + + +bool torture_rpc_scanner(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *loop_ctx; + bool ret = true; + const struct ndr_interface_list *l; + struct dcerpc_binding *b; + + status = torture_rpc_binding(torture, &b); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + for (l=ndr_table_list();l;l=l->next) { + loop_ctx = talloc_named(torture, 0, "torture_rpc_scanner loop context"); + /* some interfaces are not mappable */ + if (l->table->num_calls == 0 || + strcmp(l->table->name, "mgmt") == 0) { + talloc_free(loop_ctx); + continue; + } + + printf("\nTesting pipe '%s'\n", l->table->name); + + if (b->transport == NCACN_IP_TCP) { + status = dcerpc_epm_map_binding(torture, b, l->table, NULL, torture->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to map port for uuid %s\n", + GUID_string(loop_ctx, &l->table->syntax_id.uuid)); + talloc_free(loop_ctx); + continue; + } + } else { + b->endpoint = talloc_strdup(b, l->table->name); + } + + lp_set_cmdline(torture->lp_ctx, "torture:binding", dcerpc_binding_string(torture, b)); + + status = torture_rpc_connection(torture, &p, &ndr_table_mgmt); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(loop_ctx); + ret = false; + continue; + } + + if (!test_inq_if_ids(torture, p, torture, test_num_calls, l->table)) { + ret = false; + } + } + + return ret; +} + diff --git a/source4/torture/rpc/schannel.c b/source4/torture/rpc/schannel.c new file mode 100644 index 0000000000..fae0093e4d --- /dev/null +++ b/source4/torture/rpc/schannel.c @@ -0,0 +1,835 @@ +/* + Unix SMB/CIFS implementation. + + test suite for schannel operations + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "auth/credentials/credentials.h" +#include "torture/rpc/rpc.h" +#include "lib/cmdline/popt_common.h" +#include "auth/gensec/schannel_proto.h" +#include "libcli/auth/libcli_auth.h" +#include "libcli/security/security.h" +#include "system/filesys.h" +#include "param/param.h" +#include "librpc/rpc/dcerpc_proto.h" +#include "auth/gensec/gensec.h" +#include "libcli/composite/composite.h" +#include "lib/events/events.h" + +#define TEST_MACHINE_NAME "schannel" + +/* + try a netlogon SamLogon +*/ +bool test_netlogon_ex_ops(struct dcerpc_pipe *p, struct torture_context *tctx, + struct cli_credentials *credentials, + struct creds_CredentialState *creds) +{ + NTSTATUS status; + struct netr_LogonSamLogonEx r; + struct netr_NetworkInfo ninfo; + DATA_BLOB names_blob, chal, lm_resp, nt_resp; + int i; + int flags = CLI_CRED_NTLM_AUTH; + if (lp_client_lanman_auth(tctx->lp_ctx)) { + flags |= CLI_CRED_LANMAN_AUTH; + } + + if (lp_client_ntlmv2_auth(tctx->lp_ctx)) { + flags |= CLI_CRED_NTLMv2_AUTH; + } + + cli_credentials_get_ntlm_username_domain(cmdline_credentials, tctx, + &ninfo.identity_info.account_name.string, + &ninfo.identity_info.domain_name.string); + + generate_random_buffer(ninfo.challenge, + sizeof(ninfo.challenge)); + chal = data_blob_const(ninfo.challenge, + sizeof(ninfo.challenge)); + + names_blob = NTLMv2_generate_names_blob(tctx, lp_iconv_convenience(tctx->lp_ctx), cli_credentials_get_workstation(credentials), + cli_credentials_get_domain(credentials)); + + status = cli_credentials_get_ntlm_response(cmdline_credentials, tctx, + &flags, + chal, + names_blob, + &lm_resp, &nt_resp, + NULL, NULL); + torture_assert_ntstatus_ok(tctx, status, + "cli_credentials_get_ntlm_response failed"); + + ninfo.lm.data = lm_resp.data; + ninfo.lm.length = lm_resp.length; + + ninfo.nt.data = nt_resp.data; + ninfo.nt.length = nt_resp.length; + + ninfo.identity_info.parameter_control = 0; + ninfo.identity_info.logon_id_low = 0; + ninfo.identity_info.logon_id_high = 0; + ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials); + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(credentials); + r.in.logon_level = 2; + r.in.logon.network = &ninfo; + r.in.flags = 0; + + torture_comment(tctx, + "Testing LogonSamLogonEx with name %s\n", + ninfo.identity_info.account_name.string); + + for (i=2;i<3;i++) { + r.in.validation_level = i; + + status = dcerpc_netr_LogonSamLogonEx(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed"); + } + + return true; +} + +/* + do some samr ops using the schannel connection + */ +static bool test_samr_ops(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct samr_GetDomPwInfo r; + struct samr_Connect connect; + struct samr_OpenDomain opendom; + int i; + struct lsa_String name; + struct policy_handle handle; + struct policy_handle domain_handle; + + name.string = lp_workgroup(tctx->lp_ctx); + r.in.domain_name = &name; + + connect.in.system_name = 0; + connect.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + connect.out.connect_handle = &handle; + + printf("Testing Connect and OpenDomain on BUILTIN\n"); + + status = dcerpc_samr_Connect(p, tctx, &connect); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + printf("Connect failed (expected, schannel mapped to anonymous): %s\n", + nt_errstr(status)); + } else { + printf("Connect failed - %s\n", nt_errstr(status)); + return false; + } + } else { + opendom.in.connect_handle = &handle; + opendom.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + opendom.in.sid = dom_sid_parse_talloc(tctx, "S-1-5-32"); + opendom.out.domain_handle = &domain_handle; + + status = dcerpc_samr_OpenDomain(p, tctx, &opendom); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenDomain failed - %s\n", nt_errstr(status)); + return false; + } + } + + printf("Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); + + /* do several ops to test credential chaining */ + for (i=0;i<5;i++) { + status = dcerpc_samr_GetDomPwInfo(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + printf("GetDomPwInfo op %d failed - %s\n", i, nt_errstr(status)); + return false; + } + } + } + + return true; +} + + +/* + do some lsa ops using the schannel connection + */ +static bool test_lsa_ops(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct lsa_GetUserName r; + NTSTATUS status; + bool ret = true; + struct lsa_StringPointer authority_name_p; + + printf("\nTesting GetUserName\n"); + + r.in.system_name = "\\"; + r.in.account_name = NULL; + r.in.authority_name = &authority_name_p; + authority_name_p.string = NULL; + + /* do several ops to test credential chaining and various operations */ + status = dcerpc_lsa_GetUserName(p, tctx, &r); + + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) { + printf("not considering %s to be an error\n", nt_errstr(status)); + } else if (!NT_STATUS_IS_OK(status)) { + printf("GetUserName failed - %s\n", nt_errstr(status)); + return false; + } else { + if (!r.out.account_name) { + return false; + } + + if (strcmp(r.out.account_name->string, "ANONYMOUS LOGON") != 0) { + printf("GetUserName returned wrong user: %s, expected %s\n", + r.out.account_name->string, "ANONYMOUS LOGON"); + return false; + } + if (!r.out.authority_name || !r.out.authority_name->string) { + return false; + } + + if (strcmp(r.out.authority_name->string->string, "NT AUTHORITY") != 0) { + printf("GetUserName returned wrong user: %s, expected %s\n", + r.out.authority_name->string->string, "NT AUTHORITY"); + return false; + } + } + if (!test_many_LookupSids(p, tctx, NULL)) { + printf("LsaLookupSids3 failed!\n"); + return false; + } + + return ret; +} + + +/* + test a schannel connection with the given flags + */ +static bool test_schannel(struct torture_context *tctx, + uint16_t acct_flags, uint32_t dcerpc_flags, + int i) +{ + struct test_join *join_ctx; + NTSTATUS status; + const char *binding = torture_setting_string(tctx, "binding", NULL); + struct dcerpc_binding *b; + struct dcerpc_pipe *p = NULL; + struct dcerpc_pipe *p_netlogon = NULL; + struct dcerpc_pipe *p_netlogon2 = NULL; + struct dcerpc_pipe *p_netlogon3 = NULL; + struct dcerpc_pipe *p_samr2 = NULL; + struct dcerpc_pipe *p_lsa = NULL; + struct creds_CredentialState *creds; + struct cli_credentials *credentials; + + join_ctx = torture_join_domain(tctx, + talloc_asprintf(tctx, "%s%d", TEST_MACHINE_NAME, i), + acct_flags, &credentials); + torture_assert(tctx, join_ctx != NULL, "Failed to join domain"); + + status = dcerpc_parse_binding(tctx, binding, &b); + torture_assert_ntstatus_ok(tctx, status, "Bad binding string"); + + b->flags &= ~DCERPC_AUTH_OPTIONS; + b->flags |= dcerpc_flags; + + status = dcerpc_pipe_connect_b(tctx, &p, b, &ndr_table_samr, + credentials, tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, + "Failed to connect with schannel"); + + torture_assert(tctx, test_samr_ops(tctx, p), + "Failed to process schannel secured SAMR ops"); + + /* Also test that when we connect to the netlogon pipe, that + * the credentials we setup on the first pipe are valid for + * the second */ + + /* Swap the binding details from SAMR to NETLOGON */ + status = dcerpc_epm_map_binding(tctx, b, &ndr_table_netlogon, tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, "epm map"); + + status = dcerpc_secondary_connection(p, &p_netlogon, + b); + torture_assert_ntstatus_ok(tctx, status, "seconday connection"); + + status = dcerpc_bind_auth(p_netlogon, &ndr_table_netlogon, + credentials, tctx->lp_ctx, + DCERPC_AUTH_TYPE_SCHANNEL, + dcerpc_auth_level(p->conn), + NULL); + + torture_assert_ntstatus_ok(tctx, status, "bind auth"); + + status = dcerpc_schannel_creds(p_netlogon->conn->security_state.generic_state, tctx, &creds); + torture_assert_ntstatus_ok(tctx, status, "schannel creds"); + + /* do a couple of logins */ + torture_assert(tctx, test_netlogon_ops(p_netlogon, tctx, credentials, creds), + "Failed to process schannel secured NETLOGON ops"); + + torture_assert(tctx, test_netlogon_ex_ops(p_netlogon, tctx, credentials, creds), + "Failed to process schannel secured NETLOGON EX ops"); + + /* Swap the binding details from SAMR to LSARPC */ + status = dcerpc_epm_map_binding(tctx, b, &ndr_table_lsarpc, tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, "epm map"); + + status = dcerpc_secondary_connection(p, &p_lsa, + b); + + torture_assert_ntstatus_ok(tctx, status, "seconday connection"); + + status = dcerpc_bind_auth(p_lsa, &ndr_table_lsarpc, + credentials, tctx->lp_ctx, + DCERPC_AUTH_TYPE_SCHANNEL, + dcerpc_auth_level(p->conn), + NULL); + + torture_assert_ntstatus_ok(tctx, status, "bind auth"); + + torture_assert(tctx, test_lsa_ops(tctx, p_lsa), + "Failed to process schannel secured LSA ops"); + + /* Drop the socket, we want to start from scratch */ + talloc_free(p); + p = NULL; + + /* Now see what we are still allowed to do */ + + status = dcerpc_parse_binding(tctx, binding, &b); + torture_assert_ntstatus_ok(tctx, status, "Bad binding string"); + + b->flags &= ~DCERPC_AUTH_OPTIONS; + b->flags |= dcerpc_flags; + + status = dcerpc_pipe_connect_b(tctx, &p_samr2, b, &ndr_table_samr, + credentials, tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, + "Failed to connect with schannel"); + + /* do a some SAMR operations. We have *not* done a new serverauthenticate */ + torture_assert (tctx, test_samr_ops(tctx, p_samr2), + "Failed to process schannel secured SAMR ops (on fresh connection)"); + + /* Swap the binding details from SAMR to NETLOGON */ + status = dcerpc_epm_map_binding(tctx, b, &ndr_table_netlogon, tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, "epm"); + + status = dcerpc_secondary_connection(p_samr2, &p_netlogon2, + b); + torture_assert_ntstatus_ok(tctx, status, "seconday connection"); + + /* and now setup an SCHANNEL bind on netlogon */ + status = dcerpc_bind_auth(p_netlogon2, &ndr_table_netlogon, + credentials, tctx->lp_ctx, + DCERPC_AUTH_TYPE_SCHANNEL, + dcerpc_auth_level(p_samr2->conn), + NULL); + + torture_assert_ntstatus_ok(tctx, status, "auth failed"); + + /* Try the schannel-only SamLogonEx operation */ + torture_assert(tctx, test_netlogon_ex_ops(p_netlogon2, tctx, credentials, creds), + "Failed to process schannel secured NETLOGON EX ops (on fresh connection)"); + + + /* And the more traditional style, proving that the + * credentials chaining state is fully present */ + torture_assert(tctx, test_netlogon_ops(p_netlogon2, tctx, credentials, creds), + "Failed to process schannel secured NETLOGON ops (on fresh connection)"); + + /* Drop the socket, we want to start from scratch (again) */ + talloc_free(p_samr2); + + /* We don't want schannel for this test */ + b->flags &= ~DCERPC_AUTH_OPTIONS; + + status = dcerpc_pipe_connect_b(tctx, &p_netlogon3, b, &ndr_table_netlogon, + credentials, tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, "Failed to connect without schannel"); + + torture_assert(tctx, !test_netlogon_ex_ops(p_netlogon3, tctx, credentials, creds), + "Processed NOT schannel secured NETLOGON EX ops without SCHANNEL (unsafe)"); + + /* Required because the previous call will mark the current context as having failed */ + tctx->last_result = TORTURE_OK; + tctx->last_reason = NULL; + + torture_assert(tctx, test_netlogon_ops(p_netlogon3, tctx, credentials, creds), + "Failed to processed NOT schannel secured NETLOGON ops without new ServerAuth"); + + torture_leave_domain(tctx, join_ctx); + return true; +} + + + +/* + a schannel test suite + */ +bool torture_rpc_schannel(struct torture_context *torture) +{ + bool ret = true; + struct { + uint16_t acct_flags; + uint32_t dcerpc_flags; + } tests[] = { + { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SIGN}, + { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SEAL}, + { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128}, + { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_128 }, + { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SIGN }, + { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SEAL }, + { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128 }, + { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_128 } + }; + int i; + + for (i=0;i<ARRAY_SIZE(tests);i++) { + if (!test_schannel(torture, + tests[i].acct_flags, tests[i].dcerpc_flags, + i)) { + torture_comment(torture, "Failed with acct_flags=0x%x dcerpc_flags=0x%x \n", + tests[i].acct_flags, tests[i].dcerpc_flags); + ret = false; + } + } + + return ret; +} + +/* + test two schannel connections + */ +bool torture_rpc_schannel2(struct torture_context *torture) +{ + struct test_join *join_ctx; + NTSTATUS status; + const char *binding = torture_setting_string(torture, "binding", NULL); + struct dcerpc_binding *b; + struct dcerpc_pipe *p1 = NULL, *p2 = NULL; + struct cli_credentials *credentials1, *credentials2; + uint32_t dcerpc_flags = DCERPC_SCHANNEL | DCERPC_SIGN; + + join_ctx = torture_join_domain(torture, talloc_asprintf(torture, "%s2", TEST_MACHINE_NAME), + ACB_WSTRUST, &credentials1); + torture_assert(torture, join_ctx != NULL, + "Failed to join domain with acct_flags=ACB_WSTRUST"); + + credentials2 = (struct cli_credentials *)talloc_memdup(torture, credentials1, sizeof(*credentials1)); + credentials1->netlogon_creds = NULL; + credentials2->netlogon_creds = NULL; + + status = dcerpc_parse_binding(torture, binding, &b); + torture_assert_ntstatus_ok(torture, status, "Bad binding string"); + + b->flags &= ~DCERPC_AUTH_OPTIONS; + b->flags |= dcerpc_flags; + + printf("Opening first connection\n"); + status = dcerpc_pipe_connect_b(torture, &p1, b, &ndr_table_netlogon, + credentials1, torture->ev, torture->lp_ctx); + torture_assert_ntstatus_ok(torture, status, "Failed to connect with schannel"); + + torture_comment(torture, "Opening second connection\n"); + status = dcerpc_pipe_connect_b(torture, &p2, b, &ndr_table_netlogon, + credentials2, torture->ev, torture->lp_ctx); + torture_assert_ntstatus_ok(torture, status, "Failed to connect with schannel"); + + credentials1->netlogon_creds = NULL; + credentials2->netlogon_creds = NULL; + + torture_comment(torture, "Testing logon on pipe1\n"); + if (!test_netlogon_ex_ops(p1, torture, credentials1, NULL)) + return false; + + torture_comment(torture, "Testing logon on pipe2\n"); + if (!test_netlogon_ex_ops(p2, torture, credentials2, NULL)) + return false; + + torture_comment(torture, "Again on pipe1\n"); + if (!test_netlogon_ex_ops(p1, torture, credentials1, NULL)) + return false; + + torture_comment(torture, "Again on pipe2\n"); + if (!test_netlogon_ex_ops(p2, torture, credentials2, NULL)) + return false; + + torture_leave_domain(torture, join_ctx); + return true; +} + +struct torture_schannel_bench; + +struct torture_schannel_bench_conn { + struct torture_schannel_bench *s; + int index; + struct cli_credentials *wks_creds; + struct dcerpc_pipe *pipe; + struct netr_LogonSamLogonEx r; + struct netr_NetworkInfo ninfo; + TALLOC_CTX *tmp; + uint64_t total; + uint32_t count; +}; + +struct torture_schannel_bench { + struct torture_context *tctx; + bool progress; + int timelimit; + int nprocs; + int nconns; + struct torture_schannel_bench_conn *conns; + struct test_join *join_ctx1; + struct cli_credentials *wks_creds1; + struct test_join *join_ctx2; + struct cli_credentials *wks_creds2; + struct cli_credentials *user1_creds; + struct cli_credentials *user2_creds; + struct dcerpc_binding *b; + NTSTATUS error; + uint64_t total; + uint32_t count; + bool stopped; +}; + +static void torture_schannel_bench_connected(struct composite_context *c) +{ + struct torture_schannel_bench_conn *conn = + (struct torture_schannel_bench_conn *)c->async.private_data; + struct torture_schannel_bench *s = talloc_get_type(conn->s, + struct torture_schannel_bench); + + s->error = dcerpc_pipe_connect_b_recv(c, s->conns, &conn->pipe); + torture_comment(s->tctx, "conn[%u]: %s\n", conn->index, nt_errstr(s->error)); + if (NT_STATUS_IS_OK(s->error)) { + s->nconns++; + } +} + +static void torture_schannel_bench_recv(struct rpc_request *req); + +static bool torture_schannel_bench_start(struct torture_schannel_bench_conn *conn) +{ + struct torture_schannel_bench *s = conn->s; + NTSTATUS status; + DATA_BLOB names_blob, chal, lm_resp, nt_resp; + int flags = CLI_CRED_NTLM_AUTH; + struct rpc_request *req; + struct cli_credentials *user_creds; + + if (conn->total % 2) { + user_creds = s->user1_creds; + } else { + user_creds = s->user2_creds; + } + + if (lp_client_lanman_auth(s->tctx->lp_ctx)) { + flags |= CLI_CRED_LANMAN_AUTH; + } + + if (lp_client_ntlmv2_auth(s->tctx->lp_ctx)) { + flags |= CLI_CRED_NTLMv2_AUTH; + } + + talloc_free(conn->tmp); + conn->tmp = talloc_new(s); + ZERO_STRUCT(conn->ninfo); + ZERO_STRUCT(conn->r); + + cli_credentials_get_ntlm_username_domain(user_creds, conn->tmp, + &conn->ninfo.identity_info.account_name.string, + &conn->ninfo.identity_info.domain_name.string); + + generate_random_buffer(conn->ninfo.challenge, + sizeof(conn->ninfo.challenge)); + chal = data_blob_const(conn->ninfo.challenge, + sizeof(conn->ninfo.challenge)); + + names_blob = NTLMv2_generate_names_blob(conn->tmp, lp_iconv_convenience(s->tctx->lp_ctx), + cli_credentials_get_workstation(conn->wks_creds), + cli_credentials_get_domain(conn->wks_creds)); + + status = cli_credentials_get_ntlm_response(user_creds, conn->tmp, + &flags, + chal, + names_blob, + &lm_resp, &nt_resp, + NULL, NULL); + torture_assert_ntstatus_ok(s->tctx, status, + "cli_credentials_get_ntlm_response failed"); + + conn->ninfo.lm.data = lm_resp.data; + conn->ninfo.lm.length = lm_resp.length; + + conn->ninfo.nt.data = nt_resp.data; + conn->ninfo.nt.length = nt_resp.length; + + conn->ninfo.identity_info.parameter_control = 0; + conn->ninfo.identity_info.logon_id_low = 0; + conn->ninfo.identity_info.logon_id_high = 0; + conn->ninfo.identity_info.workstation.string = cli_credentials_get_workstation(conn->wks_creds); + + conn->r.in.server_name = talloc_asprintf(conn->tmp, "\\\\%s", dcerpc_server_name(conn->pipe)); + conn->r.in.computer_name = cli_credentials_get_workstation(conn->wks_creds); + conn->r.in.logon_level = 2; + conn->r.in.logon.network = &conn->ninfo; + conn->r.in.flags = 0; + conn->r.in.validation_level = 2; + + req = dcerpc_netr_LogonSamLogonEx_send(conn->pipe, conn->tmp, &conn->r); + torture_assert(s->tctx, req, "Failed to setup LogonSamLogonEx request"); + + req->async.callback = torture_schannel_bench_recv; + req->async.private_data = conn; + + return true; +} + +static void torture_schannel_bench_recv(struct rpc_request *req) +{ + bool ret; + struct torture_schannel_bench_conn *conn = + (struct torture_schannel_bench_conn *)req->async.private_data; + struct torture_schannel_bench *s = talloc_get_type(conn->s, + struct torture_schannel_bench); + + s->error = dcerpc_ndr_request_recv(req); + if (!NT_STATUS_IS_OK(s->error)) { + return; + } + + conn->total++; + conn->count++; + + if (s->stopped) { + return; + } + + ret = torture_schannel_bench_start(conn); + if (!ret) { + s->error = NT_STATUS_INTERNAL_ERROR; + } +} + +/* + test multiple schannel connection in parallel + */ +bool torture_rpc_schannel_bench1(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + const char *binding = torture_setting_string(torture, "binding", NULL); + struct torture_schannel_bench *s; + struct timeval start; + struct timeval end; + int i; + const char *tmp; + + s = talloc_zero(torture, struct torture_schannel_bench); + s->tctx = torture; + s->progress = torture_setting_bool(torture, "progress", true); + s->timelimit = torture_setting_int(torture, "timelimit", 10); + s->nprocs = torture_setting_int(torture, "nprocs", 4); + s->conns = talloc_zero_array(s, struct torture_schannel_bench_conn, s->nprocs); + + s->user1_creds = (struct cli_credentials *)talloc_memdup(s, + cmdline_credentials, + sizeof(*s->user1_creds)); + tmp = torture_setting_string(s->tctx, "extra_user1", NULL); + if (tmp) { + cli_credentials_parse_string(s->user1_creds, tmp, CRED_SPECIFIED); + } + s->user2_creds = (struct cli_credentials *)talloc_memdup(s, + cmdline_credentials, + sizeof(*s->user1_creds)); + tmp = torture_setting_string(s->tctx, "extra_user2", NULL); + if (tmp) { + cli_credentials_parse_string(s->user1_creds, tmp, CRED_SPECIFIED); + } + + s->join_ctx1 = torture_join_domain(s->tctx, talloc_asprintf(s, "%sb", TEST_MACHINE_NAME), + ACB_WSTRUST, &s->wks_creds1); + torture_assert(torture, s->join_ctx1 != NULL, + "Failed to join domain with acct_flags=ACB_WSTRUST"); + s->join_ctx2 = torture_join_domain(s->tctx, talloc_asprintf(s, "%sc", TEST_MACHINE_NAME), + ACB_WSTRUST, &s->wks_creds2); + torture_assert(torture, s->join_ctx2 != NULL, + "Failed to join domain with acct_flags=ACB_WSTRUST"); + + cli_credentials_set_kerberos_state(s->wks_creds1, CRED_DONT_USE_KERBEROS); + cli_credentials_set_kerberos_state(s->wks_creds2, CRED_DONT_USE_KERBEROS); + + for (i=0; i < s->nprocs; i++) { + s->conns[i].s = s; + s->conns[i].index = i; + s->conns[i].wks_creds = (struct cli_credentials *)talloc_memdup( + s->conns, s->wks_creds1,sizeof(*s->wks_creds1)); + if ((i % 2) && (torture_setting_bool(torture, "multijoin", false))) { + memcpy(s->conns[i].wks_creds, s->wks_creds2, + talloc_get_size(s->conns[i].wks_creds)); + } + s->conns[i].wks_creds->netlogon_creds = NULL; + } + + status = dcerpc_parse_binding(s, binding, &s->b); + torture_assert_ntstatus_ok(torture, status, "Bad binding string"); + s->b->flags &= ~DCERPC_AUTH_OPTIONS; + s->b->flags |= DCERPC_SCHANNEL | DCERPC_SIGN; + + torture_comment(torture, "Opening %d connections in parallel\n", s->nprocs); + for (i=0; i < s->nprocs; i++) { +#if 1 + s->error = dcerpc_pipe_connect_b(s->conns, &s->conns[i].pipe, s->b, + &ndr_table_netlogon, + s->conns[i].wks_creds, + torture->ev, torture->lp_ctx); + torture_assert_ntstatus_ok(torture, s->error, "Failed to connect with schannel"); +#else + /* + * This path doesn't work against windows, + * because of windows drops the connections + * which haven't reached a session setup yet + * + * The same as the reset on zero vc stuff. + */ + struct composite_context *c; + c = dcerpc_pipe_connect_b_send(s->conns, s->b, + &ndr_table_netlogon, + s->conns[i].wks_creds, + torture->ev, + torture->lp_ctx); + torture_assert(torture, c != NULL, "Failed to setup connect"); + c->async.fn = torture_schannel_bench_connected; + c->async.private_data = &s->conns[i]; + } + + while (NT_STATUS_IS_OK(s->error) && s->nprocs != s->nconns) { + int ev_ret = event_loop_once(torture->ev); + torture_assert(torture, ev_ret == 0, "event_loop_once failed"); +#endif + } + torture_assert_ntstatus_ok(torture, s->error, "Failed establish a connect"); + + /* + * Change the workstation password after establishing the netlogon + * schannel connections to prove that existing connections are not + * affected by a wks pwchange. + */ + + { + struct netr_ServerPasswordSet pwset; + char *password = generate_random_str(s->join_ctx1, 8); + struct creds_CredentialState *creds_state; + struct dcerpc_pipe *net_pipe; + + status = dcerpc_pipe_connect_b(s, &net_pipe, s->b, + &ndr_table_netlogon, + s->wks_creds1, + torture->ev, torture->lp_ctx); + + torture_assert_ntstatus_ok(torture, status, + "dcerpc_pipe_connect_b failed"); + + pwset.in.server_name = talloc_asprintf( + net_pipe, "\\\\%s", dcerpc_server_name(net_pipe)); + pwset.in.computer_name = + cli_credentials_get_workstation(s->wks_creds1); + pwset.in.account_name = talloc_asprintf( + net_pipe, "%s$", pwset.in.computer_name); + pwset.in.secure_channel_type = SEC_CHAN_WKSTA; + E_md4hash(password, pwset.in.new_password.hash); + + creds_state = cli_credentials_get_netlogon_creds( + s->wks_creds1); + creds_des_encrypt(creds_state, &pwset.in.new_password); + creds_client_authenticator(creds_state, &pwset.in.credential); + + status = dcerpc_netr_ServerPasswordSet(net_pipe, torture, &pwset); + torture_assert_ntstatus_ok(torture, status, + "ServerPasswordSet failed"); + + if (!creds_client_check(creds_state, + &pwset.out.return_authenticator.cred)) { + printf("Credential chaining failed\n"); + } + + cli_credentials_set_password(s->wks_creds1, password, + CRED_SPECIFIED); + + talloc_free(net_pipe); + + /* Just as a test, connect with the new creds */ + + talloc_free(s->wks_creds1->netlogon_creds); + s->wks_creds1->netlogon_creds = NULL; + + status = dcerpc_pipe_connect_b(s, &net_pipe, s->b, + &ndr_table_netlogon, + s->wks_creds1, + torture->ev, torture->lp_ctx); + + torture_assert_ntstatus_ok(torture, status, + "dcerpc_pipe_connect_b failed"); + + talloc_free(net_pipe); + } + + torture_comment(torture, "Start looping LogonSamLogonEx on %d connections for %d secs\n", + s->nprocs, s->timelimit); + for (i=0; i < s->nprocs; i++) { + ret = torture_schannel_bench_start(&s->conns[i]); + torture_assert(torture, ret, "Failed to setup LogonSamLogonEx"); + } + + start = timeval_current(); + end = timeval_add(&start, s->timelimit, 0); + + while (NT_STATUS_IS_OK(s->error) && !timeval_expired(&end)) { + int ev_ret = event_loop_once(torture->ev); + torture_assert(torture, ev_ret == 0, "event_loop_once failed"); + } + torture_assert_ntstatus_ok(torture, s->error, "Failed some request"); + s->stopped = true; + talloc_free(s->conns); + + for (i=0; i < s->nprocs; i++) { + s->total += s->conns[i].total; + } + + torture_comment(torture, + "Total ops[%llu] (%u ops/s)\n", + (unsigned long long)s->total, + (unsigned)s->total/s->timelimit); + + torture_leave_domain(torture, s->join_ctx1); + torture_leave_domain(torture, s->join_ctx2); + return true; +} diff --git a/source4/torture/rpc/session_key.c b/source4/torture/rpc/session_key.c new file mode 100644 index 0000000000..c069c62724 --- /dev/null +++ b/source4/torture/rpc/session_key.c @@ -0,0 +1,233 @@ +/* + Unix SMB/CIFS implementation. + test suite for lsa rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" + +#include "libcli/auth/libcli_auth.h" +#include "torture/rpc/rpc.h" +#include "lib/cmdline/popt_common.h" +#include "param/param.h" + +static void init_lsa_String(struct lsa_String *name, const char *s) +{ + name->string = s; +} + +static bool test_CreateSecret_basic(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct lsa_CreateSecret r; + struct lsa_SetSecret r3; + struct lsa_QuerySecret r4; + struct policy_handle sec_handle; + struct lsa_DeleteObject d; + struct lsa_DATA_BUF buf1; + struct lsa_DATA_BUF_PTR bufp1; + DATA_BLOB enc_key; + DATA_BLOB session_key; + NTTIME old_mtime, new_mtime; + DATA_BLOB blob1, blob2; + const char *secret1 = "abcdef12345699qwerty"; + char *secret2; + char *secname; + + secname = talloc_asprintf(tctx, "torturesecret-%u", (uint_t)random()); + + torture_comment(tctx, "Testing CreateSecret of %s\n", secname); + + init_lsa_String(&r.in.name, secname); + + r.in.handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.sec_handle = &sec_handle; + + status = dcerpc_lsa_CreateSecret(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "CreateSecret failed"); + + status = dcerpc_fetch_session_key(p, &session_key); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_fetch_session_key failed"); + + enc_key = sess_encrypt_string(secret1, &session_key); + + r3.in.sec_handle = &sec_handle; + r3.in.new_val = &buf1; + r3.in.old_val = NULL; + r3.in.new_val->data = enc_key.data; + r3.in.new_val->length = enc_key.length; + r3.in.new_val->size = enc_key.length; + + torture_comment(tctx, "Testing SetSecret\n"); + + status = dcerpc_lsa_SetSecret(p, tctx, &r3); + torture_assert_ntstatus_ok(tctx, status, "SetSecret failed"); + + r3.in.sec_handle = &sec_handle; + r3.in.new_val = &buf1; + r3.in.old_val = NULL; + r3.in.new_val->data = enc_key.data; + r3.in.new_val->length = enc_key.length; + r3.in.new_val->size = enc_key.length; + + /* break the encrypted data */ + enc_key.data[0]++; + + torture_comment(tctx, "Testing SetSecret with broken key\n"); + + status = dcerpc_lsa_SetSecret(p, tctx, &r3); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_UNKNOWN_REVISION, + "SetSecret should have failed UNKNOWN_REVISION"); + + data_blob_free(&enc_key); + + ZERO_STRUCT(new_mtime); + ZERO_STRUCT(old_mtime); + + /* fetch the secret back again */ + r4.in.sec_handle = &sec_handle; + r4.in.new_val = &bufp1; + r4.in.new_mtime = &new_mtime; + r4.in.old_val = NULL; + r4.in.old_mtime = NULL; + + bufp1.buf = NULL; + + torture_comment(tctx, "Testing QuerySecret\n"); + status = dcerpc_lsa_QuerySecret(p, tctx, &r4); + torture_assert_ntstatus_ok(tctx, status, "QuerySecret failed"); + if (r4.out.new_val == NULL || r4.out.new_val->buf == NULL) + torture_fail(tctx, "No secret buffer returned"); + blob1.data = r4.out.new_val->buf->data; + blob1.length = r4.out.new_val->buf->size; + + blob2 = data_blob_talloc(tctx, NULL, blob1.length); + + secret2 = sess_decrypt_string(tctx, &blob1, &session_key); + + torture_assert_str_equal(tctx, secret1, secret2, "Returned secret invalid"); + + d.in.handle = &sec_handle; + d.out.handle = &sec_handle; + status = dcerpc_lsa_DeleteObject(p, tctx, &d); + torture_assert_ntstatus_ok(tctx, status, "delete should have returned OKINVALID_HANDLE"); + return true; +} + +struct secret_settings { + uint32_t bindoptions; + bool keyexchange; + bool ntlm2; + bool lm_key; +}; + +static bool test_secrets(struct torture_context *torture, const void *_data) +{ + struct dcerpc_pipe *p; + struct policy_handle *handle; + struct dcerpc_binding *binding; + const struct secret_settings *settings = + (const struct secret_settings *)_data; + + lp_set_cmdline(torture->lp_ctx, "ntlmssp client:keyexchange", settings->keyexchange?"True":"False"); + lp_set_cmdline(torture->lp_ctx, "ntlmssp_client:ntlm2", settings->ntlm2?"True":"False"); + lp_set_cmdline(torture->lp_ctx, "ntlmssp_client:lm_key", settings->lm_key?"True":"False"); + + torture_assert_ntstatus_ok(torture, torture_rpc_binding(torture, &binding), + "Getting bindoptions"); + + binding->flags |= settings->bindoptions; + + torture_assert_ntstatus_ok(torture, + dcerpc_pipe_connect_b(torture, &p, binding, + &ndr_table_lsarpc, + cmdline_credentials, + torture->ev, + torture->lp_ctx), + "connect"); + + if (!test_lsa_OpenPolicy2(p, torture, &handle)) { + return false; + } + + torture_assert(torture, handle, "OpenPolicy2 failed. This test cannot run against this server"); + + if (!test_CreateSecret_basic(p, torture, handle)) { + return false; + } + + return true; +} + +static struct torture_tcase *add_test(struct torture_suite *suite, uint32_t bindoptions, + bool keyexchange, bool ntlm2, bool lm_key) +{ + char *name = NULL; + struct secret_settings *settings; + + settings = talloc_zero(suite, struct secret_settings); + settings->bindoptions = bindoptions; + + if (bindoptions == DCERPC_PUSH_BIGENDIAN) + name = talloc_strdup(suite, "bigendian"); + else if (bindoptions == DCERPC_SEAL) + name = talloc_strdup(suite, "seal"); + else if (bindoptions == 0) + name = talloc_strdup(suite, "none"); + else + name = talloc_strdup(suite, "unknown"); + + name = talloc_asprintf_append_buffer(name, " keyexchange:%s", keyexchange?"yes":"no"); + settings->keyexchange = keyexchange; + + name = talloc_asprintf_append_buffer(name, " ntlm2:%s", ntlm2?"yes":"no"); + settings->ntlm2 = ntlm2; + + name = talloc_asprintf_append_buffer(name, " lm_key:%s", lm_key?"yes":"no"); + settings->lm_key = lm_key; + + return torture_suite_add_simple_tcase_const(suite, name, test_secrets, + settings); +} + +static const bool bool_vals[] = { true, false }; + +/* TEST session key correctness by pushing and pulling secrets */ +struct torture_suite *torture_rpc_lsa_secrets(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "SECRETS"); + int keyexchange, ntlm2, lm_key; + + for (keyexchange = 0; keyexchange < ARRAY_SIZE(bool_vals); keyexchange++) { + for (ntlm2 = 0; ntlm2 < ARRAY_SIZE(bool_vals); ntlm2++) { + for (lm_key = 0; lm_key < ARRAY_SIZE(bool_vals); lm_key++) { + add_test(suite, DCERPC_PUSH_BIGENDIAN, bool_vals[keyexchange], bool_vals[ntlm2], + bool_vals[lm_key]); + add_test(suite, DCERPC_SEAL, bool_vals[keyexchange], bool_vals[ntlm2], bool_vals[lm_key]); + add_test(suite, 0, bool_vals[keyexchange], bool_vals[ntlm2], bool_vals[lm_key]); + } + } + } + + return suite; +} diff --git a/source4/torture/rpc/spoolss.c b/source4/torture/rpc/spoolss.c new file mode 100644 index 0000000000..4a6ff480c4 --- /dev/null +++ b/source4/torture/rpc/spoolss.c @@ -0,0 +1,1769 @@ +/* + Unix SMB/CIFS implementation. + test suite for spoolss rpc operations + + Copyright (C) Tim Potter 2003 + Copyright (C) Stefan Metzmacher 2005 + Copyright (C) Jelmer Vernooij 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/rpc/rpc.h" +#include "librpc/gen_ndr/ndr_spoolss_c.h" + +struct test_spoolss_context { + /* print server handle */ + struct policy_handle server_handle; + + /* for EnumPorts */ + uint32_t port_count[3]; + union spoolss_PortInfo *ports[3]; + + /* for EnumPrinterDrivers */ + uint32_t driver_count[7]; + union spoolss_DriverInfo *drivers[7]; + + /* for EnumMonitors */ + uint32_t monitor_count[3]; + union spoolss_MonitorInfo *monitors[3]; + + /* for EnumPrintProcessors */ + uint32_t print_processor_count[2]; + union spoolss_PrintProcessorInfo *print_processors[2]; + + /* for EnumPrinters */ + uint32_t printer_count[6]; + union spoolss_PrinterInfo *printers[6]; +}; + +#define COMPARE_STRING(tctx, c,r,e) \ + torture_assert_str_equal(tctx, c.e, r.e, "invalid value") + +/* not every compiler supports __typeof__() */ +#if (__GNUC__ >= 3) +#define _CHECK_FIELD_SIZE(c,r,e,type) do {\ + if (sizeof(__typeof__(c.e)) != sizeof(type)) { \ + torture_fail(tctx, #c "." #e "field is not " #type "\n"); \ + }\ + if (sizeof(__typeof__(r.e)) != sizeof(type)) { \ + torture_fail(tctx, #r "." #e "field is not " #type "\n"); \ + }\ +} while(0) +#else +#define _CHECK_FIELD_SIZE(c,r,e,type) do {} while(0) +#endif + +#define COMPARE_UINT32(tctx, c, r, e) do {\ + _CHECK_FIELD_SIZE(c, r, e, uint32_t); \ + torture_assert_int_equal(tctx, c.e, r.e, "invalid value"); \ +} while(0) + +#define COMPARE_STRING_ARRAY(tctx, c,r,e) + +static bool test_OpenPrinter_server(struct torture_context *tctx, struct dcerpc_pipe *p, struct test_spoolss_context *ctx) +{ + NTSTATUS status; + struct spoolss_OpenPrinter op; + + op.in.printername = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p)); + op.in.datatype = NULL; + op.in.devmode_ctr.devmode= NULL; + op.in.access_mask = 0; + op.out.handle = &ctx->server_handle; + + torture_comment(tctx, "Testing OpenPrinter(%s)\n", op.in.printername); + + status = dcerpc_spoolss_OpenPrinter(p, ctx, &op); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_OpenPrinter failed"); + torture_assert_werr_ok(tctx, op.out.result, "dcerpc_spoolss_OpenPrinter failed"); + + return true; +} + +static bool test_EnumPorts(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct test_spoolss_context *ctx) +{ + NTSTATUS status; + struct spoolss_EnumPorts r; + uint16_t levels[] = { 1, 2 }; + int i, j; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + DATA_BLOB blob; + + r.in.servername = ""; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + + torture_comment(tctx, "Testing EnumPorts level %u\n", r.in.level); + + status = dcerpc_spoolss_EnumPorts(p, ctx, &r); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPorts failed"); + if (W_ERROR_IS_OK(r.out.result)) { + /* TODO: do some more checks here */ + continue; + } + torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER, + "EnumPorts unexpected return code"); + + blob = data_blob_talloc(ctx, NULL, r.out.needed); + data_blob_clear(&blob); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_EnumPorts(p, ctx, &r); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPorts failed"); + + torture_assert_werr_ok(tctx, r.out.result, "EnumPorts failed"); + + ctx->port_count[level] = r.out.count; + ctx->ports[level] = r.out.info; + } + + for (i=1;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + int old_level = levels[i-1]; + torture_assert_int_equal(tctx, ctx->port_count[level], ctx->port_count[old_level], + "EnumPorts invalid value"); + } + /* if the array sizes are not the same we would maybe segfault in the following code */ + + for (i=0;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + for (j=0;j<ctx->port_count[level];j++) { + union spoolss_PortInfo *cur = &ctx->ports[level][j]; + union spoolss_PortInfo *ref = &ctx->ports[2][j]; + switch (level) { + case 1: + COMPARE_STRING(tctx, cur->info1, ref->info2, port_name); + break; + case 2: + /* level 2 is our reference, and it makes no sense to compare it to itself */ + break; + } + } + } + + return true; +} + +static bool test_GetPrinterDriverDirectory(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct test_spoolss_context *ctx) +{ + NTSTATUS status; + struct spoolss_GetPrinterDriverDirectory r; + struct { + uint16_t level; + const char *server; + } levels[] = {{ + .level = 1, + .server = NULL + },{ + .level = 1, + .server = "" + },{ + .level = 78, + .server = "" + },{ + .level = 1, + .server = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p)) + },{ + .level = 1024, + .server = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p)) + } + }; + int i; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + int level = levels[i].level; + DATA_BLOB blob; + + r.in.server = levels[i].server; + r.in.environment = SPOOLSS_ARCHITECTURE_NT_X86; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + + torture_comment(tctx, "Testing GetPrinterDriverDirectory level %u\n", r.in.level); + + status = dcerpc_spoolss_GetPrinterDriverDirectory(p, ctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "dcerpc_spoolss_GetPrinterDriverDirectory failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER, + "GetPrinterDriverDirectory unexpected return code"); + + blob = data_blob_talloc(ctx, NULL, r.out.needed); + data_blob_clear(&blob); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_GetPrinterDriverDirectory(p, ctx, &r); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_GetPrinterDriverDirectory failed"); + + torture_assert_werr_ok(tctx, r.out.result, "GetPrinterDriverDirectory failed"); + } + + return true; +} + +static bool test_EnumPrinterDrivers(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct test_spoolss_context *ctx) +{ + NTSTATUS status; + struct spoolss_EnumPrinterDrivers r; + uint16_t levels[] = { 1, 2, 3, 4, 5, 6 }; + int i, j; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + DATA_BLOB blob; + + r.in.server = ""; + r.in.environment = SPOOLSS_ARCHITECTURE_NT_X86; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + + torture_comment(tctx, "Testing EnumPrinterDrivers level %u\n", r.in.level); + + status = dcerpc_spoolss_EnumPrinterDrivers(p, ctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "dcerpc_spoolss_EnumPrinterDrivers failed"); + if (W_ERROR_IS_OK(r.out.result)) { + /* TODO: do some more checks here */ + continue; + } + torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER, + "EnumPrinterDrivers failed"); + + blob = data_blob_talloc(ctx, NULL, r.out.needed); + data_blob_clear(&blob); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_EnumPrinterDrivers(p, ctx, &r); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrinterDrivers failed"); + + torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterDrivers failed"); + + ctx->driver_count[level] = r.out.count; + ctx->drivers[level] = r.out.info; + } + + for (i=1;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + int old_level = levels[i-1]; + torture_assert_int_equal(tctx, ctx->driver_count[level], ctx->driver_count[old_level], + "EnumPrinterDrivers invalid value"); + } + + for (i=0;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + for (j=0;j<ctx->driver_count[level];j++) { + union spoolss_DriverInfo *cur = &ctx->drivers[level][j]; + union spoolss_DriverInfo *ref = &ctx->drivers[6][j]; + switch (level) { + case 1: + COMPARE_STRING(tctx, cur->info1, ref->info6, driver_name); + break; + case 2: + COMPARE_UINT32(tctx, cur->info2, ref->info6, version); + COMPARE_STRING(tctx, cur->info2, ref->info6, driver_name); + COMPARE_STRING(tctx, cur->info2, ref->info6, architecture); + COMPARE_STRING(tctx, cur->info2, ref->info6, driver_path); + COMPARE_STRING(tctx, cur->info2, ref->info6, data_file); + COMPARE_STRING(tctx, cur->info2, ref->info6, config_file); + break; + case 3: + COMPARE_UINT32(tctx, cur->info3, ref->info6, version); + COMPARE_STRING(tctx, cur->info3, ref->info6, driver_name); + COMPARE_STRING(tctx, cur->info3, ref->info6, architecture); + COMPARE_STRING(tctx, cur->info3, ref->info6, driver_path); + COMPARE_STRING(tctx, cur->info3, ref->info6, data_file); + COMPARE_STRING(tctx, cur->info3, ref->info6, config_file); + COMPARE_STRING(tctx, cur->info3, ref->info6, help_file); + COMPARE_STRING_ARRAY(tctx, cur->info3, ref->info6, dependent_files); + COMPARE_STRING(tctx, cur->info3, ref->info6, monitor_name); + COMPARE_STRING(tctx, cur->info3, ref->info6, default_datatype); + break; + case 4: + COMPARE_UINT32(tctx, cur->info4, ref->info6, version); + COMPARE_STRING(tctx, cur->info4, ref->info6, driver_name); + COMPARE_STRING(tctx, cur->info4, ref->info6, architecture); + COMPARE_STRING(tctx, cur->info4, ref->info6, driver_path); + COMPARE_STRING(tctx, cur->info4, ref->info6, data_file); + COMPARE_STRING(tctx, cur->info4, ref->info6, config_file); + COMPARE_STRING(tctx, cur->info4, ref->info6, help_file); + COMPARE_STRING_ARRAY(tctx, cur->info4, ref->info6, dependent_files); + COMPARE_STRING(tctx, cur->info4, ref->info6, monitor_name); + COMPARE_STRING(tctx, cur->info4, ref->info6, default_datatype); + COMPARE_STRING_ARRAY(tctx, cur->info4, ref->info6, previous_names); + break; + case 5: + COMPARE_UINT32(tctx, cur->info5, ref->info6, version); + COMPARE_STRING(tctx, cur->info5, ref->info6, driver_name); + COMPARE_STRING(tctx, cur->info5, ref->info6, architecture); + COMPARE_STRING(tctx, cur->info5, ref->info6, driver_path); + COMPARE_STRING(tctx, cur->info5, ref->info6, data_file); + COMPARE_STRING(tctx, cur->info5, ref->info6, config_file); + /*COMPARE_UINT32(tctx, cur->info5, ref->info6, driver_attributes);*/ + /*COMPARE_UINT32(tctx, cur->info5, ref->info6, config_version);*/ + /*TODO: ! COMPARE_UINT32(tctx, cur->info5, ref->info6, driver_version); */ + break; + case 6: + /* level 6 is our reference, and it makes no sense to compare it to itself */ + break; + } + } + } + + return true; +} + +static bool test_EnumMonitors(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct test_spoolss_context *ctx) +{ + NTSTATUS status; + struct spoolss_EnumMonitors r; + uint16_t levels[] = { 1, 2 }; + int i, j; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + DATA_BLOB blob; + + r.in.servername = ""; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + + torture_comment(tctx, "Testing EnumMonitors level %u\n", r.in.level); + + status = dcerpc_spoolss_EnumMonitors(p, ctx, &r); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumMonitors failed"); + if (W_ERROR_IS_OK(r.out.result)) { + /* TODO: do some more checks here */ + continue; + } + torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER, + "EnumMonitors failed"); + + blob = data_blob_talloc(ctx, NULL, r.out.needed); + data_blob_clear(&blob); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_EnumMonitors(p, ctx, &r); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumMonitors failed"); + + torture_assert_werr_ok(tctx, r.out.result, "EnumMonitors failed"); + + ctx->monitor_count[level] = r.out.count; + ctx->monitors[level] = r.out.info; + } + + for (i=1;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + int old_level = levels[i-1]; + torture_assert_int_equal(tctx, ctx->monitor_count[level], ctx->monitor_count[old_level], + "EnumMonitors invalid value"); + } + + for (i=0;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + for (j=0;j<ctx->monitor_count[level];j++) { + union spoolss_MonitorInfo *cur = &ctx->monitors[level][j]; + union spoolss_MonitorInfo *ref = &ctx->monitors[2][j]; + switch (level) { + case 1: + COMPARE_STRING(tctx, cur->info1, ref->info2, monitor_name); + break; + case 2: + /* level 2 is our reference, and it makes no sense to compare it to itself */ + break; + } + } + } + + return true; +} + +static bool test_EnumPrintProcessors(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct test_spoolss_context *ctx) +{ + NTSTATUS status; + struct spoolss_EnumPrintProcessors r; + uint16_t levels[] = { 1 }; + int i, j; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + DATA_BLOB blob; + + r.in.servername = ""; + r.in.environment = "Windows NT x86"; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + + torture_comment(tctx, "Testing EnumPrintProcessors level %u\n", r.in.level); + + status = dcerpc_spoolss_EnumPrintProcessors(p, ctx, &r); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcessors failed"); + if (W_ERROR_IS_OK(r.out.result)) { + /* TODO: do some more checks here */ + continue; + } + torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER, + "EnumPrintProcessors unexpected return code"); + + blob = data_blob_talloc(ctx, NULL, r.out.needed); + data_blob_clear(&blob); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_EnumPrintProcessors(p, ctx, &r); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcessors failed"); + + torture_assert_werr_ok(tctx, r.out.result, "EnumPrintProcessors failed"); + + ctx->print_processor_count[level] = r.out.count; + ctx->print_processors[level] = r.out.info; + } + + for (i=1;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + int old_level = levels[i-1]; + torture_assert_int_equal(tctx, ctx->print_processor_count[level], ctx->print_processor_count[old_level], + "EnumPrintProcessors failed"); + } + + for (i=0;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + for (j=0;j<ctx->print_processor_count[level];j++) { +#if 0 + union spoolss_PrintProcessorInfo *cur = &ctx->print_processors[level][j]; + union spoolss_PrintProcessorInfo *ref = &ctx->print_processors[1][j]; +#endif + switch (level) { + case 1: + /* level 1 is our reference, and it makes no sense to compare it to itself */ + break; + } + } + } + + return true; +} + +static bool test_EnumPrinters(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct test_spoolss_context *ctx) +{ + struct spoolss_EnumPrinters r; + NTSTATUS status; + uint16_t levels[] = { 0, 1, 2, 4, 5 }; + int i, j; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + DATA_BLOB blob; + + r.in.flags = PRINTER_ENUM_LOCAL; + r.in.server = ""; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + + torture_comment(tctx, "Testing EnumPrinters level %u\n", r.in.level); + + status = dcerpc_spoolss_EnumPrinters(p, ctx, &r); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrinters failed"); + if (W_ERROR_IS_OK(r.out.result)) { + /* TODO: do some more checks here */ + continue; + } + torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER, + "EnumPrinters unexpected return code"); + + blob = data_blob_talloc(ctx, NULL, r.out.needed); + data_blob_clear(&blob); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_EnumPrinters(p, ctx, &r); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrinters failed"); + + torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed"); + + ctx->printer_count[level] = r.out.count; + ctx->printers[level] = r.out.info; + } + + for (i=1;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + int old_level = levels[i-1]; + torture_assert_int_equal(tctx, ctx->printer_count[level], ctx->printer_count[old_level], + "EnumPrinters invalid value"); + } + + for (i=0;i<ARRAY_SIZE(levels);i++) { + int level = levels[i]; + for (j=0;j<ctx->printer_count[level];j++) { + union spoolss_PrinterInfo *cur = &ctx->printers[level][j]; + union spoolss_PrinterInfo *ref = &ctx->printers[2][j]; + switch (level) { + case 0: + COMPARE_STRING(tctx, cur->info0, ref->info2, printername); + COMPARE_STRING(tctx, cur->info0, ref->info2, servername); + COMPARE_UINT32(tctx, cur->info0, ref->info2, cjobs); + /*COMPARE_UINT32(tctx, cur->info0, ref->info2, total_jobs); + COMPARE_UINT32(tctx, cur->info0, ref->info2, total_bytes); + COMPARE_SPOOLSS_TIME(cur->info0, ref->info2, spoolss_Time time); + COMPARE_UINT32(tctx, cur->info0, ref->info2, global_counter); + COMPARE_UINT32(tctx, cur->info0, ref->info2, total_pages); + COMPARE_UINT32(tctx, cur->info0, ref->info2, version); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown10); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown11); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown12); + COMPARE_UINT32(tctx, cur->info0, ref->info2, session_counter); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown14); + COMPARE_UINT32(tctx, cur->info0, ref->info2, printer_errors); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown16); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown17); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown18); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown19); + COMPARE_UINT32(tctx, cur->info0, ref->info2, change_id); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown21);*/ + COMPARE_UINT32(tctx, cur->info0, ref->info2, status); + /*COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown23); + COMPARE_UINT32(tctx, cur->info0, ref->info2, c_setprinter); + COMPARE_UINT16(cur->info0, ref->info2, unknown25); + COMPARE_UINT16(cur->info0, ref->info2, unknown26); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown27); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown28); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown29);*/ + break; + case 1: + /*COMPARE_UINT32(tctx, cur->info1, ref->info2, flags);*/ + /*COMPARE_STRING(tctx, cur->info1, ref->info2, name);*/ + /*COMPARE_STRING(tctx, cur->info1, ref->info2, description);*/ + COMPARE_STRING(tctx, cur->info1, ref->info2, comment); + break; + case 2: + /* level 2 is our reference, and it makes no sense to compare it to itself */ + break; + case 4: + COMPARE_STRING(tctx, cur->info4, ref->info2, printername); + COMPARE_STRING(tctx, cur->info4, ref->info2, servername); + COMPARE_UINT32(tctx, cur->info4, ref->info2, attributes); + break; + case 5: + COMPARE_STRING(tctx, cur->info5, ref->info2, printername); + COMPARE_STRING(tctx, cur->info5, ref->info2, portname); + COMPARE_UINT32(tctx, cur->info5, ref->info2, attributes); + /*COMPARE_UINT32(tctx, cur->info5, ref->info2, device_not_selected_timeout); + COMPARE_UINT32(tctx, cur->info5, ref->info2, transmission_retry_timeout);*/ + break; + } + } + } + + /* TODO: + * - verify that the port of a printer was in the list returned by EnumPorts + */ + + return true; +} + +static bool test_GetPrinter(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_GetPrinter r; + uint16_t levels[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + int i; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + r.in.handle = handle; + r.in.level = levels[i]; + r.in.buffer = NULL; + r.in.offered = 0; + + torture_comment(tctx, "Testing GetPrinter level %u\n", r.in.level); + + status = dcerpc_spoolss_GetPrinter(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetPrinter failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed); + data_blob_clear(&blob); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + status = dcerpc_spoolss_GetPrinter(p, tctx, &r); + } + + torture_assert_ntstatus_ok(tctx, status, "GetPrinter failed"); + + torture_assert_werr_ok(tctx, r.out.result, "GetPrinter failed"); + } + + return true; +} + + +static bool test_ClosePrinter(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_ClosePrinter r; + + r.in.handle = handle; + r.out.handle = handle; + + torture_comment(tctx, "Testing ClosePrinter\n"); + + status = dcerpc_spoolss_ClosePrinter(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed"); + + return true; +} + +static bool test_GetForm(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *form_name) +{ + NTSTATUS status; + struct spoolss_GetForm r; + + r.in.handle = handle; + r.in.form_name = form_name; + r.in.level = 1; + r.in.buffer = NULL; + r.in.offered = 0; + + torture_comment(tctx, "Testing GetForm\n"); + + status = dcerpc_spoolss_GetForm(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetForm failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed); + data_blob_clear(&blob); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + status = dcerpc_spoolss_GetForm(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetForm failed"); + + torture_assert_werr_ok(tctx, r.out.result, "GetForm failed"); + + torture_assert(tctx, r.out.info, "No form info returned"); + } + + torture_assert_werr_ok(tctx, r.out.result, "GetForm failed"); + + return true; +} + +static bool test_EnumForms(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, bool print_server) +{ + NTSTATUS status; + struct spoolss_EnumForms r; + bool ret = true; + + r.in.handle = handle; + r.in.level = 1; + r.in.buffer = NULL; + r.in.offered = 0; + + torture_comment(tctx, "Testing EnumForms\n"); + + status = dcerpc_spoolss_EnumForms(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "EnumForms failed"); + + if (print_server && W_ERROR_EQUAL(r.out.result, WERR_BADFID)) + torture_fail(tctx, "EnumForms on the PrintServer isn't supported by test server (NT4)"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + union spoolss_FormInfo *info; + int j; + DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed); + data_blob_clear(&blob); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_EnumForms(p, tctx, &r); + + torture_assert(tctx, r.out.info, "No forms returned"); + + info = r.out.info; + + for (j = 0; j < r.out.count; j++) { + if (!print_server) + ret &= test_GetForm(tctx, p, handle, info[j].info1.form_name); + } + } + + torture_assert_ntstatus_ok(tctx, status, "EnumForms failed"); + + torture_assert_werr_ok(tctx, r.out.result, "EnumForms failed"); + + return true; +} + +static bool test_DeleteForm(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *form_name) +{ + NTSTATUS status; + struct spoolss_DeleteForm r; + + r.in.handle = handle; + r.in.form_name = form_name; + + status = dcerpc_spoolss_DeleteForm(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "DeleteForm failed"); + + torture_assert_werr_ok(tctx, r.out.result, "DeleteForm failed"); + + return true; +} + +static bool test_AddForm(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, bool print_server) +{ + struct spoolss_AddForm r; + struct spoolss_AddFormInfo1 addform; + const char *form_name = "testform3"; + NTSTATUS status; + bool ret = true; + + r.in.handle = handle; + r.in.level = 1; + r.in.info.info1 = &addform; + addform.flags = SPOOLSS_FORM_USER; + addform.form_name = form_name; + addform.size.width = 50; + addform.size.height = 25; + addform.area.left = 5; + addform.area.top = 10; + addform.area.right = 45; + addform.area.bottom = 15; + + status = dcerpc_spoolss_AddForm(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "AddForm failed"); + + torture_assert_werr_ok(tctx, r.out.result, "AddForm failed"); + + if (!print_server) ret &= test_GetForm(tctx, p, handle, form_name); + + { + struct spoolss_SetForm sf; + struct spoolss_AddFormInfo1 setform; + + sf.in.handle = handle; + sf.in.form_name = form_name; + sf.in.level = 1; + sf.in.info.info1= &setform; + setform.flags = addform.flags; + setform.form_name = addform.form_name; + setform.size = addform.size; + setform.area = addform.area; + + setform.size.width = 1234; + + status = dcerpc_spoolss_SetForm(p, tctx, &sf); + + torture_assert_ntstatus_ok(tctx, status, "SetForm failed"); + + torture_assert_werr_ok(tctx, r.out.result, "SetForm failed"); + } + + if (!print_server) ret &= test_GetForm(tctx, p, handle, form_name); + + if (!test_DeleteForm(tctx, p, handle, form_name)) { + ret = false; + } + + return ret; +} + +static bool test_EnumPorts_old(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct spoolss_EnumPorts r; + + r.in.servername = talloc_asprintf(tctx, "\\\\%s", + dcerpc_server_name(p)); + r.in.level = 2; + r.in.buffer = NULL; + r.in.offered = 0; + + torture_comment(tctx, "Testing EnumPorts\n"); + + status = dcerpc_spoolss_EnumPorts(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "EnumPorts failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed); + data_blob_clear(&blob); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_EnumPorts(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "EnumPorts failed"); + + torture_assert(tctx, r.out.info, "No ports returned"); + } + + return true; +} + +static bool test_AddPort(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct spoolss_AddPort r; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", + dcerpc_server_name(p)); + r.in.unknown = 0; + r.in.monitor_name = "foo"; + + torture_comment(tctx, "Testing AddPort\n"); + + status = dcerpc_spoolss_AddPort(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "AddPort failed"); + + /* win2k3 returns WERR_NOT_SUPPORTED */ + +#if 0 + + if (!W_ERROR_IS_OK(r.out.result)) { + printf("AddPort failed - %s\n", win_errstr(r.out.result)); + return false; + } + +#endif + + return true; +} + +static bool test_GetJob(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, uint32_t job_id) +{ + NTSTATUS status; + struct spoolss_GetJob r; + + r.in.handle = handle; + r.in.job_id = job_id; + r.in.level = 1; + r.in.buffer = NULL; + r.in.offered = 0; + + torture_comment(tctx, "Testing GetJob\n"); + + status = dcerpc_spoolss_GetJob(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetJob failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed); + data_blob_clear(&blob); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_GetJob(p, tctx, &r); + + torture_assert(tctx, r.out.info, "No job info returned"); + } + + return true; +} + +static bool test_SetJob(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, uint32_t job_id, + enum spoolss_JobControl command) +{ + NTSTATUS status; + struct spoolss_SetJob r; + + r.in.handle = handle; + r.in.job_id = job_id; + r.in.ctr = NULL; + r.in.command = command; + + torture_comment(tctx, "Testing SetJob\n"); + + status = dcerpc_spoolss_SetJob(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "SetJob failed"); + torture_assert_werr_ok(tctx, r.out.result, "SetJob failed"); + + return true; +} + +static bool test_EnumJobs(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_EnumJobs r; + + r.in.handle = handle; + r.in.firstjob = 0; + r.in.numjobs = 0xffffffff; + r.in.level = 1; + r.in.buffer = NULL; + r.in.offered = 0; + + torture_comment(tctx, "Testing EnumJobs\n"); + + status = dcerpc_spoolss_EnumJobs(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + union spoolss_JobInfo *info; + int j; + DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed); + data_blob_clear(&blob); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_EnumJobs(p, tctx, &r); + + torture_assert(tctx, r.out.info, "No jobs returned"); + + info = r.out.info; + + for (j = 0; j < r.out.count; j++) { + test_GetJob(tctx, p, handle, info[j].info1.job_id); + test_SetJob(tctx, p, handle, info[j].info1.job_id, SPOOLSS_JOB_CONTROL_PAUSE); + test_SetJob(tctx, p, handle, info[j].info1.job_id, SPOOLSS_JOB_CONTROL_RESUME); + } + + } else { + torture_assert_werr_ok(tctx, r.out.result, "EnumJobs failed"); + } + + return true; +} + +static bool test_DoPrintTest(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + bool ret = true; + NTSTATUS status; + struct spoolss_StartDocPrinter s; + struct spoolss_DocumentInfo1 info1; + struct spoolss_StartPagePrinter sp; + struct spoolss_WritePrinter w; + struct spoolss_EndPagePrinter ep; + struct spoolss_EndDocPrinter e; + int i; + uint32_t job_id; + + torture_comment(tctx, "Testing StartDocPrinter\n"); + + s.in.handle = handle; + s.in.level = 1; + s.in.info.info1 = &info1; + info1.document_name = "TorturePrintJob"; + info1.output_file = NULL; + info1.datatype = "RAW"; + + status = dcerpc_spoolss_StartDocPrinter(p, tctx, &s); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_StartDocPrinter failed"); + torture_assert_werr_ok(tctx, s.out.result, "StartDocPrinter failed"); + + job_id = s.out.job_id; + + for (i=1; i < 4; i++) { + torture_comment(tctx, "Testing StartPagePrinter: Page[%d]\n", i); + + sp.in.handle = handle; + + status = dcerpc_spoolss_StartPagePrinter(p, tctx, &sp); + torture_assert_ntstatus_ok(tctx, status, + "dcerpc_spoolss_StartPagePrinter failed"); + torture_assert_werr_ok(tctx, sp.out.result, "StartPagePrinter failed"); + + torture_comment(tctx, "Testing WritePrinter: Page[%d]\n", i); + + w.in.handle = handle; + w.in.data = data_blob_string_const(talloc_asprintf(tctx,"TortureTestPage: %d\nData\n",i)); + + status = dcerpc_spoolss_WritePrinter(p, tctx, &w); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_WritePrinter failed"); + torture_assert_werr_ok(tctx, w.out.result, "WritePrinter failed"); + + torture_comment(tctx, "Testing EndPagePrinter: Page[%d]\n", i); + + ep.in.handle = handle; + + status = dcerpc_spoolss_EndPagePrinter(p, tctx, &ep); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EndPagePrinter failed"); + torture_assert_werr_ok(tctx, ep.out.result, "EndPagePrinter failed"); + } + + torture_comment(tctx, "Testing EndDocPrinter\n"); + + e.in.handle = handle; + + status = dcerpc_spoolss_EndDocPrinter(p, tctx, &e); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EndDocPrinter failed"); + torture_assert_werr_ok(tctx, e.out.result, "EndDocPrinter failed"); + + ret &= test_EnumJobs(tctx, p, handle); + + ret &= test_SetJob(tctx, p, handle, job_id, SPOOLSS_JOB_CONTROL_DELETE); + + return ret; +} + +static bool test_PausePrinter(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_SetPrinter r; + + r.in.handle = handle; + r.in.level = 0; + r.in.info.info1 = NULL; + r.in.devmode_ctr.devmode= NULL; + r.in.secdesc_ctr.sd = NULL; + r.in.command = SPOOLSS_PRINTER_CONTROL_PAUSE; + + torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_PAUSE\n"); + + status = dcerpc_spoolss_SetPrinter(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "SetPrinter failed"); + + torture_assert_werr_ok(tctx, r.out.result, "SetPrinter failed"); + + return true; +} + +static bool test_ResumePrinter(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_SetPrinter r; + + r.in.handle = handle; + r.in.level = 0; + r.in.info.info1 = NULL; + r.in.devmode_ctr.devmode= NULL; + r.in.secdesc_ctr.sd = NULL; + r.in.command = SPOOLSS_PRINTER_CONTROL_RESUME; + + torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_RESUME\n"); + + status = dcerpc_spoolss_SetPrinter(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "SetPrinter failed"); + + torture_assert_werr_ok(tctx, r.out.result, "SetPrinter failed"); + + return true; +} + +static bool test_GetPrinterData(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *value_name) +{ + NTSTATUS status; + struct spoolss_GetPrinterData r; + + r.in.handle = handle; + r.in.value_name = value_name; + r.in.offered = 0; + + torture_comment(tctx, "Testing GetPrinterData\n"); + + status = dcerpc_spoolss_GetPrinterData(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetPrinterData failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_GetPrinterData(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetPrinterData failed"); + + torture_assert_werr_ok(tctx, r.out.result, "GetPrinterData failed"); + } + + return true; +} + +static bool test_GetPrinterDataEx(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *key_name, + const char *value_name) +{ + NTSTATUS status; + struct spoolss_GetPrinterDataEx r; + + r.in.handle = handle; + r.in.key_name = key_name; + r.in.value_name = value_name; + r.in.offered = 0; + + torture_comment(tctx, "Testing GetPrinterDataEx\n"); + + status = dcerpc_spoolss_GetPrinterDataEx(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status,NT_STATUS_NET_WRITE_FAULT) && + p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) { + torture_skip(tctx, "GetPrinterDataEx not supported by server\n"); + } + torture_assert_ntstatus_ok(tctx, status, "GetPrinterDataEx failed"); + } + + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_GetPrinterDataEx(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetPrinterDataEx failed"); + + torture_assert_werr_ok(tctx, r.out.result, "GetPrinterDataEx failed"); + } + + return true; +} + +static bool test_EnumPrinterData(struct torture_context *tctx, struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_EnumPrinterData r; + + ZERO_STRUCT(r); + r.in.handle = handle; + r.in.enum_index = 0; + + do { + uint32_t value_size = 0; + uint32_t data_size = 0; + uint32_t printerdata_type = 0; + DATA_BLOB data = data_blob(NULL,0); + + r.in.value_offered = value_size; + r.out.value_needed = &value_size; + r.in.data_offered = data_size; + r.out.data_needed = &data_size; + + r.out.printerdata_type = &printerdata_type; + r.out.buffer = &data; + + torture_comment(tctx, "Testing EnumPrinterData\n"); + + status = dcerpc_spoolss_EnumPrinterData(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "EnumPrinterData failed"); + + r.in.value_offered = value_size; + r.in.data_offered = data_size; + + status = dcerpc_spoolss_EnumPrinterData(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "EnumPrinterData failed"); + + test_GetPrinterData(tctx, p, handle, r.out.value_name); + + test_GetPrinterDataEx(tctx, + p, handle, "PrinterDriverData", + r.out.value_name); + + r.in.enum_index++; + + } while (W_ERROR_IS_OK(r.out.result)); + + return true; +} + +static bool test_EnumPrinterDataEx(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_EnumPrinterDataEx r; + + r.in.handle = handle; + r.in.key_name = "PrinterDriverData"; + r.in.offered = 0; + + torture_comment(tctx, "Testing EnumPrinterDataEx\n"); + + status = dcerpc_spoolss_EnumPrinterDataEx(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDataEx failed"); + + r.in.offered = r.out.needed; + + status = dcerpc_spoolss_EnumPrinterDataEx(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDataEx failed"); + + return true; +} + + +static bool test_DeletePrinterData(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *value_name) +{ + NTSTATUS status; + struct spoolss_DeletePrinterData r; + + r.in.handle = handle; + r.in.value_name = value_name; + + torture_comment(tctx, "Testing DeletePrinterData\n"); + + status = dcerpc_spoolss_DeletePrinterData(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "DeletePrinterData failed"); + + return true; +} + +static bool test_SetPrinterData(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_SetPrinterData r; + const char *value_name = "spottyfoot"; + + r.in.handle = handle; + r.in.value_name = value_name; + r.in.type = SPOOLSS_PRINTER_DATA_TYPE_STRING; + r.in.data.string = "dog"; + + torture_comment(tctx, "Testing SetPrinterData\n"); + + status = dcerpc_spoolss_SetPrinterData(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "SetPrinterData failed"); + + if (!test_GetPrinterData(tctx, p, handle, value_name)) { + return false; + } + + if (!test_DeletePrinterData(tctx, p, handle, value_name)) { + return false; + } + + return true; +} + +static bool test_SecondaryClosePrinter(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + NTSTATUS status; + struct dcerpc_binding *b; + struct dcerpc_pipe *p2; + struct spoolss_ClosePrinter cp; + + /* only makes sense on SMB */ + if (p->conn->transport.transport != NCACN_NP) { + return true; + } + + torture_comment(tctx, "testing close on secondary pipe\n"); + + status = dcerpc_parse_binding(tctx, p->conn->binding_string, &b); + torture_assert_ntstatus_ok(tctx, status, "Failed to parse dcerpc binding"); + + status = dcerpc_secondary_connection(p, &p2, b); + torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection"); + + status = dcerpc_bind_auth_none(p2, &ndr_table_spoolss); + torture_assert_ntstatus_ok(tctx, status, "Failed to create bind on secondary connection"); + + cp.in.handle = handle; + cp.out.handle = handle; + + status = dcerpc_spoolss_ClosePrinter(p2, tctx, &cp); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NET_WRITE_FAULT, + "ERROR: Allowed close on secondary connection"); + + torture_assert_int_equal(tctx, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, + "Unexpected fault code"); + + talloc_free(p2); + + return true; +} + +static bool test_OpenPrinter_badname(struct torture_context *tctx, + struct dcerpc_pipe *p, const char *name) +{ + NTSTATUS status; + struct spoolss_OpenPrinter op; + struct spoolss_OpenPrinterEx opEx; + struct policy_handle handle; + bool ret = true; + + op.in.printername = name; + op.in.datatype = NULL; + op.in.devmode_ctr.devmode= NULL; + op.in.access_mask = 0; + op.out.handle = &handle; + + torture_comment(tctx, "\nTesting OpenPrinter(%s) with bad name\n", op.in.printername); + + status = dcerpc_spoolss_OpenPrinter(p, tctx, &op); + torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed"); + if (!W_ERROR_EQUAL(WERR_INVALID_PRINTER_NAME,op.out.result)) { + torture_comment(tctx, "OpenPrinter(%s) unexpected result[%s] should be WERR_INVALID_PRINTER_NAME\n", + name, win_errstr(op.out.result)); + } + + if (W_ERROR_IS_OK(op.out.result)) { + ret &=test_ClosePrinter(tctx, p, &handle); + } + + opEx.in.printername = name; + opEx.in.datatype = NULL; + opEx.in.devmode_ctr.devmode = NULL; + opEx.in.access_mask = 0; + opEx.in.level = 1; + opEx.in.userlevel.level1 = NULL; + opEx.out.handle = &handle; + + torture_comment(tctx, "Testing OpenPrinterEx(%s) with bad name\n", opEx.in.printername); + + status = dcerpc_spoolss_OpenPrinterEx(p, tctx, &opEx); + torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed"); + if (!W_ERROR_EQUAL(WERR_INVALID_PARAM,opEx.out.result)) { + torture_comment(tctx, "OpenPrinterEx(%s) unexpected result[%s] should be WERR_INVALID_PARAM\n", + name, win_errstr(opEx.out.result)); + } + + if (W_ERROR_IS_OK(opEx.out.result)) { + ret &=test_ClosePrinter(tctx, p, &handle); + } + + return ret; +} + +static bool test_OpenPrinter(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *name) +{ + NTSTATUS status; + struct spoolss_OpenPrinter r; + struct policy_handle handle; + bool ret = true; + + r.in.printername = talloc_asprintf(tctx, "\\\\%s\\%s", dcerpc_server_name(p), name); + r.in.datatype = NULL; + r.in.devmode_ctr.devmode= NULL; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + torture_comment(tctx, "Testing OpenPrinter(%s)\n", r.in.printername); + + status = dcerpc_spoolss_OpenPrinter(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed"); + + torture_assert_werr_ok(tctx, r.out.result, "OpenPrinter failed"); + + if (!test_GetPrinter(tctx, p, &handle)) { + ret = false; + } + + if (!test_SecondaryClosePrinter(tctx, p, &handle)) { + ret = false; + } + + if (!test_ClosePrinter(tctx, p, &handle)) { + ret = false; + } + + return ret; +} + +static bool call_OpenPrinterEx(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *name, struct policy_handle *handle) +{ + struct spoolss_OpenPrinterEx r; + struct spoolss_UserLevel1 userlevel1; + NTSTATUS status; + + if (name && name[0]) { + r.in.printername = talloc_asprintf(tctx, "\\\\%s\\%s", + dcerpc_server_name(p), name); + } else { + r.in.printername = talloc_asprintf(tctx, "\\\\%s", + dcerpc_server_name(p)); + } + + r.in.datatype = NULL; + r.in.devmode_ctr.devmode= NULL; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.level = 1; + r.in.userlevel.level1 = &userlevel1; + r.out.handle = handle; + + userlevel1.size = 1234; + userlevel1.client = "hello"; + userlevel1.user = "spottyfoot!"; + userlevel1.build = 1; + userlevel1.major = 2; + userlevel1.minor = 3; + userlevel1.processor = 4; + + torture_comment(tctx, "Testing OpenPrinterEx(%s)\n", r.in.printername); + + status = dcerpc_spoolss_OpenPrinterEx(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed"); + + torture_assert_werr_ok(tctx, r.out.result, "OpenPrinterEx failed"); + + return true; +} + +static bool test_OpenPrinterEx(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *name) +{ + struct policy_handle handle; + bool ret = true; + + if (!call_OpenPrinterEx(tctx, p, name, &handle)) { + return false; + } + + if (!test_GetPrinter(tctx, p, &handle)) { + ret = false; + } + + if (!test_EnumForms(tctx, p, &handle, false)) { + ret = false; + } + + if (!test_AddForm(tctx, p, &handle, false)) { + ret = false; + } + + if (!test_EnumPrinterData(tctx, p, &handle)) { + ret = false; + } + + if (!test_EnumPrinterDataEx(tctx, p, &handle)) { + ret = false; + } + + if (!test_PausePrinter(tctx, p, &handle)) { + ret = false; + } + + if (!test_DoPrintTest(tctx, p, &handle)) { + ret = false; + } + + if (!test_ResumePrinter(tctx, p, &handle)) { + ret = false; + } + + if (!test_SetPrinterData(tctx, p, &handle)) { + ret = false; + } + + if (!test_SecondaryClosePrinter(tctx, p, &handle)) { + ret = false; + } + + if (!test_ClosePrinter(tctx, p, &handle)) { + ret = false; + } + + return ret; +} + +static bool test_EnumPrinters_old(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct spoolss_EnumPrinters r; + NTSTATUS status; + uint16_t levels[] = {1, 2, 4, 5}; + int i; + bool ret = true; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + union spoolss_PrinterInfo *info; + int j; + + r.in.flags = PRINTER_ENUM_LOCAL; + r.in.server = ""; + r.in.level = levels[i]; + r.in.buffer = NULL; + r.in.offered = 0; + + torture_comment(tctx, "Testing EnumPrinters level %u\n", r.in.level); + + status = dcerpc_spoolss_EnumPrinters(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "EnumPrinters failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed); + data_blob_clear(&blob); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + status = dcerpc_spoolss_EnumPrinters(p, tctx, &r); + } + + torture_assert_ntstatus_ok(tctx, status, "EnumPrinters failed"); + + torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed"); + + if (!r.out.info) { + torture_comment(tctx, "No printers returned\n"); + return true; + } + + info = r.out.info; + + for (j=0;j<r.out.count;j++) { + if (r.in.level == 1) { + /* the names appear to be comma-separated name lists? */ + char *name = talloc_strdup(tctx, info[j].info1.name); + char *comma = strchr(name, ','); + if (comma) *comma = 0; + if (!test_OpenPrinter(tctx, p, name)) { + ret = false; + } + if (!test_OpenPrinterEx(tctx, p, name)) { + ret = false; + } + } + } + } + + return ret; +} + +#if 0 +static bool test_GetPrinterDriver2(struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *driver_name) +{ + NTSTATUS status; + struct spoolss_GetPrinterDriver2 r; + + r.in.handle = handle; + r.in.architecture = "W32X86"; + r.in.level = 1; + r.in.buffer = NULL; + r.in.offered = 0; + r.in.client_major_version = 0; + r.in.client_minor_version = 0; + + printf("Testing GetPrinterDriver2\n"); + + status = dcerpc_spoolss_GetPrinterDriver2(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("GetPrinterDriver2 failed - %s\n", nt_errstr(status)); + return false; + } + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + r.in.offered = r.out.needed; + status = dcerpc_spoolss_GetPrinterDriver2(p, tctx, &r); + } + + if (!NT_STATUS_IS_OK(status)) { + printf("GetPrinterDriver2 failed - %s\n", + nt_errstr(status)); + return false; + } + + if (!W_ERROR_IS_OK(r.out.result)) { + printf("GetPrinterDriver2 failed - %s\n", + win_errstr(r.out.result)); + return false; + } + + return true; +} +#endif + +static bool test_EnumPrinterDrivers_old(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct spoolss_EnumPrinterDrivers r; + NTSTATUS status; + uint16_t levels[] = {1, 2, 3, 4, 5, 6}; + int i; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + + r.in.server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.environment = "Windows NT x86"; + r.in.level = levels[i]; + r.in.buffer = NULL; + r.in.offered = 0; + + torture_comment(tctx, "Testing EnumPrinterDrivers level %u\n", r.in.level); + + status = dcerpc_spoolss_EnumPrinterDrivers(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDrivers failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed); + data_blob_clear(&blob); + r.in.buffer = &blob; + r.in.offered = r.out.needed; + status = dcerpc_spoolss_EnumPrinterDrivers(p, tctx, &r); + } + + torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDrivers failed"); + + torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterDrivers failed"); + + if (!r.out.info) { + torture_comment(tctx, "No printer drivers returned\n"); + break; + } + } + + return true; +} + +/** Test that makes sure that calling ReplyOpenPrinter() + * on Samba 4 will cause an irpc broadcast call. + */ +static bool test_ReplyOpenPrinter(struct torture_context *tctx, + struct dcerpc_pipe *pipe) +{ + struct spoolss_ReplyOpenPrinter r; + struct spoolss_ReplyClosePrinter s; + struct policy_handle h; + + r.in.server_name = "earth"; + r.in.printer_local = 2; + r.in.type = REG_DWORD; + r.in.unknown1 = 0; + r.in.unknown2 = 0; + r.out.handle = &h; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_ReplyOpenPrinter(pipe, tctx, &r), + "spoolss_ReplyOpenPrinter call failed"); + + torture_assert_werr_ok(tctx, r.out.result, "error return code"); + + s.in.handle = &h; + s.out.handle = &h; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_ReplyClosePrinter(pipe, tctx, &s), + "spoolss_ReplyClosePrinter call failed"); + + torture_assert_werr_ok(tctx, r.out.result, "error return code"); + + return true; +} + +bool torture_rpc_spoolss(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct test_spoolss_context *ctx; + + status = torture_rpc_connection(torture, &p, &ndr_table_spoolss); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ctx = talloc_zero(torture, struct test_spoolss_context); + + ret &= test_OpenPrinter_server(torture, p, ctx); + + ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "W3SvcInstalled"); + ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "BeepEnabled"); + ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "EventLog"); + ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "NetPopup"); + ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "NetPopupToComputer"); + ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "MajorVersion"); + ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "MinorVersion"); + ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "DefaultSpoolDirectory"); + ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "Architecture"); + ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "DsPresent"); + ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "OSVersion"); + ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "OSVersionEx"); + ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "DNSMachineName"); + ret &= test_EnumForms(torture, p, &ctx->server_handle, true); + ret &= test_AddForm(torture, p, &ctx->server_handle, true); + ret &= test_EnumPorts(torture, p, ctx); + ret &= test_GetPrinterDriverDirectory(torture, p, ctx); + ret &= test_EnumPrinterDrivers(torture, p, ctx); + ret &= test_EnumMonitors(torture, p, ctx); + ret &= test_EnumPrintProcessors(torture, p, ctx); + ret &= test_EnumPrinters(torture, p, ctx); + ret &= test_OpenPrinter_badname(torture, p, "__INVALID_PRINTER__"); + ret &= test_OpenPrinter_badname(torture, p, "\\\\__INVALID_HOST__"); + ret &= test_OpenPrinter_badname(torture, p, ""); + ret &= test_OpenPrinter_badname(torture, p, "\\\\\\"); + ret &= test_OpenPrinter_badname(torture, p, "\\\\\\__INVALID_PRINTER__"); + ret &= test_OpenPrinter_badname(torture, p, talloc_asprintf(torture, "\\\\%s\\", dcerpc_server_name(p))); + ret &= test_OpenPrinter_badname(torture, p, + talloc_asprintf(torture, "\\\\%s\\__INVALID_PRINTER__", dcerpc_server_name(p))); + + + ret &= test_AddPort(torture, p); + ret &= test_EnumPorts_old(torture, p); + ret &= test_EnumPrinters_old(torture, p); + ret &= test_EnumPrinterDrivers_old(torture, p); + ret &= test_ReplyOpenPrinter(torture, p); + + return ret; +} diff --git a/source4/torture/rpc/spoolss_notify.c b/source4/torture/rpc/spoolss_notify.c new file mode 100644 index 0000000000..ab6309d55f --- /dev/null +++ b/source4/torture/rpc/spoolss_notify.c @@ -0,0 +1,300 @@ +/* + Unix SMB/CIFS implementation. + test suite for spoolss rpc notify operations + + Copyright (C) Jelmer Vernooij 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/rpc/rpc.h" +#include "librpc/gen_ndr/ndr_spoolss_c.h" +#include "rpc_server/dcerpc_server.h" +#include "lib/events/events.h" +#include "smbd/process_model.h" +#include "smb_server/smb_server.h" +#include "librpc/rpc/dcerpc_proto.h" +#include "lib/socket/netif.h" +#include "util/dlinklist.h" +#include "ntvfs/ntvfs.h" +#include "param/param.h" + +static NTSTATUS spoolss__op_bind(struct dcesrv_call_state *dce_call, const struct dcesrv_interface *iface) +{ + return NT_STATUS_OK; +} + +static void spoolss__op_unbind(struct dcesrv_connection_context *context, const struct dcesrv_interface *iface) +{ +} + +static NTSTATUS spoolss__op_ndr_pull(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_pull *pull, void **r) +{ + enum ndr_err_code ndr_err; + uint16_t opnum = dce_call->pkt.u.request.opnum; + + dce_call->fault_code = 0; + + if (opnum >= ndr_table_spoolss.num_calls) { + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NET_WRITE_FAULT; + } + + *r = talloc_size(mem_ctx, ndr_table_spoolss.calls[opnum].struct_size); + NT_STATUS_HAVE_NO_MEMORY(*r); + + /* unravel the NDR for the packet */ + ndr_err = ndr_table_spoolss.calls[opnum].ndr_pull(pull, NDR_IN, *r); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + dcerpc_log_packet(&ndr_table_spoolss, opnum, NDR_IN, + &dce_call->pkt.u.request.stub_and_verifier); + dce_call->fault_code = DCERPC_FAULT_NDR; + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +/* Note that received_packets are allocated in talloc_autofree_context(), + * because no other context appears to stay around long enough. */ +static struct received_packet { + uint16_t opnum; + void *r; + struct received_packet *prev, *next; +} *received_packets = NULL; + + +static NTSTATUS spoolss__op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r) +{ + uint16_t opnum = dce_call->pkt.u.request.opnum; + struct received_packet *rp; + + rp = talloc_zero(talloc_autofree_context(), struct received_packet); + rp->opnum = opnum; + rp->r = talloc_reference(rp, r); + + DLIST_ADD_END(received_packets, rp, struct received_packet *); + + switch (opnum) { + case 58: { + struct spoolss_ReplyOpenPrinter *r2 = (struct spoolss_ReplyOpenPrinter *)r; + r2->out.result = WERR_OK; + break; + } + + default: + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + break; + } + + if (dce_call->fault_code != 0) { + dcerpc_log_packet(&ndr_table_spoolss, opnum, NDR_IN, + &dce_call->pkt.u.request.stub_and_verifier); + return NT_STATUS_NET_WRITE_FAULT; + } + return NT_STATUS_OK; +} + + +static NTSTATUS spoolss__op_reply(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r) +{ + return NT_STATUS_OK; +} + + +static NTSTATUS spoolss__op_ndr_push(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_push *push, const void *r) +{ + enum ndr_err_code ndr_err; + uint16_t opnum = dce_call->pkt.u.request.opnum; + + ndr_err = ndr_table_spoolss.calls[opnum].ndr_push(push, NDR_OUT, r); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + dce_call->fault_code = DCERPC_FAULT_NDR; + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +const static struct dcesrv_interface notify_test_spoolss_interface = { + .name = "spoolss", + .syntax_id = {{0x12345678,0x1234,0xabcd,{0xef,0x00},{0x01,0x23,0x45,0x67,0x89,0xab}},1.0}, + .bind = spoolss__op_bind, + .unbind = spoolss__op_unbind, + .ndr_pull = spoolss__op_ndr_pull, + .dispatch = spoolss__op_dispatch, + .reply = spoolss__op_reply, + .ndr_push = spoolss__op_ndr_push +}; + +static bool spoolss__op_interface_by_uuid(struct dcesrv_interface *iface, const struct GUID *uuid, uint32_t if_version) +{ + if (notify_test_spoolss_interface.syntax_id.if_version == if_version && + GUID_equal(¬ify_test_spoolss_interface.syntax_id.uuid, uuid)) { + memcpy(iface,¬ify_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, ¬ify_test_spoolss_interface, sizeof(*iface)); + return true; + } + + return false; +} + +static NTSTATUS spoolss__op_init_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server) +{ + int i; + + for (i=0;i<ndr_table_spoolss.endpoints->count;i++) { + NTSTATUS ret; + const char *name = ndr_table_spoolss.endpoints->names[i]; + + ret = dcesrv_interface_register(dce_ctx, name, ¬ify_test_spoolss_interface, NULL); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1,("spoolss_op_init_server: failed to register endpoint '%s'\n",name)); + return ret; + } + } + + return NT_STATUS_OK; +} + +static bool test_RFFPCNEx(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct spoolss_OpenPrinter q; + struct spoolss_RemoteFindFirstPrinterChangeNotifyEx r; + struct dcesrv_endpoint_server ep_server; + NTSTATUS status; + struct dcesrv_context *dce_ctx; + const char *endpoints[] = { "spoolss", NULL }; + struct spoolss_NotifyOptionsContainer t1; + struct spoolss_ClosePrinter cp; + + struct policy_handle handle; + const char *address; + struct interface *ifaces; + + received_packets = NULL; + + ntvfs_init(tctx->lp_ctx); + + ZERO_STRUCT(q); + + q.in.printername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + q.in.datatype = NULL; + q.in.devmode_ctr.devmode= NULL; + q.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + q.out.handle = &handle; + + torture_comment(tctx, "Testing OpenPrinter(%s)\n", q.in.printername); + + status = dcerpc_spoolss_OpenPrinter(p, tctx, &q); + + torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed"); + + torture_assert_werr_ok(tctx, q.out.result, "OpenPrinter failed"); + + /* Start DCE/RPC server */ + + /* fill in our name */ + ep_server.name = "spoolss"; + + /* fill in all the operations */ + ep_server.init_server = spoolss__op_init_server; + + ep_server.interface_by_uuid = spoolss__op_interface_by_uuid; + ep_server.interface_by_name = spoolss__op_interface_by_name; + + torture_assert_ntstatus_ok(tctx, dcerpc_register_ep_server(&ep_server), + "unable to register spoolss server"); + + lp_set_cmdline(tctx->lp_ctx, "dcerpc endpoint servers", "spoolss"); + + load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces); + address = iface_n_ip(ifaces, 0); + torture_comment(tctx, "Listening for callbacks on %s\n", address); + status = smbsrv_add_socket(p->conn->event_ctx, tctx->lp_ctx, &single_ops, address); + torture_assert_ntstatus_ok(tctx, status, "starting smb server"); + + status = dcesrv_init_context(tctx, tctx->lp_ctx, endpoints, &dce_ctx); + torture_assert_ntstatus_ok(tctx, status, + "unable to initialize DCE/RPC server"); + + r.in.flags = 0; + r.in.str = talloc_asprintf(tctx, "\\\\%s", address); + r.in.options = 0; + r.in.printer_local = 123; + t1.version = 2; + t1.flags = 0; + t1.count = 2; + t1.options = talloc_zero_array(tctx, struct spoolss_NotifyOptionsArray, 2); + t1.options[0].type = SPOOLSS_NOTIFY_PRINTER; + t1.options[0].count = 1; + t1.options[0].fields = talloc_array(t1.options, enum spoolss_Field, 1); + t1.options[0].fields[0] = SPOOLSS_FIELD_SERVER_NAME; + + t1.options[1].type = SPOOLSS_NOTIFY_JOB; + t1.options[1].count = 1; + t1.options[1].fields = talloc_array(t1.options, enum spoolss_Field, 1); + t1.options[1].fields[0] = SPOOLSS_FIELD_PRINTER_NAME; + + r.in.t1 = &t1; + r.in.handle = &handle; + + status = dcerpc_spoolss_RemoteFindFirstPrinterChangeNotifyEx(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "FFPCNEx failed"); + + torture_assert_werr_ok(tctx, r.out.result, "error return code for FFPCNEx"); + + cp.in.handle = &handle; + cp.out.handle = &handle; + + torture_comment(tctx, "Testing ClosePrinter\n"); + + status = dcerpc_spoolss_ClosePrinter(p, tctx, &cp); + torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed"); + + /* We should've had an incoming packet 58 (ReplyOpenPrinter) */ + torture_assert(tctx, received_packets != NULL, "no packets received"); + torture_assert_int_equal(tctx, received_packets->opnum, 58, "invalid opnum"); + + /* Shut down DCE/RPC server */ + talloc_free(dce_ctx); + + return true; +} + +struct torture_suite *torture_rpc_spoolss_notify(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "SPOOLSS-NOTIFY"); + + struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite, + "notify", &ndr_table_spoolss); + + torture_rpc_tcase_add_test(tcase, "testRFFPCNEx", test_RFFPCNEx); + + return suite; +} diff --git a/source4/torture/rpc/spoolss_win.c b/source4/torture/rpc/spoolss_win.c new file mode 100644 index 0000000000..9ce9fb7526 --- /dev/null +++ b/source4/torture/rpc/spoolss_win.c @@ -0,0 +1,578 @@ +/* + Unix SMB/CIFS implementation. + test suite for spoolss rpc operations as performed by various win versions + + Copyright (C) Kai Blin 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/rpc/rpc.h" +#include "librpc/gen_ndr/ndr_spoolss_c.h" +#include "rpc_server/dcerpc_server.h" +#include "ntvfs/ntvfs.h" +#include "param/param.h" + +struct test_spoolss_win_context { + /* EnumPrinters */ + uint32_t printer_count; + union spoolss_PrinterInfo *printer_info; + union spoolss_PrinterInfo *current_info; + + /* EnumPrinterKeys */ + char *printer_keys; +}; + +/* This is a convenience function for all OpenPrinterEx calls */ +static bool test_OpenPrinterEx(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *printer_name, + uint32_t access_mask) +{ + NTSTATUS status; + struct spoolss_OpenPrinterEx op; + struct spoolss_UserLevel1 ul_1; + + torture_comment(tctx, "Opening printer '%s'\n", printer_name); + + op.in.printername = talloc_strdup(tctx, printer_name); + op.in.datatype = NULL; + op.in.devmode_ctr.devmode = NULL; + op.in.access_mask = access_mask; + op.in.level = 1; + op.in.userlevel.level1 = &ul_1; + op.out.handle = handle; + + ul_1.size = 1234; + ul_1.client = "\\clientname"; + ul_1.user = "username"; + ul_1.build = 1; + ul_1.major = 2; + ul_1.minor = 3; + ul_1.processor = 4567; + + status = dcerpc_spoolss_OpenPrinterEx(p, tctx, &op); + torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed"); + torture_assert_werr_ok(tctx, op.out.result, "OpenPrinterEx failed"); + + return true; +} + +static bool test_OpenPrinterAsAdmin(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *printername) +{ + NTSTATUS status; + struct spoolss_OpenPrinterEx op; + struct spoolss_ClosePrinter cp; + struct spoolss_UserLevel1 ul_1; + struct policy_handle handle; + + ul_1.size = 1234; + ul_1.client = "\\clientname"; + ul_1.user = "username"; + ul_1.build = 1; + ul_1.major = 2; + ul_1.minor = 3; + ul_1.processor = 4567; + + op.in.printername = talloc_strdup(tctx, printername); + op.in.datatype = NULL; + op.in.devmode_ctr.devmode = NULL; + op.in.access_mask = SERVER_ALL_ACCESS; + op.in.level = 1; + op.in.userlevel.level1 = &ul_1; + op.out.handle = &handle; + + cp.in.handle = &handle; + cp.out.handle = &handle; + + torture_comment(tctx, "Testing OpenPrinterEx(%s) with admin rights\n", + op.in.printername); + + status = dcerpc_spoolss_OpenPrinterEx(p, tctx, &op); + + if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(op.out.result)) { + status = dcerpc_spoolss_ClosePrinter(p, tctx, &cp); + torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed"); + } + + return true; +} + + +static bool test_ClosePrinter(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle); + +/* This replicates the opening sequence of OpenPrinterEx calls XP does */ +static bool test_OpenPrinterSequence(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + bool ret; + char *printername = talloc_asprintf(tctx, "\\\\%s", + dcerpc_server_name(p)); + + /* First, see if we can open the printer read_only */ + ret = test_OpenPrinterEx(tctx, p, handle, printername, 0); + torture_assert(tctx, ret == true, "OpenPrinterEx failed."); + + ret = test_ClosePrinter(tctx, p, handle); + torture_assert(tctx, ret, "ClosePrinter failed"); + + /* Now let's see if we have admin rights to it. */ + ret = test_OpenPrinterAsAdmin(tctx, p, printername); + torture_assert(tctx, ret == true, + "OpenPrinterEx as admin failed unexpectedly."); + + ret = test_OpenPrinterEx(tctx, p, handle, printername, SERVER_EXECUTE); + torture_assert(tctx, ret == true, "OpenPrinterEx failed."); + + return true; +} + +static bool test_GetPrinterData(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *value_name, + WERROR expected_werr, + uint32_t expected_value) +{ + NTSTATUS status; + struct spoolss_GetPrinterData gpd; + + torture_comment(tctx, "Testing GetPrinterData(%s).\n", value_name); + gpd.in.handle = handle; + gpd.in.value_name = value_name; + gpd.in.offered = 4; + + status = dcerpc_spoolss_GetPrinterData(p, tctx, &gpd); + torture_assert_ntstatus_ok(tctx, status, "GetPrinterData failed."); + torture_assert_werr_equal(tctx, gpd.out.result, expected_werr, + "GetPrinterData did not return expected error value."); + + if (W_ERROR_IS_OK(expected_werr)) { + torture_assert_int_equal(tctx, gpd.out.data.value, + expected_value, + "GetPrinterData did not return expected value."); + } + return true; +} + +static bool test_EnumPrinters(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct test_spoolss_win_context *ctx, + uint32_t initial_blob_size) +{ + NTSTATUS status; + struct spoolss_EnumPrinters ep; + DATA_BLOB blob = data_blob_talloc_zero(ctx, initial_blob_size); + + ep.in.flags = PRINTER_ENUM_NAME; + ep.in.server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + ep.in.level = 2; + ep.in.buffer = &blob; + ep.in.offered = initial_blob_size; + + status = dcerpc_spoolss_EnumPrinters(p, ctx, &ep); + torture_assert_ntstatus_ok(tctx, status, "EnumPrinters failed."); + + if (W_ERROR_EQUAL(ep.out.result, WERR_INSUFFICIENT_BUFFER)) { + blob = data_blob_talloc_zero(ctx, ep.out.needed); + ep.in.buffer = &blob; + ep.in.offered = ep.out.needed; + status = dcerpc_spoolss_EnumPrinters(p, ctx, &ep); + torture_assert_ntstatus_ok(tctx, status,"EnumPrinters failed."); + } + + torture_assert_werr_ok(tctx, ep.out.result, "EnumPrinters failed."); + + ctx->printer_count = ep.out.count; + ctx->printer_info = ep.out.info; + + torture_comment(tctx, "Found %d printer(s).\n", ctx->printer_count); + + return true; +} + +static bool test_GetPrinter(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + struct test_spoolss_win_context *ctx, + uint32_t level, + uint32_t initial_blob_size) +{ + NTSTATUS status; + struct spoolss_GetPrinter gp; + DATA_BLOB blob = data_blob_talloc_zero(ctx, initial_blob_size); + + torture_comment(tctx, "Test GetPrinter level %d\n", level); + + gp.in.handle = handle; + gp.in.level = level; + gp.in.buffer = (initial_blob_size == 0)?NULL:&blob; + gp.in.offered = initial_blob_size; + + status = dcerpc_spoolss_GetPrinter(p, tctx, &gp); + torture_assert_ntstatus_ok(tctx, status, "GetPrinter failed"); + + if (W_ERROR_EQUAL(gp.out.result, WERR_INSUFFICIENT_BUFFER)) { + blob = data_blob_talloc_zero(ctx, gp.out.needed); + gp.in.buffer = &blob; + gp.in.offered = gp.out.needed; + status = dcerpc_spoolss_GetPrinter(p, tctx, &gp); + torture_assert_ntstatus_ok(tctx, status, "GetPrinter failed"); + } + + torture_assert_werr_ok(tctx, gp.out.result, "GetPrinter failed"); + + ctx->current_info = gp.out.info; + return true; +} + +static bool test_EnumJobs(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_EnumJobs ej; + DATA_BLOB blob = data_blob_talloc_zero(tctx, 1024); + + torture_comment(tctx, "Test EnumJobs\n"); + + ej.in.handle = handle; + ej.in.level = 2; + ej.in.buffer = &blob; + ej.in.offered = 1024; + + status = dcerpc_spoolss_EnumJobs(p, tctx, &ej); + torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed"); + torture_assert_werr_ok(tctx, ej.out.result, "EnumJobs failed"); + + return true; +} + +static bool test_GetPrinterDriver2(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_GetPrinterDriver2 gpd2; + DATA_BLOB blob = data_blob_talloc_zero(tctx, 87424); + + torture_comment(tctx, "Testing GetPrinterDriver2\n"); + + gpd2.in.handle = handle; + gpd2.in.architecture = "Windows NT x86"; + gpd2.in.level = 101; + gpd2.in.buffer = &blob; + gpd2.in.offered = 87424; + gpd2.in.client_major_version = 3; + gpd2.in.client_minor_version = 0; + + status = dcerpc_spoolss_GetPrinterDriver2(p, tctx, &gpd2); + torture_assert_ntstatus_ok(tctx, status, "GetPrinterDriver2 failed"); + torture_assert_werr_ok(tctx, gpd2.out.result, + "GetPrinterDriver2 failed."); + + return true; +} + +static bool test_EnumForms(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + uint32_t initial_blob_size) +{ + NTSTATUS status; + struct spoolss_EnumForms ef; + DATA_BLOB blob = data_blob_talloc_zero(tctx, initial_blob_size); + + torture_comment(tctx, "Testing EnumForms\n"); + + ef.in.handle = handle; + ef.in.level = 1; + ef.in.buffer = (initial_blob_size == 0)?NULL:&blob; + ef.in.offered = initial_blob_size; + + status = dcerpc_spoolss_EnumForms(p, tctx, &ef); + torture_assert_ntstatus_ok(tctx, status, "EnumForms failed"); + + if (W_ERROR_EQUAL(ef.out.result, WERR_INSUFFICIENT_BUFFER)) { + blob = data_blob_talloc_zero(tctx, ef.out.needed); + ef.in.buffer = &blob; + ef.in.offered = ef.out.needed; + status = dcerpc_spoolss_EnumForms(p, tctx, &ef); + torture_assert_ntstatus_ok(tctx, status, "EnumForms failed"); + } + + torture_assert_werr_ok(tctx, ef.out.result, "EnumForms failed"); + + return true; +} + +static bool test_EnumPrinterKey(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char* key, + struct test_spoolss_win_context *ctx) +{ + NTSTATUS status; + struct spoolss_EnumPrinterKey epk; + uint32_t needed = 0; + + torture_comment(tctx, "Testing EnumPrinterKey(%s)\n", key); + + epk.in.handle = handle; + epk.in.key_name = talloc_strdup(tctx, key); + epk.in.needed = needed; + + status = dcerpc_spoolss_EnumPrinterKey(p, tctx, &epk); + torture_assert_ntstatus_ok(tctx, status, "EnumPrinterKey failed"); + + + if (W_ERROR_EQUAL(epk.out.result, WERR_MORE_DATA)) { + epk.in.needed = epk.out.needed; + status = dcerpc_spoolss_EnumPrinterKey(p, tctx, &epk); + torture_assert_ntstatus_ok(tctx, status, + "EnumPrinterKey failed"); + } + + torture_assert_werr_ok(tctx, epk.out.result, "EnumPrinterKey failed"); + + convert_string_talloc(ctx, lp_iconv_convenience(tctx->lp_ctx), CH_UTF16, + CH_UNIX, epk.out.key_buffer, epk.out.needed, + (void**)&ctx->printer_keys); + + return true; +} + +static bool test_EnumPrinterDataEx(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *key, + uint32_t initial_blob_size, + WERROR expected_error) +{ + NTSTATUS status; + struct spoolss_EnumPrinterDataEx epde; + + torture_comment(tctx, "Testing EnumPrinterDataEx(%s)\n", key); + + epde.in.handle = handle; + epde.in.key_name = talloc_strdup(tctx, key); + epde.in.offered = 0; + + status = dcerpc_spoolss_EnumPrinterDataEx(p, tctx, &epde); + torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDataEx failed."); + if (W_ERROR_EQUAL(epde.out.result, WERR_MORE_DATA)) { + epde.in.offered = epde.out.needed; + status = dcerpc_spoolss_EnumPrinterDataEx(p, tctx, &epde); + torture_assert_ntstatus_ok(tctx, status, + "EnumPrinterDataEx failed."); + } + + torture_assert_werr_equal(tctx, epde.out.result, expected_error, + "EnumPrinterDataEx failed."); + + return true; +} + +static bool test_ClosePrinter(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_ClosePrinter cp; + + cp.in.handle = handle; + cp.out.handle = handle; + + status = dcerpc_spoolss_ClosePrinter(p, tctx, &cp); + torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed"); + + return true; +} + +static bool test_WinXP(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + bool ret = true; + struct test_spoolss_win_context *ctx, *tmp_ctx; + struct policy_handle handle01, handle02, handle03, handle04; + /* Sometimes a handle stays unused. In order to make this clear in the + * code, the unused_handle structures are used for that. */ + struct policy_handle unused_handle1, unused_handle2; + char *server_name; + char *key_pointer; + + ntvfs_init(tctx->lp_ctx); + + ctx = talloc_zero(tctx, struct test_spoolss_win_context); + tmp_ctx = talloc_zero(tctx, struct test_spoolss_win_context); + + ret &= test_OpenPrinterSequence(tctx, p, &handle01); + ret &= test_GetPrinterData(tctx, p, &handle01,"UISingleJobStatusString", + WERR_INVALID_PARAM, 0); + torture_comment(tctx, "Skip RemoteFindNextPrinterChangeNotifyEx test\n"); + + server_name = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p)); + ret &= test_OpenPrinterEx(tctx, p, &unused_handle1, server_name, 0); + + ret &= test_EnumPrinters(tctx, p, ctx, 1024); + + ret &= test_OpenPrinterEx(tctx, p, &handle02, server_name, 0); + ret &= test_GetPrinterData(tctx, p, &handle02, "MajorVersion", WERR_OK, + 3); + ret &= test_ClosePrinter(tctx, p, &handle02); + + /* If no printers were found, skip all tests that need a printer */ + if (ctx->printer_count == 0) { + goto end_testWinXP; + } + + ret &= test_OpenPrinterEx(tctx, p, &handle02, + ctx->printer_info[0].info2.printername, + PRINTER_ACCESS_USE); + ret &= test_GetPrinter(tctx, p, &handle02, ctx, 2, 0); + + torture_assert_str_equal(tctx, ctx->current_info->info2.printername, + ctx->printer_info[0].info2.printername, + "GetPrinter returned unexpected printername"); + /*FIXME: Test more components of the PrinterInfo2 struct */ + + ret &= test_OpenPrinterEx(tctx, p, &handle03, + ctx->printer_info[0].info2.printername, 0); + ret &= test_GetPrinter(tctx, p, &handle03, ctx, 0, 1164); + ret &= test_GetPrinter(tctx, p, &handle03, ctx, 2, 0); + + ret &= test_OpenPrinterEx(tctx, p, &handle04, + ctx->printer_info[0].info2.printername, 0); + ret &= test_GetPrinter(tctx, p, &handle04, ctx, 2, 0); + ret &= test_ClosePrinter(tctx, p, &handle04); + + ret &= test_OpenPrinterEx(tctx, p, &handle04, + ctx->printer_info[0].info2.printername, 0); + ret &= test_GetPrinter(tctx, p, &handle04, ctx, 2, 4096); + ret &= test_ClosePrinter(tctx, p, &handle04); + + ret &= test_OpenPrinterAsAdmin(tctx, p, + ctx->printer_info[0].info2.printername); + + ret &= test_OpenPrinterEx(tctx, p, &handle04, + ctx->printer_info[0].info2.printername, PRINTER_READ); + ret &= test_GetPrinterData(tctx, p, &handle04,"UISingleJobStatusString", + WERR_BADFILE, 0); + torture_comment(tctx, "Skip RemoteFindNextPrinterChangeNotifyEx test\n"); + + ret &= test_OpenPrinterEx(tctx, p, &unused_handle2, + ctx->printer_info[0].info2.printername, 0); + + ret &= test_EnumJobs(tctx, p, &handle04); + ret &= test_GetPrinter(tctx, p, &handle04, ctx, 2, 4096); + + ret &= test_ClosePrinter(tctx, p, &unused_handle2); + ret &= test_ClosePrinter(tctx, p, &handle04); + + ret &= test_EnumPrinters(tctx, p, ctx, 1556); + ret &= test_GetPrinterDriver2(tctx, p, &handle03); + ret &= test_EnumForms(tctx, p, &handle03, 0); + + ret &= test_EnumPrinterKey(tctx, p, &handle03, "", ctx); + key_pointer = ctx->printer_keys; + while(*key_pointer != '\0') { + char *end_pointer; + char *key_name; + + for(end_pointer = key_pointer; *end_pointer != '\0'; + ++end_pointer) { + /* Do nothing, just move the pointer */ + } + key_name = talloc_strndup(tctx, key_pointer, + end_pointer - key_pointer); + + ret &= test_EnumPrinterKey(tctx, p, &handle03, key_name, + tmp_ctx); + ret &= test_EnumPrinterDataEx(tctx, p, &handle03, key_name, 0, + WERR_OK); + + key_pointer = ++end_pointer; + } + + ret &= test_EnumPrinterDataEx(tctx, p, &handle03, "", 0, + WERR_INVALID_PARAM); + + ret &= test_GetPrinter(tctx, p, &handle03, tmp_ctx, 2, 0); + + ret &= test_OpenPrinterEx(tctx, p, &unused_handle2, + ctx->printer_info[0].info2.printername, 0); + ret &= test_ClosePrinter(tctx, p, &unused_handle2); + + ret &= test_GetPrinter(tctx, p, &handle03, tmp_ctx, 2, 2556); + + ret &= test_OpenPrinterEx(tctx, p, &unused_handle2, + ctx->printer_info[0].info2.printername, 0); + ret &= test_ClosePrinter(tctx, p, &unused_handle2); + + ret &= test_OpenPrinterEx(tctx, p, &unused_handle2, + ctx->printer_info[0].info2.printername, 0); + ret &= test_ClosePrinter(tctx, p, &unused_handle2); + + ret &= test_GetPrinter(tctx, p, &handle03, tmp_ctx, 7, 0); + + ret &= test_OpenPrinterEx(tctx, p, &unused_handle2, + ctx->printer_info[0].info2.printername, 0); + ret &= test_ClosePrinter(tctx, p, &unused_handle2); + + ret &= test_ClosePrinter(tctx, p, &handle03); + + ret &= test_OpenPrinterEx(tctx, p, &unused_handle2, + ctx->printer_info[0].info2.printername, 0); + ret &= test_ClosePrinter(tctx, p, &unused_handle2); + + ret &= test_OpenPrinterEx(tctx, p, &handle03, server_name, 0); + ret &= test_GetPrinterData(tctx, p, &handle03, "W3SvcInstalled", + WERR_OK, 0); + ret &= test_ClosePrinter(tctx, p, &handle03); + + ret &= test_ClosePrinter(tctx, p, &unused_handle1); + ret &= test_ClosePrinter(tctx, p, &handle02); + + ret &= test_OpenPrinterEx(tctx, p, &handle02, + ctx->printer_info[0].info2.sharename, 0); + ret &= test_GetPrinter(tctx, p, &handle02, tmp_ctx, 2, 0); + ret &= test_ClosePrinter(tctx, p, &handle02); + +end_testWinXP: + ret &= test_ClosePrinter(tctx, p, &handle01); + + talloc_free(tmp_ctx); + talloc_free(ctx); + return ret; +} + +struct torture_suite *torture_rpc_spoolss_win(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "SPOOLSS-WIN"); + + struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite, + "win", &ndr_table_spoolss); + + torture_rpc_tcase_add_test(tcase, "testWinXP", test_WinXP); + + return suite; +} + diff --git a/source4/torture/rpc/srvsvc.c b/source4/torture/rpc/srvsvc.c new file mode 100644 index 0000000000..1fe1221b0d --- /dev/null +++ b/source4/torture/rpc/srvsvc.c @@ -0,0 +1,998 @@ +/* + Unix SMB/CIFS implementation. + test suite for srvsvc rpc operations + + Copyright (C) Stefan (metze) Metzmacher 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_srvsvc.h" +#include "librpc/gen_ndr/ndr_srvsvc_c.h" +#include "torture/rpc/rpc.h" + +/**************************/ +/* srvsvc_NetCharDev */ +/**************************/ +static bool test_NetCharDevGetInfo(struct dcerpc_pipe *p, struct torture_context *tctx, + const char *devname) +{ + NTSTATUS status; + struct srvsvc_NetCharDevGetInfo r; + uint32_t levels[] = {0, 1}; + int i; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.device_name = devname; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + ZERO_STRUCT(r.out); + r.in.level = levels[i]; + torture_comment(tctx, "testing NetCharDevGetInfo level %u on device '%s'\n", + r.in.level, r.in.device_name); + status = dcerpc_srvsvc_NetCharDevGetInfo(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetCharDevGetInfo failed"); + torture_assert_werr_ok(tctx, r.out.result, "NetCharDevGetInfo failed"); + } + + return true; +} + +static bool test_NetCharDevControl(struct dcerpc_pipe *p, struct torture_context *tctx, + const char *devname) +{ + NTSTATUS status; + struct srvsvc_NetCharDevControl r; + uint32_t opcodes[] = {0, 1}; + int i; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.device_name = devname; + + for (i=0;i<ARRAY_SIZE(opcodes);i++) { + ZERO_STRUCT(r.out); + r.in.opcode = opcodes[i]; + torture_comment(tctx, "testing NetCharDevControl opcode %u on device '%s'\n", + r.in.opcode, r.in.device_name); + status = dcerpc_srvsvc_NetCharDevControl(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetCharDevControl failed"); + torture_assert_werr_ok(tctx, r.out.result, "NetCharDevControl failed"); + } + + return true; +} + +static bool test_NetCharDevEnum(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetCharDevEnum r; + struct srvsvc_NetCharDevCtr0 c0; + uint32_t levels[] = {0, 1}; + int i; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.ctr.ctr0 = &c0; + r.in.ctr.ctr0->count = 0; + r.in.ctr.ctr0->array = NULL; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + int j; + + ZERO_STRUCT(r.out); + r.in.level = levels[i]; + torture_comment(tctx, "testing NetCharDevEnum level %u\n", r.in.level); + status = dcerpc_srvsvc_NetCharDevEnum(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetCharDevEnum failed"); + if (!W_ERROR_IS_OK(r.out.result)) { + torture_comment(tctx, "NetCharDevEnum failed: %s\n", win_errstr(r.out.result)); + continue; + } + + /* call test_NetCharDevGetInfo and test_NetCharDevControl for each returned share */ + if (r.in.level == 1) { + for (j=0;j<r.out.ctr.ctr1->count;j++) { + const char *device; + device = r.out.ctr.ctr1->array[j].device; + if (!test_NetCharDevGetInfo(p, tctx, device)) { + return false; + } + if (!test_NetCharDevControl(p, tctx, device)) { + return false; + } + } + } + } + + return true; +} + +/**************************/ +/* srvsvc_NetCharDevQ */ +/**************************/ +static bool test_NetCharDevQGetInfo(struct dcerpc_pipe *p, struct torture_context *tctx, + const char *devicequeue) +{ + NTSTATUS status; + struct srvsvc_NetCharDevQGetInfo r; + uint32_t levels[] = {0, 1}; + int i; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.queue_name = devicequeue; + r.in.user = talloc_asprintf(tctx,"Administrator"); + + for (i=0;i<ARRAY_SIZE(levels);i++) { + ZERO_STRUCT(r.out); + r.in.level = levels[i]; + torture_comment(tctx, "testing NetCharDevQGetInfo level %u on devicequeue '%s'\n", + r.in.level, r.in.queue_name); + status = dcerpc_srvsvc_NetCharDevQGetInfo(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetCharDevQGetInfo failed"); + torture_assert_werr_ok(tctx, r.out.result, "NetCharDevQGetInfo failed"); + } + + return true; +} + +#if 0 +static bool test_NetCharDevQSetInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + const char *devicequeue) +{ + NTSTATUS status; + struct srvsvc_NetCharDevQSetInfo r; + uint32_t parm_error; + uint32_t levels[] = {0, 1}; + int i; + bool ret = true; + + r.in.server_unc = talloc_asprintf(mem_ctx,"\\\\%s",dcerpc_server_name(p)); + r.in.queue_name = devicequeue; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + ZERO_STRUCT(r.out); + parm_error = 0; + r.in.level = levels[i]; + d_printf("testing NetCharDevQSetInfo level %u on devicequeue '%s'\n", + r.in.level, devicequeue); + switch (r.in.level) { + case 0: + r.in.info.info0 = talloc(mem_ctx, struct srvsvc_NetCharDevQInfo0); + r.in.info.info0->device = r.in.queue_name; + break; + case 1: + r.in.info.info1 = talloc(mem_ctx, struct srvsvc_NetCharDevQInfo1); + r.in.info.info1->device = r.in.queue_name; + r.in.info.info1->priority = 0x000; + r.in.info.info1->devices = r.in.queue_name; + r.in.info.info1->users = 0x000; + r.in.info.info1->num_ahead = 0x000; + break; + default: + break; + } + r.in.parm_error = &parm_error; + status = dcerpc_srvsvc_NetCharDevQSetInfo(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("NetCharDevQSetInfo level %u on devicequeue '%s' failed - %s\n", + r.in.level, r.in.queue_name, nt_errstr(status)); + ret = false; + continue; + } + if (!W_ERROR_IS_OK(r.out.result)) { + d_printf("NetCharDevQSetInfo level %u on devicequeue '%s' failed - %s\n", + r.in.level, r.in.queue_name, win_errstr(r.out.result)); + continue; + } + } + + return ret; +} +#endif + +static bool test_NetCharDevQEnum(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetCharDevQEnum r; + struct srvsvc_NetCharDevQCtr0 c0; + uint32_t levels[] = {0, 1}; + int i; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.user = talloc_asprintf(tctx,"%s","Administrator"); + r.in.ctr.ctr0 = &c0; + r.in.ctr.ctr0->count = 0; + r.in.ctr.ctr0->array = NULL; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + int j; + + ZERO_STRUCT(r.out); + r.in.level = levels[i]; + torture_comment(tctx, "testing NetCharDevQEnum level %u\n", r.in.level); + status = dcerpc_srvsvc_NetCharDevQEnum(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetCharDevQEnum failed"); + if (!W_ERROR_IS_OK(r.out.result)) { + torture_comment(tctx, "NetCharDevQEnum failed: %s\n", win_errstr(r.out.result)); + continue; + } + + /* call test_NetCharDevGetInfo and test_NetCharDevControl for each returned share */ + if (r.in.level == 1) { + for (j=0;j<r.out.ctr.ctr1->count;j++) { + const char *device; + device = r.out.ctr.ctr1->array[j].device; + if (!test_NetCharDevQGetInfo(p, tctx, device)) { + return false; + } + } + } + } + + return true; +} + +/**************************/ +/* srvsvc_NetConn */ +/**************************/ +static bool test_NetConnEnum(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetConnEnum r; + struct srvsvc_NetConnCtr0 c0; + uint32_t levels[] = {0, 1}; + int i; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.path = talloc_asprintf(tctx,"%s","ADMIN$"); + r.in.ctr.ctr0 = &c0; + r.in.ctr.ctr0->count = 0; + r.in.ctr.ctr0->array = NULL; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + ZERO_STRUCT(r.out); + r.in.level = levels[i]; + torture_comment(tctx, "testing NetConnEnum level %u\n", r.in.level); + status = dcerpc_srvsvc_NetConnEnum(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetConnEnum failed"); + if (!W_ERROR_IS_OK(r.out.result)) { + torture_comment(tctx, "NetConnEnum failed: %s\n", win_errstr(r.out.result)); + } + } + + return true; +} + +/**************************/ +/* srvsvc_NetFile */ +/**************************/ +static bool test_NetFileEnum(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetFileEnum r; + struct srvsvc_NetFileCtr3 c3; + uint32_t levels[] = {2, 3}; + int i; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.path = NULL; + r.in.user = NULL; + r.in.ctr.ctr3 = &c3; + r.in.ctr.ctr3->count = 0; + r.in.ctr.ctr3->array = NULL; + r.in.max_buffer = (uint32_t)4096; + r.in.resume_handle = NULL; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + ZERO_STRUCT(r.out); + r.in.level = levels[i]; + torture_comment(tctx, "testing NetFileEnum level %u\n", r.in.level); + status = dcerpc_srvsvc_NetFileEnum(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetFileEnum failed"); + if (!W_ERROR_IS_OK(r.out.result)) { + torture_comment(tctx, "NetFileEnum failed: %s\n", win_errstr(r.out.result)); + } + } + + return true; +} + +/**************************/ +/* srvsvc_NetSess */ +/**************************/ +static bool test_NetSessEnum(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetSessEnum r; + struct srvsvc_NetSessCtr0 c0; + uint32_t levels[] = {0, 1, 2, 10, 502}; + int i; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.client = NULL; + r.in.user = NULL; + r.in.ctr.ctr0 = &c0; + r.in.ctr.ctr0->count = 0; + r.in.ctr.ctr0->array = NULL; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + ZERO_STRUCT(r.out); + r.in.level = levels[i]; + torture_comment(tctx, "testing NetSessEnum level %u\n", r.in.level); + status = dcerpc_srvsvc_NetSessEnum(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetSessEnum failed"); + if (!W_ERROR_IS_OK(r.out.result)) { + torture_comment(tctx, "NetSessEnum failed: %s\n", win_errstr(r.out.result)); + } + } + + return true; +} + +/**************************/ +/* srvsvc_NetShare */ +/**************************/ +static bool test_NetShareCheck(struct dcerpc_pipe *p, struct torture_context *tctx, + const char *device_name) +{ + NTSTATUS status; + struct srvsvc_NetShareCheck r; + + r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.device_name = device_name; + + torture_comment(tctx, + "testing NetShareCheck on device '%s'\n", r.in.device_name); + + status = dcerpc_srvsvc_NetShareCheck(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_srvsvc_NetShareCheck failed"); + torture_assert_werr_ok(tctx, r.out.result, "NetShareCheck failed"); + + return true; +} + +static bool test_NetShareGetInfo(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *sharename, bool admin) +{ + NTSTATUS status; + struct srvsvc_NetShareGetInfo r; + struct { + uint32_t level; + WERROR anon_status; + WERROR admin_status; + } levels[] = { + { 0, WERR_OK, WERR_OK }, + { 1, WERR_OK, WERR_OK }, + { 2, WERR_ACCESS_DENIED, WERR_OK }, + { 501, WERR_OK, WERR_OK }, + { 502, WERR_ACCESS_DENIED, WERR_OK }, + { 1005, WERR_OK, WERR_OK }, + }; + int i; + + r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.share_name = sharename; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + WERROR expected; + + r.in.level = levels[i].level; + expected = levels[i].anon_status; + if (admin) expected = levels[i].admin_status; + ZERO_STRUCT(r.out); + + torture_comment(tctx, "testing NetShareGetInfo level %u on share '%s'\n", + r.in.level, r.in.share_name); + + status = dcerpc_srvsvc_NetShareGetInfo(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetShareGetInfo failed"); + torture_assert_werr_equal(tctx, r.out.result, expected, "NetShareGetInfo failed"); + + if (r.in.level != 2) continue; + if (!r.out.info.info2 || !r.out.info.info2->path) continue; + if (!test_NetShareCheck(p, tctx, r.out.info.info2->path)) { + return false; + } + } + + return true; +} + +static bool test_NetShareGetInfoAdminFull(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_NetShareGetInfo(tctx, p, "ADMIN$", true); +} + +static bool test_NetShareGetInfoAdminAnon(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_NetShareGetInfo(tctx, p, "ADMIN$", false); +} + +static bool test_NetShareAddSetDel(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetShareAdd a; + struct srvsvc_NetShareSetInfo r; + struct srvsvc_NetShareGetInfo q; + struct srvsvc_NetShareDel d; + struct { + uint32_t level; + WERROR expected; + } levels[] = { + { 0, WERR_UNKNOWN_LEVEL }, + { 1, WERR_OK }, + { 2, WERR_OK }, + { 501, WERR_UNKNOWN_LEVEL }, + { 502, WERR_OK }, + { 1004, WERR_OK }, + { 1005, WERR_OK }, + { 1006, WERR_OK }, +/* { 1007, WERR_OK }, */ + { 1501, WERR_OK }, + }; + int i; + + a.in.server_unc = r.in.server_unc = q.in.server_unc = d.in.server_unc = + talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.share_name = talloc_strdup(tctx, "testshare"); + + a.in.level = 2; + a.in.info.info2 = talloc(tctx, struct srvsvc_NetShareInfo2); + a.in.info.info2->name = r.in.share_name; + a.in.info.info2->type = STYPE_DISKTREE; + a.in.info.info2->comment = talloc_strdup(tctx, "test comment"); + a.in.info.info2->permissions = 123434566; + a.in.info.info2->max_users = -1; + a.in.info.info2->current_users = 0; + a.in.info.info2->path = talloc_strdup(tctx, "C:\\"); + a.in.info.info2->password = NULL; + + a.in.parm_error = NULL; + + status = dcerpc_srvsvc_NetShareAdd(p, tctx, &a); + torture_assert_ntstatus_ok(tctx, status, "NetShareAdd level 2 on share 'testshare' failed"); + torture_assert_werr_ok(tctx, a.out.result, "NetShareAdd level 2 on share 'testshare' failed"); + + r.in.parm_error = NULL; + + q.in.level = 502; + + for (i = 0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i].level; + ZERO_STRUCT(r.out); + + torture_comment(tctx, "testing NetShareSetInfo level %u on share '%s'\n", + r.in.level, r.in.share_name); + + switch (levels[i].level) { + case 0: + r.in.info.info0 = talloc(tctx, struct srvsvc_NetShareInfo0); + r.in.info.info0->name = r.in.share_name; + break; + case 1: + r.in.info.info1 = talloc(tctx, struct srvsvc_NetShareInfo1); + r.in.info.info1->name = r.in.share_name; + r.in.info.info1->type = STYPE_DISKTREE; + r.in.info.info1->comment = talloc_strdup(tctx, "test comment 1"); + break; + case 2: + r.in.info.info2 = talloc(tctx, struct srvsvc_NetShareInfo2); + r.in.info.info2->name = r.in.share_name; + r.in.info.info2->type = STYPE_DISKTREE; + r.in.info.info2->comment = talloc_strdup(tctx, "test comment 2"); + r.in.info.info2->permissions = 0; + r.in.info.info2->max_users = 2; + r.in.info.info2->current_users = 1; + r.in.info.info2->path = talloc_strdup(tctx, "::BLaH::"); /* "C:\\"); */ + r.in.info.info2->password = NULL; + break; + case 501: + r.in.info.info501 = talloc(tctx, struct srvsvc_NetShareInfo501); + r.in.info.info501->name = r.in.share_name; + r.in.info.info501->type = STYPE_DISKTREE; + r.in.info.info501->comment = talloc_strdup(tctx, "test comment 501"); + r.in.info.info501->csc_policy = 0; + break; + case 502: + r.in.info.info502 = talloc(tctx, struct srvsvc_NetShareInfo502); + r.in.info.info502->name = r.in.share_name; + r.in.info.info502->type = STYPE_DISKTREE; + r.in.info.info502->comment = talloc_strdup(tctx, "test comment 502"); + r.in.info.info502->permissions = 0; + r.in.info.info502->max_users = 502; + r.in.info.info502->current_users = 1; + r.in.info.info502->path = talloc_strdup(tctx, "C:\\"); + r.in.info.info502->password = NULL; + r.in.info.info502->unknown = 0; + r.in.info.info502->sd = NULL; + break; + case 1004: + r.in.info.info1004 = talloc(tctx, struct srvsvc_NetShareInfo1004); + r.in.info.info1004->comment = talloc_strdup(tctx, "test comment 1004"); + break; + case 1005: + r.in.info.info1005 = talloc(tctx, struct srvsvc_NetShareInfo1005); + r.in.info.info1005->dfs_flags = 0; + break; + case 1006: + r.in.info.info1006 = talloc(tctx, struct srvsvc_NetShareInfo1006); + r.in.info.info1006->max_users = 1006; + break; +/* case 1007: + r.in.info.info1007 = talloc(tctx, struct srvsvc_NetShareInfo1007); + r.in.info.info1007->flags = 0; + r.in.info.info1007->alternate_directory_name = talloc_strdup(tctx, "test"); + break; +*/ + case 1501: + r.in.info.info1501 = talloc_zero(tctx, struct sec_desc_buf); + break; + } + + status = dcerpc_srvsvc_NetShareSetInfo(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetShareGetInfo failed"); + torture_assert_werr_equal(tctx, r.out.result, levels[i].expected, "NetShareSetInfo failed"); + + q.in.share_name = r.in.share_name; + + status = dcerpc_srvsvc_NetShareGetInfo(p, tctx, &q); + torture_assert_ntstatus_ok(tctx, status, "NetShareGetInfo failed"); + torture_assert_werr_ok(tctx, q.out.result, "NetShareGetInfo failed"); + + torture_assert_str_equal(tctx, q.out.info.info502->name, r.in.share_name, + "share name invalid"); + + switch (levels[i].level) { + case 0: + break; + case 1: + torture_assert_str_equal(tctx, q.out.info.info502->comment, "test comment 1", "comment"); + break; + case 2: + torture_assert_str_equal(tctx, q.out.info.info502->comment, "test comment 2", "comment"); + torture_assert_int_equal(tctx, q.out.info.info2->max_users, 2, "max users"); + torture_assert_str_equal(tctx, q.out.info.info2->path, "C:\\", "path"); + break; + case 501: + torture_assert_str_equal(tctx, q.out.info.info501->comment, "test comment 501", "comment"); + break; + case 502: + torture_assert_str_equal(tctx, q.out.info.info502->comment, "test comment 502", "comment"); + torture_assert_int_equal(tctx, q.out.info.info2->max_users, 502, "max users"); + torture_assert_str_equal(tctx, q.out.info.info2->path, "C:\\", "path"); + break; + case 1004: + torture_assert_str_equal(tctx, q.out.info.info502->comment, "test comment 1004", + "comment"); + break; + case 1005: + break; + case 1006: + torture_assert_int_equal(tctx, q.out.info.info2->max_users, 1006, "Max users"); + break; +/* case 1007: + break; +*/ + case 1501: + break; + } + } + + d.in.share_name = r.in.share_name; + d.in.reserved = 0; + + status = dcerpc_srvsvc_NetShareDel(p, tctx, &d); + torture_assert_ntstatus_ok(tctx, status, "NetShareDel on share 'testshare502' failed"); + torture_assert_werr_ok(tctx, a.out.result, "NetShareDel on share 'testshare502' failed"); + + return true; +} + +/**************************/ +/* srvsvc_NetShare */ +/**************************/ +static bool test_NetShareEnumAll(struct torture_context *tctx, + struct dcerpc_pipe *p, + bool admin) +{ + NTSTATUS status; + struct srvsvc_NetShareEnumAll r; + struct srvsvc_NetShareCtr0 c0; + struct { + uint32_t level; + WERROR anon_status; + WERROR admin_status; + } levels[] = { + { 0, WERR_OK, WERR_OK }, + { 1, WERR_OK, WERR_OK }, + { 2, WERR_ACCESS_DENIED, WERR_OK }, + { 501, WERR_ACCESS_DENIED, WERR_OK }, + { 502, WERR_ACCESS_DENIED, WERR_OK }, + }; + int i; + uint32_t resume_handle; + + ZERO_STRUCT(c0); + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.ctr.ctr0 = &c0; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = &resume_handle; + r.out.resume_handle = &resume_handle; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + int j; + WERROR expected; + + r.in.level = levels[i].level; + expected = levels[i].anon_status; + if (admin) expected = levels[i].admin_status; + + ZERO_STRUCT(r.out); + resume_handle = 0; + + torture_comment(tctx, "testing NetShareEnumAll level %u\n", r.in.level); + status = dcerpc_srvsvc_NetShareEnumAll(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetShareEnumAll failed"); + torture_assert_werr_equal(tctx, r.out.result, expected, "NetShareEnumAll failed"); + + /* call srvsvc_NetShareGetInfo for each returned share */ + if (r.in.level == 2 && r.out.ctr.ctr2) { + for (j=0;j<r.out.ctr.ctr2->count;j++) { + const char *name; + name = r.out.ctr.ctr2->array[j].name; + if (!test_NetShareGetInfo(tctx, p, name, admin)) { + return false; + } + } + } + } + + return true; +} + +static bool test_NetShareEnumAllFull(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_NetShareEnumAll(tctx, p, true); +} + +static bool test_NetShareEnumAllAnon(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_NetShareEnumAll(tctx, p, false); +} + +static bool test_NetShareEnum(struct torture_context *tctx, + struct dcerpc_pipe *p, bool admin) +{ + NTSTATUS status; + struct srvsvc_NetShareEnum r; + struct srvsvc_NetShareCtr0 c0; + struct { + uint32_t level; + WERROR anon_status; + WERROR admin_status; + } levels[] = { + { 0, WERR_OK, WERR_OK }, + { 1, WERR_OK, WERR_OK }, + { 2, WERR_ACCESS_DENIED, WERR_OK }, + { 501, WERR_UNKNOWN_LEVEL, WERR_UNKNOWN_LEVEL }, + { 502, WERR_ACCESS_DENIED, WERR_OK }, + }; + int i; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.ctr.ctr0 = &c0; + r.in.ctr.ctr0->count = 0; + r.in.ctr.ctr0->array = NULL; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + WERROR expected; + + r.in.level = levels[i].level; + expected = levels[i].anon_status; + if (admin) expected = levels[i].admin_status; + + ZERO_STRUCT(r.out); + + torture_comment(tctx, "testing NetShareEnum level %u\n", r.in.level); + status = dcerpc_srvsvc_NetShareEnum(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetShareEnum failed"); + torture_assert_werr_equal(tctx, r.out.result, expected, "NetShareEnum failed"); + } + + return true; +} + +static bool test_NetShareEnumFull(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_NetShareEnum(tctx, p, true); +} + +static bool test_NetShareEnumAnon(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_NetShareEnum(tctx, p, false); +} + +/**************************/ +/* srvsvc_NetSrv */ +/**************************/ +static bool test_NetSrvGetInfo(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetSrvGetInfo r; + struct srvsvc_NetSrvInfo503 i503; + uint32_t levels[] = {100, 101, 102, 502, 503}; + int i; + uint32_t resume_handle; + + ZERO_STRUCT(i503); + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + + for (i=0;i<ARRAY_SIZE(levels);i++) { + ZERO_STRUCT(r.out); + resume_handle = 0; + r.in.level = levels[i]; + torture_comment(tctx, "testing NetSrvGetInfo level %u\n", r.in.level); + status = dcerpc_srvsvc_NetSrvGetInfo(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetSrvGetInfo failed"); + if (!W_ERROR_IS_OK(r.out.result)) { + torture_comment(tctx, "NetSrvGetInfo failed: %s\n", win_errstr(r.out.result)); + } + } + + return true; +} + +/**************************/ +/* srvsvc_NetDisk */ +/**************************/ +static bool test_NetDiskEnum(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetDiskEnum r; + uint32_t levels[] = {0}; + int i; + uint32_t resume_handle=0; + + ZERO_STRUCT(r.in); + r.in.server_unc = NULL; + r.in.resume_handle = &resume_handle; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + ZERO_STRUCT(r.out); + r.in.level = levels[i]; + torture_comment(tctx, "testing NetDiskEnum level %u\n", r.in.level); + status = dcerpc_srvsvc_NetDiskEnum(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetDiskEnum failed"); + torture_assert_werr_ok(tctx, r.out.result, "NetDiskEnum failed"); + } + + return true; +} + +/**************************/ +/* srvsvc_NetTransport */ +/**************************/ +static bool test_NetTransportEnum(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetTransportEnum r; + struct srvsvc_NetTransportCtr0 c0; + uint32_t levels[] = {0, 1}; + int i; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s", dcerpc_server_name(p)); + r.in.transports.ctr0 = &c0; + r.in.transports.ctr0->count = 0; + r.in.transports.ctr0->array = NULL; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + ZERO_STRUCT(r.out); + r.in.level = levels[i]; + torture_comment(tctx, "testing NetTransportEnum level %u\n", r.in.level); + status = dcerpc_srvsvc_NetTransportEnum(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetTransportEnum failed"); + if (!W_ERROR_IS_OK(r.out.result)) { + torture_comment(tctx, "unexpected result: %s\n", win_errstr(r.out.result)); + } + } + + return true; +} + +/**************************/ +/* srvsvc_NetRemoteTOD */ +/**************************/ +static bool test_NetRemoteTOD(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetRemoteTOD r; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + + ZERO_STRUCT(r.out); + torture_comment(tctx, "testing NetRemoteTOD\n"); + status = dcerpc_srvsvc_NetRemoteTOD(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetRemoteTOD failed"); + torture_assert_werr_ok(tctx, r.out.result, "NetRemoteTOD failed"); + + return true; +} + +/**************************/ +/* srvsvc_NetName */ +/**************************/ + +static bool test_NetNameValidate(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetNameValidate r; + char *invalidc; + char *name; + int i, n, min, max; + + r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.flags = 0x0; + + d_printf("testing NetNameValidate\n"); + + /* valid path types only between 1 and 13 */ + for (i = 1; i < 14; i++) { + +again: + /* let's limit ourselves to a maximum of 4096 bytes */ + r.in.name = name = talloc_array(tctx, char, 4097); + max = 4096; + min = 0; + n = max; + + while (1) { + + /* Find maximum length accepted by this type */ + ZERO_STRUCT(r.out); + r.in.name_type = i; + memset(name, 'A', n); + name[n] = '\0'; + + status = dcerpc_srvsvc_NetNameValidate(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("NetNameValidate failed while checking maximum size (%s)\n", + nt_errstr(status)); + break; + } + + if (W_ERROR_IS_OK(r.out.result)) { + min = n; + n += (max - min + 1)/2; + continue; + + } else { + if ((min + 1) >= max) break; /* found it */ + + max = n; + n -= (max - min)/2; + continue; + } + } + + talloc_free(name); + + d_printf("Maximum length for type %2d, flags %08x: %d\n", i, r.in.flags, max); + + /* find invalid chars for this type check only ASCII between 0x20 and 0x7e */ + + invalidc = talloc_strdup(tctx, ""); + + for (n = 0x20; n < 0x7e; n++) { + r.in.name = name = talloc_asprintf(tctx, "%c", (char)n); + + status = dcerpc_srvsvc_NetNameValidate(p, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("NetNameValidate failed while checking valid chars (%s)\n", + nt_errstr(status)); + break; + } + + if (!W_ERROR_IS_OK(r.out.result)) { + invalidc = talloc_asprintf_append_buffer(invalidc, "%c", (char)n); + } + + talloc_free(name); + } + + d_printf(" Invalid chars for type %2d, flags %08x: \"%s\"\n", i, r.in.flags, invalidc); + + /* only two values are accepted for flags: 0x0 and 0x80000000 */ + if (r.in.flags == 0x0) { + r.in.flags = 0x80000000; + goto again; + } + + r.in.flags = 0x0; + } + + return true; +} + +struct torture_suite *torture_rpc_srvsvc(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "SRVSVC"); + struct torture_rpc_tcase *tcase; + struct torture_test *test; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "srvsvc (admin access)", &ndr_table_srvsvc); + + torture_rpc_tcase_add_test(tcase, "NetCharDevEnum", test_NetCharDevEnum); + torture_rpc_tcase_add_test(tcase, "NetCharDevQEnum", test_NetCharDevQEnum); + torture_rpc_tcase_add_test(tcase, "NetConnEnum", test_NetConnEnum); + torture_rpc_tcase_add_test(tcase, "NetFileEnum", test_NetFileEnum); + torture_rpc_tcase_add_test(tcase, "NetSessEnum", test_NetSessEnum); + torture_rpc_tcase_add_test(tcase, "NetShareEnumAll", test_NetShareEnumAllFull); + torture_rpc_tcase_add_test(tcase, "NetSrvGetInfo", test_NetSrvGetInfo); + torture_rpc_tcase_add_test(tcase, "NetDiskEnum", test_NetDiskEnum); + torture_rpc_tcase_add_test(tcase, "NetTransportEnum", test_NetTransportEnum); + torture_rpc_tcase_add_test(tcase, "NetRemoteTOD", test_NetRemoteTOD); + torture_rpc_tcase_add_test(tcase, "NetShareEnum", test_NetShareEnumFull); + torture_rpc_tcase_add_test(tcase, "NetShareGetInfo", test_NetShareGetInfoAdminFull); + test = torture_rpc_tcase_add_test(tcase, "NetShareAddSetDel", + test_NetShareAddSetDel); + test->dangerous = true; + torture_rpc_tcase_add_test(tcase, "NetNameValidate", test_NetNameValidate); + + tcase = torture_suite_add_anon_rpc_iface_tcase(suite, + "srvsvc anonymous access", + &ndr_table_srvsvc); + + torture_rpc_tcase_add_test(tcase, "NetShareEnumAll", + test_NetShareEnumAllAnon); + torture_rpc_tcase_add_test(tcase, "NetShareEnum", + test_NetShareEnumAnon); + torture_rpc_tcase_add_test(tcase, "NetShareGetInfo", + test_NetShareGetInfoAdminAnon); + + return suite; +} diff --git a/source4/torture/rpc/svcctl.c b/source4/torture/rpc/svcctl.c new file mode 100644 index 0000000000..c9006baaf5 --- /dev/null +++ b/source4/torture/rpc/svcctl.c @@ -0,0 +1,131 @@ +/* + Unix SMB/CIFS implementation. + test suite for srvsvc rpc operations + + Copyright (C) Jelmer Vernooij 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_svcctl_c.h" +#include "torture/rpc/rpc.h" + +static bool test_OpenSCManager(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *h) +{ + struct svcctl_OpenSCManagerW r; + + r.in.MachineName = NULL; + r.in.DatabaseName = NULL; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = h; + + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_OpenSCManagerW(p, tctx, &r), + "OpenSCManager failed!"); + + return true; +} + +static bool test_CloseServiceHandle(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *h) +{ + struct svcctl_CloseServiceHandle r; + + r.in.handle = h; + r.out.handle = h; + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_CloseServiceHandle(p, tctx, &r), + "CloseServiceHandle failed"); + + return true; +} + +static bool test_EnumServicesStatus(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct svcctl_EnumServicesStatusW r; + struct policy_handle h; + int i; + NTSTATUS status; + uint32_t resume_handle = 0; + struct ENUM_SERVICE_STATUS *service = NULL; + + if (!test_OpenSCManager(p, tctx, &h)) + return false; + + r.in.handle = &h; + r.in.type = SERVICE_TYPE_WIN32; + r.in.state = SERVICE_STATE_ALL; + r.in.buf_size = 0; + r.in.resume_handle = &resume_handle; + r.out.service = NULL; + r.out.resume_handle = &resume_handle; + r.out.services_returned = 0; + r.out.bytes_needed = 0; + + status = dcerpc_svcctl_EnumServicesStatusW(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "EnumServicesStatus failed!"); + + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + r.in.buf_size = *r.out.bytes_needed; + r.out.service = talloc_array(tctx, uint8_t, *r.out.bytes_needed); + + status = dcerpc_svcctl_EnumServicesStatusW(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "EnumServicesStatus failed!"); + torture_assert_werr_ok(tctx, r.out.result, "EnumServicesStatus failed"); + + service = (struct ENUM_SERVICE_STATUS *)r.out.service; + } + + for(i = 0; i < *r.out.services_returned; i++) { + printf("Type: %d, State: %d\n", service[i].status.type, service[i].status.state); + } + + if (!test_CloseServiceHandle(p, tctx, &h)) + return false; + + return true; +} + +static bool test_SCManager(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct policy_handle h; + + if (!test_OpenSCManager(p, tctx, &h)) + return false; + + if (!test_CloseServiceHandle(p, tctx, &h)) + return false; + + return true; +} + +struct torture_suite *torture_rpc_svcctl(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "SVCCTL"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "svcctl", + &ndr_table_svcctl); + + torture_rpc_tcase_add_test(tcase, "SCManager", + test_SCManager); + torture_rpc_tcase_add_test(tcase, "EnumServicesStatus", + test_EnumServicesStatus); + + return suite; +} diff --git a/source4/torture/rpc/testjoin.c b/source4/torture/rpc/testjoin.c new file mode 100644 index 0000000000..2af8c4f872 --- /dev/null +++ b/source4/torture/rpc/testjoin.c @@ -0,0 +1,691 @@ +/* + Unix SMB/CIFS implementation. + + utility code to join/leave a domain + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* + this code is used by other torture modules to join/leave a domain + as either a member, bdc or thru a trust relationship +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "system/time.h" +#include "lib/crypto/crypto.h" +#include "libnet/libnet.h" +#include "lib/cmdline/popt_common.h" +#include "lib/ldb/include/ldb.h" +#include "librpc/gen_ndr/ndr_samr_c.h" + +#include "libcli/auth/libcli_auth.h" +#include "torture/rpc/rpc.h" +#include "libcli/security/security.h" +#include "param/param.h" + +struct test_join { + struct dcerpc_pipe *p; + struct policy_handle user_handle; + struct libnet_JoinDomain *libnet_r; + struct dom_sid *dom_sid; + const char *dom_netbios_name; + const char *dom_dns_name; + struct dom_sid *user_sid; + struct GUID user_guid; + const char *netbios_name; +}; + + +static NTSTATUS DeleteUser_byname(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, const char *name) +{ + NTSTATUS status; + struct samr_DeleteUser d; + struct policy_handle user_handle; + uint32_t rid; + struct samr_LookupNames n; + struct lsa_String sname; + struct samr_OpenUser r; + + sname.string = name; + + n.in.domain_handle = handle; + n.in.num_names = 1; + n.in.names = &sname; + + status = dcerpc_samr_LookupNames(p, mem_ctx, &n); + if (NT_STATUS_IS_OK(status)) { + rid = n.out.rids.ids[0]; + } else { + return status; + } + + r.in.domain_handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.user_handle = &user_handle; + + status = dcerpc_samr_OpenUser(p, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenUser(%s) failed - %s\n", name, nt_errstr(status)); + return status; + } + + d.in.user_handle = &user_handle; + d.out.user_handle = &user_handle; + status = dcerpc_samr_DeleteUser(p, mem_ctx, &d); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; +} + +/* + create a test user in the domain + an opaque pointer is returned. Pass it to torture_leave_domain() + when finished +*/ + +struct test_join *torture_create_testuser(struct torture_context *torture, + const char *username, + const char *domain, + uint16_t acct_type, + const char **random_password) +{ + NTSTATUS status; + struct samr_Connect c; + struct samr_CreateUser2 r; + struct samr_OpenDomain o; + struct samr_LookupDomain l; + struct samr_GetUserPwInfo pwp; + struct samr_SetUserInfo s; + union samr_UserInfo u; + struct policy_handle handle; + struct policy_handle domain_handle; + uint32_t access_granted; + uint32_t rid; + DATA_BLOB session_key; + struct lsa_String name; + + int policy_min_pw_len = 0; + struct test_join *join; + char *random_pw; + const char *dc_binding = torture_setting_string(torture, "dc_binding", NULL); + + join = talloc(NULL, struct test_join); + if (join == NULL) { + return NULL; + } + + ZERO_STRUCTP(join); + + printf("Connecting to SAMR\n"); + + if (dc_binding) { + status = dcerpc_pipe_connect(join, + &join->p, + dc_binding, + &ndr_table_samr, + cmdline_credentials, NULL, torture->lp_ctx); + + } else { + status = torture_rpc_connection(torture, + &join->p, + &ndr_table_samr); + } + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + + c.in.system_name = NULL; + c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + c.out.connect_handle = &handle; + + status = dcerpc_samr_Connect(join->p, join, &c); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + errstr = dcerpc_errstr(join, join->p->last_fault_code); + } + printf("samr_Connect failed - %s\n", errstr); + return NULL; + } + + printf("Opening domain %s\n", domain); + + name.string = domain; + l.in.connect_handle = &handle; + l.in.domain_name = &name; + + status = dcerpc_samr_LookupDomain(join->p, join, &l); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupDomain failed - %s\n", nt_errstr(status)); + goto failed; + } + + talloc_steal(join, l.out.sid); + join->dom_sid = l.out.sid; + join->dom_netbios_name = talloc_strdup(join, domain); + if (!join->dom_netbios_name) goto failed; + + o.in.connect_handle = &handle; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.in.sid = l.out.sid; + o.out.domain_handle = &domain_handle; + + status = dcerpc_samr_OpenDomain(join->p, join, &o); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenDomain failed - %s\n", nt_errstr(status)); + goto failed; + } + + printf("Creating account %s\n", username); + +again: + name.string = username; + r.in.domain_handle = &domain_handle; + r.in.account_name = &name; + r.in.acct_flags = acct_type; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.user_handle = &join->user_handle; + r.out.access_granted = &access_granted; + r.out.rid = &rid; + + status = dcerpc_samr_CreateUser2(join->p, join, &r); + + if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) { + status = DeleteUser_byname(join->p, join, &domain_handle, name.string); + if (NT_STATUS_IS_OK(status)) { + goto again; + } + } + + if (!NT_STATUS_IS_OK(status)) { + printf("CreateUser2 failed - %s\n", nt_errstr(status)); + goto failed; + } + + join->user_sid = dom_sid_add_rid(join, join->dom_sid, rid); + + pwp.in.user_handle = &join->user_handle; + + status = dcerpc_samr_GetUserPwInfo(join->p, join, &pwp); + if (NT_STATUS_IS_OK(status)) { + policy_min_pw_len = pwp.out.info.min_password_length; + } + + random_pw = generate_random_str(join, MAX(8, policy_min_pw_len)); + + printf("Setting account password '%s'\n", random_pw); + + ZERO_STRUCT(u); + s.in.user_handle = &join->user_handle; + s.in.info = &u; + s.in.level = 24; + + encode_pw_buffer(u.info24.password.data, random_pw, STR_UNICODE); + u.info24.pw_len = strlen(random_pw); + + status = dcerpc_fetch_session_key(join->p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + torture_leave_domain(torture, join); + goto failed; + } + + arcfour_crypt_blob(u.info24.password.data, 516, &session_key); + + status = dcerpc_samr_SetUserInfo(join->p, join, &s); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo failed - %s\n", nt_errstr(status)); + goto failed; + } + + ZERO_STRUCT(u); + s.in.user_handle = &join->user_handle; + s.in.info = &u; + s.in.level = 21; + + u.info21.acct_flags = acct_type | ACB_PWNOEXP; + u.info21.fields_present = SAMR_FIELD_ACCT_FLAGS | SAMR_FIELD_DESCRIPTION | SAMR_FIELD_COMMENT | SAMR_FIELD_FULL_NAME; + + u.info21.comment.string = talloc_asprintf(join, + "Tortured by Samba4: %s", + timestring(join, time(NULL))); + + u.info21.full_name.string = talloc_asprintf(join, + "Torture account for Samba4: %s", + timestring(join, time(NULL))); + + u.info21.description.string = talloc_asprintf(join, + "Samba4 torture account created by host %s: %s", + lp_netbios_name(torture->lp_ctx), + timestring(join, time(NULL))); + + printf("Resetting ACB flags, force pw change time\n"); + + status = dcerpc_samr_SetUserInfo(join->p, join, &s); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo failed - %s\n", nt_errstr(status)); + goto failed; + } + + if (random_password) { + *random_password = random_pw; + } + + return join; + +failed: + torture_leave_domain(torture, join); + return NULL; +} + + +_PUBLIC_ struct test_join *torture_join_domain(struct torture_context *tctx, + const char *machine_name, + uint32_t acct_flags, + struct cli_credentials **machine_credentials) +{ + NTSTATUS status; + struct libnet_context *libnet_ctx; + struct libnet_JoinDomain *libnet_r; + struct test_join *tj; + struct samr_SetUserInfo s; + union samr_UserInfo u; + + tj = talloc(tctx, struct test_join); + if (!tj) return NULL; + + libnet_r = talloc(tj, struct libnet_JoinDomain); + if (!libnet_r) { + talloc_free(tj); + return NULL; + } + + libnet_ctx = libnet_context_init(tctx->ev, tctx->lp_ctx); + if (!libnet_ctx) { + talloc_free(tj); + return NULL; + } + + tj->libnet_r = libnet_r; + + libnet_ctx->cred = cmdline_credentials; + libnet_r->in.binding = torture_setting_string(tctx, "binding", NULL); + if (!libnet_r->in.binding) { + libnet_r->in.binding = talloc_asprintf(libnet_r, "ncacn_np:%s", torture_setting_string(tctx, "host", NULL)); + } + libnet_r->in.level = LIBNET_JOINDOMAIN_SPECIFIED; + libnet_r->in.netbios_name = machine_name; + libnet_r->in.account_name = talloc_asprintf(libnet_r, "%s$", machine_name); + if (!libnet_r->in.account_name) { + talloc_free(tj); + return NULL; + } + + libnet_r->in.acct_type = acct_flags; + libnet_r->in.recreate_account = true; + + status = libnet_JoinDomain(libnet_ctx, libnet_r, libnet_r); + if (!NT_STATUS_IS_OK(status)) { + if (libnet_r->out.error_string) { + DEBUG(0, ("Domain join failed - %s\n", libnet_r->out.error_string)); + } else { + DEBUG(0, ("Domain join failed - %s\n", nt_errstr(status))); + } + talloc_free(tj); + return NULL; + } + tj->p = libnet_r->out.samr_pipe; + tj->user_handle = *libnet_r->out.user_handle; + tj->dom_sid = libnet_r->out.domain_sid; + talloc_steal(tj, libnet_r->out.domain_sid); + tj->dom_netbios_name = libnet_r->out.domain_name; + talloc_steal(tj, libnet_r->out.domain_name); + tj->dom_dns_name = libnet_r->out.realm; + talloc_steal(tj, libnet_r->out.realm); + tj->user_guid = libnet_r->out.account_guid; + tj->netbios_name = talloc_strdup(tj, machine_name); + if (!tj->netbios_name) { + talloc_free(tj); + return NULL; + } + + ZERO_STRUCT(u); + s.in.user_handle = &tj->user_handle; + s.in.info = &u; + s.in.level = 21; + + u.info21.fields_present = SAMR_FIELD_DESCRIPTION | SAMR_FIELD_COMMENT | SAMR_FIELD_FULL_NAME; + u.info21.comment.string = talloc_asprintf(tj, + "Tortured by Samba4: %s", + timestring(tj, time(NULL))); + u.info21.full_name.string = talloc_asprintf(tj, + "Torture account for Samba4: %s", + timestring(tj, time(NULL))); + + u.info21.description.string = talloc_asprintf(tj, + "Samba4 torture account created by host %s: %s", + lp_netbios_name(tctx->lp_ctx), timestring(tj, time(NULL))); + + status = dcerpc_samr_SetUserInfo(tj->p, tj, &s); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo (non-critical) failed - %s\n", nt_errstr(status)); + } + + *machine_credentials = cli_credentials_init(tj); + cli_credentials_set_conf(*machine_credentials, tctx->lp_ctx); + cli_credentials_set_workstation(*machine_credentials, machine_name, CRED_SPECIFIED); + cli_credentials_set_domain(*machine_credentials, libnet_r->out.domain_name, CRED_SPECIFIED); + if (libnet_r->out.realm) { + cli_credentials_set_realm(*machine_credentials, libnet_r->out.realm, CRED_SPECIFIED); + } + cli_credentials_set_username(*machine_credentials, libnet_r->in.account_name, CRED_SPECIFIED); + cli_credentials_set_password(*machine_credentials, libnet_r->out.join_password, CRED_SPECIFIED); + cli_credentials_set_kvno(*machine_credentials, libnet_r->out.kvno); + if (acct_flags & ACB_SVRTRUST) { + cli_credentials_set_secure_channel_type(*machine_credentials, + SEC_CHAN_BDC); + } else if (acct_flags & ACB_WSTRUST) { + cli_credentials_set_secure_channel_type(*machine_credentials, + SEC_CHAN_WKSTA); + } else { + DEBUG(0, ("Invalid account type specificed to torture_join_domain\n")); + talloc_free(*machine_credentials); + return NULL; + } + + return tj; +} + +struct dcerpc_pipe *torture_join_samr_pipe(struct test_join *join) +{ + return join->p; +} + +struct policy_handle *torture_join_samr_user_policy(struct test_join *join) +{ + return &join->user_handle; +} + +static NTSTATUS torture_leave_ads_domain(struct torture_context *torture, + TALLOC_CTX *mem_ctx, + struct libnet_JoinDomain *libnet_r) +{ + int rtn; + TALLOC_CTX *tmp_ctx; + + struct ldb_dn *server_dn; + struct ldb_context *ldb_ctx; + + char *remote_ldb_url; + + /* Check if we are a domain controller. If not, exit. */ + if (!libnet_r->out.server_dn_str) { + return NT_STATUS_OK; + } + + tmp_ctx = talloc_named(mem_ctx, 0, "torture_leave temporary context"); + if (!tmp_ctx) { + libnet_r->out.error_string = NULL; + return NT_STATUS_NO_MEMORY; + } + + ldb_ctx = ldb_init(tmp_ctx, torture->ev); + if (!ldb_ctx) { + libnet_r->out.error_string = NULL; + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + /* Remove CN=Servers,... entry from the AD. */ + server_dn = ldb_dn_new(tmp_ctx, ldb_ctx, libnet_r->out.server_dn_str); + if (! ldb_dn_validate(server_dn)) { + libnet_r->out.error_string = NULL; + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s", libnet_r->out.samr_binding->host); + if (!remote_ldb_url) { + libnet_r->out.error_string = NULL; + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + ldb_set_opaque(ldb_ctx, "credentials", cmdline_credentials); + + rtn = ldb_connect(ldb_ctx, remote_ldb_url, 0, NULL); + if (rtn != 0) { + libnet_r->out.error_string = NULL; + talloc_free(tmp_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + + rtn = ldb_delete(ldb_ctx, server_dn); + if (rtn != 0) { + libnet_r->out.error_string = NULL; + talloc_free(tmp_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + + DEBUG(0, ("%s removed successfully.\n", libnet_r->out.server_dn_str)); + + talloc_free(tmp_ctx); + return NT_STATUS_OK; +} + +/* + leave the domain, deleting the machine acct +*/ + +_PUBLIC_ void torture_leave_domain(struct torture_context *torture, struct test_join *join) +{ + struct samr_DeleteUser d; + NTSTATUS status; + + if (!join) { + return; + } + d.in.user_handle = &join->user_handle; + d.out.user_handle = &join->user_handle; + + /* Delete machine account */ + status = dcerpc_samr_DeleteUser(join->p, join, &d); + if (!NT_STATUS_IS_OK(status)) { + printf("Delete of machine account %s failed\n", + join->netbios_name); + } else { + printf("Delete of machine account %s was successful.\n", + join->netbios_name); + } + + if (join->libnet_r) { + status = torture_leave_ads_domain(torture, join, join->libnet_r); + } + + talloc_free(join); +} + +/* + return the dom sid for a test join +*/ +_PUBLIC_ const struct dom_sid *torture_join_sid(struct test_join *join) +{ + return join->dom_sid; +} + +const struct dom_sid *torture_join_user_sid(struct test_join *join) +{ + return join->user_sid; +} + +const char *torture_join_netbios_name(struct test_join *join) +{ + return join->netbios_name; +} + +const struct GUID *torture_join_user_guid(struct test_join *join) +{ + return &join->user_guid; +} + +const char *torture_join_dom_netbios_name(struct test_join *join) +{ + return join->dom_netbios_name; +} + +const char *torture_join_dom_dns_name(struct test_join *join) +{ + return join->dom_dns_name; +} + +const char *torture_join_server_dn_str(struct test_join *join) +{ + if (join->libnet_r) { + return join->libnet_r->out.server_dn_str; + } + return NULL; +} + + +#if 0 /* Left as the documentation of the join process, but see new implementation in libnet_become_dc.c */ +struct test_join_ads_dc { + struct test_join *join; +}; + +struct test_join_ads_dc *torture_join_domain_ads_dc(const char *machine_name, + const char *domain, + struct cli_credentials **machine_credentials) +{ + struct test_join_ads_dc *join; + + join = talloc(NULL, struct test_join_ads_dc); + if (join == NULL) { + return NULL; + } + + join->join = torture_join_domain(machine_name, + ACB_SVRTRUST, + machine_credentials); + + if (!join->join) { + return NULL; + } + +/* W2K: */ + /* W2K: modify userAccountControl from 4096 to 532480 */ + + /* W2K: modify RDN to OU=Domain Controllers and skip the $ from server name */ + + /* ask objectVersion of Schema Partition */ + + /* ask rIDManagerReferenz of the Domain Partition */ + + /* ask fsMORoleOwner of the RID-Manager$ object + * returns CN=NTDS Settings,CN=<DC>,CN=Servers,CN=Default-First-Site-Name, ... + */ + + /* ask for dnsHostName of CN=<DC>,CN=Servers,CN=Default-First-Site-Name, ... */ + + /* ask for objectGUID of CN=NTDS Settings,CN=<DC>,CN=Servers,CN=Default-First-Site-Name, ... */ + + /* ask for * of CN=Default-First-Site-Name, ... */ + + /* search (&(|(objectClass=user)(objectClass=computer))(sAMAccountName=<machine_name>$)) in Domain Partition + * attributes : distinguishedName, userAccountControl + */ + + /* ask * for CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,... + * should fail with noSuchObject + */ + + /* add CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,... + * + * objectClass = server + * systemFlags = 50000000 + * serverReferenz = CN=<machine_name>,OU=Domain Controllers,... + */ + + /* ask for * of CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ... + * should fail with noSuchObject + */ + + /* search for (ncname=<domain_nc>) in CN=Partitions,CN=Configuration,... + * attributes: ncName, dnsRoot + */ + + /* modify add CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,... + * serverReferenz = CN=<machine_name>,OU=Domain Controllers,... + * should fail with attributeOrValueExists + */ + + /* modify replace CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,... + * serverReferenz = CN=<machine_name>,OU=Domain Controllers,... + */ + + /* DsAddEntry to create the CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ... + * + */ + + /* replicate CN=Schema,CN=Configuration,... + * using DRSUAPI_DS_BIND_GUID_W2K ("6abec3d1-3054-41c8-a362-5a0c5b7d5d71") + * + */ + + /* replicate CN=Configuration,... + * using DRSUAPI_DS_BIND_GUID_W2K ("6abec3d1-3054-41c8-a362-5a0c5b7d5d71") + * + */ + + /* replicate Domain Partition + * using DRSUAPI_DS_BIND_GUID_W2K ("6abec3d1-3054-41c8-a362-5a0c5b7d5d71") + * + */ + + /* call DsReplicaUpdateRefs() for all partitions like this: + * req1: struct drsuapi_DsReplicaUpdateRefsRequest1 + * naming_context : * + * naming_context: struct drsuapi_DsReplicaObjectIdentifier + * __ndr_size : 0x000000ae (174) + * __ndr_size_sid : 0x00000000 (0) + * guid : 00000000-0000-0000-0000-000000000000 + * sid : S-0-0 + * dn : 'CN=Schema,CN=Configuration,DC=w2k3,DC=vmnet1,DC=vm,DC=base' + * dest_dsa_dns_name : * + * dest_dsa_dns_name : '4a0df188-a0b8-47ea-bbe5-e614723f16dd._msdcs.w2k3.vmnet1.vm.base' + * dest_dsa_guid : 4a0df188-a0b8-47ea-bbe5-e614723f16dd + * options : 0x0000001c (28) + * 0: DRSUAPI_DS_REPLICA_UPDATE_ASYNCHRONOUS_OPERATION + * 0: DRSUAPI_DS_REPLICA_UPDATE_WRITEABLE + * 1: DRSUAPI_DS_REPLICA_UPDATE_ADD_REFERENCE + * 1: DRSUAPI_DS_REPLICA_UPDATE_DELETE_REFERENCE + * 1: DRSUAPI_DS_REPLICA_UPDATE_0x00000010 + * + * 4a0df188-a0b8-47ea-bbe5-e614723f16dd is the objectGUID the DsAddEntry() returned for the + * CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ... + */ + +/* W2K3: see libnet/libnet_become_dc.c */ + return join; +} + +#endif diff --git a/source4/torture/rpc/unixinfo.c b/source4/torture/rpc/unixinfo.c new file mode 100644 index 0000000000..cbe8cf0ff1 --- /dev/null +++ b/source4/torture/rpc/unixinfo.c @@ -0,0 +1,144 @@ +/* + Unix SMB/CIFS implementation. + test suite for unixinfo rpc operations + + Copyright (C) Volker Lendecke 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/rpc/rpc.h" +#include "librpc/gen_ndr/ndr_unixinfo_c.h" +#include "libcli/security/security.h" + +/** + test the SidToUid interface +*/ +static bool test_sidtouid(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct unixinfo_SidToUid r; + struct dom_sid *sid; + uint64_t uid; + + sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1234-5432"); + r.in.sid = *sid; + r.out.uid = &uid; + + status = dcerpc_unixinfo_SidToUid(p, tctx, &r); + if (NT_STATUS_EQUAL(NT_STATUS_NONE_MAPPED, status)) { + } else torture_assert_ntstatus_ok(tctx, status, "SidToUid failed"); + + return true; +} + +/* + test the UidToSid interface +*/ +static bool test_uidtosid(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct unixinfo_UidToSid r; + struct dom_sid sid; + + r.in.uid = 1000; + r.out.sid = &sid; + + torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_UidToSid(p, tctx, &r), + "UidToSid failed"); + + return true; +} + +static bool test_getpwuid(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + uint64_t uids[512]; + uint32_t num_uids = ARRAY_SIZE(uids); + uint32_t i; + struct unixinfo_GetPWUid r; + NTSTATUS result; + + for (i=0; i<num_uids; i++) { + uids[i] = i; + } + + r.in.count = &num_uids; + r.in.uids = uids; + r.out.count = &num_uids; + r.out.infos = talloc_array(tctx, struct unixinfo_GetPWUidInfo, num_uids); + + result = dcerpc_unixinfo_GetPWUid(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, result, "GetPWUid failed"); + + return true; +} + +/* + test the SidToGid interface +*/ +static bool test_sidtogid(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct unixinfo_SidToGid r; + struct dom_sid *sid; + uint64_t gid; + + sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1234-5432"); + r.in.sid = *sid; + r.out.gid = &gid; + + status = dcerpc_unixinfo_SidToGid(p, tctx, &r); + if (NT_STATUS_EQUAL(NT_STATUS_NONE_MAPPED, status)) { + } else torture_assert_ntstatus_ok(tctx, status, "SidToGid failed"); + + return true; +} + +/* + test the GidToSid interface +*/ +static bool test_gidtosid(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct unixinfo_GidToSid r; + struct dom_sid sid; + + r.in.gid = 1000; + r.out.sid = &sid; + + torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_GidToSid(p, tctx, &r), + "GidToSid failed"); + + return true; +} + +struct torture_suite *torture_rpc_unixinfo(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + struct torture_rpc_tcase *tcase; + + suite = torture_suite_create(mem_ctx, "UNIXINFO"); + tcase = torture_suite_add_rpc_iface_tcase(suite, "unixinfo", + &ndr_table_unixinfo); + + torture_rpc_tcase_add_test(tcase, "sidtouid", test_sidtouid); + torture_rpc_tcase_add_test(tcase, "uidtosid", test_uidtosid); + torture_rpc_tcase_add_test(tcase, "getpwuid", test_getpwuid); + torture_rpc_tcase_add_test(tcase, "sidtogid", test_sidtogid); + torture_rpc_tcase_add_test(tcase, "gidtosid", test_gidtosid); + + return suite; +} diff --git a/source4/torture/rpc/winreg.c b/source4/torture/rpc/winreg.c new file mode 100644 index 0000000000..8b602ef652 --- /dev/null +++ b/source4/torture/rpc/winreg.c @@ -0,0 +1,1908 @@ +/* + Unix SMB/CIFS implementation. + test suite for winreg rpc operations + + Copyright (C) Tim Potter 2003 + Copyright (C) Jelmer Vernooij 2004-2007 + Copyright (C) Günther Deschner 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_winreg_c.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" +#include "torture/rpc/rpc.h" + +#define TEST_KEY_BASE "smbtorture test" +#define TEST_KEY1 TEST_KEY_BASE "\\spottyfoot" +#define TEST_KEY2 TEST_KEY_BASE "\\with a SD (#1)" +#define TEST_KEY3 TEST_KEY_BASE "\\with a subkey" +#define TEST_KEY4 TEST_KEY_BASE "\\sd_tests" +#define TEST_SUBKEY TEST_KEY3 "\\subkey" +#define TEST_SUBKEY_SD TEST_KEY4 "\\subkey_sd" +#define TEST_SUBSUBKEY_SD TEST_KEY4 "\\subkey_sd\\subsubkey_sd" + +#define TEST_SID "S-1-5-21-1234567890-1234567890-1234567890-500" + +static void init_lsa_StringLarge(struct lsa_StringLarge *name, const char *s) +{ + name->string = s; +} + +static void init_winreg_String(struct winreg_String *name, const char *s) +{ + name->name = s; + if (s) { + name->name_len = 2 * (strlen_m(s) + 1); + name->name_size = name->name_len; + } else { + name->name_len = 0; + name->name_size = 0; + } +} + +static bool test_GetVersion(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct winreg_GetVersion r; + uint32_t v; + + ZERO_STRUCT(r); + r.in.handle = handle; + r.out.version = &v; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_GetVersion(p, tctx, &r), + "GetVersion failed"); + + torture_assert_werr_ok(tctx, r.out.result, "GetVersion failed"); + + return true; +} + +static bool test_NotifyChangeKeyValue(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct winreg_NotifyChangeKeyValue r; + + r.in.handle = handle; + r.in.watch_subtree = true; + r.in.notify_filter = 0; + r.in.unknown = r.in.unknown2 = 0; + init_winreg_String(&r.in.string1, NULL); + init_winreg_String(&r.in.string2, NULL); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_NotifyChangeKeyValue(p, tctx, &r), + "NotifyChangeKeyValue failed"); + + if (!W_ERROR_IS_OK(r.out.result)) { + torture_comment(tctx, + "NotifyChangeKeyValue failed - %s - not considering\n", + win_errstr(r.out.result)); + return true; + } + + return true; +} + +static bool test_CreateKey(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, const char *name, + const char *class) +{ + struct winreg_CreateKey r; + struct policy_handle newhandle; + enum winreg_CreateAction action_taken = 0; + + r.in.handle = handle; + r.out.new_handle = &newhandle; + init_winreg_String(&r.in.name, name); + init_winreg_String(&r.in.keyclass, class); + r.in.options = 0x0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.action_taken = r.out.action_taken = &action_taken; + r.in.secdesc = NULL; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CreateKey(p, tctx, &r), + "CreateKey failed"); + + torture_assert_werr_ok(tctx, r.out.result, "CreateKey failed"); + + return true; +} + + +/* + createkey testing with a SD +*/ +static bool test_CreateKey_sd(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, const char *name, + const char *class, + struct policy_handle *newhandle) +{ + struct winreg_CreateKey r; + enum winreg_CreateAction action_taken = 0; + struct security_descriptor *sd; + DATA_BLOB sdblob; + struct winreg_SecBuf secbuf; + + sd = security_descriptor_dacl_create(tctx, + 0, + NULL, NULL, + SID_NT_AUTHENTICATED_USERS, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_GENERIC_ALL, + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT, + NULL); + + torture_assert_ndr_success(tctx, + ndr_push_struct_blob(&sdblob, tctx, NULL, sd, + (ndr_push_flags_fn_t)ndr_push_security_descriptor), + "Failed to push security_descriptor ?!\n"); + + secbuf.sd.data = sdblob.data; + secbuf.sd.len = sdblob.length; + secbuf.sd.size = sdblob.length; + secbuf.length = sdblob.length-10; + secbuf.inherit = 0; + + r.in.handle = handle; + r.out.new_handle = newhandle; + init_winreg_String(&r.in.name, name); + init_winreg_String(&r.in.keyclass, class); + r.in.options = 0x0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.action_taken = r.out.action_taken = &action_taken; + r.in.secdesc = &secbuf; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CreateKey(p, tctx, &r), + "CreateKey with sd failed"); + + torture_assert_werr_ok(tctx, r.out.result, "CreateKey with sd failed"); + + return true; +} + +static bool _test_GetKeySecurity(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + uint32_t *sec_info_ptr, + WERROR get_werr, + struct security_descriptor **sd_out) +{ + struct winreg_GetKeySecurity r; + struct security_descriptor *sd = NULL; + uint32_t sec_info; + DATA_BLOB sdblob; + + if (sec_info_ptr) { + sec_info = *sec_info_ptr; + } else { + sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL; + } + + ZERO_STRUCT(r); + + r.in.handle = handle; + r.in.sec_info = sec_info; + r.in.sd = r.out.sd = talloc_zero(tctx, struct KeySecurityData); + r.in.sd->size = 0x1000; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_GetKeySecurity(p, tctx, &r), + "GetKeySecurity failed"); + + torture_assert_werr_equal(tctx, r.out.result, get_werr, + "GetKeySecurity failed"); + + sdblob.data = r.out.sd->data; + sdblob.length = r.out.sd->len; + + sd = talloc_zero(tctx, struct security_descriptor); + + torture_assert_ndr_success(tctx, + ndr_pull_struct_blob(&sdblob, tctx, NULL, sd, + (ndr_pull_flags_fn_t)ndr_pull_security_descriptor), + "pull_security_descriptor failed"); + + if (p->conn->flags & DCERPC_DEBUG_PRINT_OUT) { + NDR_PRINT_DEBUG(security_descriptor, sd); + } + + if (sd_out) { + *sd_out = sd; + } else { + talloc_free(sd); + } + + return true; +} + +static bool test_GetKeySecurity(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + struct security_descriptor **sd_out) +{ + return _test_GetKeySecurity(p, tctx, handle, NULL, WERR_OK, sd_out); +} + +static bool _test_SetKeySecurity(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + uint32_t *sec_info_ptr, + struct security_descriptor *sd, + WERROR werr) +{ + struct winreg_SetKeySecurity r; + struct KeySecurityData *sdata = NULL; + DATA_BLOB sdblob; + uint32_t sec_info; + + ZERO_STRUCT(r); + + if (sd && (p->conn->flags & DCERPC_DEBUG_PRINT_OUT)) { + NDR_PRINT_DEBUG(security_descriptor, sd); + } + + torture_assert_ndr_success(tctx, + ndr_push_struct_blob(&sdblob, tctx, NULL, sd, + (ndr_push_flags_fn_t)ndr_push_security_descriptor), + "push_security_descriptor failed"); + + sdata = talloc_zero(tctx, struct KeySecurityData); + sdata->data = sdblob.data; + sdata->size = sdblob.length; + sdata->len = sdblob.length; + + if (sec_info_ptr) { + sec_info = *sec_info_ptr; + } else { + sec_info = SECINFO_UNPROTECTED_SACL | + SECINFO_UNPROTECTED_DACL; + if (sd->owner_sid) { + sec_info |= SECINFO_OWNER; + } + if (sd->group_sid) { + sec_info |= SECINFO_GROUP; + } + if (sd->sacl) { + sec_info |= SECINFO_SACL; + } + if (sd->dacl) { + sec_info |= SECINFO_DACL; + } + } + + r.in.handle = handle; + r.in.sec_info = sec_info; + r.in.sd = sdata; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_SetKeySecurity(p, tctx, &r), + "SetKeySecurity failed"); + + torture_assert_werr_equal(tctx, r.out.result, werr, + "SetKeySecurity failed"); + + return true; +} + +static bool test_SetKeySecurity(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + struct security_descriptor *sd) +{ + return _test_SetKeySecurity(p, tctx, handle, NULL, sd, WERR_OK); +} + +static bool test_CloseKey(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle) +{ + struct winreg_CloseKey r; + + r.in.handle = r.out.handle = handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CloseKey(p, tctx, &r), + "CloseKey failed"); + + torture_assert_werr_ok(tctx, r.out.result, "CloseKey failed"); + + return true; +} + +static bool test_FlushKey(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle) +{ + struct winreg_FlushKey r; + + r.in.handle = handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_FlushKey(p, tctx, &r), + "FlushKey failed"); + + torture_assert_werr_ok(tctx, r.out.result, "FlushKey failed"); + + return true; +} + +static bool _test_OpenKey(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *hive_handle, + const char *keyname, uint32_t access_mask, + struct policy_handle *key_handle, + WERROR open_werr, + bool *success) +{ + struct winreg_OpenKey r; + + r.in.parent_handle = hive_handle; + init_winreg_String(&r.in.keyname, keyname); + r.in.unknown = 0x00000000; + r.in.access_mask = access_mask; + r.out.handle = key_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_OpenKey(p, tctx, &r), + "OpenKey failed"); + + torture_assert_werr_equal(tctx, r.out.result, open_werr, + "OpenKey failed"); + + if (success && W_ERROR_EQUAL(r.out.result, WERR_OK)) { + *success = true; + } + + return true; +} + +static bool test_OpenKey(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *hive_handle, + const char *keyname, struct policy_handle *key_handle) +{ + return _test_OpenKey(p, tctx, hive_handle, keyname, + SEC_FLAG_MAXIMUM_ALLOWED, key_handle, + WERR_OK, NULL); +} + +static bool test_Cleanup(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, const char *key) +{ + struct winreg_DeleteKey r; + + r.in.handle = handle; + + init_winreg_String(&r.in.key, key); + dcerpc_winreg_DeleteKey(p, tctx, &r); + + return true; +} + +static bool _test_GetSetSecurityDescriptor(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + WERROR get_werr, + WERROR set_werr) +{ + struct security_descriptor *sd = NULL; + + if (!_test_GetKeySecurity(p, tctx, handle, NULL, get_werr, &sd)) { + return false; + } + + if (!_test_SetKeySecurity(p, tctx, handle, NULL, sd, set_werr)) { + return false; + } + + return true; +} + +static bool test_SecurityDescriptor(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key) +{ + struct policy_handle new_handle; + bool ret = true; + + torture_comment(tctx, "SecurityDescriptor get & set\n"); + + if (!test_OpenKey(p, tctx, handle, key, &new_handle)) { + return false; + } + + if (!_test_GetSetSecurityDescriptor(p, tctx, &new_handle, + WERR_OK, WERR_OK)) { + ret = false; + } + + if (!test_CloseKey(p, tctx, &new_handle)) { + return false; + } + + return ret; +} + +static bool _test_SecurityDescriptor(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + uint32_t access_mask, + const char *key, + WERROR open_werr, + WERROR get_werr, + WERROR set_werr) +{ + struct policy_handle new_handle; + bool ret = true; + bool got_key = false; + + if (!_test_OpenKey(p, tctx, handle, key, access_mask, &new_handle, + open_werr, &got_key)) { + return false; + } + + if (!got_key) { + return true; + } + + if (!_test_GetSetSecurityDescriptor(p, tctx, &new_handle, + get_werr, set_werr)) { + ret = false; + } + + if (!test_CloseKey(p, tctx, &new_handle)) { + return false; + } + + return ret; +} + +static bool test_dacl_trustee_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const struct dom_sid *sid) +{ + struct security_descriptor *sd = NULL; + int i; + + if (!test_GetKeySecurity(p, tctx, handle, &sd)) { + return false; + } + + if (!sd || !sd->dacl) { + return false; + } + + for (i = 0; i < sd->dacl->num_aces; i++) { + if (dom_sid_equal(&sd->dacl->aces[i].trustee, sid)) { + return true; + } + } + + return false; +} + +static bool _test_dacl_trustee_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + const struct dom_sid *sid) +{ + struct policy_handle new_handle; + bool ret = true; + + if (!test_OpenKey(p, tctx, handle, key, &new_handle)) { + return false; + } + + ret = test_dacl_trustee_present(p, tctx, &new_handle, sid); + + test_CloseKey(p, tctx, &new_handle); + + return ret; +} + +static bool test_sacl_trustee_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const struct dom_sid *sid) +{ + struct security_descriptor *sd = NULL; + int i; + uint32_t sec_info = SECINFO_SACL; + + if (!_test_GetKeySecurity(p, tctx, handle, &sec_info, WERR_OK, &sd)) { + return false; + } + + if (!sd || !sd->sacl) { + return false; + } + + for (i = 0; i < sd->sacl->num_aces; i++) { + if (dom_sid_equal(&sd->sacl->aces[i].trustee, sid)) { + return true; + } + } + + return false; +} + +static bool _test_sacl_trustee_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + const struct dom_sid *sid) +{ + struct policy_handle new_handle; + bool ret = true; + + if (!_test_OpenKey(p, tctx, handle, key, SEC_FLAG_SYSTEM_SECURITY, + &new_handle, WERR_OK, NULL)) { + return false; + } + + ret = test_sacl_trustee_present(p, tctx, &new_handle, sid); + + test_CloseKey(p, tctx, &new_handle); + + return ret; +} + +static bool test_owner_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const struct dom_sid *sid) +{ + struct security_descriptor *sd = NULL; + uint32_t sec_info = SECINFO_OWNER; + + if (!_test_GetKeySecurity(p, tctx, handle, &sec_info, WERR_OK, &sd)) { + return false; + } + + if (!sd || !sd->owner_sid) { + return false; + } + + return dom_sid_equal(sd->owner_sid, sid); +} + +static bool _test_owner_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + const struct dom_sid *sid) +{ + struct policy_handle new_handle; + bool ret = true; + + if (!test_OpenKey(p, tctx, handle, key, &new_handle)) { + return false; + } + + ret = test_owner_present(p, tctx, &new_handle, sid); + + test_CloseKey(p, tctx, &new_handle); + + return ret; +} + +static bool test_group_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const struct dom_sid *sid) +{ + struct security_descriptor *sd = NULL; + uint32_t sec_info = SECINFO_GROUP; + + if (!_test_GetKeySecurity(p, tctx, handle, &sec_info, WERR_OK, &sd)) { + return false; + } + + if (!sd || !sd->group_sid) { + return false; + } + + return dom_sid_equal(sd->group_sid, sid); +} + +static bool _test_group_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + const struct dom_sid *sid) +{ + struct policy_handle new_handle; + bool ret = true; + + if (!test_OpenKey(p, tctx, handle, key, &new_handle)) { + return false; + } + + ret = test_group_present(p, tctx, &new_handle, sid); + + test_CloseKey(p, tctx, &new_handle); + + return ret; +} + +static bool test_dacl_trustee_flags_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const struct dom_sid *sid, + uint8_t flags) +{ + struct security_descriptor *sd = NULL; + int i; + + if (!test_GetKeySecurity(p, tctx, handle, &sd)) { + return false; + } + + if (!sd || !sd->dacl) { + return false; + } + + for (i = 0; i < sd->dacl->num_aces; i++) { + if ((dom_sid_equal(&sd->dacl->aces[i].trustee, sid)) && + (sd->dacl->aces[i].flags == flags)) { + return true; + } + } + + return false; +} + +static bool test_dacl_ace_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const struct security_ace *ace) +{ + struct security_descriptor *sd = NULL; + int i; + + if (!test_GetKeySecurity(p, tctx, handle, &sd)) { + return false; + } + + if (!sd || !sd->dacl) { + return false; + } + + for (i = 0; i < sd->dacl->num_aces; i++) { + if (security_ace_equal(&sd->dacl->aces[i], ace)) { + return true; + } + } + + return false; +} + +static bool test_RestoreSecurity(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + struct security_descriptor *sd) +{ + struct policy_handle new_handle; + bool ret = true; + + if (!test_OpenKey(p, tctx, handle, key, &new_handle)) { + return false; + } + + if (!test_SetKeySecurity(p, tctx, &new_handle, sd)) { + ret = false; + } + + if (!test_CloseKey(p, tctx, &new_handle)) { + ret = false; + } + + return ret; +} + +static bool test_BackupSecurity(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + struct security_descriptor **sd) +{ + struct policy_handle new_handle; + bool ret = true; + + if (!test_OpenKey(p, tctx, handle, key, &new_handle)) { + return false; + } + + if (!test_GetKeySecurity(p, tctx, &new_handle, sd)) { + ret = false; + } + + if (!test_CloseKey(p, tctx, &new_handle)) { + ret = false; + } + + return ret; +} + +static bool test_SecurityDescriptorInheritance(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key) +{ + /* get sd + add ace SEC_ACE_FLAG_CONTAINER_INHERIT + set sd + get sd + check ace + add subkey + get sd + check ace + add subsubkey + get sd + check ace + del subsubkey + del subkey + reset sd + */ + + struct security_descriptor *sd = NULL; + struct security_descriptor *sd_orig = NULL; + struct security_ace *ace = NULL; + struct policy_handle new_handle; + NTSTATUS status; + bool ret = true; + + torture_comment(tctx, "SecurityDescriptor inheritance\n"); + + if (!test_OpenKey(p, tctx, handle, key, &new_handle)) { + return false; + } + + if (!_test_GetKeySecurity(p, tctx, &new_handle, NULL, WERR_OK, &sd)) { + return false; + } + + sd_orig = security_descriptor_copy(tctx, sd); + if (sd_orig == NULL) { + return false; + } + + ace = security_ace_create(tctx, + TEST_SID, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_STD_REQUIRED, + SEC_ACE_FLAG_CONTAINER_INHERIT); + + status = security_descriptor_dacl_add(sd, ace); + if (!NT_STATUS_IS_OK(status)) { + printf("failed to add ace: %s\n", nt_errstr(status)); + return false; + } + + /* FIXME: add further tests for these flags */ + sd->type |= SEC_DESC_DACL_AUTO_INHERIT_REQ | + SEC_DESC_SACL_AUTO_INHERITED; + + if (!test_SetKeySecurity(p, tctx, &new_handle, sd)) { + return false; + } + + if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) { + printf("new ACE not present!\n"); + return false; + } + + if (!test_CloseKey(p, tctx, &new_handle)) { + return false; + } + + if (!test_CreateKey(p, tctx, handle, TEST_SUBKEY_SD, NULL)) { + ret = false; + goto out; + } + + if (!test_OpenKey(p, tctx, handle, TEST_SUBKEY_SD, &new_handle)) { + ret = false; + goto out; + } + + if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) { + printf("inherited ACE not present!\n"); + ret = false; + goto out; + } + + test_CloseKey(p, tctx, &new_handle); + if (!test_CreateKey(p, tctx, handle, TEST_SUBSUBKEY_SD, NULL)) { + ret = false; + goto out; + } + + if (!test_OpenKey(p, tctx, handle, TEST_SUBSUBKEY_SD, &new_handle)) { + ret = false; + goto out; + } + + if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) { + printf("inherited ACE not present!\n"); + ret = false; + goto out; + } + + out: + test_CloseKey(p, tctx, &new_handle); + test_Cleanup(p, tctx, handle, TEST_SUBKEY_SD); + test_RestoreSecurity(p, tctx, handle, key, sd_orig); + + return true; +} + +static bool test_SecurityDescriptorBlockInheritance(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key) +{ + /* get sd + add ace SEC_ACE_FLAG_NO_PROPAGATE_INHERIT + set sd + add subkey/subkey + get sd + check ace + get sd from subkey + check ace + del subkey/subkey + del subkey + reset sd + */ + + struct security_descriptor *sd = NULL; + struct security_descriptor *sd_orig = NULL; + struct security_ace *ace = NULL; + struct policy_handle new_handle; + struct dom_sid *sid = NULL; + NTSTATUS status; + bool ret = true; + uint8_t ace_flags = 0x0; + + torture_comment(tctx, "SecurityDescriptor inheritance block\n"); + + if (!test_OpenKey(p, tctx, handle, key, &new_handle)) { + return false; + } + + if (!_test_GetKeySecurity(p, tctx, &new_handle, NULL, WERR_OK, &sd)) { + return false; + } + + sd_orig = security_descriptor_copy(tctx, sd); + if (sd_orig == NULL) { + return false; + } + + ace = security_ace_create(tctx, + TEST_SID, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_STD_REQUIRED, + SEC_ACE_FLAG_CONTAINER_INHERIT | + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT); + + status = security_descriptor_dacl_add(sd, ace); + if (!NT_STATUS_IS_OK(status)) { + printf("failed to add ace: %s\n", nt_errstr(status)); + return false; + } + + if (!_test_SetKeySecurity(p, tctx, &new_handle, NULL, sd, WERR_OK)) { + return false; + } + + if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) { + printf("new ACE not present!\n"); + return false; + } + + if (!test_CloseKey(p, tctx, &new_handle)) { + return false; + } + + if (!test_CreateKey(p, tctx, handle, TEST_SUBSUBKEY_SD, NULL)) { + return false; + } + + if (!test_OpenKey(p, tctx, handle, TEST_SUBSUBKEY_SD, &new_handle)) { + ret = false; + goto out; + } + + if (test_dacl_ace_present(p, tctx, &new_handle, ace)) { + printf("inherited ACE present but should not!\n"); + ret = false; + goto out; + } + + sid = dom_sid_parse_talloc(tctx, TEST_SID); + if (sid == NULL) { + return false; + } + + if (test_dacl_trustee_present(p, tctx, &new_handle, sid)) { + printf("inherited trustee SID present but should not!\n"); + ret = false; + goto out; + } + + test_CloseKey(p, tctx, &new_handle); + + if (!test_OpenKey(p, tctx, handle, TEST_SUBKEY_SD, &new_handle)) { + ret = false; + goto out; + } + + if (test_dacl_ace_present(p, tctx, &new_handle, ace)) { + printf("inherited ACE present but should not!\n"); + ret = false; + goto out; + } + + if (!test_dacl_trustee_flags_present(p, tctx, &new_handle, sid, ace_flags)) { + printf("inherited trustee SID with flags 0x%02x not present!\n", + ace_flags); + ret = false; + goto out; + } + + out: + test_CloseKey(p, tctx, &new_handle); + test_Cleanup(p, tctx, handle, TEST_SUBKEY_SD); + test_RestoreSecurity(p, tctx, handle, key, sd_orig); + + return ret; +} + +static bool test_SecurityDescriptorsMasks(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key) +{ + bool ret = true; + int i; + + struct winreg_mask_result_table { + uint32_t access_mask; + WERROR open_werr; + WERROR get_werr; + WERROR set_werr; + } sd_mask_tests[] = { + { 0, + WERR_ACCESS_DENIED, WERR_BADFILE, WERR_FOOBAR }, + { SEC_FLAG_MAXIMUM_ALLOWED, + WERR_OK, WERR_OK, WERR_OK }, + { SEC_STD_WRITE_DAC, + WERR_OK, WERR_ACCESS_DENIED, WERR_FOOBAR }, + { SEC_FLAG_SYSTEM_SECURITY, + WERR_OK, WERR_ACCESS_DENIED, WERR_FOOBAR } + }; + + /* FIXME: before this test can ever run successfully we need a way to + * correctly read a NULL security_descritpor in ndr, get the required + * length, requery, etc. + */ + + return true; + + for (i=0; i < ARRAY_SIZE(sd_mask_tests); i++) { + + torture_comment(tctx, + "SecurityDescriptor get & set with access_mask: 0x%08x\n", + sd_mask_tests[i].access_mask); + torture_comment(tctx, + "expecting: open %s, get: %s, set: %s\n", + win_errstr(sd_mask_tests[i].open_werr), + win_errstr(sd_mask_tests[i].get_werr), + win_errstr(sd_mask_tests[i].set_werr)); + + if (_test_SecurityDescriptor(p, tctx, handle, + sd_mask_tests[i].access_mask, key, + sd_mask_tests[i].open_werr, + sd_mask_tests[i].get_werr, + sd_mask_tests[i].set_werr)) { + ret = false; + } + } + + return ret; +} + +typedef bool (*secinfo_verify_fn)(struct dcerpc_pipe *, + struct torture_context *, + struct policy_handle *, + const char *, + const struct dom_sid *); + +static bool test_SetSecurityDescriptor_SecInfo(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + const char *test, + uint32_t access_mask, + uint32_t sec_info, + struct security_descriptor *sd, + WERROR set_werr, + bool expect_present, + bool (*fn) (struct dcerpc_pipe *, + struct torture_context *, + struct policy_handle *, + const char *, + const struct dom_sid *), + const struct dom_sid *sid) +{ + struct policy_handle new_handle; + bool open_success = false; + + torture_comment(tctx, "SecurityDescriptor (%s) sets for secinfo: " + "0x%08x, access_mask: 0x%08x\n", + test, sec_info, access_mask); + + if (!_test_OpenKey(p, tctx, handle, key, + access_mask, + &new_handle, + WERR_OK, + &open_success)) { + return false; + } + + if (!open_success) { + printf("key did not open\n"); + test_CloseKey(p, tctx, &new_handle); + return false; + } + + if (!_test_SetKeySecurity(p, tctx, &new_handle, &sec_info, + sd, + set_werr)) { + torture_warning(tctx, + "SetKeySecurity with secinfo: 0x%08x has failed\n", + sec_info); + smb_panic(""); + test_CloseKey(p, tctx, &new_handle); + return false; + } + + test_CloseKey(p, tctx, &new_handle); + + if (W_ERROR_IS_OK(set_werr)) { + bool present; + present = fn(p, tctx, handle, key, sid); + if ((expect_present) && (!present)) { + torture_warning(tctx, + "%s sid is not present!\n", + test); + return false; + } + if ((!expect_present) && (present)) { + torture_warning(tctx, + "%s sid is present but not expected!\n", + test); + return false; + } + } + + return true; +} + +static bool test_SecurityDescriptorsSecInfo(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key) +{ + struct security_descriptor *sd_orig = NULL; + struct dom_sid *sid = NULL; + bool ret = true; + int i, a; + + struct security_descriptor *sd_owner = + security_descriptor_dacl_create(tctx, + 0, + TEST_SID, NULL, NULL); + + struct security_descriptor *sd_group = + security_descriptor_dacl_create(tctx, + 0, + NULL, TEST_SID, NULL); + + struct security_descriptor *sd_dacl = + security_descriptor_dacl_create(tctx, + 0, + NULL, NULL, + TEST_SID, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_GENERIC_ALL, + 0, + SID_NT_AUTHENTICATED_USERS, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_GENERIC_ALL, + 0, + NULL); + + struct security_descriptor *sd_sacl = + security_descriptor_sacl_create(tctx, + 0, + NULL, NULL, + TEST_SID, + SEC_ACE_TYPE_SYSTEM_AUDIT, + SEC_GENERIC_ALL, + SEC_ACE_FLAG_SUCCESSFUL_ACCESS, + NULL); + + struct winreg_secinfo_table { + struct security_descriptor *sd; + uint32_t sec_info; + WERROR set_werr; + bool sid_present; + secinfo_verify_fn fn; + }; + + struct winreg_secinfo_table sec_info_owner_tests[] = { + { sd_owner, 0, WERR_OK, + false, (secinfo_verify_fn)_test_owner_present }, + { sd_owner, SECINFO_OWNER, WERR_OK, + true, (secinfo_verify_fn)_test_owner_present }, + { sd_owner, SECINFO_GROUP, WERR_INVALID_PARAM }, + { sd_owner, SECINFO_DACL, WERR_OK, + true, (secinfo_verify_fn)_test_owner_present }, + { sd_owner, SECINFO_SACL, WERR_ACCESS_DENIED }, + }; + + uint32_t sd_owner_good_access_masks[] = { + SEC_FLAG_MAXIMUM_ALLOWED, + /* SEC_STD_WRITE_OWNER, */ + }; + + struct winreg_secinfo_table sec_info_group_tests[] = { + { sd_group, 0, WERR_OK, + false, (secinfo_verify_fn)_test_group_present }, + { sd_group, SECINFO_OWNER, WERR_INVALID_PARAM }, + { sd_group, SECINFO_GROUP, WERR_OK, + true, (secinfo_verify_fn)_test_group_present }, + { sd_group, SECINFO_DACL, WERR_OK, + true, (secinfo_verify_fn)_test_group_present }, + { sd_group, SECINFO_SACL, WERR_ACCESS_DENIED }, + }; + + uint32_t sd_group_good_access_masks[] = { + SEC_FLAG_MAXIMUM_ALLOWED, + }; + + struct winreg_secinfo_table sec_info_dacl_tests[] = { + { sd_dacl, 0, WERR_OK, + false, (secinfo_verify_fn)_test_dacl_trustee_present }, + { sd_dacl, SECINFO_OWNER, WERR_INVALID_PARAM }, + { sd_dacl, SECINFO_GROUP, WERR_INVALID_PARAM }, + { sd_dacl, SECINFO_DACL, WERR_OK, + true, (secinfo_verify_fn)_test_dacl_trustee_present }, + { sd_dacl, SECINFO_SACL, WERR_ACCESS_DENIED }, + }; + + uint32_t sd_dacl_good_access_masks[] = { + SEC_FLAG_MAXIMUM_ALLOWED, + SEC_STD_WRITE_DAC, + }; + + struct winreg_secinfo_table sec_info_sacl_tests[] = { + { sd_sacl, 0, WERR_OK, + false, (secinfo_verify_fn)_test_sacl_trustee_present }, + { sd_sacl, SECINFO_OWNER, WERR_INVALID_PARAM }, + { sd_sacl, SECINFO_GROUP, WERR_INVALID_PARAM }, + { sd_sacl, SECINFO_DACL, WERR_OK, + false, (secinfo_verify_fn)_test_sacl_trustee_present }, + { sd_sacl, SECINFO_SACL, WERR_OK, + true, (secinfo_verify_fn)_test_sacl_trustee_present }, + }; + + uint32_t sd_sacl_good_access_masks[] = { + SEC_FLAG_MAXIMUM_ALLOWED | SEC_FLAG_SYSTEM_SECURITY, + /* SEC_FLAG_SYSTEM_SECURITY, */ + }; + + sid = dom_sid_parse_talloc(tctx, TEST_SID); + if (sid == NULL) { + return false; + } + + if (!test_BackupSecurity(p, tctx, handle, key, &sd_orig)) { + return false; + } + + /* OWNER */ + + for (i=0; i < ARRAY_SIZE(sec_info_owner_tests); i++) { + + for (a=0; a < ARRAY_SIZE(sd_owner_good_access_masks); a++) { + + if (!test_SetSecurityDescriptor_SecInfo(p, tctx, handle, + key, + "OWNER", + sd_owner_good_access_masks[a], + sec_info_owner_tests[i].sec_info, + sec_info_owner_tests[i].sd, + sec_info_owner_tests[i].set_werr, + sec_info_owner_tests[i].sid_present, + sec_info_owner_tests[i].fn, + sid)) + { + printf("test_SetSecurityDescriptor_SecInfo failed for OWNER\n"); + ret = false; + goto out; + } + } + } + + /* GROUP */ + + for (i=0; i < ARRAY_SIZE(sec_info_group_tests); i++) { + + for (a=0; a < ARRAY_SIZE(sd_group_good_access_masks); a++) { + + if (!test_SetSecurityDescriptor_SecInfo(p, tctx, handle, + key, + "GROUP", + sd_group_good_access_masks[a], + sec_info_group_tests[i].sec_info, + sec_info_group_tests[i].sd, + sec_info_group_tests[i].set_werr, + sec_info_group_tests[i].sid_present, + sec_info_group_tests[i].fn, + sid)) + { + printf("test_SetSecurityDescriptor_SecInfo failed for GROUP\n"); + ret = false; + goto out; + } + } + } + + /* DACL */ + + for (i=0; i < ARRAY_SIZE(sec_info_dacl_tests); i++) { + + for (a=0; a < ARRAY_SIZE(sd_dacl_good_access_masks); a++) { + + if (!test_SetSecurityDescriptor_SecInfo(p, tctx, handle, + key, + "DACL", + sd_dacl_good_access_masks[a], + sec_info_dacl_tests[i].sec_info, + sec_info_dacl_tests[i].sd, + sec_info_dacl_tests[i].set_werr, + sec_info_dacl_tests[i].sid_present, + sec_info_dacl_tests[i].fn, + sid)) + { + printf("test_SetSecurityDescriptor_SecInfo failed for DACL\n"); + ret = false; + goto out; + } + } + } + + /* SACL */ + + for (i=0; i < ARRAY_SIZE(sec_info_sacl_tests); i++) { + + for (a=0; a < ARRAY_SIZE(sd_sacl_good_access_masks); a++) { + + if (!test_SetSecurityDescriptor_SecInfo(p, tctx, handle, + key, + "SACL", + sd_sacl_good_access_masks[a], + sec_info_sacl_tests[i].sec_info, + sec_info_sacl_tests[i].sd, + sec_info_sacl_tests[i].set_werr, + sec_info_sacl_tests[i].sid_present, + sec_info_sacl_tests[i].fn, + sid)) + { + printf("test_SetSecurityDescriptor_SecInfo failed for SACL\n"); + ret = false; + goto out; + } + } + } + + out: + test_RestoreSecurity(p, tctx, handle, key, sd_orig); + + return ret; +} + +static bool test_SecurityDescriptors(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key) +{ + bool ret = true; + + if (!test_SecurityDescriptor(p, tctx, handle, key)) { + printf("test_SecurityDescriptor failed\n"); + ret = false; + } + + if (!test_SecurityDescriptorInheritance(p, tctx, handle, key)) { + printf("test_SecurityDescriptorInheritance failed\n"); + ret = false; + } + + if (!test_SecurityDescriptorBlockInheritance(p, tctx, handle, key)) { + printf("test_SecurityDescriptorBlockInheritance failed\n"); + ret = false; + } + + if (!test_SecurityDescriptorsSecInfo(p, tctx, handle, key)) { + printf("test_SecurityDescriptorsSecInfo failed\n"); + ret = false; + } + + if (!test_SecurityDescriptorsMasks(p, tctx, handle, key)) { + printf("test_SecurityDescriptorsMasks failed\n"); + ret = false; + } + + return ret; +} + +static bool test_DeleteKey(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, const char *key) +{ + NTSTATUS status; + struct winreg_DeleteKey r; + + r.in.handle = handle; + init_winreg_String(&r.in.key, key); + + status = dcerpc_winreg_DeleteKey(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "DeleteKey failed"); + torture_assert_werr_ok(tctx, r.out.result, "DeleteKey failed"); + + return true; +} + +static bool test_QueryInfoKey(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, char *class) +{ + struct winreg_QueryInfoKey r; + uint32_t num_subkeys, max_subkeylen, max_subkeysize, + num_values, max_valnamelen, max_valbufsize, + secdescsize; + NTTIME last_changed_time; + + ZERO_STRUCT(r); + r.in.handle = handle; + r.out.num_subkeys = &num_subkeys; + r.out.max_subkeylen = &max_subkeylen; + r.out.max_subkeysize = &max_subkeysize; + r.out.num_values = &num_values; + r.out.max_valnamelen = &max_valnamelen; + r.out.max_valbufsize = &max_valbufsize; + r.out.secdescsize = &secdescsize; + r.out.last_changed_time = &last_changed_time; + + r.out.classname = talloc(tctx, struct winreg_String); + + r.in.classname = talloc(tctx, struct winreg_String); + init_winreg_String(r.in.classname, class); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_QueryInfoKey(p, tctx, &r), + "QueryInfoKey failed"); + + torture_assert_werr_ok(tctx, r.out.result, "QueryInfoKey failed"); + + return true; +} + +static bool test_key(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, int depth, + bool test_security); + +static bool test_EnumKey(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, int depth, + bool test_security) +{ + struct winreg_EnumKey r; + struct winreg_StringBuf class, name; + NTSTATUS status; + NTTIME t = 0; + + class.name = ""; + class.size = 1024; + + r.in.handle = handle; + r.in.enum_index = 0; + r.in.name = &name; + r.in.keyclass = &class; + r.out.name = &name; + r.in.last_changed_time = &t; + + do { + name.name = NULL; + name.size = 1024; + + status = dcerpc_winreg_EnumKey(p, tctx, &r); + + if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result)) { + struct policy_handle key_handle; + + torture_comment(tctx, "EnumKey: %d: %s\n", + r.in.enum_index, + r.out.name->name); + + if (!test_OpenKey(p, tctx, handle, r.out.name->name, + &key_handle)) { + } else { + test_key(p, tctx, &key_handle, + depth + 1, test_security); + } + } + + r.in.enum_index++; + + } while (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result)); + + torture_assert_ntstatus_ok(tctx, status, "EnumKey failed"); + + if (!W_ERROR_IS_OK(r.out.result) && + !W_ERROR_EQUAL(r.out.result, WERR_NO_MORE_ITEMS)) { + torture_fail(tctx, "EnumKey failed"); + } + + return true; +} + +static bool test_QueryMultipleValues(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *valuename) +{ + struct winreg_QueryMultipleValues r; + NTSTATUS status; + uint32_t bufsize=0; + + r.in.key_handle = handle; + r.in.values = r.out.values = talloc_array(tctx, struct QueryMultipleValue, 1); + r.in.values[0].name = talloc(tctx, struct winreg_String); + r.in.values[0].name->name = valuename; + r.in.values[0].offset = 0; + r.in.values[0].length = 0; + r.in.values[0].type = 0; + + r.in.num_values = 1; + r.in.buffer_size = r.out.buffer_size = talloc(tctx, uint32_t); + *r.in.buffer_size = bufsize; + do { + *r.in.buffer_size = bufsize; + r.in.buffer = r.out.buffer = talloc_zero_array(tctx, uint8_t, + *r.in.buffer_size); + + status = dcerpc_winreg_QueryMultipleValues(p, tctx, &r); + + if(NT_STATUS_IS_ERR(status)) + torture_fail(tctx, "QueryMultipleValues failed"); + + talloc_free(r.in.buffer); + bufsize += 0x20; + } while (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)); + + torture_assert_werr_ok(tctx, r.out.result, "QueryMultipleValues failed"); + + return true; +} + +static bool test_QueryValue(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *valuename) +{ + struct winreg_QueryValue r; + NTSTATUS status; + enum winreg_Type zero_type = 0; + uint32_t offered = 0xfff; + uint32_t zero = 0; + + r.in.handle = handle; + r.in.data = NULL; + r.in.value_name.name = valuename; + r.in.type = &zero_type; + r.in.size = &offered; + r.in.length = &zero; + + status = dcerpc_winreg_QueryValue(p, tctx, &r); + if (NT_STATUS_IS_ERR(status)) { + torture_fail(tctx, "QueryValue failed"); + } + + torture_assert_werr_ok(tctx, r.out.result, "QueryValue failed"); + + return true; +} + +static bool test_EnumValue(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, int max_valnamelen, + int max_valbufsize) +{ + struct winreg_EnumValue r; + enum winreg_Type type = 0; + uint32_t size = max_valbufsize, zero = 0; + bool ret = true; + uint8_t buf8; + struct winreg_StringBuf name; + + name.name = ""; + name.size = 1024; + + r.in.handle = handle; + r.in.enum_index = 0; + r.in.name = &name; + r.out.name = &name; + r.in.type = &type; + r.in.value = &buf8; + r.in.length = &zero; + r.in.size = &size; + + do { + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_EnumValue(p, tctx, &r), + "EnumValue failed"); + + if (W_ERROR_IS_OK(r.out.result)) { + ret &= test_QueryValue(p, tctx, handle, + r.out.name->name); + ret &= test_QueryMultipleValues(p, tctx, handle, + r.out.name->name); + } + + r.in.enum_index++; + } while (W_ERROR_IS_OK(r.out.result)); + + torture_assert_werr_equal(tctx, r.out.result, WERR_NO_MORE_ITEMS, + "EnumValue failed"); + + return ret; +} + +static bool test_AbortSystemShutdown(struct dcerpc_pipe *p, + struct torture_context *tctx) +{ + struct winreg_AbortSystemShutdown r; + uint16_t server = 0x0; + + r.in.server = &server; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_AbortSystemShutdown(p, tctx, &r), + "AbortSystemShutdown failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "AbortSystemShutdown failed"); + + return true; +} + +static bool test_InitiateSystemShutdown(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct winreg_InitiateSystemShutdown r; + uint16_t hostname = 0x0; + + r.in.hostname = &hostname; + r.in.message = talloc(tctx, struct lsa_StringLarge); + init_lsa_StringLarge(r.in.message, "spottyfood"); + r.in.force_apps = 1; + r.in.timeout = 30; + r.in.reboot = 1; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_InitiateSystemShutdown(p, tctx, &r), + "InitiateSystemShutdown failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "InitiateSystemShutdown failed"); + + return test_AbortSystemShutdown(p, tctx); +} + + +static bool test_InitiateSystemShutdownEx(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct winreg_InitiateSystemShutdownEx r; + uint16_t hostname = 0x0; + + r.in.hostname = &hostname; + r.in.message = talloc(tctx, struct lsa_StringLarge); + init_lsa_StringLarge(r.in.message, "spottyfood"); + r.in.force_apps = 1; + r.in.timeout = 30; + r.in.reboot = 1; + r.in.reason = 0; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_InitiateSystemShutdownEx(p, tctx, &r), + "InitiateSystemShutdownEx failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "InitiateSystemShutdownEx failed"); + + return test_AbortSystemShutdown(p, tctx); +} +#define MAX_DEPTH 2 /* Only go this far down the tree */ + +static bool test_key(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, int depth, + bool test_security) +{ + if (depth == MAX_DEPTH) + return true; + + if (!test_QueryInfoKey(p, tctx, handle, NULL)) { + } + + if (!test_NotifyChangeKeyValue(p, tctx, handle)) { + } + + if (test_security && !test_GetKeySecurity(p, tctx, handle, NULL)) { + } + + if (!test_EnumKey(p, tctx, handle, depth, test_security)) { + } + + if (!test_EnumValue(p, tctx, handle, 0xFF, 0xFFFF)) { + } + + test_CloseKey(p, tctx, handle); + + return true; +} + +typedef NTSTATUS (*winreg_open_fn)(struct dcerpc_pipe *, TALLOC_CTX *, void *); + +static bool test_Open_Security(struct torture_context *tctx, + struct dcerpc_pipe *p, void *userdata) +{ + struct policy_handle handle, newhandle; + bool ret = true, created2 = false; + bool created4 = false; + struct winreg_OpenHKLM r; + + winreg_open_fn open_fn = userdata; + + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, open_fn(p, tctx, &r), + "open"); + + test_Cleanup(p, tctx, &handle, TEST_KEY_BASE); + + if (!test_CreateKey(p, tctx, &handle, TEST_KEY_BASE, NULL)) { + torture_comment(tctx, + "CreateKey (TEST_KEY_BASE) failed\n"); + } + + if (test_CreateKey_sd(p, tctx, &handle, TEST_KEY2, + NULL, &newhandle)) { + created2 = true; + } + + if (created2 && !test_CloseKey(p, tctx, &newhandle)) { + printf("CloseKey failed\n"); + ret = false; + } + + if (test_CreateKey_sd(p, tctx, &handle, TEST_KEY4, NULL, &newhandle)) { + created4 = true; + } + + if (created4 && !test_CloseKey(p, tctx, &newhandle)) { + printf("CloseKey failed\n"); + ret = false; + } + + if (created4 && !test_SecurityDescriptors(p, tctx, &handle, TEST_KEY4)) { + ret = false; + } + + if (created4 && !test_DeleteKey(p, tctx, &handle, TEST_KEY4)) { + printf("DeleteKey failed\n"); + ret = false; + } + + if (created2 && !test_DeleteKey(p, tctx, &handle, TEST_KEY2)) { + printf("DeleteKey failed\n"); + ret = false; + } + + /* The HKCR hive has a very large fanout */ + if (open_fn == (void *)dcerpc_winreg_OpenHKCR) { + if(!test_key(p, tctx, &handle, MAX_DEPTH - 1, true)) { + ret = false; + } + } else { + if (!test_key(p, tctx, &handle, 0, true)) { + ret = false; + } + } + + test_Cleanup(p, tctx, &handle, TEST_KEY_BASE); + + return ret; +} + +static bool test_Open(struct torture_context *tctx, struct dcerpc_pipe *p, + void *userdata) +{ + struct policy_handle handle, newhandle; + bool ret = true, created = false, deleted = false; + bool created3 = false, created_subkey = false; + struct winreg_OpenHKLM r; + + winreg_open_fn open_fn = userdata; + + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, open_fn(p, tctx, &r), + "open"); + + test_Cleanup(p, tctx, &handle, TEST_KEY_BASE); + + if (!test_CreateKey(p, tctx, &handle, TEST_KEY_BASE, NULL)) { + torture_comment(tctx, + "CreateKey (TEST_KEY_BASE) failed\n"); + } + + if (!test_CreateKey(p, tctx, &handle, TEST_KEY1, NULL)) { + torture_comment(tctx, + "CreateKey failed - not considering a failure\n"); + } else { + created = true; + } + + if (created && !test_FlushKey(p, tctx, &handle)) { + torture_comment(tctx, "FlushKey failed\n"); + ret = false; + } + + if (created && !test_OpenKey(p, tctx, &handle, TEST_KEY1, &newhandle)) + torture_fail(tctx, + "CreateKey failed (OpenKey after Create didn't work)\n"); + + if (created && !test_CloseKey(p, tctx, &newhandle)) + torture_fail(tctx, + "CreateKey failed (CloseKey after Open didn't work)\n"); + + if (created && !test_DeleteKey(p, tctx, &handle, TEST_KEY1)) { + torture_comment(tctx, "DeleteKey failed\n"); + ret = false; + } else { + deleted = true; + } + + if (created && !test_FlushKey(p, tctx, &handle)) { + torture_comment(tctx, "FlushKey failed\n"); + ret = false; + } + + if (created && deleted && + !_test_OpenKey(p, tctx, &handle, TEST_KEY1, + SEC_FLAG_MAXIMUM_ALLOWED, &newhandle, + WERR_BADFILE, NULL)) { + torture_comment(tctx, + "DeleteKey failed (OpenKey after Delete " + "did not return WERR_BADFILE)\n"); + ret = false; + } + + if (!test_GetVersion(p, tctx, &handle)) { + torture_comment(tctx, "GetVersion failed\n"); + ret = false; + } + + if (created && test_CreateKey(p, tctx, &handle, TEST_KEY3, NULL)) { + created3 = true; + } + + if (created3 && + test_CreateKey(p, tctx, &handle, TEST_SUBKEY, NULL)) { + created_subkey = true; + } + + if (created_subkey && + !test_DeleteKey(p, tctx, &handle, TEST_KEY3)) { + printf("DeleteKey failed\n"); + ret = false; + } + + /* The HKCR hive has a very large fanout */ + if (open_fn == (void *)dcerpc_winreg_OpenHKCR) { + if(!test_key(p, tctx, &handle, MAX_DEPTH - 1, false)) { + ret = false; + } + } else { + if (!test_key(p, tctx, &handle, 0, false)) { + ret = false; + } + } + + test_Cleanup(p, tctx, &handle, TEST_KEY_BASE); + + return ret; +} + +struct torture_suite *torture_rpc_winreg(TALLOC_CTX *mem_ctx) +{ + struct torture_rpc_tcase *tcase; + struct torture_suite *suite = torture_suite_create(mem_ctx, "WINREG"); + struct torture_test *test; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "winreg", + &ndr_table_winreg); + + test = torture_rpc_tcase_add_test(tcase, "InitiateSystemShutdown", + test_InitiateSystemShutdown); + test->dangerous = true; + + test = torture_rpc_tcase_add_test(tcase, "InitiateSystemShutdownEx", + test_InitiateSystemShutdownEx); + test->dangerous = true; + + /* Basic tests without security descriptors */ + torture_rpc_tcase_add_test_ex(tcase, "HKLM-basic", + test_Open, + (winreg_open_fn)dcerpc_winreg_OpenHKLM); + torture_rpc_tcase_add_test_ex(tcase, "HKU-basic", + test_Open, + (winreg_open_fn)dcerpc_winreg_OpenHKU); + torture_rpc_tcase_add_test_ex(tcase, "HKCR-basic", + test_Open, + (winreg_open_fn)dcerpc_winreg_OpenHKCR); + torture_rpc_tcase_add_test_ex(tcase, "HKCU-basic", + test_Open, + (winreg_open_fn)dcerpc_winreg_OpenHKCU); + + /* Security descriptor tests */ + torture_rpc_tcase_add_test_ex(tcase, "HKLM-security", + test_Open_Security, + (winreg_open_fn)dcerpc_winreg_OpenHKLM); + torture_rpc_tcase_add_test_ex(tcase, "HKU-security", + test_Open_Security, + (winreg_open_fn)dcerpc_winreg_OpenHKU); + torture_rpc_tcase_add_test_ex(tcase, "HKCR-security", + test_Open_Security, + (winreg_open_fn)dcerpc_winreg_OpenHKCR); + torture_rpc_tcase_add_test_ex(tcase, "HKCU-security", + test_Open_Security, + (winreg_open_fn)dcerpc_winreg_OpenHKCU); + + return suite; +} diff --git a/source4/torture/rpc/wkssvc.c b/source4/torture/rpc/wkssvc.c new file mode 100644 index 0000000000..b212a44b33 --- /dev/null +++ b/source4/torture/rpc/wkssvc.c @@ -0,0 +1,1452 @@ +/* + Unix SMB/CIFS implementation. + test suite for wkssvc rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Günther Deschner 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_wkssvc_c.h" +#include "torture/rpc/rpc.h" +#include "lib/cmdline/popt_common.h" +#include "param/param.h" +#include "lib/crypto/crypto.h" +#include "libcli/auth/libcli_auth.h" + +#define SMBTORTURE_MACHINE_NAME "smbtrt_name" +#define SMBTORTURE_ALTERNATE_NAME "smbtrt_altname" +#define SMBTORTURE_TRANSPORT_NAME "\\Device\\smbtrt_transport_name" +#define SMBTORTURE_USE_NAME "S:" +#define SMBTORTURE_MESSAGE "You are currently tortured by Samba" + +static bool test_NetWkstaGetInfo(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetWkstaGetInfo r; + union wkssvc_NetWkstaInfo info; + uint16_t levels[] = {100, 101, 102, 502}; + int i; + + r.in.server_name = dcerpc_server_name(p); + r.out.info = &info; + + for (i=0;i<ARRAY_SIZE(levels);i++) { + r.in.level = levels[i]; + torture_comment(tctx, "testing NetWkstaGetInfo level %u\n", + r.in.level); + status = dcerpc_wkssvc_NetWkstaGetInfo(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "NetWkstaGetInfo level %u failed", + r.in.level)); + torture_assert_werr_ok(tctx, r.out.result, + talloc_asprintf(tctx, "NetWkstaGetInfo level %u failed", + r.in.level)); + } + + return true; +} + +static bool test_NetWkstaTransportEnum(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetWkstaTransportEnum r; + uint32_t resume_handle = 0; + struct wkssvc_NetWkstaTransportInfo info; + union wkssvc_NetWkstaTransportCtr ctr; + struct wkssvc_NetWkstaTransportCtr0 ctr0; + uint32_t total_entries = 0; + + ZERO_STRUCT(ctr0); + ctr.ctr0 = &ctr0; + + info.level = 0; + info.ctr = ctr; + + r.in.server_name = dcerpc_server_name(p); + r.in.info = &info; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = &resume_handle; + r.out.total_entries = &total_entries; + r.out.info = &info; + r.out.resume_handle = &resume_handle; + + torture_comment(tctx, "testing NetWkstaTransportEnum level 0\n"); + + status = dcerpc_wkssvc_NetWkstaTransportEnum(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetWkstaTransportEnum failed"); + torture_assert_werr_ok(tctx, r.out.result, talloc_asprintf(tctx, + "NetWkstaTransportEnum level %u failed", + info.level)); + + return true; +} + +static bool test_NetrWkstaTransportAdd(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrWkstaTransportAdd r; + struct wkssvc_NetWkstaTransportInfo0 info0; + uint32_t parm_err = 0; + + ZERO_STRUCT(info0); + + info0.quality_of_service = 0xffff; + info0.vc_count = 0; + info0.name = SMBTORTURE_TRANSPORT_NAME; + info0.address = "000000000000"; + info0.wan_link = 0x400; + + r.in.server_name = dcerpc_server_name(p); + r.in.level = 0; + r.in.info0 = &info0; + r.in.parm_err = r.out.parm_err = &parm_err; + + torture_comment(tctx, "testing NetrWkstaTransportAdd level 0\n"); + + status = dcerpc_wkssvc_NetrWkstaTransportAdd(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrWkstaTransportAdd failed"); + torture_assert_werr_equal(tctx, r.out.result, + WERR_INVALID_PARAM, + "NetrWkstaTransportAdd level 0 failed"); + + return true; +} + +static bool test_NetrWkstaTransportDel(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrWkstaTransportDel r; + + r.in.server_name = dcerpc_server_name(p); + r.in.transport_name = SMBTORTURE_TRANSPORT_NAME; + r.in.unknown3 = 0; + + torture_comment(tctx, "testing NetrWkstaTransportDel\n"); + + status = dcerpc_wkssvc_NetrWkstaTransportDel(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrWkstaTransportDel failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrWkstaTransportDel"); + + return true; +} + +static bool test_NetWkstaEnumUsers(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetWkstaEnumUsers r; + uint32_t handle = 0; + uint32_t entries_read = 0; + struct wkssvc_NetWkstaEnumUsersInfo info; + struct wkssvc_NetWkstaEnumUsersCtr0 *user0; + struct wkssvc_NetWkstaEnumUsersCtr1 *user1; + uint32_t levels[] = { 0, 1 }; + int i; + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + ZERO_STRUCT(info); + + info.level = levels[i]; + switch (info.level) { + case 0: + user0 = talloc_zero(tctx, + struct wkssvc_NetWkstaEnumUsersCtr0); + info.ctr.user0 = user0; + break; + case 1: + user1 = talloc_zero(tctx, + struct wkssvc_NetWkstaEnumUsersCtr1); + info.ctr.user1 = user1; + break; + default: + break; + } + + r.in.server_name = dcerpc_server_name(p); + r.in.prefmaxlen = (uint32_t)-1; + r.in.info = r.out.info = &info; + r.in.resume_handle = r.out.resume_handle = &handle; + + r.out.entries_read = &entries_read; + + torture_comment(tctx, "testing NetWkstaEnumUsers level %u\n", + levels[i]); + + status = dcerpc_wkssvc_NetWkstaEnumUsers(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetWkstaEnumUsers failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetWkstaEnumUsers failed"); + } + + return true; +} + +static bool test_NetrWkstaUserGetInfo(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrWkstaUserGetInfo r; + union wkssvc_NetrWkstaUserInfo info; + const char *dom = lp_workgroup(tctx->lp_ctx); + struct cli_credentials *creds = cmdline_credentials; + const char *user = cli_credentials_get_username(creds); + int i; + + const struct { + const char *unknown; + uint32_t level; + WERROR result; + } tests[] = { + { NULL, 0, WERR_NO_SUCH_LOGON_SESSION }, + { NULL, 1, WERR_NO_SUCH_LOGON_SESSION }, + { NULL, 1101, WERR_OK }, + { dom, 0, WERR_INVALID_PARAM }, + { dom, 1, WERR_INVALID_PARAM }, + { dom, 1101, WERR_INVALID_PARAM }, + { user, 0, WERR_INVALID_PARAM }, + { user, 1, WERR_INVALID_PARAM }, + { user, 1101, WERR_INVALID_PARAM }, + }; + + for (i=0; i<ARRAY_SIZE(tests); i++) { + r.in.unknown = tests[i].unknown; + r.in.level = tests[i].level; + r.out.info = &info; + + torture_comment(tctx, "testing NetrWkstaUserGetInfo level %u\n", + r.in.level); + + status = dcerpc_wkssvc_NetrWkstaUserGetInfo(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrWkstaUserGetInfo failed"); + torture_assert_werr_equal(tctx, r.out.result, + tests[i].result, + "NetrWkstaUserGetInfo failed"); + } + + return true; +} + +static bool test_NetrUseEnum(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrUseEnum r; + uint32_t handle = 0; + uint32_t entries_read = 0; + struct wkssvc_NetrUseEnumInfo info; + struct wkssvc_NetrUseEnumCtr0 *use0; + struct wkssvc_NetrUseEnumCtr1 *use1; + struct wkssvc_NetrUseEnumCtr2 *use2; + uint32_t levels[] = { 0, 1, 2 }; + int i; + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + ZERO_STRUCT(info); + + info.level = levels[i]; + switch (info.level) { + case 0: + use0 = talloc_zero(tctx, struct wkssvc_NetrUseEnumCtr0); + info.ctr.ctr0 = use0; + break; + case 1: + use1 = talloc_zero(tctx, struct wkssvc_NetrUseEnumCtr1); + info.ctr.ctr1 = use1; + break; + case 2: + use2 = talloc_zero(tctx, struct wkssvc_NetrUseEnumCtr2); + info.ctr.ctr2 = use2; + break; + default: + break; + } + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.prefmaxlen = (uint32_t)-1; + r.in.info = r.out.info = &info; + r.in.resume_handle = r.out.resume_handle = &handle; + + r.out.entries_read = &entries_read; + + torture_comment(tctx, "testing NetrUseEnum level %u\n", + levels[i]); + + status = dcerpc_wkssvc_NetrUseEnum(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrUseEnum failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrUseEnum failed"); + } + + return true; +} + +static bool test_NetrUseAdd(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrUseAdd r; + struct wkssvc_NetrUseInfo0 info0; + struct wkssvc_NetrUseInfo1 info1; + union wkssvc_NetrUseGetInfoCtr *ctr; + uint32_t parm_err = 0; + + ctr = talloc(tctx, union wkssvc_NetrUseGetInfoCtr); + + ZERO_STRUCT(info0); + + info0.local = SMBTORTURE_USE_NAME; + info0.remote = "\\\\localhost\\c$"; + + ctr->info0 = &info0; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.level = 0; + r.in.ctr = ctr; + r.in.parm_err = r.out.parm_err = &parm_err; + + torture_comment(tctx, "testing NetrUseAdd level %u\n", + r.in.level); + + status = dcerpc_wkssvc_NetrUseAdd(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrUseAdd failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_UNKNOWN_LEVEL, + "NetrUseAdd failed"); + + ZERO_STRUCT(r); + ZERO_STRUCT(info1); + + info1.local = SMBTORTURE_USE_NAME; + info1.remote = "\\\\localhost\\sysvol"; + info1.password = NULL; + + ctr->info1 = &info1; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.level = 1; + r.in.ctr = ctr; + r.in.parm_err = r.out.parm_err = &parm_err; + + torture_comment(tctx, "testing NetrUseAdd level %u\n", + r.in.level); + + status = dcerpc_wkssvc_NetrUseAdd(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrUseAdd failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrUseAdd failed"); + + return true; +} + +static bool test_NetrUseDel(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrUseDel r; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.use_name = SMBTORTURE_USE_NAME; + r.in.force_cond = 0; + + torture_comment(tctx, "testing NetrUseDel\n"); + + status = dcerpc_wkssvc_NetrUseDel(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrUseDel failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrUseDel failed"); + return true; +} + +static bool test_NetrUseGetInfo_level(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *use_name, + uint32_t level, + WERROR werr) +{ + NTSTATUS status; + struct wkssvc_NetrUseGetInfo r; + union wkssvc_NetrUseGetInfoCtr ctr; + + ZERO_STRUCT(ctr); + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.use_name = use_name; + r.in.level = level; + r.out.ctr = &ctr; + status = dcerpc_wkssvc_NetrUseGetInfo(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, + "NetrUseGetInfo failed"); + torture_assert_werr_equal(tctx, r.out.result, werr, + "NetrUseGetInfo failed"); + return true; +} + +static bool test_NetrUseGetInfo(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrUseEnum r; + uint32_t handle = 0; + uint32_t entries_read = 0; + struct wkssvc_NetrUseEnumInfo info; + struct wkssvc_NetrUseEnumCtr0 *use0; + uint32_t levels[] = { 0, 1, 2 }; + const char *use_name = NULL; + int i, k; + + ZERO_STRUCT(info); + + info.level = 0; + use0 = talloc_zero(tctx, struct wkssvc_NetrUseEnumCtr0); + info.ctr.ctr0 = use0; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.prefmaxlen = (uint32_t)-1; + r.in.info = r.out.info = &info; + r.in.resume_handle = r.out.resume_handle = &handle; + r.out.entries_read = &entries_read; + + status = dcerpc_wkssvc_NetrUseEnum(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrUseEnum failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrUseEnum failed"); + + for (k=0; k < r.out.info->ctr.ctr0->count; k++) { + + use_name = r.out.info->ctr.ctr0->array[k].local; + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + if (!test_NetrUseGetInfo_level(tctx, p, use_name, + levels[i], + WERR_OK)) + { + if (levels[i] != 0) { + return false; + } + } + } + + use_name = r.out.info->ctr.ctr0->array[k].remote; + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + if (!test_NetrUseGetInfo_level(tctx, p, use_name, + levels[i], + WERR_NOT_CONNECTED)) + { + if (levels[i] != 0) { + return false; + } + } + } + } + + return true; +} + +static bool test_NetrLogonDomainNameAdd(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrLogonDomainNameAdd r; + + r.in.domain_name = lp_workgroup(tctx->lp_ctx); + + torture_comment(tctx, "testing NetrLogonDomainNameAdd\n"); + + status = dcerpc_wkssvc_NetrLogonDomainNameAdd(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrLogonDomainNameAdd failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, + "NetrLogonDomainNameAdd failed"); + return true; +} + +static bool test_NetrLogonDomainNameDel(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrLogonDomainNameDel r; + + r.in.domain_name = lp_workgroup(tctx->lp_ctx); + + torture_comment(tctx, "testing NetrLogonDomainNameDel\n"); + + status = dcerpc_wkssvc_NetrLogonDomainNameDel(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrLogonDomainNameDel failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, + "NetrLogonDomainNameDel failed"); + return true; +} + +static bool test_NetrEnumerateComputerNames_level(struct torture_context *tctx, + struct dcerpc_pipe *p, + uint16_t level, + const char ***names, + int *num_names) +{ + NTSTATUS status; + struct wkssvc_NetrEnumerateComputerNames r; + struct wkssvc_ComputerNamesCtr *ctr; + int i; + + ctr = talloc_zero(tctx, struct wkssvc_ComputerNamesCtr); + + r.in.server_name = dcerpc_server_name(p); + r.in.name_type = level; + r.in.Reserved = 0; + r.out.ctr = &ctr; + + torture_comment(tctx, "testing NetrEnumerateComputerNames level %u\n", + r.in.name_type); + + status = dcerpc_wkssvc_NetrEnumerateComputerNames(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrEnumerateComputerNames failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrEnumerateComputerNames failed"); + + if ((level == NetPrimaryComputerName) && ctr->count != 1) { + torture_comment(tctx, + "NetrEnumerateComputerNames did not return one " + "name but %u\n", ctr->count); + return false; + } + + if (names && num_names) { + *num_names = 0; + *names = NULL; + for (i=0; i<ctr->count; i++) { + if (!add_string_to_array(tctx, + ctr->computer_name[i].string, + names, + num_names)) + { + return false; + } + } + } + + return true; +} + +static bool test_NetrEnumerateComputerNames(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + uint16_t levels[] = {0,1,2}; + int i; + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + if (!test_NetrEnumerateComputerNames_level(tctx, + p, + levels[i], + NULL, NULL)) + { + return false; + } + } + + return true; +} + +static bool test_NetrValidateName(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrValidateName r; + uint16_t levels[] = {0,1,2,3,4,5}; + int i; + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.name = lp_workgroup(tctx->lp_ctx); + r.in.Account = NULL; + r.in.Password = NULL; + r.in.name_type = levels[i]; + + torture_comment(tctx, "testing NetrValidateName level %u\n", + r.in.name_type); + + status = dcerpc_wkssvc_NetrValidateName(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrValidateName failed"); + torture_assert_werr_equal(tctx, r.out.result, + WERR_NOT_SUPPORTED, + "NetrValidateName failed"); + } + + return true; +} + +static bool test_NetrValidateName2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrValidateName2 r; + uint16_t levels[] = {0,1,2,3,4,5}; + int i; + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.name = lp_workgroup(tctx->lp_ctx); + r.in.Account = NULL; + r.in.EncryptedPassword = NULL; + r.in.name_type = levels[i]; + + torture_comment(tctx, "testing NetrValidateName2 level %u\n", + r.in.name_type); + + status = dcerpc_wkssvc_NetrValidateName2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrValidateName2 failed"); + torture_assert_werr_equal(tctx, r.out.result, + WERR_RPC_E_REMOTE_DISABLED, + "NetrValidateName2 failed"); + } + + return true; +} + +static bool test_NetrAddAlternateComputerName(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrAddAlternateComputerName r; + const char **names = NULL; + int num_names = 0; + int i; + + r.in.server_name = dcerpc_server_name(p); + r.in.NewAlternateMachineName = SMBTORTURE_ALTERNATE_NAME; + r.in.Account = NULL; + r.in.EncryptedPassword = NULL; + r.in.Reserved = 0; + + torture_comment(tctx, "testing NetrAddAlternateComputerName\n"); + + status = dcerpc_wkssvc_NetrAddAlternateComputerName(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrAddAlternateComputerName failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrAddAlternateComputerName failed"); + + if (!test_NetrEnumerateComputerNames_level(tctx, p, + NetAlternateComputerNames, + &names, &num_names)) + { + return false; + } + + for (i=0; i<num_names; i++) { + if (strequal(names[i], SMBTORTURE_ALTERNATE_NAME)) { + return true; + } + } + + torture_comment(tctx, "new alternate name not set\n"); + + return false; +} + +static bool test_NetrRemoveAlternateComputerName(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrRemoveAlternateComputerName r; + const char **names = NULL; + int num_names = 0; + int i; + + r.in.server_name = dcerpc_server_name(p); + r.in.AlternateMachineNameToRemove = SMBTORTURE_ALTERNATE_NAME; + r.in.Account = NULL; + r.in.EncryptedPassword = NULL; + r.in.Reserved = 0; + + torture_comment(tctx, "testing NetrRemoveAlternateComputerName\n"); + + status = dcerpc_wkssvc_NetrRemoveAlternateComputerName(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrRemoveAlternateComputerName failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrRemoveAlternateComputerName failed"); + + if (!test_NetrEnumerateComputerNames_level(tctx, p, + NetAlternateComputerNames, + &names, &num_names)) + { + return false; + } + + for (i=0; i<num_names; i++) { + if (strequal(names[i], SMBTORTURE_ALTERNATE_NAME)) { + return false; + } + } + + return true; +} + +static bool test_NetrSetPrimaryComputername_name(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *name) +{ + NTSTATUS status; + struct wkssvc_NetrSetPrimaryComputername r; + + r.in.server_name = dcerpc_server_name(p); + r.in.primary_name = name; + r.in.Account = NULL; + r.in.EncryptedPassword = NULL; + r.in.Reserved = 0; + + status = dcerpc_wkssvc_NetrSetPrimaryComputername(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrSetPrimaryComputername failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrSetPrimaryComputername failed"); + return true; +} + + +static bool test_NetrSetPrimaryComputername(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + /* + add alternate, + check if there + store old primary + set new primary (alternate) + check if there + later: check if del is possible + set primary back to origin + check if there + del alternate + */ + + const char **names_o = NULL, **names = NULL; + int num_names_o = 0, num_names = 0; + + torture_comment(tctx, "testing NetrSetPrimaryComputername\n"); + + if (!test_NetrAddAlternateComputerName(tctx, p)) { + return false; + } + + if (!test_NetrEnumerateComputerNames_level(tctx, p, + NetPrimaryComputerName, + &names_o, &num_names_o)) + { + return false; + } + + if (num_names_o != 1) { + return false; + } + + if (!test_NetrSetPrimaryComputername_name(tctx, p, + SMBTORTURE_ALTERNATE_NAME)) + { + return false; + } + + if (!test_NetrEnumerateComputerNames_level(tctx, p, + NetPrimaryComputerName, + &names, &num_names)) + { + return false; + } + + if (num_names != 1) { + return false; + } + + if (!strequal(names[0], SMBTORTURE_ALTERNATE_NAME)) { + torture_comment(tctx, + "name mismatch (%s != %s) after NetrSetPrimaryComputername!\n", + names[0], SMBTORTURE_ALTERNATE_NAME); + /*return false */; + } + + if (!test_NetrSetPrimaryComputername_name(tctx, p, + names_o[0])) + { + return false; + } + + if (!test_NetrRemoveAlternateComputerName(tctx, p)) { + return false; + } + + + return true; +} + +static bool test_NetrRenameMachineInDomain(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrRenameMachineInDomain r; + + r.in.server_name = dcerpc_server_name(p); + r.in.NewMachineName = SMBTORTURE_MACHINE_NAME; + r.in.Account = NULL; + r.in.password = NULL; + r.in.RenameOptions = 0; + + torture_comment(tctx, "testing NetrRenameMachineInDomain\n"); + + status = dcerpc_wkssvc_NetrRenameMachineInDomain(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrRenameMachineInDomain failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, + "NetrRenameMachineInDomain failed"); + return true; +} + +static bool test_NetrRenameMachineInDomain2_name(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *new_name) +{ + NTSTATUS status; + struct wkssvc_NetrRenameMachineInDomain2 r; + + r.in.server_name = dcerpc_server_name(p); + r.in.NewMachineName = new_name; + r.in.Account = NULL; + r.in.EncryptedPassword = NULL; + r.in.RenameOptions = 0; + + status = dcerpc_wkssvc_NetrRenameMachineInDomain2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrRenameMachineInDomain2 failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrRenameMachineInDomain2 failed"); + return true; +} + +static bool test_NetrRenameMachineInDomain2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + const char **names_o = NULL, **names = NULL; + int num_names_o = 0, num_names = 0; + + torture_comment(tctx, "testing NetrRenameMachineInDomain2\n"); + + if (!test_NetrEnumerateComputerNames_level(tctx, p, + NetPrimaryComputerName, + &names_o, &num_names_o)) + { + return false; + } + + if (num_names_o != 1) { + return false; + } + + if (!test_NetrRenameMachineInDomain2_name(tctx, p, + SMBTORTURE_MACHINE_NAME)) + { + return false; + } + + if (!test_NetrEnumerateComputerNames_level(tctx, p, + NetPrimaryComputerName, + &names, &num_names)) + { + return false; + } + + if (num_names != 1) { + return false; + } + + if (strequal(names[0], names_o[0])) { + test_NetrRenameMachineInDomain2_name(tctx, p, names_o[0]); + return false; + } + + if (!strequal(names[0], SMBTORTURE_MACHINE_NAME)) { + test_NetrRenameMachineInDomain2_name(tctx, p, names_o[0]); + return false; + } + + if (!test_NetrRenameMachineInDomain2_name(tctx, p, names_o[0])) + { + return false; + } + + if (!test_NetrEnumerateComputerNames_level(tctx, p, + NetPrimaryComputerName, + &names, &num_names)) + { + return false; + } + + if (num_names != 1) { + return false; + } + + if (!strequal(names[0], names_o[0])) { + return false; + } + + return true; +} + +static bool test_NetrWorkstationStatisticsGet(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrWorkstationStatisticsGet r; + struct wkssvc_NetrWorkstationStatistics *info; + + ZERO_STRUCT(r); + + info = talloc_zero(tctx, struct wkssvc_NetrWorkstationStatistics); + + r.in.server_name = dcerpc_server_name(p); + r.out.info = &info; + + torture_comment(tctx, "testing NetrWorkstationStatisticsGet\n"); + + status = dcerpc_wkssvc_NetrWorkstationStatisticsGet(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrWorkstationStatisticsGet failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrWorkstationStatisticsGet failed"); + return true; +} + +/* only succeeds as long as the local messenger service is running - Guenther */ + +static bool test_NetrMessageBufferSend(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrMessageBufferSend r; + const char *message = SMBTORTURE_MESSAGE; + size_t size; + uint8_t *msg; + + size = push_ucs2_talloc(tctx, lp_iconv_convenience(tctx->lp_ctx), + (void **)&msg, message); + + r.in.server_name = dcerpc_server_name(p); + r.in.message_name = dcerpc_server_name(p); + r.in.message_sender_name = dcerpc_server_name(p); + r.in.message_buffer = msg; + r.in.message_size = size; + + torture_comment(tctx, "testing NetrMessageBufferSend\n"); + + status = dcerpc_wkssvc_NetrMessageBufferSend(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrMessageBufferSend failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrMessageBufferSend failed"); + return true; +} + +static bool test_NetrGetJoinInformation(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrGetJoinInformation r; + enum wkssvc_NetJoinStatus join_status; + const char *name_buffer = ""; + + r.in.server_name = dcerpc_server_name(p); + r.in.name_buffer = r.out.name_buffer = &name_buffer; + r.out.name_type = &join_status; + + torture_comment(tctx, "testing NetrGetJoinInformation\n"); + + status = dcerpc_wkssvc_NetrGetJoinInformation(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrGetJoinInformation failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrGetJoinInformation failed"); + return true; +} + +static bool test_GetJoinInformation(struct torture_context *tctx, + struct dcerpc_pipe *p, + enum wkssvc_NetJoinStatus *join_status_p, + const char **name) +{ + NTSTATUS status; + struct wkssvc_NetrGetJoinInformation r; + enum wkssvc_NetJoinStatus join_status; + const char *name_buffer = ""; + + r.in.server_name = dcerpc_server_name(p); + r.in.name_buffer = r.out.name_buffer = &name_buffer; + r.out.name_type = &join_status; + + status = dcerpc_wkssvc_NetrGetJoinInformation(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrGetJoinInformation failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrGetJoinInformation failed"); + + if (join_status_p) { + *join_status_p = join_status; + } + + if (*name) { + *name = talloc_strdup(tctx, name_buffer); + } + + return true; + +} + +static bool test_NetrGetJoinableOus(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrGetJoinableOus r; + uint32_t num_ous = 0; + const char **ous = NULL; + + r.in.server_name = dcerpc_server_name(p); + r.in.domain_name = lp_workgroup(tctx->lp_ctx); + r.in.Account = NULL; + r.in.unknown = NULL; + r.in.num_ous = r.out.num_ous = &num_ous; + r.out.ous = &ous; + + torture_comment(tctx, "testing NetrGetJoinableOus\n"); + + status = dcerpc_wkssvc_NetrGetJoinableOus(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetrGetJoinableOus failed"); + torture_assert_werr_equal(tctx, r.out.result, + WERR_NOT_SUPPORTED, + "NetrGetJoinableOus failed"); + + return true; +} + +static bool test_NetrGetJoinableOus2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrGetJoinableOus2 r; + uint32_t num_ous = 0; + const char **ous = NULL; + + r.in.server_name = dcerpc_server_name(p); + r.in.domain_name = lp_workgroup(tctx->lp_ctx); + r.in.Account = NULL; + r.in.EncryptedPassword = NULL; + r.in.num_ous = r.out.num_ous = &num_ous; + r.out.ous = &ous; + + torture_comment(tctx, "testing NetrGetJoinableOus2\n"); + + status = dcerpc_wkssvc_NetrGetJoinableOus2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetrGetJoinableOus2 failed"); + torture_assert_werr_equal(tctx, r.out.result, + WERR_RPC_E_REMOTE_DISABLED, + "NetrGetJoinableOus2 failed"); + + return true; +} + +static bool test_NetrUnjoinDomain(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrUnjoinDomain r; + struct cli_credentials *creds = cmdline_credentials; + const char *user = cli_credentials_get_username(creds); + const char *admin_account = NULL; + + admin_account = talloc_asprintf(tctx, "%s\\%s", + lp_workgroup(tctx->lp_ctx), + user); + + r.in.server_name = dcerpc_server_name(p); + r.in.Account = admin_account; + r.in.password = NULL; + r.in.unjoin_flags = 0; + + torture_comment(tctx, "testing NetrUnjoinDomain\n"); + + status = dcerpc_wkssvc_NetrUnjoinDomain(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrUnjoinDomain failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, + "NetrUnjoinDomain failed"); + return true; +} + +static bool test_NetrJoinDomain(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrJoinDomain r; + struct cli_credentials *creds = cmdline_credentials; + const char *user = cli_credentials_get_username(creds); + const char *admin_account = NULL; + + admin_account = talloc_asprintf(tctx, "%s\\%s", + lp_workgroup(tctx->lp_ctx), + user); + + r.in.server_name = dcerpc_server_name(p); + r.in.domain_name = lp_realm(tctx->lp_ctx); + r.in.account_ou = NULL; + r.in.Account = admin_account; + r.in.password = NULL; + r.in.join_flags = 0; + + torture_comment(tctx, "testing NetrJoinDomain\n"); + + status = dcerpc_wkssvc_NetrJoinDomain(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrJoinDomain failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, + "NetrJoinDomain failed"); + return true; +} + +/* encode a wkssvc_PasswordBuffer for remote joining/unjoining: + * + * similar to samr_CryptPasswordEx. Different: 8byte confounder (instead of + * 16byte), confounder in front of the 516 byte buffer (instead of after that + * buffer), calling MD5Update() first with session_key and then with confounder + * (vice versa in samr) - Guenther */ + +static void encode_wkssvc_join_password_buffer(TALLOC_CTX *mem_ctx, + const char *pwd, + DATA_BLOB *session_key, + struct wkssvc_PasswordBuffer *pwd_buf) +{ + uint8_t buffer[516]; + struct MD5Context ctx; + + DATA_BLOB confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16); + + int confounder_len = 8; + uint8_t confounder[8]; + + encode_pw_buffer(buffer, pwd, STR_UNICODE); + + generate_random_buffer((uint8_t *)confounder, confounder_len); + + MD5Init(&ctx); + MD5Update(&ctx, session_key->data, session_key->length); + MD5Update(&ctx, confounder, confounder_len); + MD5Final(confounded_session_key.data, &ctx); + + arcfour_crypt_blob(buffer, 516, &confounded_session_key); + + memcpy(&pwd_buf->data[0], confounder, confounder_len); + memcpy(&pwd_buf->data[8], buffer, 516); + + data_blob_free(&confounded_session_key); +} + +/* + * prerequisites for remotely joining an unjoined XP SP2 workstation: + * - firewall needs to be disabled (or open for ncacn_np access) + * - HKLM\System\CurrentControlSet\Control\Lsa\forceguest needs to 0 + * see also: + * http://support.microsoft.com/kb/294355/EN-US/ and + * http://support.microsoft.com/kb/290403/EN-US/ + */ + +static bool test_NetrJoinDomain2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrJoinDomain2 r; + const char *domain_admin_account = NULL; + const char *domain_admin_password = NULL; + const char *domain_name = NULL; + struct wkssvc_PasswordBuffer pwd_buf; + enum wkssvc_NetJoinStatus join_status; + const char *join_name = NULL; + WERROR expected_err; + DATA_BLOB session_key; + + /* FIXME: this test assumes to join workstations / servers and does not + * handle DCs (WERR_SETUP_DOMAIN_CONTROLLER) */ + + if (!test_GetJoinInformation(tctx, p, &join_status, &join_name)) + { + return false; + } + + switch (join_status) { + case NetSetupDomainName: + expected_err = WERR_SETUP_ALREADY_JOINED; + break; + case NetSetupUnknownStatus: + case NetSetupUnjoined: + case NetSetupWorkgroupName: + default: + expected_err = WERR_OK; + break; + } + + domain_admin_account = torture_setting_string(tctx, "domain_admin_account", NULL); + + domain_admin_password = torture_setting_string(tctx, "domain_admin_password", NULL); + + domain_name = torture_setting_string(tctx, "domain_name", NULL); + + if ((domain_admin_account == NULL) || + (domain_admin_password == NULL) || + (domain_name == NULL)) { + torture_comment(tctx, "not enough input parameter\n"); + return false; + } + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + encode_wkssvc_join_password_buffer(tctx, domain_admin_password, + &session_key, &pwd_buf); + + r.in.server_name = dcerpc_server_name(p); + r.in.domain_name = domain_name; + r.in.account_ou = NULL; + r.in.admin_account = domain_admin_account; + r.in.encrypted_password = &pwd_buf; + r.in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE; + + torture_comment(tctx, "testing NetrJoinDomain2 (assuming non-DC)\n"); + + status = dcerpc_wkssvc_NetrJoinDomain2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrJoinDomain2 failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_err, + "NetrJoinDomain2 failed"); + + if (!test_GetJoinInformation(tctx, p, &join_status, &join_name)) + { + return false; + } + + if (join_status != NetSetupDomainName) { + torture_comment(tctx, + "Join verify failed: got %d\n", join_status); + return false; + } + + return true; +} + +static bool test_NetrUnjoinDomain2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrUnjoinDomain2 r; + const char *domain_admin_account = NULL; + const char *domain_admin_password = NULL; + struct wkssvc_PasswordBuffer pwd_buf; + enum wkssvc_NetJoinStatus join_status; + const char *join_name = NULL; + WERROR expected_err; + DATA_BLOB session_key; + + /* FIXME: this test assumes to join workstations / servers and does not + * handle DCs (WERR_SETUP_DOMAIN_CONTROLLER) */ + + if (!test_GetJoinInformation(tctx, p, &join_status, &join_name)) + { + return false; + } + + switch (join_status) { + case NetSetupUnjoined: + expected_err = WERR_SETUP_NOT_JOINED; + break; + case NetSetupDomainName: + case NetSetupUnknownStatus: + case NetSetupWorkgroupName: + default: + expected_err = WERR_OK; + break; + } + + domain_admin_account = torture_setting_string(tctx, "domain_admin_account", NULL); + + domain_admin_password = torture_setting_string(tctx, "domain_admin_password", NULL); + + if ((domain_admin_account == NULL) || + (domain_admin_password == NULL)) { + torture_comment(tctx, "not enough input parameter\n"); + return false; + } + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + encode_wkssvc_join_password_buffer(tctx, domain_admin_password, + &session_key, &pwd_buf); + + r.in.server_name = dcerpc_server_name(p); + r.in.account = domain_admin_account; + r.in.encrypted_password = &pwd_buf; + r.in.unjoin_flags = 0; + + torture_comment(tctx, "testing NetrUnjoinDomain2 (assuming non-DC)\n"); + + status = dcerpc_wkssvc_NetrUnjoinDomain2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrUnjoinDomain2 failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_err, + "NetrUnjoinDomain2 failed"); + + if (!test_GetJoinInformation(tctx, p, &join_status, &join_name)) + { + return false; + } + + switch (join_status) { + case NetSetupUnjoined: + case NetSetupWorkgroupName: + break; + case NetSetupUnknown: + case NetSetupDomainName: + default: + torture_comment(tctx, + "Unjoin verify failed: got %d\n", join_status); + return false; + } + + return true; +} + + +struct torture_suite *torture_rpc_wkssvc(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + struct torture_rpc_tcase *tcase; + struct torture_test *test; + + suite = torture_suite_create(mem_ctx, "WKSSVC"); + tcase = torture_suite_add_rpc_iface_tcase(suite, "wkssvc", + &ndr_table_wkssvc); + + torture_rpc_tcase_add_test(tcase, "NetWkstaGetInfo", + test_NetWkstaGetInfo); + + torture_rpc_tcase_add_test(tcase, "NetWkstaTransportEnum", + test_NetWkstaTransportEnum); + torture_rpc_tcase_add_test(tcase, "NetrWkstaTransportDel", + test_NetrWkstaTransportDel); + torture_rpc_tcase_add_test(tcase, "NetrWkstaTransportAdd", + test_NetrWkstaTransportAdd); + + torture_rpc_tcase_add_test(tcase, "NetWkstaEnumUsers", + test_NetWkstaEnumUsers); + torture_rpc_tcase_add_test(tcase, "NetrWkstaUserGetInfo", + test_NetrWkstaUserGetInfo); + + torture_rpc_tcase_add_test(tcase, "NetrUseDel", + test_NetrUseDel); + torture_rpc_tcase_add_test(tcase, "NetrUseGetInfo", + test_NetrUseGetInfo); + torture_rpc_tcase_add_test(tcase, "NetrUseEnum", + test_NetrUseEnum); + torture_rpc_tcase_add_test(tcase, "NetrUseAdd", + test_NetrUseAdd); + + torture_rpc_tcase_add_test(tcase, "NetrValidateName", + test_NetrValidateName); + torture_rpc_tcase_add_test(tcase, "NetrValidateName2", + test_NetrValidateName2); + torture_rpc_tcase_add_test(tcase, "NetrLogonDomainNameDel", + test_NetrLogonDomainNameDel); + torture_rpc_tcase_add_test(tcase, "NetrLogonDomainNameAdd", + test_NetrLogonDomainNameAdd); + torture_rpc_tcase_add_test(tcase, "NetrRemoveAlternateComputerName", + test_NetrRemoveAlternateComputerName); + torture_rpc_tcase_add_test(tcase, "NetrAddAlternateComputerName", + test_NetrAddAlternateComputerName); + test = torture_rpc_tcase_add_test(tcase, "NetrSetPrimaryComputername", + test_NetrSetPrimaryComputername); + test->dangerous = true; + test = torture_rpc_tcase_add_test(tcase, "NetrRenameMachineInDomain", + test_NetrRenameMachineInDomain); + test->dangerous = true; + test = torture_rpc_tcase_add_test(tcase, "NetrRenameMachineInDomain2", + test_NetrRenameMachineInDomain2); + test->dangerous = true; + torture_rpc_tcase_add_test(tcase, "NetrEnumerateComputerNames", + test_NetrEnumerateComputerNames); + + test = torture_rpc_tcase_add_test(tcase, "NetrJoinDomain2", + test_NetrJoinDomain2); + test->dangerous = true; + test = torture_rpc_tcase_add_test(tcase, "NetrUnjoinDomain2", + test_NetrUnjoinDomain2); + test->dangerous = true; + + torture_rpc_tcase_add_test(tcase, "NetrJoinDomain", + test_NetrJoinDomain); + test->dangerous = true; + torture_rpc_tcase_add_test(tcase, "NetrUnjoinDomain", + test_NetrUnjoinDomain); + test->dangerous = true; + torture_rpc_tcase_add_test(tcase, "NetrGetJoinInformation", + test_NetrGetJoinInformation); + torture_rpc_tcase_add_test(tcase, "NetrGetJoinableOus", + test_NetrGetJoinableOus); + torture_rpc_tcase_add_test(tcase, "NetrGetJoinableOus2", + test_NetrGetJoinableOus2); + + torture_rpc_tcase_add_test(tcase, "NetrWorkstationStatisticsGet", + test_NetrWorkstationStatisticsGet); + torture_rpc_tcase_add_test(tcase, "NetrMessageBufferSend", + test_NetrMessageBufferSend); + + return suite; +} |