/* 
 *  Unix SMB/CIFS implementation.
 *  MS-RPC client library implementation (LSA pipe)
 *  Copyright (C) Chris Nicholls              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 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 "libmsrpc.h"
#include "libsmb_internal.h"

int cac_LsaOpenPolicy( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
		       struct LsaOpenPolicy *op )
{
	SMBCSRV *srv = NULL;
	POLICY_HND *policy = NULL;
	struct rpc_pipe_client *pipe_hnd = NULL;

	if ( !hnd )
		return CAC_FAILURE;

	if ( !hnd->_internal.ctx ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !mem_ctx || !op ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	op->out.pol = NULL;

	srv = cac_GetServer( hnd );
	if ( !srv ) {
		hnd->status = NT_STATUS_INVALID_CONNECTION;
		return CAC_FAILURE;
	}

	/*see if there is already an active session on this pipe, if not then open one */
	if ( !hnd->_internal.pipes[PI_LSARPC] ) {
		pipe_hnd =
			cli_rpc_pipe_open_noauth( srv->cli, PI_LSARPC,
						  &hnd->status );

		if ( !pipe_hnd ) {
			hnd->status = NT_STATUS_UNSUCCESSFUL;
			return CAC_FAILURE;
		}

		hnd->_internal.pipes[PI_LSARPC] = True;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	policy = TALLOC_P( mem_ctx, POLICY_HND );
	if ( !policy ) {
		errno = ENOMEM;
		hnd->status = NT_STATUS_NO_MEMORY;
		return CAC_FAILURE;
	}

	/*need to make sure that our nt status is good otherwise check might fail below */
	hnd->status = NT_STATUS_OK;

	if ( hnd->_internal.srv_level >= SRV_WIN_2K ) {

		/*try using open_policy2, if this fails try again in next block using open_policy, if that works then adjust hnd->_internal.srv_level */

		/*we shouldn't need to modify the access mask to make it work here */
		hnd->status =
			rpccli_lsa_open_policy2( pipe_hnd, mem_ctx,
						 op->in.security_qos,
						 op->in.access, policy );

	}

	if ( hnd->_internal.srv_level < SRV_WIN_2K
	     || !NT_STATUS_IS_OK( hnd->status ) ) {
		hnd->status =
			rpccli_lsa_open_policy( pipe_hnd, mem_ctx,
						op->in.security_qos,
						op->in.access, policy );

		if ( hnd->_internal.srv_level > SRV_WIN_NT4
		     && NT_STATUS_IS_OK( hnd->status ) ) {
			/*change the server level to 1 */
			hnd->_internal.srv_level = SRV_WIN_NT4;
		}

	}

	if ( !NT_STATUS_IS_OK( hnd->status ) ) {
		return CAC_FAILURE;
	}

	op->out.pol = policy;

	return CAC_SUCCESS;
}

int cac_LsaClosePolicy( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
			POLICY_HND * pol )
{

	struct rpc_pipe_client *pipe_hnd = NULL;

	if ( !hnd )
		return CAC_FAILURE;

	if ( !pol )
		return CAC_SUCCESS;	/*if the policy handle doesnt exist then it's already closed */

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	hnd->status = rpccli_lsa_Close( pipe_hnd, mem_ctx, pol );

	TALLOC_FREE( pol );

	if ( !NT_STATUS_IS_OK( hnd->status ) )
		return CAC_FAILURE;

	return CAC_SUCCESS;
}

int cac_LsaGetNamesFromSids( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
			     struct LsaGetNamesFromSids *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;

	int result = -1;

	int i;

	/*buffers for outputs */
	char **domains = NULL;
	char **names = NULL;
	enum lsa_SidType *types = NULL;

	CacSidInfo *sids_out = NULL;
	DOM_SID *unknown_out = NULL;
	int num_unknown = 0;

	int num_sids;

	int found_idx;
	int unknown_idx;

	if ( !hnd )
		return CAC_FAILURE;

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !mem_ctx || !op || !op->in.pol || !op->in.sids ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	num_sids = op->in.num_sids;

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}



	/*now actually lookup the names */
	hnd->status =
		rpccli_lsa_lookup_sids( pipe_hnd, mem_ctx, op->in.pol,
					op->in.num_sids, op->in.sids,
					&domains, &names, &types );

	if ( NT_STATUS_IS_OK( hnd->status ) ) {
		/*this is the easy part, just make the out.sids array */
		if (num_sids) {
			sids_out = TALLOC_ARRAY( mem_ctx, CacSidInfo, num_sids );
			if ( !sids_out ) {
				errno = ENOMEM;
				hnd->status = NT_STATUS_NO_MEMORY;
				return CAC_FAILURE;
			}
		} else {
			sids_out = NULL;
		}

		for ( i = 0; i < num_sids; i++ ) {
			sids_out[i].sid = op->in.sids[i];
			sids_out[i].name = names[i];
			sids_out[i].domain = domains[i];
		}

		result = CAC_SUCCESS;
	} else if ( NT_STATUS_V( hnd->status ) ==
		    NT_STATUS_V( STATUS_SOME_UNMAPPED ) ) {
		/*first find out how many couldn't be looked up */

		for ( i = 0; i < num_sids; i++ ) {
			if ( names[i] == NULL ) {
				num_unknown++;
			}
		}

		if ( num_unknown >= num_sids ) {
			hnd->status = NT_STATUS_UNSUCCESSFUL;
			return CAC_FAILURE;
		}

		if ( num_sids - num_unknown) {
			sids_out =
				TALLOC_ARRAY( mem_ctx, CacSidInfo,
				      ( num_sids - num_unknown ) );
			if ( !sids_out ) {
				errno = ENOMEM;
				hnd->status = NT_STATUS_NO_MEMORY;
				return CAC_FAILURE;
			}
		} else {
			sids_out = NULL;
		}

		if (num_unknown) {
			unknown_out = TALLOC_ARRAY( mem_ctx, DOM_SID, num_unknown );
			if ( !unknown_out ) {
				errno = ENOMEM;
				hnd->status = NT_STATUS_NO_MEMORY;
				return CAC_FAILURE;
			}
		} else {
			unknown_out = NULL;
		}
		found_idx = unknown_idx = 0;

		/*now we can actually do the real work */
		for ( i = 0; i < num_sids; i++ ) {
			if ( names[i] != NULL ) {
				sids_out[found_idx].sid = op->in.sids[i];
				sids_out[found_idx].name = names[i];
				sids_out[found_idx].domain = domains[i];

				found_idx++;
			} else {	/*then this one didnt work out */
				unknown_out[unknown_idx] = op->in.sids[i];

				unknown_idx++;
			}
		}

		result = CAC_PARTIAL_SUCCESS;
	} else {		/*then it failed for some reason */
		return CAC_FAILURE;
	}

	op->out.num_found = num_sids - num_unknown;
	op->out.sids = sids_out;
	op->out.unknown = unknown_out;

	return result;

}

int cac_LsaGetSidsFromNames( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
			     struct LsaGetSidsFromNames *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;
	int result = -1;

	int i;

	/*buffers for outputs */
	DOM_SID *sids = NULL;
	enum lsa_SidType *types = NULL;

	CacSidInfo *sids_out = NULL;
	char **unknown_out = NULL;
	int num_unknown = 0;

	int num_names;

	int found_idx = 0;
	int unknown_idx = 0;

	if ( !hnd )
		return CAC_FAILURE;

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !mem_ctx || !op || !op->in.pol || !op->in.names ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	num_names = op->in.num_names;

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}


	/*now actually lookup the names */
	hnd->status =
		rpccli_lsa_lookup_names( pipe_hnd, mem_ctx, op->in.pol,
					 num_names,
					 ( const char ** ) op->in.names, NULL,
					 &sids, &types );

	if ( NT_STATUS_IS_OK( hnd->status ) ) {
		/*this is the easy part, just make the out.sids array */
		if (num_names) {
			sids_out = TALLOC_ARRAY( mem_ctx, CacSidInfo, num_names );
			if ( !sids_out ) {
				errno = ENOMEM;
				hnd->status = NT_STATUS_NO_MEMORY;
				return CAC_FAILURE;
			}
		} else {
			sids_out = NULL;
		}

		for ( i = 0; i < num_names; i++ ) {
			sids_out[i].sid = sids[i];
			sids_out[i].name =
				talloc_strdup( mem_ctx, op->in.names[i] );
			sids_out[i].domain = NULL;
		}

		result = CAC_SUCCESS;
	} else if ( NT_STATUS_V( hnd->status ) ==
		    NT_STATUS_V( STATUS_SOME_UNMAPPED ) ) {
		/*first find out how many couldn't be looked up */

		for ( i = 0; i < num_names; i++ ) {
			if ( types[i] == SID_NAME_UNKNOWN ) {
				num_unknown++;
			}
		}

		if ( num_unknown >= num_names ) {
			hnd->status = NT_STATUS_UNSUCCESSFUL;
			return CAC_FAILURE;
		}

		if (num_names - num_unknown) {
			sids_out =
				TALLOC_ARRAY( mem_ctx, CacSidInfo,
				      ( num_names - num_unknown ) );
			if ( !sids_out ) {
				errno = ENOMEM;
				hnd->status = NT_STATUS_NO_MEMORY;
				return CAC_FAILURE;
			}
		} else {
			sids_out = NULL;
		}

		if (num_unknown) {
			unknown_out = TALLOC_ARRAY( mem_ctx, char *, num_unknown );
			if ( !unknown_out ) {
				errno = ENOMEM;
				hnd->status = NT_STATUS_NO_MEMORY;
				return CAC_FAILURE;
			}
		} else {
			unknown_out = NULL;
		}

		unknown_idx = found_idx = 0;

		/*now we can actually do the real work */
		for ( i = 0; i < num_names; i++ ) {
			if ( types[i] != SID_NAME_UNKNOWN ) {
				sids_out[found_idx].sid = sids[i];
				sids_out[found_idx].name =
					talloc_strdup( mem_ctx,
						       op->in.names[i] );
				sids_out[found_idx].domain = NULL;

				found_idx++;
			} else {	/*then this one didnt work out */
				unknown_out[unknown_idx] =
					talloc_strdup( mem_ctx,
						       op->in.names[i] );

				unknown_idx++;
			}
		}

		result = CAC_PARTIAL_SUCCESS;
	} else {		/*then it failed for some reason */
		return CAC_FAILURE;
	}

	op->out.num_found = num_names - num_unknown;
	op->out.sids = sids_out;
	op->out.unknown = unknown_out;

	return result;

}

int cac_LsaFetchSid( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
		     struct LsaFetchSid *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;
	int result = -1;

	if ( !hnd )
		return CAC_FAILURE;

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !mem_ctx || !op || !op->in.pol ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	op->out.local_sid = NULL;
	op->out.domain_sid = NULL;

	if ( ( op->in.info_class & CAC_LOCAL_INFO ) == CAC_LOCAL_INFO ) {
		DOM_SID *local_sid = NULL;
		char *dom_name = NULL;

		hnd->status =
			rpccli_lsa_query_info_policy( pipe_hnd, mem_ctx,
						      op->in.pol,
						      CAC_LOCAL_INFO,
						      &dom_name, &local_sid );

		if ( !NT_STATUS_IS_OK( hnd->status ) ) {
			result = CAC_FAILURE;
			goto domain;
		}

		op->out.local_sid = talloc( mem_ctx, CacSidInfo );
		if ( !op->out.local_sid ) {
			hnd->status = NT_STATUS_NO_MEMORY;
			result = CAC_FAILURE;
			goto domain;
		}

		op->out.local_sid->domain = dom_name;

		sid_copy( &op->out.local_sid->sid, local_sid );
		TALLOC_FREE( local_sid );
	}

      domain:

	if ( ( op->in.info_class & CAC_DOMAIN_INFO ) == CAC_DOMAIN_INFO ) {
		DOM_SID *domain_sid;
		char *dom_name;

		hnd->status =
			rpccli_lsa_query_info_policy( pipe_hnd, mem_ctx,
						      op->in.pol,
						      CAC_DOMAIN_INFO,
						      &dom_name,
						      &domain_sid );
		if ( !NT_STATUS_IS_OK( hnd->status ) ) {
			/*if we succeeded above, report partial success */
			result = CAC_FAILURE;
			goto done;
		} else if ( result == CAC_FAILURE ) {
			/*if we failed above but succeded here then report partial success */
			result = CAC_PARTIAL_SUCCESS;
		}

		op->out.domain_sid = talloc( mem_ctx, CacSidInfo );
		if ( !op->out.domain_sid ) {
			hnd->status = NT_STATUS_NO_MEMORY;
			result = CAC_FAILURE;
			goto done;
		}

		op->out.domain_sid->domain = dom_name;
		sid_copy( &op->out.domain_sid->sid, domain_sid );
		TALLOC_FREE( domain_sid );
	}

      done:
	return result;
}

int cac_LsaQueryInfoPolicy( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
			    struct LsaQueryInfoPolicy *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;

	char *domain_name = NULL;
	char *dns_name = NULL;
	char *forest_name = NULL;
	struct GUID *domain_guid = NULL;
	DOM_SID *domain_sid = NULL;

	if ( !hnd )
		return CAC_FAILURE;

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !op || !op->in.pol ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	/*only works if info_class parm is 12 */
	hnd->status =
		rpccli_lsa_query_info_policy2( pipe_hnd, mem_ctx, op->in.pol,
					       12, &domain_name, &dns_name,
					       &forest_name, &domain_guid,
					       &domain_sid );

	if ( !NT_STATUS_IS_OK( hnd->status ) ) {
		return CAC_FAILURE;
	}

	op->out.domain_name = domain_name;
	op->out.dns_name = dns_name;
	op->out.forest_name = forest_name;
	op->out.domain_guid = domain_guid;
	op->out.domain_sid = domain_sid;

	return CAC_SUCCESS;
}

int cac_LsaEnumSids( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
		     struct LsaEnumSids *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;

	uint32 num_sids;
	DOM_SID *sids;

	if ( !hnd )
		return CAC_FAILURE;

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !op || !op->in.pol ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	hnd->status =
		rpccli_lsa_enum_sids( pipe_hnd, mem_ctx, op->in.pol,
				      &( op->out.resume_idx ),
				      op->in.pref_max_sids, &num_sids,
				      &sids );

	if ( !NT_STATUS_IS_OK( hnd->status ) ) {
		return CAC_FAILURE;
	}

	op->out.num_sids = num_sids;
	op->out.sids = sids;

	return CAC_SUCCESS;

}

int cac_LsaEnumAccountRights( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
			      struct LsaEnumAccountRights *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;

	uint32 count = 0;
	char **privs = NULL;

	if ( !hnd )
		return CAC_FAILURE;

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !op->in.pol ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	if ( !op->in.name && !op->in.sid ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( op->in.name && !op->in.sid ) {
		DOM_SID *user_sid = NULL;
		enum lsa_SidType *type;

		/*lookup the SID */
		hnd->status =
			rpccli_lsa_lookup_names( pipe_hnd, mem_ctx,
						 op->in.pol, 1,
						 ( const char ** ) &( op->in.
								      name ),
						 NULL, &user_sid, &type );

		if ( !NT_STATUS_IS_OK( hnd->status ) )
			return CAC_FAILURE;

		op->in.sid = user_sid;
	}

	hnd->status =
		rpccli_lsa_enum_account_rights( pipe_hnd, mem_ctx, op->in.pol,
						op->in.sid, &count, &privs );

	if ( !NT_STATUS_IS_OK( hnd->status ) ) {
		return CAC_FAILURE;
	}

	op->out.num_privs = count;
	op->out.priv_names = privs;

	return CAC_SUCCESS;
}

int cac_LsaEnumTrustedDomains( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
			       struct LsaEnumTrustedDomains *op )
{
	struct rpc_pipe_client *pipe_hnd;

	uint32 num_domains;
	char **domain_names;
	DOM_SID *domain_sids;

	if ( !hnd )
		return CAC_FAILURE;

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !op->in.pol ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	hnd->status =
		rpccli_lsa_enum_trust_dom( pipe_hnd, mem_ctx, op->in.pol,
					   &( op->out.resume_idx ),
					   &num_domains, &domain_names,
					   &domain_sids );

	if ( !NT_STATUS_IS_OK( hnd->status ) ) {
		return CAC_FAILURE;
	}

	op->out.num_domains = num_domains;
	op->out.domain_names = domain_names;
	op->out.domain_sids = domain_sids;

	return CAC_SUCCESS;
}

int cac_LsaOpenTrustedDomain( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
			      struct LsaOpenTrustedDomain *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;

	POLICY_HND *dom_pol = NULL;

	if ( !hnd )
		return CAC_FAILURE;

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !op->in.pol || !op->in.access || !op->in.domain_sid ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	dom_pol = talloc( mem_ctx, POLICY_HND );
	if ( !dom_pol ) {
		hnd->status = NT_STATUS_NO_MEMORY;
		errno = ENOMEM;
		return CAC_FAILURE;
	}

	hnd->status =
		rpccli_lsa_open_trusted_domain( pipe_hnd, mem_ctx, op->in.pol,
						op->in.domain_sid,
						op->in.access, dom_pol );

	if ( !NT_STATUS_IS_OK( hnd->status ) ) {
		return CAC_FAILURE;
	}

	op->out.domain_pol = dom_pol;

	return CAC_SUCCESS;
}

int cac_LsaQueryTrustedDomainInfo( CacServerHandle * hnd,
				   TALLOC_CTX * mem_ctx,
				   struct LsaQueryTrustedDomainInfo *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;

	LSA_TRUSTED_DOMAIN_INFO *dom_info;

	if ( !hnd )
		return CAC_FAILURE;

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !op->in.pol || !op->in.info_class ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	if ( !op->in.domain_sid && !op->in.domain_name ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( op->in.domain_sid ) {
		hnd->status =
			rpccli_lsa_query_trusted_domain_info_by_sid( pipe_hnd,
								     mem_ctx,
								     op->in.
								     pol,
								     op->in.
								     info_class,
								     op->in.
								     domain_sid,
								     &dom_info );
	} else if ( op->in.domain_name ) {
		hnd->status =
			rpccli_lsa_query_trusted_domain_info_by_name
			( pipe_hnd, mem_ctx, op->in.pol, op->in.info_class,
			  op->in.domain_name, &dom_info );
	}

	if ( !NT_STATUS_IS_OK( hnd->status ) ) {
		return CAC_FAILURE;
	}

	op->out.info = dom_info;

	return CAC_SUCCESS;

}

int cac_LsaEnumPrivileges( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
			   struct LsaEnumPrivileges *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;

	uint32 num_privs;
	char **priv_names;
	uint32 *high_bits;
	uint32 *low_bits;

	if ( !hnd ) {
		return CAC_FAILURE;
	}

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !op || !op->in.pol ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	hnd->status =
		rpccli_lsa_enum_privilege( pipe_hnd, mem_ctx, op->in.pol,
					   &( op->out.resume_idx ),
					   op->in.pref_max_privs, &num_privs,
					   &priv_names, &high_bits,
					   &low_bits );

	if ( !NT_STATUS_IS_OK( hnd->status ) ) {
		return CAC_FAILURE;
	}

	op->out.num_privs = num_privs;
	op->out.priv_names = priv_names;
	op->out.high_bits = high_bits;
	op->out.low_bits = low_bits;

	return CAC_SUCCESS;
}

int cac_LsaOpenAccount( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
			struct LsaOpenAccount *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;

	POLICY_HND *user_pol = NULL;

	if ( !hnd ) {
		return CAC_FAILURE;
	}

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !op || !op->in.pol ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	if ( !op->in.sid && !op->in.name ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	/*look up the user's SID if we have to */
	if ( op->in.name && !op->in.sid ) {
		DOM_SID *user_sid = NULL;
		enum lsa_SidType *type;

		/*lookup the SID */
		hnd->status =
			rpccli_lsa_lookup_names( pipe_hnd, mem_ctx,
						 op->in.pol, 1,
						 ( const char ** ) &( op->in.
								      name ),
						 NULL, &user_sid, &type );

		if ( !NT_STATUS_IS_OK( hnd->status ) )
			return CAC_FAILURE;

		op->in.sid = user_sid;
	}

	user_pol = talloc( mem_ctx, POLICY_HND );
	if ( !user_pol ) {
		hnd->status = NT_STATUS_NO_MEMORY;
		return CAC_FAILURE;
	}

	hnd->status =
		rpccli_lsa_open_account( pipe_hnd, mem_ctx, op->in.pol,
					 op->in.sid, op->in.access,
					 user_pol );

	if ( !NT_STATUS_IS_OK( hnd->status ) ) {
		TALLOC_FREE( user_pol );
		return CAC_FAILURE;
	}

	op->out.user = user_pol;

	return CAC_SUCCESS;
}


int cac_LsaAddPrivileges( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
			  struct LsaAddPrivileges *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;

	DOM_SID *user_sid = NULL;
	enum lsa_SidType *type = NULL;

	if ( !hnd ) {
		return CAC_FAILURE;
	}

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !op || !op->in.pol || !op->in.priv_names ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	if ( !op->in.sid && !op->in.name ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( op->in.name && !op->in.sid ) {
		/*lookup the SID */
		hnd->status =
			rpccli_lsa_lookup_names( pipe_hnd, mem_ctx,
						 op->in.pol, 1,
						 ( const char ** ) &( op->in.
								      name ),
						 NULL, &user_sid, &type );

		if ( !NT_STATUS_IS_OK( hnd->status ) )
			return CAC_FAILURE;

		op->in.sid = user_sid;
	}

	hnd->status =
		rpccli_lsa_add_account_rights( pipe_hnd, mem_ctx, op->in.pol,
					       *( op->in.sid ),
					       op->in.num_privs,
					       ( const char ** ) op->in.
					       priv_names );

	if ( !NT_STATUS_IS_OK( hnd->status ) ) {
		return CAC_FAILURE;
	}

	return CAC_SUCCESS;
}

int cac_LsaRemovePrivileges( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
			     struct LsaRemovePrivileges *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;

	DOM_SID *user_sid = NULL;
	enum lsa_SidType *type = NULL;

	if ( !hnd ) {
		return CAC_FAILURE;
	}

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !op || !op->in.pol || !op->in.priv_names ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	if ( !op->in.sid && !op->in.name ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( op->in.name && !op->in.sid ) {
		/*lookup the SID */
		hnd->status =
			rpccli_lsa_lookup_names( pipe_hnd, mem_ctx,
						 op->in.pol, 1,
						 ( const char ** ) &( op->in.
								      name ),
						 NULL, &user_sid, &type );

		if ( !NT_STATUS_IS_OK( hnd->status ) )
			return CAC_FAILURE;

		op->in.sid = user_sid;
	}

	hnd->status =
		rpccli_lsa_remove_account_rights( pipe_hnd, mem_ctx,
						  op->in.pol, *( op->in.sid ),
						  False, op->in.num_privs,
						  ( const char ** ) op->in.
						  priv_names );

	if ( !NT_STATUS_IS_OK( hnd->status ) ) {
		return CAC_FAILURE;
	}

	return CAC_SUCCESS;
}

int cac_LsaClearPrivileges( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
			    struct LsaClearPrivileges *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;

	DOM_SID *user_sid = NULL;
	enum lsa_SidType *type = NULL;

	if ( !hnd ) {
		return CAC_FAILURE;
	}

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !op || !op->in.pol ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	if ( !op->in.sid && !op->in.name ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( op->in.name && !op->in.sid ) {
		/*lookup the SID */
		hnd->status =
			rpccli_lsa_lookup_names( pipe_hnd, mem_ctx,
						 op->in.pol, 1,
						 ( const char ** ) &( op->in.
								      name ),
						 NULL, &user_sid, &type );

		if ( !NT_STATUS_IS_OK( hnd->status ) )
			return CAC_FAILURE;

		op->in.sid = user_sid;
	}

	hnd->status =
		rpccli_lsa_remove_account_rights( pipe_hnd, mem_ctx,
						  op->in.pol, *( op->in.sid ),
						  True, 0, NULL );

	if ( !NT_STATUS_IS_OK( hnd->status ) ) {
		return CAC_FAILURE;
	}

	return CAC_SUCCESS;
}

int cac_LsaSetPrivileges( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
			  struct LsaAddPrivileges *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;

	DOM_SID *user_sid = NULL;
	enum lsa_SidType *type = NULL;

	if ( !hnd ) {
		return CAC_FAILURE;
	}

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !op || !op->in.pol || !op->in.priv_names ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	if ( !op->in.sid && !op->in.name ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		return CAC_FAILURE;
	}

	if ( op->in.name && !op->in.sid ) {
		/*lookup the SID */
		hnd->status =
			rpccli_lsa_lookup_names( pipe_hnd, mem_ctx,
						 op->in.pol, 1,
						 ( const char ** ) &( op->in.
								      name ),
						 NULL, &user_sid, &type );

		if ( !NT_STATUS_IS_OK( hnd->status ) )
			return CAC_FAILURE;

		op->in.sid = user_sid;
	}

	/*first remove all privileges */
	hnd->status =
		rpccli_lsa_remove_account_rights( pipe_hnd, mem_ctx,
						  op->in.pol, *( op->in.sid ),
						  True, 0, NULL );

	if ( !NT_STATUS_IS_OK( hnd->status ) ) {
		return CAC_FAILURE;
	}

	hnd->status =
		rpccli_lsa_add_account_rights( pipe_hnd, mem_ctx, op->in.pol,
					       *( op->in.sid ),
					       op->in.num_privs,
					       ( const char ** ) op->in.
					       priv_names );

	if ( !NT_STATUS_IS_OK( hnd->status ) ) {
		return CAC_FAILURE;
	}

	return CAC_SUCCESS;
}

int cac_LsaGetSecurityObject( CacServerHandle * hnd, TALLOC_CTX * mem_ctx,
			      struct LsaGetSecurityObject *op )
{
	struct rpc_pipe_client *pipe_hnd = NULL;

	/*this is taken from rpcclient/cmd_lsarpc.c */
	uint16 info_level = 4;

	SEC_DESC_BUF *sec_out = NULL;

	if ( !hnd ) {
		return CAC_FAILURE;
	}

	if ( !hnd->_internal.ctx || !hnd->_internal.pipes[PI_LSARPC] ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	if ( !op || !op->in.pol ) {
		hnd->status = NT_STATUS_INVALID_PARAMETER;
		return CAC_FAILURE;
	}

	pipe_hnd = cac_GetPipe( hnd, PI_LSARPC );
	if ( !pipe_hnd ) {
		hnd->status = NT_STATUS_INVALID_HANDLE;
		return CAC_FAILURE;
	}

	hnd->status =
		rpccli_lsa_query_secobj( pipe_hnd, mem_ctx, op->in.pol,
					 info_level, &sec_out );

	if ( !NT_STATUS_IS_OK( hnd->status ) )
		return CAC_FAILURE;

	op->out.sec = sec_out;

	return CAC_FAILURE;
}