////////////////////////////////////////////////////////////
//
//     	Implementation of a class
//	DceCallee
//
//      Copyright 2001 
//
////////////////////////////////////////////////////////////

#include <string.h>
#include <stdio.h>
#include <dce/dce_error.h>
#include <dce/binding.h>
#include <dce/pgo.h>
#include "DceCallee.h"

#define OUT stdout

const int CLIENT_PRINCIPAL_NAME_MAX_SIZE = 10000;

	// DceCalleeReport

int DceCalleeReport::set_name(unsigned_char_t * z)
{
	if(!z) return 0;
	
	strncpy(client_principal_name,(char*)z,CLIENT_PRINCIPAL_NAME_SIZE);
	
	int len = strlen((char*) z);
	
	if(len <= CLIENT_PRINCIPAL_NAME_SIZE) return 0;
	
	if( len > CLIENT_PRINCIPAL_NAME_MAX_SIZE) return 2;
	
	client_principal_name_overflow = new char[len - CLIENT_PRINCIPAL_NAME_SIZE +1];
	
	strcpy(client_principal_name_overflow,(char*)z + CLIENT_PRINCIPAL_NAME_SIZE);

	return 0;
}


	// DceCallee

DceCallee::DceCallee():
level1_check(true),
level2_check(true),
server_principal_name(0),
option_count(0)
{
}

DceCallee::~DceCallee()
{
	delete server_principal_name;
}

int DceCallee::set_server_principal_name(const char *name)
{
	if(!name) return 1;
	
	delete server_principal_name; server_principal_name=0;
	
	int size = strlen( name ) + 1 ;
	
	server_principal_name = new unsigned_char_t[ size ];
	
	strcpy((char *)server_principal_name,name);
		
	return 0;
}


int DceCallee::allow_option(const DceCalleeOption& z)
{
	if(option_count>=10) return 1;
	
	options[option_count++]=z;
	
	return 0;
}


void DceCallee::check_level1(DceCalleeSecurityData& z,DceCalleeReport& report)
{
	error_status_t s=0;
	
	bool flag=false;
	for(int i=0;i<option_count;++i)
	{
		if(options[i].protection_level != rpc_c_protect_level_none &&
			options[i].protection_level != z.protection_level)
			continue;
			   
		if(options[i].authentication_service != rpc_c_authn_none &&
			 options[i].authentication_service != z.authentication_service)
			 continue;
			   
		if(options[i].authorization_service != rpc_c_authz_none &&
			 options[i].authorization_service != z.authorization_service)
			 continue;
			   
		flag=true;
						   
		break;
	}
		
	if(option_count > 0 && !flag)
		report.authorized=false;
		
	if(report.authorized)
	{
		if(FALSE==sec_cred_is_authenticated(z.callers_identity,&s))
			report.authorized=false;
		
		if(s!=rpc_s_ok)
		{
			report.authorized=false;
			dce_log("can't authenticate",s);
		}
	}	
	
		
	unsigned_char_t *client_principal_name=0;
	
	sec_cred_get_client_princ_name(z.callers_identity,&client_principal_name,&s);
	
	if(s!=rpc_s_ok )
	{
		sec_rgy_handle_t sec_context=0;
		unsigned_char_t *client_site_name = 0;  // local "/.:"
		uuid_t *id=0;
		sec_rgy_name_t pgo_name;
		sec_cred_pa_handle_t pa_handle;
		sec_id_pa_t *sec_id_handle;
		
		pa_handle=sec_cred_get_initiator(
		z.callers_identity,
		&s);
		
		if(s==rpc_s_ok )
		{
			sec_id_handle=sec_cred_get_pa_data(
			pa_handle,
			&s);
		
			id=&(sec_id_handle->principal.uuid);
		}
		
		if(s==rpc_s_ok )
		sec_rgy_site_open(
		client_site_name,
		&sec_context,
		&s);
		
		if(s==rpc_s_ok )
		sec_rgy_pgo_id_to_name(
		sec_context,
		sec_rgy_domain_person,
		id,
		pgo_name,
		&s);
		
		if(s!=rpc_s_ok )
			dce_log("can't get client principal name",s);
		else
		{
			client_principal_name=(unsigned_char_t*)pgo_name;
			if( report.set_name(client_principal_name) )
				report.authorized=false;	
		}
		
		sec_rgy_site_close(
		sec_context,
		&s);
		
	}
	else	
	{
		if( report.set_name(client_principal_name) )
			report.authorized=false;	
	}
		

	
	if(report.authorized)
		report.authorized=check_client_principal_name((char*)client_principal_name);
		
		
	if(server_principal_name && z.server_principal_name && report.authorized)
	{
		if( strcmp((char*) server_principal_name, (char*)z.server_principal_name) )
			report.authorized=false;
	}
				
									
}
	
void DceCallee::start_dce_response(handle_t h,DceCalleeReport& report,error_status_t *s)
{
	DceCalleeSecurityData z;
	
	rpc_binding_inq_auth_caller(
	h,
	&z.callers_identity,
	&z.server_principal_name,
	&z.protection_level,
	&z.authentication_service,
	&z.authorization_service,
	s);
	
	if(*s!=rpc_s_ok)
		dce_log("can't get caller data",*s);
	
	
	if(*s==rpc_s_ok && level1_check)
	check_level1(z,report);
		
	if(*s==rpc_s_ok && level1_check && level2_check && report.authorized)
	check_level2(h,z,report,s);
		
}

int DceCallee::dce_log(const char *message, error_status_t ss)
{
	dce_error_string_t error_text;
	int s=0;

#ifdef OUT
	
	dce_error_inq_text(ss,error_text,&s);
	
	if(!s)
	fprintf(OUT,"%s; dce call error: %s\n",message,(char*)error_text);
	else
	fprintf(OUT,"%s; dce call error: %d\n",message,(int)ss);
	
#endif
	
	
	return 0;
}