/* 
   Unix SMB/CIFS implementation.

   Test code to simulate an XP logon.

   Copyright (C) Volker Lendecke 2004
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "includes.h"
#include "libcli/auth/credentials.h"
#include "libcli/raw/libcliraw.h"
#include "librpc/gen_ndr/ndr_samr.h"
#include "librpc/gen_ndr/ndr_netlogon.h"
#include "librpc/gen_ndr/ndr_srvsvc.h"
#include "libcli/composite/composite.h"

#if 0

static NTSTATUS after_negprot(struct smbcli_transport **dst_transport,
			      const char *dest_host, uint16_t port,
			      const char *my_name)
{
	struct smbcli_socket *sock;
	struct smbcli_transport *transport;
	NTSTATUS status;

	sock = smbcli_sock_init(NULL, NULL);
	if (sock == NULL)
		return NT_STATUS_NO_MEMORY;

	if (!smbcli_sock_connect_byname(sock, dest_host, port)) {
		talloc_free(sock);
		DEBUG(2,("Failed to establish socket connection - %s\n",
			 strerror(errno)));
		return NT_STATUS_UNSUCCESSFUL;
	}

	transport = smbcli_transport_init(sock, NULL, True);
	if (transport == NULL)
		return NT_STATUS_NO_MEMORY;

	{
		struct nbt_name calling;
		struct nbt_name called;

		/* send a NBT session request, if applicable */
		make_nbt_name_client(&calling, my_name);

		nbt_choose_called_name(transport, &called, dest_host, NBT_NAME_SERVER);

		if (!smbcli_transport_connect(transport, &calling, &called)) {
			talloc_free(transport);
			return NT_STATUS_NO_MEMORY;
		}
	}

	/* negotiate protocol options with the server */
	status = smb_raw_negotiate(transport, lp_maxprotocol());
	if (!NT_STATUS_IS_OK(status)) {
		talloc_free(transport);
		return NT_STATUS_UNSUCCESSFUL;
	}

	*dst_transport = transport;

	return NT_STATUS_OK;
}

static int destroy_session(void *ptr)
{
	struct smbcli_session *session = ptr;
	smb_raw_ulogoff(session);
	return 0;
}

static int destroy_tree_and_session(void *ptr)
{
	struct smbcli_tree *tree = ptr;
	smb_tree_disconnect(tree);
	talloc_free(tree->session);
	return 0;
}

static NTSTATUS anon_ipc(struct smbcli_transport *transport,
			 struct smbcli_tree **dst_tree)
{
	struct smbcli_tree *tree;
	struct smbcli_session *session;
	struct smb_composite_sesssetup setup;
	union smb_tcon tcon;
	TALLOC_CTX *mem_ctx;
	NTSTATUS status;

	session = smbcli_session_init(transport, NULL, True);
	if (session == NULL)
		return NT_STATUS_NO_MEMORY;

	mem_ctx = talloc_init("session_init");
	if (mem_ctx == NULL) {
		talloc_free(session);
		return NT_STATUS_NO_MEMORY;
	}

	/* prepare a session setup to establish a security context */
	setup.in.sesskey = transport->negotiate.sesskey;
	setup.in.capabilities = transport->negotiate.capabilities;
	setup.in.capabilities &= ~CAP_EXTENDED_SECURITY;

	setup.in.credentials = cli_credentials_init(mem_ctx);
	cli_credentials_set_anonymous(setup.in.credentials);

	status = smb_composite_sesssetup(session, &setup);
	if (!NT_STATUS_IS_OK(status)) {
		talloc_free(session);
		talloc_free(mem_ctx);
		return NT_STATUS_UNSUCCESSFUL;
	}

	session->vuid = setup.out.vuid;

	talloc_set_destructor(session, destroy_session);

	tree = smbcli_tree_init(session, NULL, True);
	if (tree == NULL) {
		talloc_free(mem_ctx);
		return NT_STATUS_NO_MEMORY;
	}

	tcon.generic.level = RAW_TCON_TCONX;
	tcon.tconx.in.flags = 0;
	tcon.tconx.in.password = data_blob(NULL, 0);
	tcon.tconx.in.path = talloc_asprintf(mem_ctx, "\\\\%s\\IPC$",
					    transport->called.name);
	tcon.tconx.in.device = "IPC";

	status = smb_raw_tcon(tree, mem_ctx, &tcon);

	if (!NT_STATUS_IS_OK(status)) {
		talloc_free(tree);
		talloc_free(mem_ctx);
		return NT_STATUS_UNSUCCESSFUL;
	}

	tree->tid = tcon.tconx.out.tid;

	if (tcon.tconx.out.dev_type != NULL)
		tree->device = talloc_strdup(tree, tcon.tconx.out.dev_type);

	if (tcon.tconx.out.fs_type != NULL)
		tree->fs_type = talloc_strdup(tree, tcon.tconx.out.fs_type);

	talloc_set_destructor(tree, destroy_tree_and_session);

	talloc_free(mem_ctx);

	*dst_tree = tree;

	return NT_STATUS_OK;
}

static NTSTATUS connect_to_pipe(struct dcerpc_pipe **pp,
				TALLOC_CTX *mem_ctx,
				struct smbcli_transport *transport,
				const char *pipe_name,
				const char *pipe_uuid,
				uint32_t pipe_version)
{
	const char *binding = lp_parm_string(-1, "torture", "binding");
	struct dcerpc_binding *b;
	NTSTATUS status;
	struct dcerpc_pipe *p;
	TALLOC_CTX *tmp_ctx;
	struct smbcli_tree *tree;
	
	if (!NT_STATUS_IS_OK(status = anon_ipc(transport, &tree)))
		return status;

	if (binding == NULL)
		return NT_STATUS_INVALID_PARAMETER;

	p = dcerpc_pipe_init(mem_ctx);
	if (p == NULL) {
		return NT_STATUS_NO_MEMORY;
	}
	tmp_ctx = talloc_new(p);

	status = dcerpc_parse_binding(tmp_ctx, binding, &b);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(0,("Failed to parse dcerpc binding '%s'\n", binding));
		talloc_free(p);
		return status;
	}

	DEBUG(3,("Using binding %s\n", dcerpc_binding_string(tmp_ctx, b)));

	/* Look up identifier using the epmapper */
	if (!b->endpoint) {
		status = dcerpc_epm_map_binding(tmp_ctx, b, pipe_uuid, pipe_version,
						NULL);
		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(0,("Failed to map DCERPC/TCP NCACN_NP pipe for '%s' - %s\n", 
				 pipe_uuid, nt_errstr(status)));
			talloc_free(p);
			return status;
		}
		DEBUG(1,("Mapped to DCERPC/NP pipe %s\n", b->endpoint));
	}

	pipe_name = b->endpoint;


	status = dcerpc_pipe_open_smb(p->conn, tree, pipe_name);

	if (!NT_STATUS_IS_OK(status)) {
		talloc_free(p);
		return status;
	}

	talloc_free(tmp_ctx);
	(*pp) = p;
	
	return NT_STATUS_OK;
}

static NTSTATUS test_enumtrusts(struct smbcli_transport *transport)
{
	struct policy_handle handle;
	struct lsa_EnumTrustDom r2;
	uint32_t resume_handle = 0;
	struct lsa_ObjectAttribute attr;
	struct lsa_OpenPolicy2 r1;
	struct lsa_DomainList domains;
	TALLOC_CTX *mem_ctx;
	NTSTATUS status;
        struct dcerpc_pipe *p;

	mem_ctx = talloc_init("test_enumtrusts");
	if (mem_ctx == NULL)
		return NT_STATUS_NO_MEMORY;

	status = connect_to_pipe(&p, mem_ctx, transport, DCERPC_LSARPC_NAME,
				 DCERPC_LSARPC_UUID, 
				 DCERPC_LSARPC_VERSION);

	if (!NT_STATUS_IS_OK(status)) {
		talloc_free(mem_ctx);
		return status;
	}

	status = dcerpc_bind_auth_none(p, DCERPC_LSARPC_UUID,
				       DCERPC_LSARPC_VERSION);

	if (!NT_STATUS_IS_OK(status))
		return status;

	printf("\ntesting OpenPolicy2\n");

	attr.len = 0;
	attr.root_dir = NULL;
	attr.object_name = NULL;
	attr.attributes = 0;
	attr.sec_desc = NULL;
	attr.sec_qos = NULL;

	r1.in.system_name = talloc_asprintf(mem_ctx,
					    "\\\\%s", dcerpc_server_name(p));
	r1.in.attr = &attr;
	r1.in.access_mask = 1;
	r1.out.handle = &handle;

	status = dcerpc_lsa_OpenPolicy2(p, mem_ctx, &r1);
	if (!NT_STATUS_IS_OK(status)) {
		printf("OpenPolicy2 failed - %s\n", nt_errstr(status));
		return status;
	}

	printf("\nTesting EnumTrustDom\n");

	r2.in.handle = &handle;
	r2.in.resume_handle = &resume_handle;
	r2.in.max_size = 1000;
	r2.out.domains = &domains;
	r2.out.resume_handle = &resume_handle;

	status = dcerpc_lsa_EnumTrustDom(p, mem_ctx, &r2);

	if (!NT_STATUS_IS_OK(status) &&
	    !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES))
		return status;

	talloc_free(p);

	talloc_free(mem_ctx);

	return NT_STATUS_OK;
}

static NTSTATUS test_lookupnames(struct smbcli_transport *transport,
				 const char *name)
{
	struct policy_handle handle;
	struct lsa_ObjectAttribute attr;
	struct lsa_OpenPolicy2 r1;
	TALLOC_CTX *mem_ctx;
	NTSTATUS status;
        struct dcerpc_pipe *p;

	mem_ctx = talloc_init("test_lookupnames");
	if (mem_ctx == NULL)
		return NT_STATUS_NO_MEMORY;

	status = connect_to_pipe(&p, mem_ctx, transport, DCERPC_LSARPC_NAME,
				 DCERPC_LSARPC_UUID, 
				 DCERPC_LSARPC_VERSION);

	if (!NT_STATUS_IS_OK(status)) {
		talloc_free(mem_ctx);
		return status;
	}

	status = dcerpc_bind_auth_none(p, DCERPC_LSARPC_UUID,
				       DCERPC_LSARPC_VERSION);

	if (!NT_STATUS_IS_OK(status))
		return status;

	attr.len = 0;
	attr.root_dir = NULL;
	attr.object_name = NULL;
	attr.attributes = 0;
	attr.sec_desc = NULL;
	attr.sec_qos = NULL;

	r1.in.system_name = talloc_asprintf(mem_ctx,
					    "\\\\%s", dcerpc_server_name(p));
	r1.in.attr = &attr;
	r1.in.access_mask = 0x801;
	r1.out.handle = &handle;

	status = dcerpc_lsa_OpenPolicy2(p, mem_ctx, &r1);
	if (!NT_STATUS_IS_OK(status)) {
		printf("OpenPolicy2 failed - %s\n", nt_errstr(status));
		return status;
	}

	{
		struct lsa_LookupNames l;
		struct lsa_TransSidArray sids;
		struct lsa_String lsaname;
		uint32_t count = 0;

		sids.count = 0;
		sids.sids = NULL;

		lsaname.string = name;

		l.in.handle = &handle;
		l.in.num_names = 1;
		l.in.names = &lsaname;
		l.in.sids = &sids;
		l.in.level = 2;
		l.in.count = &count;
		l.out.count = &count;
		l.out.sids = &sids;

		status = dcerpc_lsa_LookupNames(p, mem_ctx, &l);
		if (!NT_STATUS_IS_OK(status) &&
		    !NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
			printf("LookupNames failed - %s\n", nt_errstr(status));
			talloc_free(p);
			talloc_free(mem_ctx);
			return NT_STATUS_OK;
		}
	}

	{
		struct lsa_Close c;
		struct policy_handle handle2;

		c.in.handle = &handle;
		c.out.handle = &handle2;

		status = dcerpc_lsa_Close(p, mem_ctx, &c);
		if (!NT_STATUS_IS_OK(status)) {
			printf("Close failed - %s\n", nt_errstr(status));
			return status;
		}
	}

	talloc_free(p);

	talloc_free(mem_ctx);

	return NT_STATUS_OK;
}

static NTSTATUS setup_netlogon_creds(struct smbcli_transport *transport,
				     struct dcerpc_pipe **p,
				     const char *machine_name,
				     const char *domain,
				     const char *machine_pwd,
				     struct creds_CredentialState *creds)
{
        NTSTATUS status;
	TALLOC_CTX *mem_ctx;
	struct netr_ServerReqChallenge r;
	struct netr_ServerAuthenticate2 a;
	struct netr_Credential credentials1, credentials2, credentials3;
	const char *plain_pass;
	struct samr_Password mach_password;
	uint32_t negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;

	
	mem_ctx = talloc_init("torture_rpc_login");

	if (mem_ctx == NULL)
		return NT_STATUS_NO_MEMORY;

	status = connect_to_pipe(p, mem_ctx, transport, DCERPC_NETLOGON_NAME,
				 DCERPC_NETLOGON_UUID,
				 DCERPC_NETLOGON_VERSION);

	if (!NT_STATUS_IS_OK(status)) {
		talloc_free(mem_ctx);
		return status;
	}

	status = dcerpc_bind_auth_none(*p, DCERPC_NETLOGON_UUID,
				       DCERPC_NETLOGON_VERSION);

	if (!NT_STATUS_IS_OK(status))
		return status;

	printf("Testing ServerReqChallenge\n");

	r.in.server_name = talloc_asprintf(mem_ctx, "\\\\%s",
					   dcerpc_server_name(*p));
	r.in.computer_name = machine_name;
	r.in.credentials = &credentials1;
	r.out.credentials = &credentials2;

	generate_random_buffer(credentials1.data, sizeof(credentials1.data));

	status = dcerpc_netr_ServerReqChallenge(*p, mem_ctx, &r);
	if (!NT_STATUS_IS_OK(status)) {
		printf("ServerReqChallenge - %s\n", nt_errstr(status));
		return status;
	}

	plain_pass = machine_pwd;
	if (!plain_pass) {
		printf("Unable to fetch machine password!\n");
		return status;
	}

	E_md4hash(plain_pass, mach_password.hash);

	a.in.server_name = talloc_asprintf(mem_ctx, "\\\\%s",
					   dcerpc_server_name(*p));
	a.in.account_name = talloc_asprintf(mem_ctx, "%s$", machine_name);
	a.in.secure_channel_type = SEC_CHAN_WKSTA;
	a.in.computer_name = machine_name;
	a.in.negotiate_flags = &negotiate_flags;
	a.out.negotiate_flags = &negotiate_flags;
	a.in.credentials = &credentials3;
	a.out.credentials = &credentials3;

	creds_client_init(creds, &credentials1, &credentials2,
			  &mach_password, &credentials3, 
			  negotiate_flags);

	printf("Testing ServerAuthenticate2\n");

	status = dcerpc_netr_ServerAuthenticate2(*p, mem_ctx, &a);
	if (!NT_STATUS_IS_OK(status)) {
		printf("ServerAuthenticate2 - %s\n", nt_errstr(status));
		return status;
	}

	if (!creds_client_check(creds, &credentials3)) {
		printf("Credential chaining failed\n");
		return status;
	}

	printf("negotiate_flags=0x%08x\n", negotiate_flags);

	talloc_free(mem_ctx);
	return NT_STATUS_OK;
}

static NTSTATUS test_getgroups(struct smbcli_transport *transport,
			       const char *name)
{
	TALLOC_CTX *mem_ctx;
	NTSTATUS status;
        struct dcerpc_pipe *p;

	struct samr_Connect4 r4;
	struct policy_handle connect_handle, domain_handle, user_handle;

	mem_ctx = talloc_init("test_lookupnames");
	if (mem_ctx == NULL)
		return NT_STATUS_NO_MEMORY;

	status = connect_to_pipe(&p, mem_ctx, transport, DCERPC_SAMR_NAME,
				 DCERPC_SAMR_UUID, 
				 DCERPC_SAMR_VERSION);

	if (!NT_STATUS_IS_OK(status)) {
		talloc_free(mem_ctx);
		return status;
	}

	status = dcerpc_bind_auth_none(p, DCERPC_SAMR_UUID,
				       DCERPC_SAMR_VERSION);

	if (!NT_STATUS_IS_OK(status))
		return status;

	r4.in.system_name = talloc_asprintf(mem_ctx, "\\\\%s",
					    dcerpc_server_name(p));
	r4.in.unknown = 0;
	r4.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
	r4.out.connect_handle = &connect_handle;

	status = dcerpc_samr_Connect4(p, mem_ctx, &r4);
	if (!NT_STATUS_IS_OK(status))
		return status;

	{
		struct samr_EnumDomains e;
		uint32_t resume_handle = 0;
		int i;

		e.in.connect_handle = &connect_handle;
		e.in.resume_handle = &resume_handle;
		e.in.buf_size = (uint32_t)-1;
		e.out.resume_handle = &resume_handle;
		status = dcerpc_samr_EnumDomains(p, mem_ctx, &e);
		if (!NT_STATUS_IS_OK(status))
			return status;

		for (i=0; i<e.out.sam->count; i++) {

			struct samr_LookupDomain l;
			struct samr_OpenDomain o;

			if (strcmp(e.out.sam->entries[i].name.string,
				   "Builtin") == 0)
				continue;

			l.in.connect_handle = &connect_handle;
			l.in.domain_name = &e.out.sam->entries[i].name;

			status = dcerpc_samr_LookupDomain(p, mem_ctx, &l);

			if (!NT_STATUS_IS_OK(status))
				return status;

			o.in.connect_handle = &connect_handle;
			o.in.access_mask = 0x200;
			o.in.sid = l.out.sid;
			o.out.domain_handle = &domain_handle;

			status = dcerpc_samr_OpenDomain(p, mem_ctx, &o);

			if (!NT_STATUS_IS_OK(status))
				return status;

			break;
		}
	}

	{
		struct samr_LookupNames l;
		struct lsa_String samr_name;
		struct samr_OpenUser o;

		samr_name.string = name;

		l.in.domain_handle = &domain_handle;
		l.in.num_names = 1;
		l.in.names = &samr_name;

		status = dcerpc_samr_LookupNames(p, mem_ctx, &l);

		if (!NT_STATUS_IS_OK(status))
			return status;

		o.in.domain_handle = &domain_handle;
		o.in.rid = l.out.rids.ids[0];
		o.in.access_mask = 0x100;
		o.out.user_handle = &user_handle;

		status = dcerpc_samr_OpenUser(p, mem_ctx, &o);
		
		if (!NT_STATUS_IS_OK(status))
			return status;
	}

	{
		struct samr_GetGroupsForUser g;
		struct samr_LookupRids l;
		int i;

		g.in.user_handle = &user_handle;

		status = dcerpc_samr_GetGroupsForUser(p, mem_ctx, &g);
		if (!NT_STATUS_IS_OK(status))
			return status;

		l.in.domain_handle = &domain_handle;
		l.in.num_rids = g.out.rids->count;
		l.in.rids = talloc_array(mem_ctx, uint32_t, g.out.rids->count);

		for (i=0; i<g.out.rids->count; i++)
			l.in.rids[i] = g.out.rids->rids[i].rid;

		status = dcerpc_samr_LookupRids(p, mem_ctx, &l);
		if (!NT_STATUS_IS_OK(status)) {
			talloc_free(mem_ctx);
			return status;
		}
	}

	{
		struct samr_Close c;

		c.in.handle = &user_handle;
		c.out.handle = &user_handle;
		dcerpc_samr_Close(p, mem_ctx, &c);

		c.in.handle = &domain_handle;
		c.out.handle = &domain_handle;
		dcerpc_samr_Close(p, mem_ctx, &c);

		c.in.handle = &connect_handle;
		c.out.handle = &connect_handle;
		dcerpc_samr_Close(p, mem_ctx, &c);
	}

	talloc_free(p);
	talloc_free(mem_ctx);

	return NT_STATUS_OK;
}

static NTSTATUS test_getallsids(struct smbcli_transport *transport,
				const char *name, BOOL includeDomain)
{
	TALLOC_CTX *mem_ctx;
	NTSTATUS status;
        struct dcerpc_pipe *p;

	struct samr_Connect4 r4;
	struct policy_handle connect_handle, user_handle;
	struct policy_handle builtin_handle, domain_handle;
	struct dom_sid *domain_sid = NULL;

	struct dom_sid *user_sid;
	struct dom_sid *primary_group_sid;
	struct samr_GetGroupsForUser g;


	mem_ctx = talloc_init("test_getallsids");
	if (mem_ctx == NULL)
		return NT_STATUS_NO_MEMORY;

	status = connect_to_pipe(&p, mem_ctx, transport, DCERPC_SAMR_NAME,
				 DCERPC_SAMR_UUID, 
				 DCERPC_SAMR_VERSION);

	if (!NT_STATUS_IS_OK(status)) {
		talloc_free(mem_ctx);
		return status;
	}

	status = dcerpc_bind_auth_none(p, DCERPC_SAMR_UUID,
				       DCERPC_SAMR_VERSION);

	if (!NT_STATUS_IS_OK(status))
		return status;

	r4.in.system_name = talloc_asprintf(mem_ctx, "\\\\%s",
					    dcerpc_server_name(p));
	r4.in.unknown = 0;
	r4.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
	r4.out.connect_handle = &connect_handle;

	status = dcerpc_samr_Connect4(p, mem_ctx, &r4);
	if (!NT_STATUS_IS_OK(status))
		return status;

	{
		struct samr_EnumDomains e;
		struct samr_OpenDomain o;
		uint32_t resume_handle = 0;
		int i;

		e.in.connect_handle = &connect_handle;
		e.in.resume_handle = &resume_handle;
		e.in.buf_size = (uint32_t)-1;
		e.out.resume_handle = &resume_handle;
		status = dcerpc_samr_EnumDomains(p, mem_ctx, &e);
		if (!NT_STATUS_IS_OK(status))
			return status;

		for (i=0; i<e.out.sam->count; i++) {

			struct samr_LookupDomain l;

			if (strcmp(e.out.sam->entries[i].name.string,
				   "Builtin") == 0)
				continue;

			l.in.connect_handle = &connect_handle;
			l.in.domain_name = &e.out.sam->entries[i].name;

			status = dcerpc_samr_LookupDomain(p, mem_ctx, &l);

			if (!NT_STATUS_IS_OK(status))
				return status;

			o.in.connect_handle = &connect_handle;
			o.in.access_mask = 0x280;
			domain_sid = l.out.sid;
			o.in.sid = l.out.sid;
			o.out.domain_handle = &domain_handle;

			status = dcerpc_samr_OpenDomain(p, mem_ctx, &o);

			if (!NT_STATUS_IS_OK(status))
				return status;
			break;
		}
		o.in.connect_handle = &connect_handle;
		o.in.access_mask = 0x280;
		o.in.sid = dom_sid_parse_talloc(mem_ctx, "S-1-5-32");
		o.out.domain_handle = &builtin_handle;

		status = dcerpc_samr_OpenDomain(p, mem_ctx, &o);

		if (!NT_STATUS_IS_OK(status))
			return status;
	}

	{
		struct samr_LookupNames l;
		struct lsa_String samr_name;
		struct samr_OpenUser o;

		samr_name.string = name;

		l.in.domain_handle = &domain_handle;
		l.in.num_names = 1;
		l.in.names = &samr_name;

		status = dcerpc_samr_LookupNames(p, mem_ctx, &l);

		if (!NT_STATUS_IS_OK(status))
			return status;

		o.in.domain_handle = &domain_handle;
		o.in.rid = l.out.rids.ids[0];
		o.in.access_mask = 0x100;
		o.out.user_handle = &user_handle;

		status = dcerpc_samr_OpenUser(p, mem_ctx, &o);
		
		if (!NT_STATUS_IS_OK(status))
			return status;
	}

	{
		struct samr_QueryUserInfo q;

		q.in.user_handle = &user_handle;
		q.in.level = 21;

		status = dcerpc_samr_QueryUserInfo(p, mem_ctx, &q);

		if (!NT_STATUS_IS_OK(status))
			return status;

		user_sid = dom_sid_add_rid(mem_ctx, domain_sid,
					   q.out.info->info21.rid);
		primary_group_sid = dom_sid_add_rid(mem_ctx, domain_sid,
						    q.out.info->info21.primary_gid);
	}

	g.in.user_handle = &user_handle;

	status = dcerpc_samr_GetGroupsForUser(p, mem_ctx, &g);
	if (!NT_STATUS_IS_OK(status))
		return status;

	{
		struct lsa_SidArray sids;
		struct samr_Ids rids;
		struct samr_GetAliasMembership ga;
		int i;

		ga.in.domain_handle = &builtin_handle;

		sids.num_sids = g.out.rids->count+2;
		sids.sids = talloc_array(mem_ctx, struct lsa_SidPtr,
					   g.out.rids->count+2);
		sids.sids[0].sid = user_sid;
		sids.sids[1].sid = primary_group_sid;
		for (i=0; i<g.out.rids->count; i++) {
			sids.sids[i+2].sid = dom_sid_add_rid(mem_ctx,
							     domain_sid,
							     g.out.rids->rids[i].rid);
		}
		ga.in.sids = &sids;
		ga.out.rids = &rids;

		status = dcerpc_samr_GetAliasMembership(p, mem_ctx, &ga);
		if (!NT_STATUS_IS_OK(status))
			return status;

		if (includeDomain) {
			ga.in.domain_handle = &domain_handle;
			status = dcerpc_samr_GetAliasMembership(p, mem_ctx,
								&ga);
			if (!NT_STATUS_IS_OK(status))
				return status;
		}
	}

	{
		struct samr_Close c;

		c.in.handle = &user_handle;
		c.out.handle = &user_handle;
		dcerpc_samr_Close(p, mem_ctx, &c);

		c.in.handle = &domain_handle;
		c.out.handle = &domain_handle;
		dcerpc_samr_Close(p, mem_ctx, &c);

		c.in.handle = &builtin_handle;
		c.out.handle = &builtin_handle;
		dcerpc_samr_Close(p, mem_ctx, &c);

		c.in.handle = &connect_handle;
		c.out.handle = &connect_handle;
		dcerpc_samr_Close(p, mem_ctx, &c);
	}

	talloc_free(p);
	talloc_free(mem_ctx);

	return NT_STATUS_OK;
}

static NTSTATUS test_remoteTOD(struct smbcli_transport *transport)
{
	TALLOC_CTX *mem_ctx;
	NTSTATUS status;
        struct dcerpc_pipe *p;
	struct srvsvc_NetRemoteTOD r;

	mem_ctx = talloc_init("test_lookupnames");
	if (mem_ctx == NULL)
		return NT_STATUS_NO_MEMORY;

	status = connect_to_pipe(&p, mem_ctx, transport, DCERPC_SRVSVC_NAME,
				 DCERPC_SRVSVC_UUID,
				 DCERPC_SRVSVC_VERSION);

	if (!NT_STATUS_IS_OK(status)) {
		talloc_free(mem_ctx);
		return status;
	}

	status = dcerpc_bind_auth_none(p, DCERPC_SRVSVC_UUID,
				       DCERPC_SRVSVC_VERSION);

	if (!NT_STATUS_IS_OK(status))
		return status;

	r.in.server_unc = talloc_asprintf(mem_ctx,"\\\\%s",dcerpc_server_name(p));

	ZERO_STRUCT(r.out);
	status = dcerpc_srvsvc_NetRemoteTOD(p, mem_ctx, &r);
	talloc_free(mem_ctx);
	talloc_free(p);
	return status;
}

static BOOL xp_login(const char *dcname, const char *wksname,
		     const char *domain, const char *wkspwd,
		     const char *user1name, const char *user1pw,
		     const char *user2name, const char *user2pw)
{
        NTSTATUS status;
	TALLOC_CTX *mem_ctx;
	char *user1dom;

	struct smbcli_transport *transport;

        struct dcerpc_pipe *netlogon_pipe;
	struct creds_CredentialState *netlogon_creds;

	struct dcerpc_pipe *netlogon_schannel_pipe;

	talloc_enable_leak_report();

	mem_ctx = talloc_init("rpc_login");

	if (mem_ctx == NULL)
		return False;

	netlogon_creds = talloc(mem_ctx, struct creds_CredentialState);
	if (!netlogon_creds) {
		return False;
	}

	if (!NT_STATUS_IS_OK(after_negprot(&transport, dcname, 139,
					   wksname)))
		return False;

	if (!NT_STATUS_IS_OK(setup_netlogon_creds(transport, &netlogon_pipe,
						  wksname, domain, wkspwd,
						  netlogon_creds)))
		return False;

	if (!NT_STATUS_IS_OK(test_enumtrusts(transport)))
		return False;

	user1dom = talloc_asprintf(mem_ctx, "%s\\%s", domain, user1name);

	if (!NT_STATUS_IS_OK(test_lookupnames(transport, user1dom)))
		return False;

	status = connect_to_pipe(&netlogon_schannel_pipe,
				 mem_ctx, transport, DCERPC_NETLOGON_NAME,
				 DCERPC_NETLOGON_UUID,
				 DCERPC_NETLOGON_VERSION);

	if (!NT_STATUS_IS_OK(status))
		return False;

	netlogon_schannel_pipe->conn->flags |= DCERPC_SEAL;

	status = dcerpc_bind_auth_password(netlogon_schannel_pipe,
					   DCERPC_NETLOGON_UUID,
					   DCERPC_NETLOGON_VERSION,
					   creds, NULL);

	if (!NT_STATUS_IS_OK(status))
                return False;

	if (!test_InteractiveLogon(netlogon_schannel_pipe, mem_ctx, 
				   netlogon_creds, wksname, domain,
				   user1name, user1pw)) {
		return False;
	}
		
	talloc_free(netlogon_pipe);

	if (!test_InteractiveLogon(netlogon_schannel_pipe, mem_ctx, 
				   netlogon_creds, wksname, domain,
				   user1name, user1pw)) {
		return False;
	}
		
	status = test_getgroups(transport, user2name);
	
	if (!NT_STATUS_IS_OK(status))
                return False;

	status = test_remoteTOD(transport);
	
	if (!NT_STATUS_IS_OK(status))
                return False;

	status = test_remoteTOD(transport);
	
	if (!NT_STATUS_IS_OK(status))
                return False;

	status = test_getallsids(transport, user2name, False);
	
	if (!NT_STATUS_IS_OK(status))
                return False;

	status = test_getgroups(transport, user2name);
	
	if (!NT_STATUS_IS_OK(status))
                return False;

	status = test_getallsids(transport, user2name, True);
	
	if (!NT_STATUS_IS_OK(status))
                return False;

	talloc_free(netlogon_schannel_pipe);

	talloc_free(transport);

	talloc_free(mem_ctx);

	return True;
}

struct user_pw {
	const char *username;
	const char *password;
};

static const struct user_pw users[] = {
	{ "username1", "password1" },
	{ "username2", "password2" }
};

static const struct user_pw machines[] = {
	{ "machine1", "mpw1" },
	{ "machine2", "mpw2" }
};

BOOL torture_rpc_login(void)
{
	const char *pdcname = "pdcname";
	const char *domainname = "domain";

	int useridx1 = rand() % ARRAY_SIZE(users);
	int useridx2 = rand() % ARRAY_SIZE(users);
	int machidx = rand() % ARRAY_SIZE(machines);
	printf("machine: %s user1: %s user2: %s\n",
	       machines[machidx].username,
	       users[useridx1].username,
	       users[useridx2].username);

	return xp_login(pdcname, machines[machidx].username,
			domainname, machines[machidx].password,
			users[useridx1].username,
			users[useridx1].password,
			users[useridx2].username,
			users[useridx2].password);
	return False;
}
#else 

BOOL torture_rpc_login(void)
{
	return False;
}
#endif