diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /source4/torture/libnet | |
parent | Initial commit. (diff) | |
download | samba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip |
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
22 files changed, 5015 insertions, 0 deletions
diff --git a/source4/torture/libnet/domain.c b/source4/torture/libnet/domain.c new file mode 100644 index 0000000..c1cfc91 --- /dev/null +++ b/source4/torture/libnet/domain.c @@ -0,0 +1,117 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "param/param.h" +#include "torture/libnet/proto.h" + +static bool test_domainopen(struct torture_context *tctx, + struct libnet_context *net_ctx, TALLOC_CTX *mem_ctx, + struct lsa_String *domname, + struct policy_handle *domain_handle) +{ + NTSTATUS status; + struct libnet_DomainOpen io; + + ZERO_STRUCT(io); + + torture_comment(tctx, "opening domain\n"); + + io.in.domain_name = talloc_strdup(mem_ctx, domname->string); + io.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + + status = libnet_DomainOpen(net_ctx, mem_ctx, &io); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Composite domain open failed for domain '%s' - %s\n", + domname->string, nt_errstr(status)); + return false; + } + + *domain_handle = io.out.domain_handle; + return true; +} + + +static bool test_cleanup(struct torture_context *tctx, + struct dcerpc_binding_handle *b, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle) +{ + struct samr_Close r; + struct policy_handle handle; + + r.in.handle = domain_handle; + r.out.handle = &handle; + + torture_comment(tctx, "closing domain handle\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_Close_r(b, mem_ctx, &r), + "Close failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Close failed"); + + return true; +} + + +bool torture_domainopen(struct torture_context *torture) +{ + NTSTATUS status; + struct libnet_context *net_ctx; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct policy_handle h; + struct lsa_String name; + + mem_ctx = talloc_init("test_domain_open"); + + net_ctx = libnet_context_init(torture->ev, torture->lp_ctx); + + status = torture_rpc_connection(torture, + &net_ctx->samr.pipe, + &ndr_table_samr); + + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + name.string = lpcfg_workgroup(torture->lp_ctx); + + /* + * Testing synchronous version + */ + if (!test_domainopen(torture, net_ctx, mem_ctx, &name, &h)) { + ret = false; + goto done; + } + + if (!test_cleanup(torture, net_ctx->samr.pipe->binding_handle, mem_ctx, &h)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + + return ret; +} diff --git a/source4/torture/libnet/groupinfo.c b/source4/torture/libnet/groupinfo.c new file mode 100644 index 0000000..a738ab3 --- /dev/null +++ b/source4/torture/libnet/groupinfo.c @@ -0,0 +1,128 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "libnet/libnet.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "param/param.h" +#include "torture/libnet/proto.h" + +#define TEST_GROUPNAME "libnetgroupinfotest" + + +static bool test_groupinfo(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + struct dom_sid2 *domain_sid, const char* group_name, + uint32_t *rid) +{ + const uint16_t level = 5; + NTSTATUS status; + struct libnet_rpc_groupinfo group; + struct dom_sid *group_sid; + + group_sid = dom_sid_add_rid(mem_ctx, domain_sid, *rid); + + ZERO_STRUCT(group); + + group.in.domain_handle = *domain_handle; + group.in.sid = dom_sid_string(mem_ctx, group_sid); + group.in.level = level; /* this should be extended */ + + torture_comment(tctx, "Testing sync libnet_rpc_groupinfo (SID argument)\n"); + status = libnet_rpc_groupinfo(tctx->ev, p->binding_handle, mem_ctx, &group); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to call sync libnet_rpc_userinfo - %s\n", nt_errstr(status)); + return false; + } + + ZERO_STRUCT(group); + + group.in.domain_handle = *domain_handle; + group.in.sid = NULL; + group.in.groupname = TEST_GROUPNAME; + group.in.level = level; + + printf("Testing sync libnet_rpc_groupinfo (groupname argument)\n"); + status = libnet_rpc_groupinfo(tctx->ev, p->binding_handle, mem_ctx, &group); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to call sync libnet_rpc_groupinfo - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + + +bool torture_groupinfo(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct policy_handle h; + struct lsa_String name; + struct dom_sid2 sid; + uint32_t rid; + struct dcerpc_binding_handle *b; + + mem_ctx = talloc_init("test_userinfo"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + name.string = lpcfg_workgroup(torture->lp_ctx); + + /* + * Testing synchronous version + */ + if (!test_domain_open(torture, b, &name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_group_create(torture, b, mem_ctx, &h, TEST_GROUPNAME, &rid)) { + ret = false; + goto done; + } + + if (!test_groupinfo(torture, p, mem_ctx, &h, &sid, TEST_GROUPNAME, &rid)) { + ret = false; + goto done; + } + + if (!test_group_cleanup(torture, b, mem_ctx, &h, TEST_GROUPNAME)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + + return ret; +} diff --git a/source4/torture/libnet/groupman.c b/source4/torture/libnet/groupman.c new file mode 100644 index 0000000..8cd49db --- /dev/null +++ b/source4/torture/libnet/groupman.c @@ -0,0 +1,97 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/libnet/grouptest.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "param/param.h" +#include "torture/libnet/proto.h" + + +static bool test_groupadd(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + const char *name) +{ + NTSTATUS status; + bool ret = true; + struct libnet_rpc_groupadd group; + + ZERO_STRUCT(group); + + group.in.domain_handle = *domain_handle; + group.in.groupname = name; + + printf("Testing libnet_rpc_groupadd\n"); + + status = libnet_rpc_groupadd(tctx->ev, p->binding_handle, + mem_ctx, &group); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to call sync libnet_rpc_groupadd - %s\n", nt_errstr(status)); + return false; + } + + return ret; +} + + +bool torture_groupadd(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + struct policy_handle h; + struct lsa_String domain_name; + struct dom_sid2 sid; + const char *name = TEST_GROUPNAME; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct dcerpc_binding_handle *b; + + mem_ctx = talloc_init("test_groupadd"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + + torture_assert_ntstatus_ok(torture, status, "RPC connection"); + b = p->binding_handle; + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + if (!test_domain_open(torture, b, &domain_name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_groupadd(torture, p, mem_ctx, &h, name)) { + ret = false; + goto done; + } + + if (!test_group_cleanup(torture, b, mem_ctx, &h, name)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + return ret; +} diff --git a/source4/torture/libnet/grouptest.h b/source4/torture/libnet/grouptest.h new file mode 100644 index 0000000..8b65e6e --- /dev/null +++ b/source4/torture/libnet/grouptest.h @@ -0,0 +1,20 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Rafal Szczesniak 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/>. +*/ + +#define TEST_GROUPNAME "libnetgrptest" diff --git a/source4/torture/libnet/libnet.c b/source4/torture/libnet/libnet.c new file mode 100644 index 0000000..faf7bca --- /dev/null +++ b/source4/torture/libnet/libnet.c @@ -0,0 +1,70 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + 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 "torture/smbtorture.h" +#include "librpc/rpc/dcerpc.h" +#include "librpc/gen_ndr/lsa.h" +#include "libnet/libnet.h" +#include "torture/libnet/proto.h" + +NTSTATUS torture_net_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create( + ctx, "net"); + + torture_suite_add_simple_test(suite, "userinfo", torture_userinfo); + torture_suite_add_simple_test(suite, "useradd", torture_useradd); + torture_suite_add_simple_test(suite, "userdel", torture_userdel); + torture_suite_add_simple_test(suite, "usermod", torture_usermod); + torture_suite_add_simple_test(suite, "domopen", torture_domainopen); + torture_suite_add_simple_test(suite, "groupinfo", torture_groupinfo); + torture_suite_add_simple_test(suite, "groupadd", torture_groupadd); + torture_suite_add_simple_test(suite, "api.lookup", torture_lookup); + torture_suite_add_simple_test(suite, "api.lookuphost", torture_lookup_host); + torture_suite_add_simple_test(suite, "api.lookuppdc", torture_lookup_pdc); + torture_suite_add_simple_test(suite, "api.lookupname", torture_lookup_sam_name); + torture_suite_add_simple_test(suite, "api.createuser", torture_createuser); + torture_suite_add_simple_test(suite, "api.deleteuser", torture_deleteuser); + torture_suite_add_simple_test(suite, "api.modifyuser", torture_modifyuser); + torture_suite_add_simple_test(suite, "api.userinfo", torture_userinfo_api); + torture_suite_add_simple_test(suite, "api.userlist", torture_userlist); + torture_suite_add_simple_test(suite, "api.groupinfo", torture_groupinfo_api); + torture_suite_add_simple_test(suite, "api.grouplist", torture_grouplist); + torture_suite_add_simple_test(suite, "api.creategroup", torture_creategroup); + torture_suite_add_simple_test(suite, "api.rpcconn.bind", torture_rpc_connect_binding); + torture_suite_add_simple_test(suite, "api.rpcconn.srv", torture_rpc_connect_srv); + torture_suite_add_simple_test(suite, "api.rpcconn.pdc", torture_rpc_connect_pdc); + torture_suite_add_simple_test(suite, "api.rpcconn.dc", torture_rpc_connect_dc); + torture_suite_add_simple_test(suite, "api.rpcconn.dcinfo", torture_rpc_connect_dc_info); + torture_suite_add_simple_test(suite, "api.listshares", torture_listshares); + torture_suite_add_simple_test(suite, "api.delshare", torture_delshare); + torture_suite_add_simple_test(suite, "api.domopenlsa", torture_domain_open_lsa); + torture_suite_add_simple_test(suite, "api.domcloselsa", torture_domain_close_lsa); + torture_suite_add_simple_test(suite, "api.domopensamr", torture_domain_open_samr); + torture_suite_add_simple_test(suite, "api.domclosesamr", torture_domain_close_samr); + torture_suite_add_simple_test(suite, "api.become.dc", torture_net_become_dc); + torture_suite_add_simple_test(suite, "api.domlist", torture_domain_list); + + suite->description = talloc_strdup(suite, "libnet convenience interface tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/libnet/libnet_BecomeDC.c b/source4/torture/libnet/libnet_BecomeDC.c new file mode 100644 index 0000000..45d386b --- /dev/null +++ b/source4/torture/libnet/libnet_BecomeDC.c @@ -0,0 +1,191 @@ +/* + Unix SMB/CIFS implementation. + + libnet_BecomeDC() tests + + Copyright (C) Stefan Metzmacher <metze@samba.org> 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "torture/rpc/torture_rpc.h" +#include "libnet/libnet.h" +#include "dsdb/samdb/samdb.h" +#include "../lib/util/dlinklist.h" +#include "librpc/gen_ndr/ndr_drsuapi.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "system/time.h" +#include "ldb_wrap.h" +#include "auth/auth.h" +#include "param/param.h" +#include "param/provision.h" +#include "libcli/resolve/resolve.h" +#include "torture/libnet/proto.h" + +bool torture_net_become_dc(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + struct libnet_BecomeDC b; + struct libnet_UnbecomeDC u; + struct libnet_vampire_cb_state *s; + struct ldb_message *msg; + int ldb_ret; + uint32_t i; + char *private_dir; + const char *address; + struct nbt_name name; + const char *netbios_name; + struct cli_credentials *machine_account; + struct test_join *tj; + struct loadparm_context *lp_ctx; + struct ldb_context *ldb; + struct libnet_context *ctx; + struct dsdb_schema *schema; + + char *location = NULL; + torture_assert_ntstatus_ok(torture, torture_temp_dir(torture, "libnet_BecomeDC", &location), + "torture_temp_dir should return NT_STATUS_OK" ); + + netbios_name = lpcfg_parm_string(torture->lp_ctx, NULL, "become dc", "smbtorture dc"); + if (!netbios_name || !netbios_name[0]) { + netbios_name = "smbtorturedc"; + } + + make_nbt_name_server(&name, torture_setting_string(torture, "host", NULL)); + + /* do an initial name resolution to find its IP */ + status = resolve_name_ex(lpcfg_resolve_context(torture->lp_ctx), + 0, 0, + &name, torture, &address, torture->ev); + torture_assert_ntstatus_ok(torture, status, talloc_asprintf(torture, + "Failed to resolve %s - %s\n", + name.name, nt_errstr(status))); + + + /* Join domain as a member server. */ + tj = torture_join_domain(torture, netbios_name, + ACB_WSTRUST, + &machine_account); + torture_assert(torture, tj, talloc_asprintf(torture, + "%s failed to join domain as workstation\n", + netbios_name)); + + s = libnet_vampire_cb_state_init(torture, torture->lp_ctx, torture->ev, + netbios_name, + torture_join_dom_netbios_name(tj), + torture_join_dom_dns_name(tj), + location); + torture_assert(torture, s, "libnet_vampire_cb_state_init"); + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + ZERO_STRUCT(b); + b.in.domain_dns_name = torture_join_dom_dns_name(tj); + b.in.domain_netbios_name = torture_join_dom_netbios_name(tj); + b.in.domain_sid = torture_join_sid(tj); + b.in.source_dsa_address = address; + b.in.dest_dsa_netbios_name = netbios_name; + + b.in.callbacks.private_data = s; + b.in.callbacks.check_options = libnet_vampire_cb_check_options; + b.in.callbacks.prepare_db = libnet_vampire_cb_prepare_db; + b.in.callbacks.schema_chunk = libnet_vampire_cb_schema_chunk; + b.in.callbacks.config_chunk = libnet_vampire_cb_store_chunk; + b.in.callbacks.domain_chunk = libnet_vampire_cb_store_chunk; + + status = libnet_BecomeDC(ctx, s, &b); + torture_assert_ntstatus_ok_goto(torture, status, ret, cleanup, talloc_asprintf(torture, + "libnet_BecomeDC() failed - %s %s\n", + nt_errstr(status), b.out.error_string)); + ldb = libnet_vampire_cb_ldb(s); + + msg = ldb_msg_new(s); + torture_assert_int_equal_goto(torture, (msg?1:0), 1, ret, cleanup, + "ldb_msg_new() failed\n"); + msg->dn = ldb_dn_new(msg, ldb, "@ROOTDSE"); + torture_assert_int_equal_goto(torture, (msg->dn?1:0), 1, ret, cleanup, + "ldb_msg_new(@ROOTDSE) failed\n"); + + ldb_ret = ldb_msg_add_string(msg, "isSynchronized", "TRUE"); + torture_assert_int_equal_goto(torture, ldb_ret, LDB_SUCCESS, ret, cleanup, + "ldb_msg_add_string(msg, isSynchronized, TRUE) failed\n"); + + for (i=0; i < msg->num_elements; i++) { + msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; + } + + torture_comment(torture, "mark ROOTDSE with isSynchronized=TRUE\n"); + ldb_ret = ldb_modify(libnet_vampire_cb_ldb(s), msg); + torture_assert_int_equal_goto(torture, ldb_ret, LDB_SUCCESS, ret, cleanup, + "ldb_modify() failed\n"); + + /* commit the transaction now we know the secrets were written + * out properly + */ + ldb_ret = ldb_transaction_commit(ldb); + torture_assert_int_equal_goto(torture, ldb_ret, LDB_SUCCESS, ret, cleanup, + "ldb_transaction_commit() failed\n"); + + /* reopen the ldb */ + talloc_unlink(s, ldb); + + lp_ctx = libnet_vampire_cb_lp_ctx(s); + private_dir = talloc_asprintf(s, "%s/%s", location, "private"); + lpcfg_set_cmdline(lp_ctx, "private dir", private_dir); + torture_comment(torture, "Reopen the SAM LDB with system credentials and all replicated data: %s\n", private_dir); + ldb = samdb_connect(s, + torture->ev, + lp_ctx, + system_session(lp_ctx), + NULL, + 0); + torture_assert_goto(torture, ldb != NULL, ret, cleanup, + talloc_asprintf(torture, + "Failed to open '%s/sam.ldb'\n", private_dir)); + + torture_assert_goto(torture, dsdb_uses_global_schema(ldb), ret, cleanup, + "Uses global schema"); + + schema = dsdb_get_schema(ldb, s); + torture_assert_goto(torture, schema != NULL, ret, cleanup, + "Failed to get loaded dsdb_schema\n"); + + /* Make sure we get this from the command line */ + if (lpcfg_parm_bool(torture->lp_ctx, NULL, "become dc", "do not unjoin", false)) { + talloc_free(s); + return ret; + } + +cleanup: + ZERO_STRUCT(u); + u.in.domain_dns_name = torture_join_dom_dns_name(tj); + u.in.domain_netbios_name = torture_join_dom_netbios_name(tj); + u.in.source_dsa_address = address; + u.in.dest_dsa_netbios_name = netbios_name; + + status = libnet_UnbecomeDC(ctx, s, &u); + torture_assert_ntstatus_ok(torture, status, talloc_asprintf(torture, + "libnet_UnbecomeDC() failed - %s %s\n", + nt_errstr(status), u.out.error_string)); + + /* Leave domain. */ + torture_leave_domain(torture, tj); + + talloc_free(s); + return ret; +} diff --git a/source4/torture/libnet/libnet_domain.c b/source4/torture/libnet/libnet_domain.c new file mode 100644 index 0000000..2444000 --- /dev/null +++ b/source4/torture/libnet/libnet_domain.c @@ -0,0 +1,440 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + 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 "lib/cmdline/cmdline.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" +#include "torture/libnet/proto.h" + + +static bool test_opendomain_samr(struct torture_context *tctx, + struct dcerpc_binding_handle *b, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, struct lsa_String *domname, + uint32_t *access_mask, struct dom_sid **sid_p) +{ + struct policy_handle h, domain_handle; + struct samr_Connect r1; + struct samr_LookupDomain r2; + struct dom_sid2 *sid = NULL; + struct samr_OpenDomain r3; + + torture_comment(tctx, "connecting\n"); + + *access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + + r1.in.system_name = 0; + r1.in.access_mask = *access_mask; + r1.out.connect_handle = &h; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_Connect_r(b, mem_ctx, &r1), + "Connect failed"); + torture_assert_ntstatus_ok(tctx, r1.out.result, + "Connect failed"); + + r2.in.connect_handle = &h; + r2.in.domain_name = domname; + r2.out.sid = &sid; + + torture_comment(tctx, "domain lookup on %s\n", domname->string); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_LookupDomain_r(b, mem_ctx, &r2), + "LookupDomain failed"); + torture_assert_ntstatus_ok(tctx, r2.out.result, + "LookupDomain failed"); + + r3.in.connect_handle = &h; + r3.in.access_mask = *access_mask; + r3.in.sid = *sid_p = *r2.out.sid; + r3.out.domain_handle = &domain_handle; + + torture_comment(tctx, "opening domain\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenDomain_r(b, mem_ctx, &r3), + "OpenDomain failed"); + torture_assert_ntstatus_ok(tctx, r3.out.result, + "OpenDomain failed"); + + *handle = domain_handle; + + return true; +} + + +static bool test_opendomain_lsa(struct torture_context *tctx, + struct dcerpc_binding_handle *b, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, struct lsa_String *domname, + uint32_t *access_mask) +{ + struct lsa_OpenPolicy2 open; + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + + *access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + + ZERO_STRUCT(attr); + ZERO_STRUCT(qos); + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.sec_qos = &qos; + + open.in.system_name = domname->string; + open.in.attr = &attr; + open.in.access_mask = *access_mask; + open.out.handle = handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_OpenPolicy2_r(b, mem_ctx, &open), + "OpenPolicy2 failed"); + torture_assert_ntstatus_ok(tctx, open.out.result, + "OpenPolicy2 failed"); + + return true; +} + +bool torture_domain_open_lsa(struct torture_context *torture) +{ + NTSTATUS status; + bool ret = true; + struct libnet_context *ctx; + struct libnet_DomainOpen r; + struct lsa_Close lsa_close; + struct policy_handle h; + const char *domain_name; + + /* we're accessing domain controller so the domain name should be + passed (it's going to be resolved to dc name and address) instead + of specific server name. */ + domain_name = lpcfg_workgroup(torture->lp_ctx); + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + if (ctx == NULL) { + torture_comment(torture, "failed to create libnet context\n"); + return false; + } + + ctx->cred = samba_cmdline_get_creds(); + + ZERO_STRUCT(r); + r.in.type = DOMAIN_LSA; + r.in.domain_name = domain_name; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + + status = libnet_DomainOpen(ctx, torture, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "failed to open domain on lsa service: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + ZERO_STRUCT(lsa_close); + lsa_close.in.handle = &ctx->lsa.handle; + lsa_close.out.handle = &h; + + torture_assert_ntstatus_ok(torture, + dcerpc_lsa_Close_r(ctx->lsa.pipe->binding_handle, ctx, &lsa_close), + "failed to close domain on lsa service"); + torture_assert_ntstatus_ok(torture, lsa_close.out.result, + "failed to close domain on lsa service"); + +done: + talloc_free(ctx); + return ret; +} + + +bool torture_domain_close_lsa(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx=NULL; + struct libnet_context *ctx; + struct lsa_String domain_name; + struct dcerpc_binding *binding; + uint32_t access_mask; + struct policy_handle h; + struct dcerpc_pipe *p; + struct libnet_DomainClose r; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + if (ctx == NULL) { + torture_comment(torture, "failed to create libnet context\n"); + ret = false; + goto done; + } + + ctx->cred = samba_cmdline_get_creds(); + + mem_ctx = talloc_init("torture_domain_close_lsa"); + status = dcerpc_pipe_connect_b(mem_ctx, &p, binding, &ndr_table_lsarpc, + samba_cmdline_get_creds(), + torture->ev, torture->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "failed to connect to server: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + + if (!test_opendomain_lsa(torture, p->binding_handle, torture, &h, &domain_name, &access_mask)) { + torture_comment(torture, "failed to open domain on lsa service\n"); + ret = false; + goto done; + } + + ctx->lsa.pipe = p; + ctx->lsa.name = domain_name.string; + ctx->lsa.access_mask = access_mask; + ctx->lsa.handle = h; + + ZERO_STRUCT(r); + r.in.type = DOMAIN_LSA; + r.in.domain_name = domain_name.string; + + status = libnet_DomainClose(ctx, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + talloc_free(ctx); + return ret; +} + + +bool torture_domain_open_samr(struct torture_context *torture) +{ + NTSTATUS status; + struct libnet_context *ctx; + TALLOC_CTX *mem_ctx; + struct policy_handle domain_handle, handle; + struct libnet_DomainOpen io; + struct samr_Close r; + const char *domain_name; + bool ret = true; + + mem_ctx = talloc_init("test_domainopen_lsa"); + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + /* we're accessing domain controller so the domain name should be + passed (it's going to be resolved to dc name and address) instead + of specific server name. */ + domain_name = lpcfg_workgroup(torture->lp_ctx); + + /* + * Testing synchronous version + */ + torture_comment(torture, "opening domain\n"); + + io.in.type = DOMAIN_SAMR; + io.in.domain_name = domain_name; + io.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + + status = libnet_DomainOpen(ctx, mem_ctx, &io); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "Composite domain open failed for domain '%s' - %s\n", + domain_name, nt_errstr(status)); + ret = false; + goto done; + } + + domain_handle = ctx->samr.handle; + + r.in.handle = &domain_handle; + r.out.handle = &handle; + + torture_comment(torture, "closing domain handle\n"); + + torture_assert_ntstatus_ok(torture, + dcerpc_samr_Close_r(ctx->samr.pipe->binding_handle, mem_ctx, &r), + "Close failed"); + torture_assert_ntstatus_ok(torture, r.out.result, + "Close failed"); + +done: + talloc_free(mem_ctx); + talloc_free(ctx); + + return ret; +} + + +bool torture_domain_close_samr(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + struct libnet_context *ctx; + struct lsa_String domain_name; + struct dcerpc_binding *binding; + uint32_t access_mask; + struct policy_handle h; + struct dcerpc_pipe *p; + struct libnet_DomainClose r; + struct dom_sid *sid; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + if (ctx == NULL) { + torture_comment(torture, "failed to create libnet context\n"); + ret = false; + goto done; + } + + ctx->cred = samba_cmdline_get_creds(); + + mem_ctx = talloc_init("torture_domain_close_samr"); + status = dcerpc_pipe_connect_b(mem_ctx, &p, binding, &ndr_table_samr, + ctx->cred, torture->ev, torture->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "failed to connect to server: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + domain_name.string = talloc_strdup(mem_ctx, lpcfg_workgroup(torture->lp_ctx)); + + if (!test_opendomain_samr(torture, p->binding_handle, torture, &h, &domain_name, &access_mask, &sid)) { + torture_comment(torture, "failed to open domain on samr service\n"); + ret = false; + goto done; + } + + ctx->samr.pipe = p; + ctx->samr.name = talloc_steal(ctx, domain_name.string); + ctx->samr.access_mask = access_mask; + ctx->samr.handle = h; + ctx->samr.sid = talloc_steal(ctx, sid); + + ZERO_STRUCT(r); + r.in.type = DOMAIN_SAMR; + r.in.domain_name = domain_name.string; + + status = libnet_DomainClose(ctx, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + talloc_free(ctx); + return ret; +} + + +bool torture_domain_list(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + struct dcerpc_binding *binding; + struct libnet_context *ctx; + struct libnet_DomainList r; + int i; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + if (ctx == NULL) { + torture_comment(torture, "failed to create libnet context\n"); + ret = false; + goto done; + } + + ctx->cred = samba_cmdline_get_creds(); + + mem_ctx = talloc_init("torture_domain_close_samr"); + + /* + * querying the domain list using default buffer size + */ + + ZERO_STRUCT(r); + r.in.hostname = dcerpc_binding_get_string_option(binding, "host"); + + status = libnet_DomainList(ctx, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + + torture_comment(torture, "Received list or domains (everything in one piece):\n"); + + for (i = 0; i < r.out.count; i++) { + torture_comment(torture, "Name[%d]: %s\n", i, r.out.domains[i].name); + } + + /* + * querying the domain list using specified (much smaller) buffer size + */ + + ctx->samr.buf_size = 32; + + ZERO_STRUCT(r); + r.in.hostname = dcerpc_binding_get_string_option(binding, "host"); + + status = libnet_DomainList(ctx, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + + torture_comment(torture, "Received list or domains (collected in more than one round):\n"); + + for (i = 0; i < r.out.count; i++) { + torture_comment(torture, "Name[%d]: %s\n", i, r.out.domains[i].name); + } + +done: + torture_comment(torture, "\nStatus: %s\n", nt_errstr(status)); + + talloc_free(mem_ctx); + talloc_free(ctx); + return ret; +} diff --git a/source4/torture/libnet/libnet_group.c b/source4/torture/libnet/libnet_group.c new file mode 100644 index 0000000..e3e2030 --- /dev/null +++ b/source4/torture/libnet/libnet_group.c @@ -0,0 +1,210 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 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 "lib/cmdline/cmdline.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/libnet/proto.h" +#include "param/param.h" + + +#define TEST_GROUPNAME "libnetgrouptest" + + +bool torture_groupinfo_api(struct torture_context *torture) +{ + const char *name = TEST_GROUPNAME; + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL, *prep_mem_ctx; + struct libnet_context *ctx = NULL; + struct dcerpc_pipe *p; + struct policy_handle h; + struct lsa_String domain_name; + struct libnet_GroupInfo req; + + prep_mem_ctx = talloc_init("prepare torture group info"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + if (!test_domain_open(torture, p->binding_handle, &domain_name, prep_mem_ctx, &h, NULL)) { + ret = false; + goto done; + } + + if (!test_group_create(torture, p->binding_handle, prep_mem_ctx, &h, name, NULL)) { + ret = false; + goto done; + } + + mem_ctx = talloc_init("torture group info"); + + if (!test_libnet_context_init(torture, true, &ctx)) { + return false; + } + + ZERO_STRUCT(req); + + req.in.domain_name = domain_name.string; + req.in.level = GROUP_INFO_BY_NAME; + req.in.data.group_name = name; + + status = libnet_GroupInfo(ctx, mem_ctx, &req); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "libnet_GroupInfo call failed: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + if (!test_group_cleanup(torture, ctx->samr.pipe->binding_handle, + mem_ctx, &ctx->samr.handle, TEST_GROUPNAME)) { + torture_comment(torture, "cleanup failed\n"); + ret = false; + goto done; + } + + if (!test_samr_close_handle(torture, + ctx->samr.pipe->binding_handle, mem_ctx, &ctx->samr.handle)) { + torture_comment(torture, "domain close failed\n"); + ret = false; + } + +done: + talloc_free(ctx); + talloc_free(mem_ctx); + return ret; +} + + +bool torture_grouplist(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + struct libnet_context *ctx; + struct lsa_String domain_name; + struct libnet_GroupList req; + int i; + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + mem_ctx = talloc_init("torture group list"); + + ZERO_STRUCT(req); + + torture_comment(torture, "listing group accounts:\n"); + + do { + req.in.domain_name = domain_name.string; + req.in.page_size = 128; + req.in.resume_index = req.out.resume_index; + + status = libnet_GroupList(ctx, mem_ctx, &req); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) break; + + for (i = 0; i < req.out.count; i++) { + torture_comment(torture, "\tgroup: %s, sid=%s\n", + req.out.groups[i].groupname, req.out.groups[i].sid); + } + + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + + if (!(NT_STATUS_IS_OK(status) || + NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES))) { + torture_comment(torture, "libnet_GroupList call failed: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + if (!test_samr_close_handle(torture, + ctx->samr.pipe->binding_handle, mem_ctx, &ctx->samr.handle)) { + torture_comment(torture, "domain close failed\n"); + ret = false; + } + + if (!test_lsa_close_handle(torture, + ctx->lsa.pipe->binding_handle, mem_ctx, &ctx->lsa.handle)) { + torture_comment(torture, "lsa domain close failed\n"); + ret = false; + } + + talloc_free(ctx); + +done: + talloc_free(mem_ctx); + return ret; +} + + +bool torture_creategroup(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + struct libnet_context *ctx; + struct libnet_CreateGroup req; + + mem_ctx = talloc_init("test_creategroup"); + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + req.in.group_name = TEST_GROUPNAME; + req.in.domain_name = lpcfg_workgroup(torture->lp_ctx); + req.out.error_string = NULL; + + status = libnet_CreateGroup(ctx, mem_ctx, &req); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "libnet_CreateGroup call failed: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + if (!test_group_cleanup(torture, ctx->samr.pipe->binding_handle, + mem_ctx, &ctx->samr.handle, TEST_GROUPNAME)) { + torture_comment(torture, "cleanup failed\n"); + ret = false; + goto done; + } + + if (!test_samr_close_handle(torture, + ctx->samr.pipe->binding_handle, mem_ctx, &ctx->samr.handle)) { + torture_comment(torture, "domain close failed\n"); + ret = false; + } + +done: + talloc_free(ctx); + talloc_free(mem_ctx); + return ret; +} diff --git a/source4/torture/libnet/libnet_lookup.c b/source4/torture/libnet/libnet_lookup.c new file mode 100644 index 0000000..e6e23dc --- /dev/null +++ b/source4/torture/libnet/libnet_lookup.c @@ -0,0 +1,191 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 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/cmdline.h" +#include "libnet/libnet.h" +#include "libcli/libcli.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/libnet/proto.h" +#include "param/param.h" + + +bool torture_lookup(struct torture_context *torture) +{ + bool ret; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + struct libnet_context *ctx; + struct libnet_Lookup lookup; + struct dcerpc_binding *binding; + + mem_ctx = talloc_init("test_lookup"); + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + lookup.in.hostname = torture_setting_string(torture, "host", NULL); + if (lookup.in.hostname == NULL) { + status = torture_rpc_binding(torture, &binding); + if (NT_STATUS_IS_OK(status)) { + lookup.in.hostname = dcerpc_binding_get_string_option(binding, "host"); + } + } + + lookup.in.type = NBT_NAME_CLIENT; + lookup.in.resolve_ctx = NULL; + lookup.out.address = NULL; + + status = libnet_Lookup(ctx, mem_ctx, &lookup); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "Couldn't lookup name %s: %s\n", lookup.in.hostname, nt_errstr(status)); + ret = false; + goto done; + } + + ret = true; + + torture_comment(torture, "Name [%s] found at address: %s.\n", lookup.in.hostname, *lookup.out.address); + +done: + talloc_free(mem_ctx); + return ret; +} + + +bool torture_lookup_host(struct torture_context *torture) +{ + bool ret; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + struct libnet_context *ctx; + struct libnet_Lookup lookup; + struct dcerpc_binding *binding; + + mem_ctx = talloc_init("test_lookup_host"); + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + lookup.in.hostname = torture_setting_string(torture, "host", NULL); + if (lookup.in.hostname == NULL) { + status = torture_rpc_binding(torture, &binding); + if (NT_STATUS_IS_OK(status)) { + lookup.in.hostname = dcerpc_binding_get_string_option(binding, "host"); + } + } + + lookup.in.resolve_ctx = NULL; + lookup.out.address = NULL; + + status = libnet_LookupHost(ctx, mem_ctx, &lookup); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "Couldn't lookup host %s: %s\n", lookup.in.hostname, nt_errstr(status)); + ret = false; + goto done; + } + + ret = true; + + torture_comment(torture, "Host [%s] found at address: %s.\n", lookup.in.hostname, *lookup.out.address); + +done: + talloc_free(mem_ctx); + return ret; +} + + +bool torture_lookup_pdc(struct torture_context *torture) +{ + bool ret; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + struct libnet_context *ctx; + struct libnet_LookupDCs *lookup; + int i; + + mem_ctx = talloc_init("test_lookup_pdc"); + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + talloc_steal(ctx, mem_ctx); + + lookup = talloc(mem_ctx, struct libnet_LookupDCs); + if (!lookup) { + ret = false; + goto done; + } + + lookup->in.domain_name = lpcfg_workgroup(torture->lp_ctx); + lookup->in.name_type = NBT_NAME_PDC; + + status = libnet_LookupDCs(ctx, mem_ctx, lookup); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "Couldn't lookup pdc %s: %s\n", lookup->in.domain_name, + nt_errstr(status)); + ret = false; + goto done; + } + + ret = true; + + torture_comment(torture, "DCs of domain [%s] found.\n", lookup->in.domain_name); + for (i = 0; i < lookup->out.num_dcs; i++) { + torture_comment(torture, "\tDC[%d]: name=%s, address=%s\n", i, lookup->out.dcs[i].name, + lookup->out.dcs[i].address); + } + +done: + talloc_free(mem_ctx); + return ret; +} + + +bool torture_lookup_sam_name(struct torture_context *torture) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx; + struct libnet_context *ctx; + struct libnet_LookupName r; + bool ret = true; + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + mem_ctx = talloc_init("torture lookup sam name"); + if (mem_ctx == NULL) return false; + + r.in.name = "Administrator"; + r.in.domain_name = lpcfg_workgroup(torture->lp_ctx); + + status = libnet_LookupName(ctx, mem_ctx, &r); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "libnet_LookupName: failed"); + +done: + talloc_free(mem_ctx); + talloc_free(ctx); + + return ret; +} diff --git a/source4/torture/libnet/libnet_rpc.c b/source4/torture/libnet/libnet_rpc.c new file mode 100644 index 0000000..9820432 --- /dev/null +++ b/source4/torture/libnet/libnet_rpc.c @@ -0,0 +1,230 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 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/cmdline.h" +#include "libnet/libnet.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "librpc/gen_ndr/ndr_samr.h" +#include "librpc/gen_ndr/ndr_srvsvc.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/libnet/proto.h" +#include "param/param.h" + + +static bool test_connect_service(struct torture_context *tctx, + struct libnet_context *ctx, + const struct ndr_interface_table *iface, + const char *binding_string, + const char *hostname, + const enum libnet_RpcConnect_level level, + bool badcreds, NTSTATUS expected_status) +{ + NTSTATUS status; + struct libnet_RpcConnect connect_r; + ZERO_STRUCT(connect_r); + + connect_r.level = level; + connect_r.in.binding = binding_string; + connect_r.in.name = hostname; + connect_r.in.dcerpc_iface = iface; + + /* if bad credentials are needed, set baduser%badpassword instead + of default commandline-passed credentials */ + if (badcreds) { + cli_credentials_set_username(ctx->cred, "baduser", CRED_SPECIFIED); + cli_credentials_set_password(ctx->cred, "badpassword", CRED_SPECIFIED); + } + + status = libnet_RpcConnect(ctx, ctx, &connect_r); + + if (!NT_STATUS_EQUAL(status, expected_status)) { + torture_comment(tctx, "Connecting to rpc service %s on %s.\n\tFAILED. Expected: %s." + "Received: %s\n", + connect_r.in.dcerpc_iface->name, connect_r.in.binding, nt_errstr(expected_status), + nt_errstr(status)); + + return false; + } + + torture_comment(tctx, "PASSED. Expected: %s, received: %s\n", nt_errstr(expected_status), + nt_errstr(status)); + + if (connect_r.level == LIBNET_RPC_CONNECT_DC_INFO && NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Domain Controller Info:\n"); + torture_comment(tctx, "\tDomain Name:\t %s\n", connect_r.out.domain_name); + torture_comment(tctx, "\tDomain SID:\t %s\n", dom_sid_string(ctx, connect_r.out.domain_sid)); + torture_comment(tctx, "\tRealm:\t\t %s\n", connect_r.out.realm); + torture_comment(tctx, "\tGUID:\t\t %s\n", GUID_string(ctx, connect_r.out.guid)); + + } else if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Error string: %s\n", connect_r.out.error_string); + } + + return true; +} + + +static bool torture_rpc_connect(struct torture_context *torture, + const enum libnet_RpcConnect_level level, + const char *bindstr, const char *hostname) +{ + struct libnet_context *ctx; + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + torture_comment(torture, "Testing connection to LSA interface\n"); + + if (!test_connect_service(torture, ctx, &ndr_table_lsarpc, bindstr, + hostname, level, false, NT_STATUS_OK)) { + torture_comment(torture, "failed to connect LSA interface\n"); + return false; + } + + torture_comment(torture, "Testing connection to SAMR interface\n"); + if (!test_connect_service(torture, ctx, &ndr_table_samr, bindstr, + hostname, level, false, NT_STATUS_OK)) { + torture_comment(torture, "failed to connect SAMR interface\n"); + return false; + } + + torture_comment(torture, "Testing connection to SRVSVC interface\n"); + if (!test_connect_service(torture, ctx, &ndr_table_srvsvc, bindstr, + hostname, level, false, NT_STATUS_OK)) { + torture_comment(torture, "failed to connect SRVSVC interface\n"); + return false; + } + + torture_comment(torture, "Testing connection to LSA interface with wrong credentials\n"); + if (!test_connect_service(torture, ctx, &ndr_table_lsarpc, bindstr, + hostname, level, true, NT_STATUS_LOGON_FAILURE)) { + torture_comment(torture, "failed to test wrong credentials on LSA interface\n"); + return false; + } + + torture_comment(torture, "Testing connection to SAMR interface with wrong credentials\n"); + if (!test_connect_service(torture, ctx, &ndr_table_samr, bindstr, + hostname, level, true, NT_STATUS_LOGON_FAILURE)) { + torture_comment(torture, "failed to test wrong credentials on SAMR interface\n"); + return false; + } + + talloc_free(ctx); + + return true; +} + + +bool torture_rpc_connect_srv(struct torture_context *torture) +{ + const enum libnet_RpcConnect_level level = LIBNET_RPC_CONNECT_SERVER; + NTSTATUS status; + struct dcerpc_binding *binding; + const char *host; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + host = dcerpc_binding_get_string_option(binding, "host"); + + return torture_rpc_connect(torture, level, NULL, host); +} + + +bool torture_rpc_connect_pdc(struct torture_context *torture) +{ + const enum libnet_RpcConnect_level level = LIBNET_RPC_CONNECT_PDC; + NTSTATUS status; + struct dcerpc_binding *binding; + const char *domain_name; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + /* we're accessing domain controller so the domain name should be + passed (it's going to be resolved to dc name and address) instead + of specific server name. */ + domain_name = lpcfg_workgroup(torture->lp_ctx); + return torture_rpc_connect(torture, level, NULL, domain_name); +} + + +bool torture_rpc_connect_dc(struct torture_context *torture) +{ + const enum libnet_RpcConnect_level level = LIBNET_RPC_CONNECT_DC; + NTSTATUS status; + struct dcerpc_binding *binding; + const char *domain_name; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + /* we're accessing domain controller so the domain name should be + passed (it's going to be resolved to dc name and address) instead + of specific server name. */ + domain_name = lpcfg_workgroup(torture->lp_ctx); + return torture_rpc_connect(torture, level, NULL, domain_name); +} + + +bool torture_rpc_connect_dc_info(struct torture_context *torture) +{ + const enum libnet_RpcConnect_level level = LIBNET_RPC_CONNECT_DC_INFO; + NTSTATUS status; + struct dcerpc_binding *binding; + const char *domain_name; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + /* we're accessing domain controller so the domain name should be + passed (it's going to be resolved to dc name and address) instead + of specific server name. */ + domain_name = lpcfg_workgroup(torture->lp_ctx); + return torture_rpc_connect(torture, level, NULL, domain_name); +} + + +bool torture_rpc_connect_binding(struct torture_context *torture) +{ + const enum libnet_RpcConnect_level level = LIBNET_RPC_CONNECT_BINDING; + NTSTATUS status; + struct dcerpc_binding *binding; + const char *bindstr; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + bindstr = dcerpc_binding_string(torture, binding); + + return torture_rpc_connect(torture, level, bindstr, NULL); +} diff --git a/source4/torture/libnet/libnet_share.c b/source4/torture/libnet/libnet_share.c new file mode 100644 index 0000000..da74b99 --- /dev/null +++ b/source4/torture/libnet/libnet_share.c @@ -0,0 +1,285 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Gregory LEOCADIE <gleocadie@idealx.com> 2005 + Copyright (C) Rafal Szczesniak 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "libnet/libnet.h" +#include "lib/cmdline/cmdline.h" +#include "librpc/gen_ndr/ndr_srvsvc_c.h" +#include "torture/libnet/proto.h" + + +#define TEST_SHARENAME "libnetsharetest" + + +static void test_displayshares(struct torture_context *tctx, + struct libnet_ListShares s) +{ + int i, j; + + struct share_type { + enum srvsvc_ShareType type; + const char *desc; + } share_types[] = { + { STYPE_DISKTREE, "STYPE_DISKTREE" }, + { STYPE_DISKTREE_TEMPORARY, "STYPE_DISKTREE_TEMPORARY" }, + { STYPE_DISKTREE_HIDDEN, "STYPE_DISKTREE_HIDDEN" }, + { STYPE_PRINTQ, "STYPE_PRINTQ" }, + { STYPE_PRINTQ_TEMPORARY, "STYPE_PRINTQ_TEMPORARY" }, + { STYPE_PRINTQ_HIDDEN, "STYPE_PRINTQ_HIDDEN" }, + { STYPE_DEVICE, "STYPE_DEVICE" }, + { STYPE_DEVICE_TEMPORARY, "STYPE_DEVICE_TEMPORARY" }, + { STYPE_DEVICE_HIDDEN, "STYPE_DEVICE_HIDDEN" }, + { STYPE_IPC, "STYPE_IPC" }, + { STYPE_IPC_TEMPORARY, "STYPE_IPC_TEMPORARY" }, + { STYPE_IPC_HIDDEN, "STYPE_IPC_HIDDEN" } + }; + + switch (s.in.level) { + case 0: + for (i = 0; i < s.out.ctr.ctr0->count; i++) { + struct srvsvc_NetShareInfo0 *info = &s.out.ctr.ctr0->array[i]; + torture_comment(tctx, "\t[%d] %s\n", i, info->name); + } + break; + + case 1: + for (i = 0; i < s.out.ctr.ctr1->count; i++) { + struct srvsvc_NetShareInfo1 *info = &s.out.ctr.ctr1->array[i]; + for (j = 0; j < ARRAY_SIZE(share_types); j++) { + if (share_types[j].type == info->type) { + torture_comment(tctx, + "\t[%d] %s (%s)\t%s\n", + i, + info->name, + info->comment, + share_types[j].desc); + break; + } + } + } + break; + + case 2: + for (i = 0; i < s.out.ctr.ctr2->count; i++) { + struct srvsvc_NetShareInfo2 *info = &s.out.ctr.ctr2->array[i]; + for (j = 0; j < ARRAY_SIZE(share_types); j++) { + if (share_types[j].type == info->type) { + torture_comment(tctx, + "\t[%d] %s\t%s\n" + "\t %s\n" + "\t [perms=0x%08x, " + "max_usr=%d, " + "cur_usr=%d, " + "path=%s, " + "pass=%s]\n", + i, + info->name, + share_types[j].desc, + info->comment, + info->permissions, + info->max_users, + info->current_users, + info->path, + info->password); + break; + } + } + } + break; + + case 501: + for (i = 0; i < s.out.ctr.ctr501->count; i++) { + struct srvsvc_NetShareInfo501 *info = &s.out.ctr.ctr501->array[i]; + for (j = 0; j < ARRAY_SIZE(share_types); j++) { + if (share_types[j].type == info->type) { + torture_comment(tctx, + "\t[%d] %s" + "\t%s " + "[csc_policy=0x%08x]\n" + "\t %s\n", + i, + info->name, + share_types[j].desc, + info->csc_policy, + info->comment); + break; + } + } + } + break; + + case 502: + for (i = 0; i < s.out.ctr.ctr502->count; i++) { + struct srvsvc_NetShareInfo502 *info = &s.out.ctr.ctr502->array[i]; + for (j = 0; j < ARRAY_SIZE(share_types); j++) { + if (share_types[j].type == info->type) { + torture_comment(tctx, + "\t[%d] %s\t%s\n" + "\t %s\n" + "\t [perms=0x%08x, " + "max_usr=%d, " + "cur_usr=%d, " + "path=%s, pass=%s]\n", + i, + info->name, + share_types[j].desc, + info->comment, + info->permissions, + info->max_users, + info->current_users, + info->path, + info->password); + break; + } + } + } + break; + } +} + + +bool torture_listshares(struct torture_context *torture) +{ + struct libnet_ListShares share; + NTSTATUS status; + uint32_t levels[] = { 0, 1, 2, 501, 502 }; + int i; + bool ret = true; + struct libnet_context* libnetctx; + struct dcerpc_binding *binding; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("test_listshares"); + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + + libnetctx = libnet_context_init(torture->ev, torture->lp_ctx); + if (!libnetctx) { + torture_comment(torture, "Couldn't allocate libnet context\n"); + ret = false; + goto done; + } + + libnetctx->cred = samba_cmdline_get_creds(); + + torture_comment(torture, "Testing libnet_ListShare\n"); + + share.in.server_name = dcerpc_binding_get_string_option(binding, "host"); + + for (i = 0; i < ARRAY_SIZE(levels); i++) { + share.in.level = levels[i]; + torture_comment(torture, "Testing libnet_ListShare level %u\n", share.in.level); + + status = libnet_ListShares(libnetctx, mem_ctx, &share); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "libnet_ListShare level %u failed - %s\n", share.in.level, share.out.error_string); + ret = false; + goto done; + } + + torture_comment(torture, "listing shares:\n"); + test_displayshares(torture, share); + } + +done: + talloc_free(mem_ctx); + return ret; +} + + +static bool test_addshare(struct torture_context *tctx, + struct dcerpc_binding_handle *b, TALLOC_CTX *mem_ctx, const char *host, + const char* share) +{ + NTSTATUS status; + struct srvsvc_NetShareAdd add; + union srvsvc_NetShareInfo info; + struct srvsvc_NetShareInfo2 i; + + ZERO_STRUCT(i); + ZERO_STRUCT(info); + ZERO_STRUCT(add); + + i.name = share; + i.type = STYPE_DISKTREE; + i.path = "C:\\WINDOWS\\TEMP"; + i.max_users = 5; + i.comment = "Comment to the test share"; + i.password = NULL; + i.permissions = 0x0; + + info.info2 = &i; + + add.in.server_unc = host; + add.in.level = 2; + add.in.info = &info; + add.in.parm_error = NULL; + + status = dcerpc_srvsvc_NetShareAdd_r(b, mem_ctx, &add); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to add a new share\n"); + return false; + } + + torture_comment(tctx, "share added\n"); + return true; +} + + +bool torture_delshare(struct torture_context *torture) +{ + struct dcerpc_pipe *p; + struct dcerpc_binding *binding; + struct libnet_context* libnetctx; + const char *host; + NTSTATUS status; + bool ret = true; + struct libnet_DelShare share; + + host = torture_setting_string(torture, "host", NULL); + status = torture_rpc_binding(torture, &binding); + torture_assert_ntstatus_ok(torture, status, "Failed to get binding"); + + libnetctx = libnet_context_init(torture->ev, torture->lp_ctx); + libnetctx->cred = samba_cmdline_get_creds(); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_srvsvc); + + torture_assert_ntstatus_ok(torture, status, "Failed to get rpc connection"); + + if (!test_addshare(torture, p->binding_handle, torture, host, TEST_SHARENAME)) { + return false; + } + + share.in.server_name = dcerpc_binding_get_string_option(binding, "host"); + share.in.share_name = TEST_SHARENAME; + + status = libnet_DelShare(libnetctx, torture, &share); + torture_assert_ntstatus_ok(torture, status, "Failed to delete share"); + + return ret; +} diff --git a/source4/torture/libnet/libnet_user.c b/source4/torture/libnet/libnet_user.c new file mode 100644 index 0000000..9029827 --- /dev/null +++ b/source4/torture/libnet/libnet_user.c @@ -0,0 +1,520 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 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 "system/time.h" +#include "lib/cmdline/cmdline.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/libnet/usertest.h" +#include "torture/libnet/proto.h" +#include "param/param.h" + + + +bool torture_createuser(struct torture_context *torture) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx; + struct libnet_context *ctx = NULL; + struct libnet_CreateUser req; + bool ret = true; + + mem_ctx = talloc_init("test_createuser"); + + if (!test_libnet_context_init(torture, true, &ctx)) { + return false; + } + + req.in.user_name = TEST_USERNAME; + req.in.domain_name = lpcfg_workgroup(torture->lp_ctx); + req.out.error_string = NULL; + + status = libnet_CreateUser(ctx, mem_ctx, &req); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "libnet_CreateUser call failed: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + if (!test_user_cleanup(torture, ctx->samr.pipe->binding_handle, + mem_ctx, &ctx->samr.handle, TEST_USERNAME)) { + torture_comment(torture, "cleanup failed\n"); + ret = false; + goto done; + } + + if (!test_samr_close_handle(torture, + ctx->samr.pipe->binding_handle, mem_ctx, &ctx->samr.handle)) { + torture_comment(torture, "domain close failed\n"); + ret = false; + } + +done: + talloc_free(ctx); + talloc_free(mem_ctx); + return ret; +} + + +bool torture_deleteuser(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *mem_ctx; + struct policy_handle h; + struct lsa_String domain_name; + const char *name = TEST_USERNAME; + struct libnet_context *ctx = NULL; + struct libnet_DeleteUser req; + bool ret = false; + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + torture_assert_ntstatus_ok(torture, status, "torture_rpc_connection() failed"); + + mem_ctx = talloc_init("torture_deleteuser"); + + /* + * Pre-create a user to be deleted later + */ + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + ret = test_domain_open(torture, p->binding_handle, &domain_name, mem_ctx, &h, NULL); + torture_assert_goto(torture, ret, ret, done, "test_domain_open() failed"); + + ret = test_user_create(torture, p->binding_handle, mem_ctx, &h, name, NULL); + torture_assert_goto(torture, ret, ret, done, "test_user_create() failed"); + + /* + * Delete the user using libnet layer + */ + ret = test_libnet_context_init(torture, true, &ctx); + torture_assert_goto(torture, ret, ret, done, "test_libnet_context_init() failed"); + + req.in.user_name = TEST_USERNAME; + req.in.domain_name = lpcfg_workgroup(torture->lp_ctx); + + status = libnet_DeleteUser(ctx, mem_ctx, &req); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, "libnet_DeleteUser() failed"); + + /* mark test as successful */ + ret = true; + +done: + talloc_free(ctx); + talloc_free(mem_ctx); + return ret; +} + + +/* + Generate testing set of random changes +*/ + +static void set_test_changes(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, struct libnet_ModifyUser *r, + int num_changes, char **user_name, enum test_fields req_change) +{ + const char* logon_scripts[] = { "start_login.cmd", "login.bat", "start.cmd" }; + const char* home_dirs[] = { "\\\\srv\\home", "\\\\homesrv\\home\\user", "\\\\pdcsrv\\domain" }; + const char* home_drives[] = { "H:", "z:", "I:", "J:", "n:" }; + const uint32_t flags[] = { (ACB_DISABLED | ACB_NORMAL | ACB_PW_EXPIRED), + (ACB_NORMAL | ACB_PWNOEXP), + (ACB_NORMAL | ACB_PW_EXPIRED) }; + const char *homedir, *homedrive, *logonscript; + struct timeval now; + int i, testfld; + + torture_comment(tctx, "Fields to change: ["); + + for (i = 0; i < num_changes && i <= USER_FIELD_LAST; i++) { + const char *fldname; + + testfld = (req_change == none) ? (random() % USER_FIELD_LAST) + 1 : req_change; + + /* get one in case we hit time field this time */ + gettimeofday(&now, NULL); + + switch (testfld) { + case acct_name: + continue_if_field_set(r->in.account_name); + r->in.account_name = talloc_asprintf(mem_ctx, TEST_CHG_ACCOUNTNAME, + (int)(random() % 100)); + fldname = "account_name"; + + /* update the test's user name in case it's about to change */ + *user_name = talloc_strdup(mem_ctx, r->in.account_name); + break; + + case acct_full_name: + continue_if_field_set(r->in.full_name); + r->in.full_name = talloc_asprintf(mem_ctx, TEST_CHG_FULLNAME, + (unsigned int)random(), (unsigned int)random()); + fldname = "full_name"; + break; + + case acct_description: + continue_if_field_set(r->in.description); + r->in.description = talloc_asprintf(mem_ctx, TEST_CHG_DESCRIPTION, + (long)random()); + fldname = "description"; + break; + + case acct_home_directory: + continue_if_field_set(r->in.home_directory); + homedir = home_dirs[random() % ARRAY_SIZE(home_dirs)]; + r->in.home_directory = talloc_strdup(mem_ctx, homedir); + fldname = "home_dir"; + break; + + case acct_home_drive: + continue_if_field_set(r->in.home_drive); + homedrive = home_drives[random() % ARRAY_SIZE(home_drives)]; + r->in.home_drive = talloc_strdup(mem_ctx, homedrive); + fldname = "home_drive"; + break; + + case acct_comment: + continue_if_field_set(r->in.comment); + r->in.comment = talloc_asprintf(mem_ctx, TEST_CHG_COMMENT, + (unsigned long)random(), (unsigned long)random()); + fldname = "comment"; + break; + + case acct_logon_script: + continue_if_field_set(r->in.logon_script); + logonscript = logon_scripts[random() % ARRAY_SIZE(logon_scripts)]; + r->in.logon_script = talloc_strdup(mem_ctx, logonscript); + fldname = "logon_script"; + break; + + case acct_profile_path: + continue_if_field_set(r->in.profile_path); + r->in.profile_path = talloc_asprintf(mem_ctx, TEST_CHG_PROFILEPATH, + (unsigned long)random(), (unsigned int)random()); + fldname = "profile_path"; + break; + + case acct_expiry: + continue_if_field_set(r->in.acct_expiry); + now = timeval_add(&now, (random() % (31*24*60*60)), 0); + r->in.acct_expiry = (struct timeval *)talloc_memdup(mem_ctx, &now, sizeof(now)); + fldname = "acct_expiry"; + break; + + case acct_flags: + continue_if_field_set(r->in.acct_flags); + r->in.acct_flags = flags[random() % ARRAY_SIZE(flags)]; + fldname = "acct_flags"; + break; + + default: + fldname = "unknown_field"; + } + + torture_comment(tctx, ((i < num_changes - 1) ? "%s," : "%s"), fldname); + + /* disable requested field (it's supposed to be the only one used) */ + if (req_change != none) req_change = none; + } + + torture_comment(tctx, "]\n"); +} + + +#define TEST_STR_FLD(fld) \ + if (!strequal(req.in.fld, user_req.out.fld)) { \ + torture_comment(torture, "failed to change '%s'\n", #fld); \ + ret = false; \ + goto cleanup; \ + } + +#define TEST_TIME_FLD(fld) \ + if (timeval_compare(req.in.fld, user_req.out.fld)) { \ + torture_comment(torture, "failed to change '%s'\n", #fld); \ + ret = false; \ + goto cleanup; \ + } + +#define TEST_NUM_FLD(fld) \ + if (req.in.fld != user_req.out.fld) { \ + torture_comment(torture, "failed to change '%s'\n", #fld); \ + ret = false; \ + goto cleanup; \ + } + + +bool torture_modifyuser(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *prep_mem_ctx; + struct policy_handle h; + struct lsa_String domain_name; + char *name; + struct libnet_context *ctx = NULL; + struct libnet_ModifyUser req; + struct libnet_UserInfo user_req; + int fld; + bool ret = true; + struct dcerpc_binding_handle *b; + + prep_mem_ctx = talloc_init("prepare test_deleteuser"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + b = p->binding_handle; + + name = talloc_strdup(prep_mem_ctx, TEST_USERNAME); + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + if (!test_domain_open(torture, b, &domain_name, prep_mem_ctx, &h, NULL)) { + ret = false; + goto done; + } + + if (!test_user_create(torture, b, prep_mem_ctx, &h, name, NULL)) { + ret = false; + goto done; + } + + torture_comment(torture, "Testing change of all fields - each single one in turn\n"); + + if (!test_libnet_context_init(torture, true, &ctx)) { + return false; + } + + for (fld = USER_FIELD_FIRST; fld <= USER_FIELD_LAST; fld++) { + ZERO_STRUCT(req); + req.in.domain_name = lpcfg_workgroup(torture->lp_ctx); + req.in.user_name = name; + + set_test_changes(torture, torture, &req, 1, &name, fld); + + status = libnet_ModifyUser(ctx, torture, &req); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "libnet_ModifyUser call failed: %s\n", nt_errstr(status)); + ret = false; + continue; + } + + ZERO_STRUCT(user_req); + user_req.in.domain_name = lpcfg_workgroup(torture->lp_ctx); + user_req.in.data.user_name = name; + user_req.in.level = USER_INFO_BY_NAME; + + status = libnet_UserInfo(ctx, torture, &user_req); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "libnet_UserInfo call failed: %s\n", nt_errstr(status)); + ret = false; + continue; + } + + switch (fld) { + case acct_name: TEST_STR_FLD(account_name); + break; + case acct_full_name: TEST_STR_FLD(full_name); + break; + case acct_comment: TEST_STR_FLD(comment); + break; + case acct_description: TEST_STR_FLD(description); + break; + case acct_home_directory: TEST_STR_FLD(home_directory); + break; + case acct_home_drive: TEST_STR_FLD(home_drive); + break; + case acct_logon_script: TEST_STR_FLD(logon_script); + break; + case acct_profile_path: TEST_STR_FLD(profile_path); + break; + case acct_expiry: TEST_TIME_FLD(acct_expiry); + break; + case acct_flags: TEST_NUM_FLD(acct_flags); + break; + default: + break; + } + } + +cleanup: + if (!test_user_cleanup(torture, ctx->samr.pipe->binding_handle, + torture, &ctx->samr.handle, TEST_USERNAME)) { + torture_comment(torture, "cleanup failed\n"); + ret = false; + goto done; + } + + if (!test_samr_close_handle(torture, + ctx->samr.pipe->binding_handle, torture, &ctx->samr.handle)) { + torture_comment(torture, "domain close failed\n"); + ret = false; + } + +done: + talloc_free(ctx); + talloc_free(prep_mem_ctx); + return ret; +} + + +bool torture_userinfo_api(struct torture_context *torture) +{ + const char *name = TEST_USERNAME; + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL, *prep_mem_ctx; + struct libnet_context *ctx = NULL; + struct dcerpc_pipe *p; + struct policy_handle h; + struct lsa_String domain_name; + struct libnet_UserInfo req; + struct dcerpc_binding_handle *b; + + prep_mem_ctx = talloc_init("prepare torture user info"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + if (!test_domain_open(torture, b, &domain_name, prep_mem_ctx, &h, NULL)) { + ret = false; + goto done; + } + + if (!test_user_create(torture, b, prep_mem_ctx, &h, name, NULL)) { + ret = false; + goto done; + } + + mem_ctx = talloc_init("torture user info"); + + if (!test_libnet_context_init(torture, true, &ctx)) { + return false; + } + + ZERO_STRUCT(req); + + req.in.domain_name = domain_name.string; + req.in.data.user_name = name; + req.in.level = USER_INFO_BY_NAME; + + status = libnet_UserInfo(ctx, mem_ctx, &req); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "libnet_UserInfo call failed: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + if (!test_user_cleanup(torture, ctx->samr.pipe->binding_handle, + mem_ctx, &ctx->samr.handle, TEST_USERNAME)) { + torture_comment(torture, "cleanup failed\n"); + ret = false; + goto done; + } + + if (!test_samr_close_handle(torture, + ctx->samr.pipe->binding_handle, mem_ctx, &ctx->samr.handle)) { + torture_comment(torture, "domain close failed\n"); + ret = false; + } + +done: + talloc_free(ctx); + talloc_free(mem_ctx); + return ret; +} + + +bool torture_userlist(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + struct libnet_context *ctx; + struct lsa_String domain_name; + struct libnet_UserList req; + int i; + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + mem_ctx = talloc_init("torture user list"); + + ZERO_STRUCT(req); + + torture_comment(torture, "listing user accounts:\n"); + + do { + + req.in.domain_name = domain_name.string; + req.in.page_size = 128; + req.in.resume_index = req.out.resume_index; + + status = libnet_UserList(ctx, mem_ctx, &req); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) break; + + for (i = 0; i < req.out.count; i++) { + torture_comment(torture, "\tuser: %s, sid=%s\n", + req.out.users[i].username, req.out.users[i].sid); + } + + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + + if (!(NT_STATUS_IS_OK(status) || + NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES))) { + torture_comment(torture, "libnet_UserList call failed: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + if (!test_samr_close_handle(torture, + ctx->samr.pipe->binding_handle, mem_ctx, &ctx->samr.handle)) { + torture_comment(torture, "samr domain close failed\n"); + ret = false; + goto done; + } + + if (!test_lsa_close_handle(torture, + ctx->lsa.pipe->binding_handle, mem_ctx, &ctx->lsa.handle)) { + torture_comment(torture, "lsa domain close failed\n"); + ret = false; + } + + talloc_free(ctx); + +done: + talloc_free(mem_ctx); + return ret; +} diff --git a/source4/torture/libnet/python/samr-test.py b/source4/torture/libnet/python/samr-test.py new file mode 100644 index 0000000..4181e56 --- /dev/null +++ b/source4/torture/libnet/python/samr-test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Unix SMB/CIFS implementation. +# Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2009 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# +# Usage: +# export ACCOUNT_NAME=kamen +# export NEW_PASS=test +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$samba4srcdir/torture/libnet/python" $SUBUNITRUN samr-test -Ukma-exch.devel/Administrator%333 +# + +import os + +from samba import net +import samba.tests + +if "ACCOUNT_NAME" not in os.environ.keys(): + raise Exception("Please supply ACCOUNT_NAME in environment") + +if "NEW_PASS" not in os.environ.keys(): + raise Exception("Please supply NEW_PASS in environment") + +account_name = os.environ["ACCOUNT_NAME"] +new_pass = os.environ["NEW_PASS"] + +# +# Tests start here +# + + +class Libnet_SetPwdTest(samba.tests.TestCase): + + ######################################################################################## + + def test_SetPassword(self): + creds = self.get_credentials() + net.SetPassword(account_name=account_name, + domain_name=creds.get_domain(), + newpassword=new_pass, + credentials=creds) + + ######################################################################################## diff --git a/source4/torture/libnet/userinfo.c b/source4/torture/libnet/userinfo.c new file mode 100644 index 0000000..897273b --- /dev/null +++ b/source4/torture/libnet/userinfo.c @@ -0,0 +1,192 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "libnet/libnet.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "param/param.h" +#include "torture/libnet/proto.h" + + +#define TEST_USERNAME "libnetuserinfotest" + +static bool test_userinfo(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + struct dom_sid2 *domain_sid, const char* user_name, + uint32_t *rid) +{ + const uint16_t level = 5; + NTSTATUS status; + struct libnet_rpc_userinfo user; + struct dom_sid *user_sid; + + user_sid = dom_sid_add_rid(mem_ctx, domain_sid, *rid); + + ZERO_STRUCT(user); + + user.in.domain_handle = *domain_handle; + user.in.sid = dom_sid_string(mem_ctx, user_sid); + user.in.level = level; /* this should be extended */ + + torture_comment(tctx, "Testing sync libnet_rpc_userinfo (SID argument)\n"); + status = libnet_rpc_userinfo(tctx->ev, p->binding_handle, mem_ctx, &user); + torture_assert_ntstatus_ok(tctx, status, "Calling sync libnet_rpc_userinfo() failed"); + + ZERO_STRUCT(user); + + user.in.domain_handle = *domain_handle; + user.in.sid = NULL; + user.in.username = user_name; + user.in.level = level; + + torture_comment(tctx, "Testing sync libnet_rpc_userinfo (username argument)\n"); + status = libnet_rpc_userinfo(tctx->ev, p->binding_handle, mem_ctx, &user); + torture_assert_ntstatus_ok(tctx, status, "Calling sync libnet_rpc_userinfo failed"); + + return true; +} + + +static bool test_userinfo_async(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + struct dom_sid2 *domain_sid, const char* user_name, + uint32_t *rid) +{ + const uint16_t level = 10; + NTSTATUS status; + struct composite_context *c; + struct libnet_rpc_userinfo user; + struct dom_sid *user_sid; + + user_sid = dom_sid_add_rid(mem_ctx, domain_sid, *rid); + + ZERO_STRUCT(user); + + user.in.domain_handle = *domain_handle; + user.in.sid = dom_sid_string(mem_ctx, user_sid); + user.in.level = level; /* this should be extended */ + + torture_comment(tctx, "Testing async libnet_rpc_userinfo (SID argument)\n"); + + c = libnet_rpc_userinfo_send(mem_ctx, tctx->ev, p->binding_handle, &user, msg_handler); + torture_assert(tctx, c != NULL, "Failed to call async libnet_rpc_userinfo_send"); + + status = libnet_rpc_userinfo_recv(c, mem_ctx, &user); + torture_assert_ntstatus_ok(tctx, status, "Calling async libnet_rpc_userinfo_recv failed"); + + ZERO_STRUCT(user); + + user.in.domain_handle = *domain_handle; + user.in.sid = NULL; + user.in.username = user_name; + user.in.level = level; + + torture_comment(tctx, "Testing async libnet_rpc_userinfo (username argument)\n"); + + c = libnet_rpc_userinfo_send(mem_ctx, tctx->ev, p->binding_handle, &user, msg_handler); + torture_assert(tctx, c != NULL, "Failed to call async libnet_rpc_userinfo_send"); + + status = libnet_rpc_userinfo_recv(c, mem_ctx, &user); + torture_assert_ntstatus_ok(tctx, status, "Calling async libnet_rpc_userinfo_recv failed"); + + return true; +} + + +bool torture_userinfo(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct policy_handle h; + struct lsa_String name; + struct dom_sid2 sid; + uint32_t rid; + struct dcerpc_binding_handle *b; + + mem_ctx = talloc_init("test_userinfo"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + name.string = lpcfg_workgroup(torture->lp_ctx); + + /* + * Testing synchronous version + */ + if (!test_domain_open(torture, b, &name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_user_create(torture, b, mem_ctx, &h, TEST_USERNAME, &rid)) { + ret = false; + goto done; + } + + if (!test_userinfo(torture, p, mem_ctx, &h, &sid, TEST_USERNAME, &rid)) { + ret = false; + goto done; + } + + if (!test_user_cleanup(torture, b, mem_ctx, &h, TEST_USERNAME)) { + ret = false; + goto done; + } + + /* + * Testing asynchronous version and monitor messages + */ + if (!test_domain_open(torture, b, &name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_user_create(torture, b, mem_ctx, &h, TEST_USERNAME, &rid)) { + ret = false; + goto done; + } + + if (!test_userinfo_async(torture, p, mem_ctx, &h, &sid, TEST_USERNAME, &rid)) { + ret = false; + goto done; + } + + if (!test_user_cleanup(torture, b, mem_ctx, &h, TEST_USERNAME)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + + return ret; +} diff --git a/source4/torture/libnet/userman.c b/source4/torture/libnet/userman.c new file mode 100644 index 0000000..8c49bb6 --- /dev/null +++ b/source4/torture/libnet/userman.c @@ -0,0 +1,473 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/libnet/usertest.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "param/param.h" + +#include "torture/libnet/proto.h" + + +static bool test_useradd(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + const char *name) +{ + NTSTATUS status; + bool ret = true; + struct libnet_rpc_useradd user; + + user.in.domain_handle = *domain_handle; + user.in.username = name; + + torture_comment(tctx, "Testing libnet_rpc_useradd\n"); + + status = libnet_rpc_useradd(tctx->ev, p->binding_handle, mem_ctx, &user); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to call libnet_rpc_useradd - %s\n", nt_errstr(status)); + return false; + } + + return ret; +} + + +static bool test_useradd_async(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, const char* username) +{ + NTSTATUS status; + struct composite_context *c; + struct libnet_rpc_useradd user; + + user.in.domain_handle = *handle; + user.in.username = username; + + torture_comment(tctx, "Testing async libnet_rpc_useradd\n"); + + c = libnet_rpc_useradd_send(mem_ctx, tctx->ev, p->binding_handle, + &user, msg_handler); + if (!c) { + torture_comment(tctx, "Failed to call async libnet_rpc_useradd\n"); + return false; + } + + status = libnet_rpc_useradd_recv(c, mem_ctx, &user); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Calling async libnet_rpc_useradd failed - %s\n", nt_errstr(status)); + return false; + } + + return true; + +} + +static bool test_usermod(struct torture_context *tctx, struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, int num_changes, + struct libnet_rpc_usermod *mod, char **username) +{ + const char* logon_scripts[] = { "start_login.cmd", "login.bat", "start.cmd" }; + const char* home_dirs[] = { "\\\\srv\\home", "\\\\homesrv\\home\\user", "\\\\pdcsrv\\domain" }; + const char* home_drives[] = { "H:", "z:", "I:", "J:", "n:" }; + const char *homedir, *homedrive, *logonscript; + const uint32_t flags[] = { (ACB_DISABLED | ACB_NORMAL | ACB_PW_EXPIRED), + (ACB_NORMAL | ACB_PWNOEXP), + (ACB_NORMAL | ACB_PW_EXPIRED) }; + + NTSTATUS status; + struct timeval now; + enum test_fields testfld; + int i; + + ZERO_STRUCT(*mod); + srandom((unsigned)time(NULL)); + + mod->in.username = talloc_strdup(mem_ctx, *username); + mod->in.domain_handle = *handle; + + torture_comment(tctx, "modifying user (%d simultaneous change(s))\n", + num_changes); + + torture_comment(tctx, "fields to change: ["); + + for (i = 0; i < num_changes && i <= USER_FIELD_LAST; i++) { + const char *fldname; + + testfld = (random() % USER_FIELD_LAST) + 1; + + GetTimeOfDay(&now); + + switch (testfld) { + case acct_name: + continue_if_field_set(mod->in.change.account_name); + mod->in.change.account_name = talloc_asprintf(mem_ctx, TEST_CHG_ACCOUNTNAME, + (int)(random() % 100)); + mod->in.change.fields |= USERMOD_FIELD_ACCOUNT_NAME; + fldname = "account_name"; + *username = talloc_strdup(mem_ctx, mod->in.change.account_name); + break; + + case acct_full_name: + continue_if_field_set(mod->in.change.full_name); + mod->in.change.full_name = talloc_asprintf(mem_ctx, TEST_CHG_FULLNAME, + (int)random(), (int)random()); + mod->in.change.fields |= USERMOD_FIELD_FULL_NAME; + fldname = "full_name"; + break; + + case acct_description: + continue_if_field_set(mod->in.change.description); + mod->in.change.description = talloc_asprintf(mem_ctx, TEST_CHG_DESCRIPTION, + random()); + mod->in.change.fields |= USERMOD_FIELD_DESCRIPTION; + fldname = "description"; + break; + + case acct_home_directory: + continue_if_field_set(mod->in.change.home_directory); + homedir = home_dirs[random() % (sizeof(home_dirs)/sizeof(char*))]; + mod->in.change.home_directory = talloc_strdup(mem_ctx, homedir); + mod->in.change.fields |= USERMOD_FIELD_HOME_DIRECTORY; + fldname = "home_directory"; + break; + + case acct_home_drive: + continue_if_field_set(mod->in.change.home_drive); + homedrive = home_drives[random() % (sizeof(home_drives)/sizeof(char*))]; + mod->in.change.home_drive = talloc_strdup(mem_ctx, homedrive); + mod->in.change.fields |= USERMOD_FIELD_HOME_DRIVE; + fldname = "home_drive"; + break; + + case acct_comment: + continue_if_field_set(mod->in.change.comment); + mod->in.change.comment = talloc_asprintf(mem_ctx, TEST_CHG_COMMENT, + random(), random()); + mod->in.change.fields |= USERMOD_FIELD_COMMENT; + fldname = "comment"; + break; + + case acct_logon_script: + continue_if_field_set(mod->in.change.logon_script); + logonscript = logon_scripts[random() % (sizeof(logon_scripts)/sizeof(char*))]; + mod->in.change.logon_script = talloc_strdup(mem_ctx, logonscript); + mod->in.change.fields |= USERMOD_FIELD_LOGON_SCRIPT; + fldname = "logon_script"; + break; + + case acct_profile_path: + continue_if_field_set(mod->in.change.profile_path); + mod->in.change.profile_path = talloc_asprintf(mem_ctx, TEST_CHG_PROFILEPATH, + (long int)random(), (unsigned int)random()); + mod->in.change.fields |= USERMOD_FIELD_PROFILE_PATH; + fldname = "profile_path"; + break; + + case acct_expiry: + continue_if_field_set(mod->in.change.acct_expiry); + now = timeval_add(&now, (random() % (31*24*60*60)), 0); + mod->in.change.acct_expiry = (struct timeval *)talloc_memdup(mem_ctx, &now, sizeof(now)); + mod->in.change.fields |= USERMOD_FIELD_ACCT_EXPIRY; + fldname = "acct_expiry"; + break; + + case acct_flags: + continue_if_field_set(mod->in.change.acct_flags); + mod->in.change.acct_flags = flags[random() % ARRAY_SIZE(flags)]; + mod->in.change.fields |= USERMOD_FIELD_ACCT_FLAGS; + fldname = "acct_flags"; + break; + + default: + fldname = talloc_asprintf(mem_ctx, "unknown_field (%d)", testfld); + break; + } + + torture_comment(tctx, ((i < num_changes - 1) ? "%s," : "%s"), fldname); + } + torture_comment(tctx, "]\n"); + + status = libnet_rpc_usermod(tctx->ev, p->binding_handle, mem_ctx, mod); + torture_assert_ntstatus_ok(tctx, status, "Failed to call sync libnet_rpc_usermod"); + + return true; +} + + +static bool test_userdel(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, const char *username) +{ + NTSTATUS status; + struct libnet_rpc_userdel user; + + ZERO_STRUCT(user); + + user.in.domain_handle = *handle; + user.in.username = username; + + status = libnet_rpc_userdel(tctx->ev, p->binding_handle, mem_ctx, &user); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to call sync libnet_rpc_userdel - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + + +#define CMP_LSA_STRING_FLD(fld, flags) \ + if ((mod->in.change.fields & flags) && \ + !strequal(i->fld.string, mod->in.change.fld)) { \ + torture_comment(tctx, "'%s' field does not match\n", #fld); \ + torture_comment(tctx, "received: '%s'\n", i->fld.string); \ + torture_comment(tctx, "expected: '%s'\n", mod->in.change.fld); \ + return false; \ + } + + +#define CMP_TIME_FLD(fld, flags) \ + if (mod->in.change.fields & flags) { \ + nttime_to_timeval(&t, i->fld); \ + if (timeval_compare(&t, mod->in.change.fld)) { \ + torture_comment(tctx, "'%s' field does not match\n", #fld); \ + torture_comment(tctx, "received: '%s (+%ld us)'\n", \ + timestring(mem_ctx, t.tv_sec), (long)t.tv_usec); \ + torture_comment(tctx, "expected: '%s (+%ld us)'\n", \ + timestring(mem_ctx, mod->in.change.fld->tv_sec), \ + (long)mod->in.change.fld->tv_usec); \ + return false; \ + } \ + } + +#define CMP_NUM_FLD(fld, flags) \ + if ((mod->in.change.fields & flags) && \ + (i->fld != mod->in.change.fld)) { \ + torture_comment(tctx, "'%s' field does not match\n", #fld); \ + torture_comment(tctx, "received: '%04x'\n", i->fld); \ + torture_comment(tctx, "expected: '%04x'\n", mod->in.change.fld); \ + return false; \ + } + + +static bool test_compare(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, struct libnet_rpc_usermod *mod, + const char *username) +{ + NTSTATUS status; + struct libnet_rpc_userinfo info; + struct samr_UserInfo21 *i; + struct timeval t; + + ZERO_STRUCT(info); + + info.in.username = username; + info.in.domain_handle = *handle; + info.in.level = 21; /* the most rich infolevel available */ + + status = libnet_rpc_userinfo(tctx->ev, p->binding_handle, mem_ctx, &info); + torture_assert_ntstatus_ok(tctx, status, "Failed to call sync libnet_rpc_userinfo"); + + i = &info.out.info.info21; + + CMP_LSA_STRING_FLD(account_name, USERMOD_FIELD_ACCOUNT_NAME); + CMP_LSA_STRING_FLD(full_name, USERMOD_FIELD_FULL_NAME); + CMP_LSA_STRING_FLD(description, USERMOD_FIELD_DESCRIPTION); + CMP_LSA_STRING_FLD(comment, USERMOD_FIELD_COMMENT); + CMP_LSA_STRING_FLD(logon_script, USERMOD_FIELD_LOGON_SCRIPT); + CMP_LSA_STRING_FLD(profile_path, USERMOD_FIELD_PROFILE_PATH); + CMP_LSA_STRING_FLD(home_directory, USERMOD_FIELD_HOME_DIRECTORY); + CMP_LSA_STRING_FLD(home_drive, USERMOD_FIELD_HOME_DRIVE); + CMP_TIME_FLD(acct_expiry, USERMOD_FIELD_ACCT_EXPIRY); + CMP_NUM_FLD(acct_flags, USERMOD_FIELD_ACCT_FLAGS) + + return true; +} + + +bool torture_useradd(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + struct policy_handle h; + struct lsa_String domain_name; + struct dom_sid2 sid; + const char *name = TEST_USERNAME; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct dcerpc_binding_handle *b; + + mem_ctx = talloc_init("test_useradd"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + + torture_assert_ntstatus_ok(torture, status, "RPC connect failed"); + b = p->binding_handle; + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + if (!test_domain_open(torture, b, &domain_name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_useradd(torture, p, mem_ctx, &h, name)) { + ret = false; + goto done; + } + + if (!test_user_cleanup(torture, b, mem_ctx, &h, name)) { + ret = false; + goto done; + } + + if (!test_domain_open(torture, b, &domain_name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_useradd_async(torture, p, mem_ctx, &h, name)) { + ret = false; + goto done; + } + + if (!test_user_cleanup(torture, b, mem_ctx, &h, name)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + return ret; +} + + +bool torture_userdel(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + struct policy_handle h; + struct lsa_String domain_name; + struct dom_sid2 sid; + uint32_t rid; + const char *name = TEST_USERNAME; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct dcerpc_binding_handle *b; + + mem_ctx = talloc_init("test_userdel"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + if (!test_domain_open(torture, b, &domain_name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_user_create(torture, b, mem_ctx, &h, name, &rid)) { + ret = false; + goto done; + } + + if (!test_userdel(torture, p, mem_ctx, &h, name)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + return ret; +} + + +bool torture_usermod(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + struct policy_handle h; + struct lsa_String domain_name; + struct dom_sid2 sid; + uint32_t rid; + int i; + char *name; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct dcerpc_binding_handle *b; + + mem_ctx = talloc_init("test_userdel"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + + torture_assert_ntstatus_ok(torture, status, "RPC connect"); + b = p->binding_handle; + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + name = talloc_strdup(mem_ctx, TEST_USERNAME); + + if (!test_domain_open(torture, b, &domain_name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_user_create(torture, b, mem_ctx, &h, name, &rid)) { + ret = false; + goto done; + } + + for (i = USER_FIELD_FIRST; i <= USER_FIELD_LAST; i++) { + struct libnet_rpc_usermod m; + + if (!test_usermod(torture, p, mem_ctx, &h, i, &m, &name)) { + ret = false; + goto cleanup; + } + + if (!test_compare(torture, p, mem_ctx, &h, &m, name)) { + ret = false; + goto cleanup; + } + } + +cleanup: + if (!test_user_cleanup(torture, b, mem_ctx, &h, TEST_USERNAME)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + return ret; +} diff --git a/source4/torture/libnet/usertest.h b/source4/torture/libnet/usertest.h new file mode 100644 index 0000000..b3b2dc1 --- /dev/null +++ b/source4/torture/libnet/usertest.h @@ -0,0 +1,42 @@ +/* + Unix SMB/CIFS implementation. + + 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/>. +*/ + +#define TEST_USERNAME "libnetusertest" + +#define continue_if_field_set(field) \ + if (field != 0) { \ + i--; \ + continue; \ + } + + +#define USER_FIELD_FIRST acct_name +#define USER_FIELD_LAST acct_flags + +enum test_fields { none = 0, + acct_name, acct_full_name, acct_description, + acct_home_directory, acct_home_drive, acct_comment, + acct_logon_script, acct_profile_path, acct_expiry, acct_flags }; + + +#define TEST_CHG_ACCOUNTNAME "newlibnetusertest%02d" +#define TEST_CHG_DESCRIPTION "Sample description %ld" +#define TEST_CHG_FULLNAME "First%04x Last%04x" +#define TEST_CHG_COMMENT "Comment[%04lu%04lu]" +#define TEST_CHG_PROFILEPATH "\\\\srv%04ld\\profile%02u\\prof" diff --git a/source4/torture/libnet/utils.c b/source4/torture/libnet/utils.c new file mode 100644 index 0000000..c0e0e65 --- /dev/null +++ b/source4/torture/libnet/utils.c @@ -0,0 +1,556 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 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/>. +*/ + +/* + * These are more general use functions shared among the tests. + */ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "torture/rpc/torture_rpc.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "torture/libnet/proto.h" +#include "ldb_wrap.h" + +/** + * Opens handle on Domain using SAMR + * + * @param _domain_handle [out] Ptr to storage to store Domain handle + * @param _dom_sid [out] If NULL, Domain SID won't be returned + */ +bool test_domain_open(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct lsa_String *domname, + TALLOC_CTX *mem_ctx, + struct policy_handle *_domain_handle, + struct dom_sid2 *_dom_sid) +{ + struct policy_handle connect_handle; + struct policy_handle domain_handle; + struct samr_Connect r1; + struct samr_LookupDomain r2; + struct dom_sid2 *sid = NULL; + struct samr_OpenDomain r3; + + torture_comment(tctx, "connecting\n"); + + r1.in.system_name = 0; + r1.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r1.out.connect_handle = &connect_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_Connect_r(b, mem_ctx, &r1), + "Connect failed"); + torture_assert_ntstatus_ok(tctx, r1.out.result, + "Connect failed"); + + r2.in.connect_handle = &connect_handle; + r2.in.domain_name = domname; + r2.out.sid = &sid; + + torture_comment(tctx, "domain lookup on %s\n", domname->string); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_LookupDomain_r(b, mem_ctx, &r2), + "LookupDomain failed"); + torture_assert_ntstatus_ok(tctx, r2.out.result, + "LookupDomain failed"); + + r3.in.connect_handle = &connect_handle; + r3.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r3.in.sid = *r2.out.sid; + r3.out.domain_handle = &domain_handle; + + torture_comment(tctx, "opening domain %s\n", domname->string); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenDomain_r(b, mem_ctx, &r3), + "OpenDomain failed"); + torture_assert_ntstatus_ok(tctx, r3.out.result, + "OpenDomain failed"); + + *_domain_handle = domain_handle; + + if (_dom_sid) { + *_dom_sid = **r2.out.sid; + } + + /* Close connect_handle, we don't need it anymore */ + test_samr_close_handle(tctx, b, mem_ctx, &connect_handle); + + return true; +} + + +/** + * Find out user's samAccountName for given + * user RDN. We need samAccountName value + * when deleting users. + */ +static bool _get_account_name_for_user_rdn(struct torture_context *tctx, + const char *user_rdn, + TALLOC_CTX *mem_ctx, + const char **_account_name) +{ + const char *url; + struct ldb_context *ldb; + TALLOC_CTX *tmp_ctx; + bool test_res = true; + const char *hostname = torture_setting_string(tctx, "host", NULL); + int ldb_ret; + struct ldb_result *ldb_res; + const char *account_name = NULL; + static const char *attrs[] = { + "samAccountName", + NULL + }; + + torture_assert(tctx, hostname != NULL, "Failed to get hostname"); + + tmp_ctx = talloc_new(tctx); + torture_assert(tctx, tmp_ctx != NULL, "Failed to create temporary mem context"); + + url = talloc_asprintf(tmp_ctx, "ldap://%s/", hostname); + torture_assert_goto(tctx, url != NULL, test_res, done, "Failed to allocate URL for ldb"); + + ldb = ldb_wrap_connect(tmp_ctx, + tctx->ev, tctx->lp_ctx, + url, NULL, samba_cmdline_get_creds(), 0); + torture_assert_goto(tctx, ldb != NULL, test_res, done, "Failed to make LDB connection"); + + ldb_ret = ldb_search(ldb, tmp_ctx, &ldb_res, + ldb_get_default_basedn(ldb), LDB_SCOPE_SUBTREE, + attrs, + "(&(objectClass=user)(name=%s))", user_rdn); + if (LDB_SUCCESS == ldb_ret && 1 == ldb_res->count) { + account_name = ldb_msg_find_attr_as_string(ldb_res->msgs[0], "samAccountName", NULL); + } + + /* return user_rdn by default */ + if (!account_name) { + account_name = user_rdn; + } + + /* duplicate memory in parent context */ + *_account_name = talloc_strdup(mem_ctx, account_name); + +done: + talloc_free(tmp_ctx); + return test_res; +} + +/** + * Removes user by RDN through SAMR interface. + * + * @param domain_handle [in] Domain handle + * @param user_rdn [in] User's RDN in ldap database + */ +bool test_user_cleanup(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + const char *user_rdn) +{ + struct samr_LookupNames r1; + struct samr_OpenUser r2; + struct samr_DeleteUser r3; + struct lsa_String names[2]; + uint32_t rid; + struct policy_handle user_handle; + struct samr_Ids rids, types; + const char *account_name; + + if (!_get_account_name_for_user_rdn(tctx, user_rdn, mem_ctx, &account_name)) { + torture_result(tctx, TORTURE_FAIL, + __location__": Failed to find samAccountName for %s", user_rdn); + return false; + } + + names[0].string = account_name; + + r1.in.domain_handle = domain_handle; + r1.in.num_names = 1; + r1.in.names = names; + r1.out.rids = &rids; + r1.out.types = &types; + + torture_comment(tctx, "user account lookup '%s'\n", account_name); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_LookupNames_r(b, mem_ctx, &r1), + "LookupNames failed"); + torture_assert_ntstatus_ok(tctx, r1.out.result, + "LookupNames failed"); + + rid = r1.out.rids->ids[0]; + + r2.in.domain_handle = domain_handle; + r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r2.in.rid = rid; + r2.out.user_handle = &user_handle; + + torture_comment(tctx, "opening user account\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenUser_r(b, mem_ctx, &r2), + "OpenUser failed"); + torture_assert_ntstatus_ok(tctx, r2.out.result, + "OpenUser failed"); + + r3.in.user_handle = &user_handle; + r3.out.user_handle = &user_handle; + + torture_comment(tctx, "deleting user account\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_DeleteUser_r(b, mem_ctx, &r3), + "DeleteUser failed"); + torture_assert_ntstatus_ok(tctx, r3.out.result, + "DeleteUser failed"); + + return true; +} + + +/** + * Creates new user using SAMR + * + * @param name [in] Username for user to create + * @param rid [out] If NULL, User's RID is not returned + */ +bool test_user_create(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + const char *name, + uint32_t *rid) +{ + struct policy_handle user_handle; + struct lsa_String username; + struct samr_CreateUser r; + uint32_t user_rid; + + username.string = name; + + r.in.domain_handle = domain_handle; + r.in.account_name = &username; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.user_handle = &user_handle; + /* return user's RID only if requested */ + r.out.rid = rid ? rid : &user_rid; + + torture_comment(tctx, "creating user '%s'\n", username.string); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_CreateUser_r(b, mem_ctx, &r), + "CreateUser RPC call failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "CreateUser failed - %s\n", nt_errstr(r.out.result)); + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_USER_EXISTS)) { + torture_comment(tctx, + "User (%s) already exists - " + "attempting to delete and recreate account again\n", + username.string); + if (!test_user_cleanup(tctx, b, mem_ctx, domain_handle, username.string)) { + return false; + } + + torture_comment(tctx, "creating user account\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_CreateUser_r(b, mem_ctx, &r), + "CreateUser RPC call failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "CreateUser failed"); + + /* be nice and close opened handles */ + test_samr_close_handle(tctx, b, mem_ctx, &user_handle); + + return true; + } + return false; + } + + /* be nice and close opened handles */ + test_samr_close_handle(tctx, b, mem_ctx, &user_handle); + + return true; +} + + +/** + * Deletes a Group using SAMR interface + */ +bool test_group_cleanup(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + const char *name) +{ + struct samr_LookupNames r1; + struct samr_OpenGroup r2; + struct samr_DeleteDomainGroup r3; + struct lsa_String names[2]; + uint32_t rid; + struct policy_handle group_handle; + struct samr_Ids rids, types; + + names[0].string = name; + + r1.in.domain_handle = domain_handle; + r1.in.num_names = 1; + r1.in.names = names; + r1.out.rids = &rids; + r1.out.types = &types; + + torture_comment(tctx, "group account lookup '%s'\n", name); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_LookupNames_r(b, mem_ctx, &r1), + "LookupNames failed"); + torture_assert_ntstatus_ok(tctx, r1.out.result, + "LookupNames failed"); + + rid = r1.out.rids->ids[0]; + + r2.in.domain_handle = domain_handle; + r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r2.in.rid = rid; + r2.out.group_handle = &group_handle; + + torture_comment(tctx, "opening group account\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenGroup_r(b, mem_ctx, &r2), + "OpenGroup failed"); + torture_assert_ntstatus_ok(tctx, r2.out.result, + "OpenGroup failed"); + + r3.in.group_handle = &group_handle; + r3.out.group_handle = &group_handle; + + torture_comment(tctx, "deleting group account\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_DeleteDomainGroup_r(b, mem_ctx, &r3), + "DeleteGroup failed"); + torture_assert_ntstatus_ok(tctx, r3.out.result, + "DeleteGroup failed"); + + return true; +} + + +/** + * Creates a Group object using SAMR interface + * + * @param group_name [in] Name of the group to create + * @param rid [out] RID of group created. May be NULL in + * which case RID is not required by caller + */ +bool test_group_create(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + const char *group_name, + uint32_t *rid) +{ + uint32_t group_rid; + struct lsa_String groupname; + struct samr_CreateDomainGroup r; + struct policy_handle group_handle; + + groupname.string = group_name; + + r.in.domain_handle = handle; + r.in.name = &groupname; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.group_handle = &group_handle; + /* use local variable in case caller + * don't care about the group RID */ + r.out.rid = rid ? rid : &group_rid; + + torture_comment(tctx, "creating group account %s\n", group_name); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_CreateDomainGroup_r(b, mem_ctx, &r), + "CreateGroup failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "CreateGroup failed - %s\n", nt_errstr(r.out.result)); + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_GROUP_EXISTS)) { + torture_comment(tctx, + "Group (%s) already exists - " + "attempting to delete and recreate group again\n", + group_name); + if (!test_group_cleanup(tctx, b, mem_ctx, handle, group_name)) { + return false; + } + + torture_comment(tctx, "creating group account\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_CreateDomainGroup_r(b, mem_ctx, &r), + "CreateGroup failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "CreateGroup failed"); + + /* be nice and close opened handles */ + test_samr_close_handle(tctx, b, mem_ctx, &group_handle); + + return true; + } + return false; + } + + /* be nice and close opened handles */ + test_samr_close_handle(tctx, b, mem_ctx, &group_handle); + + return true; +} + +/** + * Closes SAMR handle obtained from Connect, Open User/Domain, etc + */ +bool test_samr_close_handle(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + struct policy_handle *samr_handle) +{ + struct samr_Close r; + + r.in.handle = samr_handle; + r.out.handle = samr_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_Close_r(b, mem_ctx, &r), + "Close SAMR handle RPC call failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Close SAMR handle failed"); + + return true; +} + +/** + * Closes LSA handle obtained from Connect, Open Group, etc + */ +bool test_lsa_close_handle(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + struct policy_handle *lsa_handle) +{ + struct lsa_Close r; + + r.in.handle = lsa_handle; + r.out.handle = lsa_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_Close_r(b, mem_ctx, &r), + "Close LSA handle RPC call failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Close LSA handle failed"); + + return true; +} + +/** + * Create and initialize libnet_context Context. + * Use this function in cases where we need to have SAMR and LSA pipes + * of libnet_context to be connected before executing any other + * libnet call + * + * @param rpc_connect [in] Connects SAMR and LSA pipes + */ +bool test_libnet_context_init(struct torture_context *tctx, + bool rpc_connect, + struct libnet_context **_net_ctx) +{ + NTSTATUS status; + bool bret = true; + struct libnet_context *net_ctx; + + net_ctx = libnet_context_init(tctx->ev, tctx->lp_ctx); + torture_assert(tctx, net_ctx != NULL, "Failed to create libnet_context"); + + /* Use command line credentials for testing */ + net_ctx->cred = samba_cmdline_get_creds(); + + if (rpc_connect) { + /* connect SAMR pipe */ + status = torture_rpc_connection(tctx, + &net_ctx->samr.pipe, + &ndr_table_samr); + torture_assert_ntstatus_ok_goto(tctx, status, bret, done, + "Failed to connect SAMR pipe"); + + net_ctx->samr.samr_handle = net_ctx->samr.pipe->binding_handle; + + /* connect LSARPC pipe */ + status = torture_rpc_connection(tctx, + &net_ctx->lsa.pipe, + &ndr_table_lsarpc); + torture_assert_ntstatus_ok_goto(tctx, status, bret, done, + "Failed to connect LSA pipe"); + + net_ctx->lsa.lsa_handle = net_ctx->lsa.pipe->binding_handle; + } + + *_net_ctx = net_ctx; + +done: + if (!bret) { + /* a previous call has failed, + * clean up memory before exit */ + talloc_free(net_ctx); + } + return bret; +} + + +void msg_handler(struct monitor_msg *m) +{ + struct msg_rpc_open_user *msg_open; + struct msg_rpc_query_user *msg_query; + struct msg_rpc_close_user *msg_close; + struct msg_rpc_create_user *msg_create; + + switch (m->type) { + case mon_SamrOpenUser: + msg_open = (struct msg_rpc_open_user*)m->data; + printf("monitor_msg: user opened (rid=%d, access_mask=0x%08x)\n", + msg_open->rid, msg_open->access_mask); + break; + case mon_SamrQueryUser: + msg_query = (struct msg_rpc_query_user*)m->data; + printf("monitor_msg: user queried (level=%d)\n", msg_query->level); + break; + case mon_SamrCloseUser: + msg_close = (struct msg_rpc_close_user*)m->data; + printf("monitor_msg: user closed (rid=%d)\n", msg_close->rid); + break; + case mon_SamrCreateUser: + msg_create = (struct msg_rpc_create_user*)m->data; + printf("monitor_msg: user created (rid=%d)\n", msg_create->rid); + break; + } +} diff --git a/source4/torture/libnetapi/libnetapi.c b/source4/torture/libnetapi/libnetapi.c new file mode 100644 index 0000000..320ca54 --- /dev/null +++ b/source4/torture/libnetapi/libnetapi.c @@ -0,0 +1,98 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Guenther Deschner 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "source3/include/includes.h" +#include "torture/smbtorture.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/cmdline.h" +#include "source3/lib/netapi/netapi.h" +#include "source3/lib/netapi/netapi_private.h" +#include "lib/param/param.h" +#include "torture/libnetapi/proto.h" + +bool torture_libnetapi_init_context(struct torture_context *tctx, + struct libnetapi_ctx **ctx_p) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!lp_load_global(lpcfg_configfile(tctx->lp_ctx))) { + fprintf(stderr, "error loading %s\n", lpcfg_configfile(tctx->lp_ctx)); + talloc_free(frame); + return W_ERROR_V(WERR_GEN_FAILURE); + } + + load_interfaces(); + + status = libnetapi_net_init(&ctx); + if (status != 0) { + talloc_free(frame); + return false; + } + + libnetapi_set_username(ctx, + cli_credentials_get_username(samba_cmdline_get_creds())); + libnetapi_set_password(ctx, + cli_credentials_get_password(samba_cmdline_get_creds())); + + *ctx_p = ctx; + + talloc_free(frame); + return true; +} + +static bool torture_libnetapi_initialize(struct torture_context *tctx) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx; + + /* We must do this first, as otherwise we fail if we don't + * have an smb.conf in the default path (we need to use the + * torture smb.conf */ + torture_assert(tctx, torture_libnetapi_init_context(tctx, &ctx), + "failed to initialize libnetapi"); + + status = libnetapi_init(&ctx); + + torture_assert(tctx, ctx != NULL, "Failed to get a libnetapi_ctx"); + torture_assert_int_equal(tctx, status, 0, "libnetapi_init failed despite alredy being set up"); + + libnetapi_free(ctx); + + return true; +} + +NTSTATUS torture_libnetapi_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite; + + suite = torture_suite_create(ctx, "netapi"); + + torture_suite_add_simple_test(suite, "server", torture_libnetapi_server); + torture_suite_add_simple_test(suite, "group", torture_libnetapi_group); + torture_suite_add_simple_test(suite, "user", torture_libnetapi_user); + torture_suite_add_simple_test(suite, "initialize", torture_libnetapi_initialize); + + suite->description = talloc_strdup(suite, "libnetapi convenience interface tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/libnetapi/libnetapi_group.c b/source4/torture/libnetapi/libnetapi_group.c new file mode 100644 index 0000000..f4b446d --- /dev/null +++ b/source4/torture/libnetapi/libnetapi_group.c @@ -0,0 +1,522 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Guenther Deschner 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include <netapi.h> +#include "torture/libnetapi/proto.h" + +#undef strcasecmp + +#define TORTURE_TEST_USER "testuser" + +#define NETAPI_STATUS(tctx, x,y,fn) \ + torture_warning(tctx, "FAILURE: line %d: %s failed with status: %s (%d)\n", \ + __LINE__, fn, libnetapi_get_error_string(x,y), y); + +#define NETAPI_STATUS_MSG(tctx, x,y,fn,z) \ + torture_warning(tctx, "FAILURE: line %d: %s failed with status: %s (%d), %s\n", \ + __LINE__, fn, libnetapi_get_error_string(x,y), y, z); + +static NET_API_STATUS test_netgroupenum(struct torture_context *tctx, + const char *hostname, + uint32_t level, + const char *groupname) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + int found_group = 0; + const char *current_name = NULL; + uint8_t *buffer = NULL; + int i; + + struct GROUP_INFO_0 *info0 = NULL; + struct GROUP_INFO_1 *info1 = NULL; + struct GROUP_INFO_2 *info2 = NULL; + struct GROUP_INFO_3 *info3 = NULL; + + torture_comment(tctx, "Testing NetGroupEnum level %d\n", level); + + do { + status = NetGroupEnum(hostname, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + switch (level) { + case 0: + info0 = (struct GROUP_INFO_0 *)buffer; + break; + case 1: + info1 = (struct GROUP_INFO_1 *)buffer; + break; + case 2: + info2 = (struct GROUP_INFO_2 *)buffer; + break; + case 3: + info3 = (struct GROUP_INFO_3 *)buffer; + break; + default: + return -1; + } + + for (i=0; i<entries_read; i++) { + + switch (level) { + case 0: + current_name = info0->grpi0_name; + break; + case 1: + current_name = info1->grpi1_name; + break; + case 2: + current_name = info2->grpi2_name; + break; + case 3: + current_name = info3->grpi3_name; + break; + default: + break; + } + + if (strcasecmp(current_name, groupname) == 0) { + found_group = 1; + } + + switch (level) { + case 0: + info0++; + break; + case 1: + info1++; + break; + case 2: + info2++; + break; + case 3: + info3++; + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (!found_group) { + torture_comment(tctx, "failed to get group\n"); + return -1; + } + + return 0; +} + +static NET_API_STATUS test_netgroupgetusers(struct torture_context *tctx, + const char *hostname, + uint32_t level, + const char *groupname, + const char *username) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + int found_user = 0; + const char *current_name = NULL; + uint8_t *buffer = NULL; + int i; + + struct GROUP_USERS_INFO_0 *info0 = NULL; + struct GROUP_USERS_INFO_1 *info1 = NULL; + + torture_comment(tctx, "Testing NetGroupGetUsers level %d\n", level); + + do { + status = NetGroupGetUsers(hostname, + groupname, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + + switch (level) { + case 0: + info0 = (struct GROUP_USERS_INFO_0 *)buffer; + break; + case 1: + info1 = (struct GROUP_USERS_INFO_1 *)buffer; + break; + default: + break; + } + for (i=0; i<entries_read; i++) { + switch (level) { + case 0: + current_name = info0->grui0_name; + break; + case 1: + current_name = info1->grui1_name; + break; + default: + break; + } + + if (username && strcasecmp(current_name, username) == 0) { + found_user = 1; + } + + switch (level) { + case 0: + info0++; + break; + case 1: + info1++; + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (username && !found_user) { + torture_comment(tctx, "failed to get user\n"); + return -1; + } + + return 0; +} + +static NET_API_STATUS test_netgroupsetusers(struct torture_context *tctx, + const char *hostname, + const char *groupname, + uint32_t level, + size_t num_entries, + const char **names) +{ + NET_API_STATUS status; + uint8_t *buffer = NULL; + int i = 0; + size_t buf_size = 0; + + struct GROUP_USERS_INFO_0 *g0 = NULL; + struct GROUP_USERS_INFO_1 *g1 = NULL; + + torture_comment(tctx, "Testing NetGroupSetUsers level %d\n", level); + + switch (level) { + case 0: + buf_size = sizeof(struct GROUP_USERS_INFO_0) * num_entries; + + status = NetApiBufferAllocate(buf_size, (void **)&g0); + if (status) { + goto out; + } + + for (i=0; i<num_entries; i++) { + g0[i].grui0_name = names[i]; + } + + buffer = (uint8_t *)g0; + break; + case 1: + buf_size = sizeof(struct GROUP_USERS_INFO_1) * num_entries; + + status = NetApiBufferAllocate(buf_size, (void **)&g1); + if (status) { + goto out; + } + + for (i=0; i<num_entries; i++) { + g1[i].grui1_name = names[i]; + } + + buffer = (uint8_t *)g1; + break; + default: + break; + } + + /* NetGroupSetUsers */ + + status = NetGroupSetUsers(hostname, + groupname, + level, + buffer, + num_entries); + if (status) { + goto out; + } + + out: + NetApiBufferFree(buffer); + return status; +} + +bool torture_libnetapi_group(struct torture_context *tctx) +{ + NET_API_STATUS status = 0; + const char *username, *groupname, *groupname2; + uint8_t *buffer = NULL; + struct GROUP_INFO_0 g0; + uint32_t parm_err = 0; + uint32_t levels[] = { 0, 1, 2, 3}; + uint32_t enum_levels[] = { 0, 1, 2, 3}; + uint32_t getmem_levels[] = { 0, 1}; + int i; + const char *hostname = torture_setting_string(tctx, "host", NULL); + struct libnetapi_ctx *ctx; + + torture_assert(tctx, torture_libnetapi_init_context(tctx, &ctx), + "failed to initialize libnetapi"); + + torture_comment(tctx, "NetGroup tests\n"); + + username = "torture_test_user"; + groupname = "torture_test_group"; + groupname2 = "torture_test_group2"; + + /* cleanup */ + NetGroupDel(hostname, groupname); + NetGroupDel(hostname, groupname2); + NetUserDel(hostname, username); + + /* add a group */ + + g0.grpi0_name = groupname; + + torture_comment(tctx, "Testing NetGroupAdd\n"); + + status = NetGroupAdd(hostname, 0, (uint8_t *)&g0, &parm_err); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupAdd"); + goto out; + } + + /* 2nd add must fail */ + + status = NetGroupAdd(hostname, 0, (uint8_t *)&g0, &parm_err); + if (status == 0) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupAdd"); + status = -1; + goto out; + } + + /* test enum */ + + for (i=0; i<ARRAY_SIZE(enum_levels); i++) { + + status = test_netgroupenum(tctx, hostname, enum_levels[i], groupname); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupEnum"); + goto out; + } + } + + /* basic queries */ + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + torture_comment(tctx, "Testing NetGroupGetInfo level %d\n", levels[i]); + + status = NetGroupGetInfo(hostname, groupname, levels[i], &buffer); + if (status && status != 124) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupGetInfo"); + goto out; + } + } + + /* group rename */ + + g0.grpi0_name = groupname2; + + torture_comment(tctx, "Testing NetGroupSetInfo level 0\n"); + + status = NetGroupSetInfo(hostname, groupname, 0, (uint8_t *)&g0, &parm_err); + switch ((uint32_t)status) { + case 0: + break; + case 50: /* not supported */ + case 124: /* not implemented */ + groupname2 = groupname; + goto skip_rename; + default: + NETAPI_STATUS(tctx, ctx, status, "NetGroupSetInfo"); + goto out; + } + + /* should not exist anymore */ + + status = NetGroupDel(hostname, groupname); + if (status == 0) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupDel"); + goto out; + } + + skip_rename: + /* query info */ + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + status = NetGroupGetInfo(hostname, groupname2, levels[i], &buffer); + if (status && status != 124) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupGetInfo"); + goto out; + } + } + + /* add user to group */ + + status = test_netuseradd(tctx, hostname, username); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetUserAdd"); + goto out; + } + + /* should not be member */ + + for (i=0; i<ARRAY_SIZE(getmem_levels); i++) { + + status = test_netgroupgetusers(tctx, hostname, getmem_levels[i], groupname2, NULL); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupGetUsers"); + goto out; + } + } + + torture_comment(tctx, "Testing NetGroupAddUser\n"); + + status = NetGroupAddUser(hostname, groupname2, username); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupAddUser"); + goto out; + } + + /* should be member */ + + for (i=0; i<ARRAY_SIZE(getmem_levels); i++) { + + status = test_netgroupgetusers(tctx, hostname, getmem_levels[i], groupname2, username); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupGetUsers"); + goto out; + } + } + + torture_comment(tctx, "Testing NetGroupDelUser\n"); + + status = NetGroupDelUser(hostname, groupname2, username); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupDelUser"); + goto out; + } + + /* should not be member */ + + status = test_netgroupgetusers(tctx, hostname, 0, groupname2, NULL); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupGetUsers"); + goto out; + } + + /* set it again via exlicit member set */ + + status = test_netgroupsetusers(tctx, hostname, groupname2, 0, 1, &username); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupSetUsers"); + goto out; + } + + /* should be member */ + + status = test_netgroupgetusers(tctx, hostname, 0, groupname2, username); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupGetUsers"); + goto out; + } +#if 0 + /* wipe out member list */ + + status = test_netgroupsetusers(hostname, groupname2, 0, 0, NULL); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupSetUsers"); + goto out; + } + + /* should not be member */ + + status = test_netgroupgetusers(hostname, 0, groupname2, NULL); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupGetUsers"); + goto out; + } +#endif + status = NetUserDel(hostname, username); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetUserDel"); + goto out; + } + + /* delete */ + + torture_comment(tctx, "Testing NetGroupDel\n"); + + status = NetGroupDel(hostname, groupname2); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetGroupDel"); + goto out; + }; + + /* should not exist anymore */ + + status = NetGroupGetInfo(hostname, groupname2, 0, &buffer); + if (status == 0) { + NETAPI_STATUS_MSG(tctx, ctx, status, "NetGroupGetInfo", "expected failure and error code"); + status = -1; + goto out; + }; + + status = 0; + + torture_comment(tctx, "NetGroup tests succeeded\n"); + out: + if (status != 0) { + torture_comment(tctx, "NetGroup testsuite failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + libnetapi_free(ctx); + return false; + } + + libnetapi_free(ctx); + return true; +} diff --git a/source4/torture/libnetapi/libnetapi_server.c b/source4/torture/libnetapi/libnetapi_server.c new file mode 100644 index 0000000..1888009 --- /dev/null +++ b/source4/torture/libnetapi/libnetapi_server.c @@ -0,0 +1,76 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Guenther Deschner 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include <netapi.h> +#include "torture/libnetapi/proto.h" + +#define NETAPI_STATUS(tctx, x,y,fn) \ + torture_warning(tctx, "FAILURE: line %d: %s failed with status: %s (%d)\n", \ + __LINE__, fn, libnetapi_get_error_string(x,y), y); + +bool torture_libnetapi_server(struct torture_context *tctx) +{ + NET_API_STATUS status = 0; + uint8_t *buffer = NULL; + int i; + + const char *hostname = torture_setting_string(tctx, "host", NULL); + struct libnetapi_ctx *ctx; + + torture_assert(tctx, torture_libnetapi_init_context(tctx, &ctx), + "failed to initialize libnetapi"); + + torture_comment(tctx, "NetServer tests\n"); + + torture_comment(tctx, "Testing NetRemoteTOD\n"); + + status = NetRemoteTOD(hostname, &buffer); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetRemoteTOD"); + goto out; + } + NetApiBufferFree(buffer); + + torture_comment(tctx, "Testing NetRemoteTOD 10 times\n"); + + for (i=0; i<10; i++) { + status = NetRemoteTOD(hostname, &buffer); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetRemoteTOD"); + goto out; + } + NetApiBufferFree(buffer); + } + + status = 0; + + torture_comment(tctx, "NetServer tests succeeded\n"); + out: + if (status != 0) { + torture_comment(tctx, "NetServer testsuite failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + libnetapi_free(ctx); + return false; + } + + libnetapi_free(ctx); + return true; +} diff --git a/source4/torture/libnetapi/libnetapi_user.c b/source4/torture/libnetapi/libnetapi_user.c new file mode 100644 index 0000000..1411d7e --- /dev/null +++ b/source4/torture/libnetapi/libnetapi_user.c @@ -0,0 +1,487 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Guenther Deschner 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include <netapi.h> +#include "torture/libnetapi/proto.h" + +#undef strcasecmp + +#define TORTURE_TEST_USER "torture_testuser" +#define TORTURE_TEST_USER2 "torture_testuser2" + +#define NETAPI_STATUS(tctx, x,y,fn) \ + torture_warning(tctx, "FAILURE: line %d: %s failed with status: %s (%d)\n", \ + __LINE__, fn, libnetapi_get_error_string(x,y), y); + +static NET_API_STATUS test_netuserenum(struct torture_context *tctx, + const char *hostname, + uint32_t level, + const char *username) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + const char *current_name = NULL; + int found_user = 0; + uint8_t *buffer = NULL; + int i; + + struct USER_INFO_0 *info0 = NULL; + struct USER_INFO_1 *info1 = NULL; + struct USER_INFO_2 *info2 = NULL; + struct USER_INFO_3 *info3 = NULL; + struct USER_INFO_4 *info4 = NULL; + struct USER_INFO_10 *info10 = NULL; + struct USER_INFO_11 *info11 = NULL; + struct USER_INFO_20 *info20 = NULL; + struct USER_INFO_23 *info23 = NULL; + + torture_comment(tctx, "Testing NetUserEnum level %d\n", level); + + do { + status = NetUserEnum(hostname, + level, + FILTER_NORMAL_ACCOUNT, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + switch (level) { + case 0: + info0 = (struct USER_INFO_0 *)buffer; + break; + case 1: + info1 = (struct USER_INFO_1 *)buffer; + break; + case 2: + info2 = (struct USER_INFO_2 *)buffer; + break; + case 3: + info3 = (struct USER_INFO_3 *)buffer; + break; + case 4: + info4 = (struct USER_INFO_4 *)buffer; + break; + case 10: + info10 = (struct USER_INFO_10 *)buffer; + break; + case 11: + info11 = (struct USER_INFO_11 *)buffer; + break; + case 20: + info20 = (struct USER_INFO_20 *)buffer; + break; + case 23: + info23 = (struct USER_INFO_23 *)buffer; + break; + default: + return -1; + } + + for (i=0; i<entries_read; i++) { + + switch (level) { + case 0: + current_name = info0->usri0_name; + break; + case 1: + current_name = info1->usri1_name; + break; + case 2: + current_name = info2->usri2_name; + break; + case 3: + current_name = info3->usri3_name; + break; + case 4: + current_name = info4->usri4_name; + break; + case 10: + current_name = info10->usri10_name; + break; + case 11: + current_name = info11->usri11_name; + break; + case 20: + current_name = info20->usri20_name; + break; + case 23: + current_name = info23->usri23_name; + break; + default: + return -1; + } + + if (strcasecmp(current_name, username) == 0) { + found_user = 1; + } + + switch (level) { + case 0: + info0++; + break; + case 1: + info1++; + break; + case 2: + info2++; + break; + case 3: + info3++; + break; + case 4: + info4++; + break; + case 10: + info10++; + break; + case 11: + info11++; + break; + case 20: + info20++; + break; + case 23: + info23++; + break; + default: + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (!found_user) { + torture_comment(tctx, "failed to get user\n"); + return -1; + } + + return 0; +} + +NET_API_STATUS test_netuseradd(struct torture_context *tctx, + const char *hostname, + const char *username) +{ + struct USER_INFO_1 u1; + uint32_t parm_err = 0; + + ZERO_STRUCT(u1); + + torture_comment(tctx, "Testing NetUserAdd\n"); + + u1.usri1_name = username; + u1.usri1_password = "W297!832jD8J"; + u1.usri1_password_age = 0; + u1.usri1_priv = 0; + u1.usri1_home_dir = NULL; + u1.usri1_comment = "User created using Samba NetApi Example code"; + u1.usri1_flags = 0; + u1.usri1_script_path = NULL; + + return NetUserAdd(hostname, 1, (uint8_t *)&u1, &parm_err); +} + +static NET_API_STATUS test_netusermodals(struct torture_context *tctx, + struct libnetapi_ctx *ctx, + const char *hostname) +{ + NET_API_STATUS status; + struct USER_MODALS_INFO_0 *u0 = NULL; + struct USER_MODALS_INFO_0 *_u0 = NULL; + uint8_t *buffer = NULL; + uint32_t parm_err = 0; + uint32_t levels[] = { 0, 1, 2, 3 }; + int i = 0; + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + torture_comment(tctx, "Testing NetUserModalsGet level %d\n", levels[i]); + + status = NetUserModalsGet(hostname, levels[i], &buffer); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetUserModalsGet"); + return status; + } + } + + status = NetUserModalsGet(hostname, 0, (uint8_t **)&u0); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetUserModalsGet"); + return status; + } + + torture_comment(tctx, "Testing NetUserModalsSet\n"); + + status = NetUserModalsSet(hostname, 0, (uint8_t *)u0, &parm_err); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetUserModalsSet"); + return status; + } + + status = NetUserModalsGet(hostname, 0, (uint8_t **)&_u0); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetUserModalsGet"); + return status; + } + + if (memcmp(u0, _u0, sizeof(*u0)) != 0) { + torture_comment(tctx, "USER_MODALS_INFO_0 struct has changed!!!!\n"); + return -1; + } + + return 0; +} + +static NET_API_STATUS test_netusergetgroups(struct torture_context *tctx, + const char *hostname, + uint32_t level, + const char *username, + const char *groupname) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + const char *current_name; + int found_group = 0; + uint8_t *buffer = NULL; + int i; + + struct GROUP_USERS_INFO_0 *i0 = NULL; + struct GROUP_USERS_INFO_1 *i1 = NULL; + + torture_comment(tctx, "Testing NetUserGetGroups level %d\n", level); + + do { + status = NetUserGetGroups(hostname, + username, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries); + if (status == 0 || status == ERROR_MORE_DATA) { + switch (level) { + case 0: + i0 = (struct GROUP_USERS_INFO_0 *)buffer; + break; + case 1: + i1 = (struct GROUP_USERS_INFO_1 *)buffer; + break; + default: + return -1; + } + + for (i=0; i<entries_read; i++) { + + switch (level) { + case 0: + current_name = i0->grui0_name; + break; + case 1: + current_name = i1->grui1_name; + break; + default: + return -1; + } + + if (groupname && strcasecmp(current_name, groupname) == 0) { + found_group = 1; + } + + switch (level) { + case 0: + i0++; + break; + case 1: + i1++; + break; + default: + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (groupname && !found_group) { + torture_comment(tctx, "failed to get membership\n"); + return -1; + } + + return 0; +} + +bool torture_libnetapi_user(struct torture_context *tctx) +{ + NET_API_STATUS status = 0; + uint8_t *buffer = NULL; + uint32_t levels[] = { 0, 1, 2, 3, 4, 10, 11, 20, 23 }; + uint32_t enum_levels[] = { 0, 1, 2, 3, 4, 10, 11, 20, 23 }; + uint32_t getgr_levels[] = { 0, 1 }; + int i; + + struct USER_INFO_0 u0; + struct USER_INFO_1007 u1007; + uint32_t parm_err = 0; + + const char *hostname = torture_setting_string(tctx, "host", NULL); + struct libnetapi_ctx *ctx; + + torture_assert(tctx, torture_libnetapi_init_context(tctx, &ctx), + "failed to initialize libnetapi"); + + torture_comment(tctx, "NetUser tests\n"); + + /* cleanup */ + + NetUserDel(hostname, TORTURE_TEST_USER); + NetUserDel(hostname, TORTURE_TEST_USER2); + + /* add a user */ + + status = test_netuseradd(tctx, hostname, TORTURE_TEST_USER); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetUserAdd"); + goto out; + } + + /* enum the new user */ + + for (i=0; i<ARRAY_SIZE(enum_levels); i++) { + + status = test_netuserenum(tctx, hostname, enum_levels[i], TORTURE_TEST_USER); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetUserEnum"); + goto out; + } + } + + /* basic queries */ + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + torture_comment(tctx, "Testing NetUserGetInfo level %d\n", levels[i]); + + status = NetUserGetInfo(hostname, TORTURE_TEST_USER, levels[i], &buffer); + if (status && status != 124) { + NETAPI_STATUS(tctx, ctx, status, "NetUserGetInfo"); + goto out; + } + } + + /* testing getgroups */ + + for (i=0; i<ARRAY_SIZE(getgr_levels); i++) { + + status = test_netusergetgroups(tctx, hostname, getgr_levels[i], TORTURE_TEST_USER, NULL); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetUserGetGroups"); + goto out; + } + } + + /* modify description */ + + torture_comment(tctx, "Testing NetUserSetInfo level %d\n", 1007); + + u1007.usri1007_comment = "NetApi modified user"; + + status = NetUserSetInfo(hostname, TORTURE_TEST_USER, 1007, (uint8_t *)&u1007, &parm_err); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetUserSetInfo"); + goto out; + } + + /* query info */ + + for (i=0; i<ARRAY_SIZE(levels); i++) { + status = NetUserGetInfo(hostname, TORTURE_TEST_USER, levels[i], &buffer); + if (status && status != 124) { + NETAPI_STATUS(tctx, ctx, status, "NetUserGetInfo"); + goto out; + } + } + + torture_comment(tctx, "Testing NetUserSetInfo level 0\n"); + + u0.usri0_name = TORTURE_TEST_USER2; + + status = NetUserSetInfo(hostname, TORTURE_TEST_USER, 0, (uint8_t *)&u0, &parm_err); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetUserSetInfo"); + goto out; + } + + /* delete */ + + torture_comment(tctx, "Testing NetUserDel\n"); + + status = NetUserDel(hostname, TORTURE_TEST_USER2); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetUserDel"); + goto out; + } + + /* should not exist anymore */ + + status = NetUserGetInfo(hostname, TORTURE_TEST_USER2, 0, &buffer); + if (status == 0) { + NETAPI_STATUS(tctx, ctx, status, "NetUserGetInfo"); + status = -1; + goto out; + } + + status = test_netusermodals(tctx, ctx, hostname); + if (status) { + goto out; + } + + status = 0; + + torture_comment(tctx, "NetUser tests succeeded\n"); + out: + /* cleanup */ + NetUserDel(hostname, TORTURE_TEST_USER); + NetUserDel(hostname, TORTURE_TEST_USER2); + + if (status != 0) { + torture_comment(tctx, "NetUser testsuite failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + libnetapi_free(ctx); + return false; + } + + libnetapi_free(ctx); + return true; +} diff --git a/source4/torture/libnetapi/wscript_build b/source4/torture/libnetapi/wscript_build new file mode 100644 index 0000000..988ba9b --- /dev/null +++ b/source4/torture/libnetapi/wscript_build @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +bld.SAMBA_MODULE('TORTURE_LIBNETAPI', + source='libnetapi.c libnetapi_user.c libnetapi_group.c libnetapi_server.c', + autoproto='proto.h', + subsystem='smbtorture', + init_function='torture_libnetapi_init', + deps='netapi CMDLINE_S4', + internal_module=True, + ) + |