summaryrefslogtreecommitdiffstats
path: root/source4/torture/libnet
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--source4/torture/libnet/domain.c117
-rw-r--r--source4/torture/libnet/groupinfo.c128
-rw-r--r--source4/torture/libnet/groupman.c97
-rw-r--r--source4/torture/libnet/grouptest.h20
-rw-r--r--source4/torture/libnet/libnet.c70
-rw-r--r--source4/torture/libnet/libnet_BecomeDC.c191
-rw-r--r--source4/torture/libnet/libnet_domain.c440
-rw-r--r--source4/torture/libnet/libnet_group.c210
-rw-r--r--source4/torture/libnet/libnet_lookup.c191
-rw-r--r--source4/torture/libnet/libnet_rpc.c230
-rw-r--r--source4/torture/libnet/libnet_share.c285
-rw-r--r--source4/torture/libnet/libnet_user.c520
-rw-r--r--source4/torture/libnet/python/samr-test.py59
-rw-r--r--source4/torture/libnet/userinfo.c192
-rw-r--r--source4/torture/libnet/userman.c473
-rw-r--r--source4/torture/libnet/usertest.h42
-rw-r--r--source4/torture/libnet/utils.c556
-rw-r--r--source4/torture/libnetapi/libnetapi.c98
-rw-r--r--source4/torture/libnetapi/libnetapi_group.c522
-rw-r--r--source4/torture/libnetapi/libnetapi_server.c76
-rw-r--r--source4/torture/libnetapi/libnetapi_user.c487
-rw-r--r--source4/torture/libnetapi/wscript_build11
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,
+ )
+