/*
 *  lib/xssl.c
 *
 *  Wrapper of SSL APIs
 *
 *  Copyright (C) 2004  Phoenix Grid Computing Project
 *                      ( http://www.logos.ic.i.u-tokyo.ac.jp/phoenix/ )
 *
 *  2004-11-26  Implemented basic functions 
 *              by Kaneda Kenji
 */


#include "common.h"
#include "debug.h"


/* Read a number of bytes from file <filename> and adds them to the
 * PRNG. If <max_bytes> is non-negative, up to to max_bytes are read;
 * starting with OpenSSL 0.9.5, if <max_bytes> is -1, the complete
 * file is read. */
int
xRAND_load_file ( const char *filename, long max_bytes )
{
	int ret;

	assert ( filename != NULL );

	ret = RAND_load_file ( filename, max_bytes );

	if ( ret == 0 )
		ssl_failure ( "RAND_load_file" );

	return ret;
}

/* Generate a key pair and returns it in a newly allocated RSA
 * structure. The pseudo-random number generator must be
 * seeded prior to calling RSA_generate_key(). */
RSA *
xRSA_generate_key ( int num, unsigned long e, void (*callback)(int,int,void *), void *cb_arg )
{
	RSA *ret;

	ret = RSA_generate_key( num, e, callback, cb_arg );

	if ( ret == NULL )
		ssl_failure ( "RSA_generate_key" );

	return ret;
}

/* Expect <a> to contain DSA parameters. It generates a new key pair
 * and stores it in <a->pub_key> and <a->priv_key>. */
void
xDSA_generate_key ( DSA *a )
{
	int ret;

	assert ( a != NULL );
	
	ret = DSA_generate_key ( a );

	if ( ret == 0 )
		ssl_failure ( "DSA_generate_key" );
}

/* Generate primes p and q and a generator g for use in the DSA. */
DSA*
xDSA_generate_parameters ( int bits, unsigned char *seed,
			   int seed_len, int *counter_ret, unsigned long *h_ret,
			   void (*callback)(int, int, void *), void *cb_arg )
{
	DSA *ret;

	ret = DSA_generate_parameters ( bits, seed, seed_len, counter_ret, h_ret,
					callback, cb_arg );

	if ( ret == NULL )
		ssl_failure ( "DSA_generate_parameters" );	

	return ret;
}

/* Duplicates DSA parameters/keys as DH parameters/keys. q is lost
 * during that conversion, but the resulting DH parameters contain its
 * length. */
DH *
xDSA_dup_DH ( const DSA *r )
{
	DH *ret;

	assert ( r != NULL );

	ret = DSA_dup_DH ( r );

	if ( ret == NULL )
		ssl_failure ( "DSA_dup_DH" );

	return ret;
}

/* Create a new file BIO with mode <mode> the meaning of <mode> is the
 * same as the stdio function fopen().
 * [NOTE] The BIO_CLOSE flag is set on the returned BIO. */
BIO *
xBIO_new_file ( const char *filename, const char *mode )
{
	BIO *ret;

	assert ( filename != NULL );
	
	ret = BIO_new_file ( filename, mode );

	if ( ret == NULL )
		ssl_failure ( "BIO_new_file" );

	return ret;
}

/* Create a file BIO wrapping <stream>. <flags> can be: BIO_CLOSE,
 * BIO_NOCLOSE (the close flag) BIO_FP_TEXT (sets the underlying
 * stream to text mode, default is binary: this only has any effect
 * under Win32). */
BIO *
xBIO_new_fp ( FILE *stream, int flags )
{
	BIO *ret;

	assert ( stream != NULL );
	
	ret = BIO_new_fp ( stream, flags );

	if ( ret == NULL )
		ssl_failure ( "BIO_new_fp" );

	return ret;
}

/* Return a socket BIO using <sock> and <close_flag>. */
BIO *
xBIO_new_socket ( int sock, int close_flag )
{
	BIO *ret;
	
	ret = BIO_new_socket ( sock, close_flag );

	if ( ret == NULL )
		ssl_failure ( "BIO_new_socket" );

	return ret;
}

/* Create a new SSL_CTX object as framework to establish TLS/SSL
 * enabled connections. */
SSL_CTX *
xSSL_CTX_new ( SSL_METHOD *method )
{
	SSL_CTX *ret;

	assert ( method != NULL );

	ret = SSL_CTX_new ( method );

	if ( ret == NULL )
		ssl_failure ( "SSL_CTX_new" );

	return ret;
}

/* Load the first certificate stored in <file> into <ctx>. The
 * formatting <type> of the certificate must be specified from the
 * known types SSL_FILETYPE_PEM, SSL_FILETYPE_ASN1.
 * SSL_use_certificate_file () loads the certificate from <file> into ssl. 
 * [NOTE] SSL_CTX_use_certificate_chain_file () should be preferred. */
void
xSSL_CTX_use_certificate_file (SSL_CTX *ctx, const char *file, int type)
{
	int ret;

	assert ( ctx != NULL );
	assert ( file != NULL );

	ret = SSL_CTX_use_certificate_file ( ctx, file, type );

	if ( ret != 1 )
		ssl_failure ( "SSL_CTX_use_certificate_file" );
}

/* Load a certificate chain from <file> into <ctx>. The certificates
 * must be in PEM format and must be sorted starting with the
 * subject's certificate  (actual client or server certificate),
 * followed by intermediate CA certificates if applicable, and ending
 * at the highest level  (root) CA. */
void
xSSL_CTX_use_certificate_chain_file ( SSL_CTX *ctx, const char *file )
{
	int ret;

	assert ( ctx != NULL );
	assert ( file != NULL );

	ret = SSL_CTX_use_certificate_chain_file ( ctx, file );

	if ( ret != 1 )
		ssl_failure ( "SSL_CTX_use_certificate_chain_file" );
}



/* Add the first private key found in <file> to <ctx>. The formatting
 * <type> of the certificate must be specified from the known types
 * SSL_FILETYPE_PEM, SSL_FILETYPE_ASN1. */
void
xSSL_CTX_use_PrivateKey_file ( SSL_CTX *ctx, const char *file, int type )
{
	int ret;

	assert ( ctx != NULL );
	assert ( file != NULL );

	ret = SSL_CTX_use_PrivateKey_file ( ctx, file, type );

	if ( ret != 1 )
		ssl_failure ( "SSL_CTX_use_PrivateKey_file" );
}

/* Checks the consistency of a private key with the corresponding
 * certificate loaded into <ctx>. */
void
xSSL_CTX_check_private_key ( SSL_CTX *ctx )
{
	int ret;

	assert ( ctx != NULL );

	ret = SSL_CTX_check_private_key ( ctx );

	if ( ret != 1 )
		ssl_failure ( "SSL_CTX_check_private_key" );
}

/* Specify the locations for <ctx>, at which CA certificates for
 * verification purposes are located. The certificates available via
 * <CAfile> and <CApath> are trusted. */
void
xSSL_CTX_load_verify_locations ( SSL_CTX *ctx, const char *CAfile, const char *CApath )
{
	int ret;

	assert ( ctx != NULL );

	ret = SSL_CTX_load_verify_locations ( ctx, CAfile, CApath );

	if ( ret != 1 )
		ssl_failure ( "SSL_CTX_load_verify_locations" );
}

/* Set DH parameters to be used to be <dh>.
 * [NOTE] The key is inherited by all <ssl> objects created from
 * <ctx>. */
long
xSSL_CTX_set_tmp_dh ( SSL_CTX *ctx, DH *dh )
{
	int ret;

	assert ( ctx != NULL );
	assert ( dh != NULL );

	ret = SSL_CTX_set_tmp_dh ( ctx, dh );

	if ( ret == 0 )
		ssl_failure ( "SSL_CTX_set_tmp_dh" );

	return ret;
}

/* Set the temporary/ephemeral RSA key to be used to be <rsa>. 
 * [NOTE] The key is inherited by all SSL objects newly created from
 * <ctx>. */
long
xSSL_CTX_set_tmp_rsa ( SSL_CTX *ctx, RSA *rsa )
{
	int ret;

	assert ( ctx != NULL );
	assert ( rsa != NULL );

	ret = SSL_CTX_set_tmp_rsa ( ctx, rsa );

	if ( ret == 0 )
		ssl_failure ( "SSL_CTX_set_tmp_rsa" );	

	return ret;
}

/* Set the context <sid_ctx> of length <sid_ctx_len> within which a
 * session can be reused for the <ctx> object. */
void
xSSL_CTX_set_session_id_context ( SSL_CTX *ctx, const unsigned char *sid_ctx, unsigned int sid_ctx_len )
{
	int ret;
	
	assert ( ctx != NULL );
	assert ( sid_ctx != NULL );

	ret = SSL_CTX_set_session_id_context ( ctx, sid_ctx, sid_ctx_len );
	
	if ( ret != 1 )
		ssl_failure ( "SSL_CTX_set_session_id_context" );
}

/* Read certificates from file and returns a STACK_OF(X509_NAME) with
 * the subject names found. */
STACK_OF ( X509_NAME ) *
xSSL_load_client_CA_file ( const char *file )
{
	STACK_OF ( X509_NAME ) *ret;

	assert ( file != NULL );
	
	ret = SSL_load_client_CA_file ( file );
	
	if ( ret == NULL )
		ssl_failure ( "SSL_load_client_CA_file" );

	return ret;
}

/* Create a new SSL structure which is needed to hold the data for a
 * TLS/SSL connection. The new structure inherits the settings of the
 * underlying context <ctx>: connection method  (SSLv2/v3/TLSv1),
 * options, verification settings, timeout settings. */
SSL *
xSSL_new ( SSL_CTX *ctx )
{
	SSL *ret;
	
	assert ( ctx != NULL );
	
	ret = SSL_new ( ctx );

	if ( ret == NULL )
		ssl_failure ( "SSL_new" );

	return ret;
}

/* Initiate the TLS/SSL handshake with a server. The communication
 * channel must already have been set and assigned to the <ssl> by
 * setting an underlying BIO. */
int
xSSL_connect ( SSL *ssl )
{
	int ret;

	assert ( ssl != NULL );

	ret = SSL_connect ( ssl );

/*
    非ブロックの場合のエラーの識別
     ( socket 版とうまく対応させる )

    If the underlying BIO is non-blocking, SSL_connect () will also return when
       the underlying BIO could not satisfy the needs of SSL_connect () to con-
       tinue the handshake, indicating the problem by the return value -1.  In
       this case a call to SSL_get_error () with the return value of SSL_connect ()
       will yield SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. The calling pro-
       cess then must repeat the call after taking appropriate action to satisfy
       the needs of SSL_connect ().  The action depends on the underlying BIO.
       When using a non-blocking socket, nothing is to be done, but select () can
       be used to check for the required condition. When using a buffering BIO,
       like a BIO pair, data must be written into or retrieved out of the BIO
       before being able to continue.

*/
	
	return ret;
}

/* Waits for a TLS/SSL client to initiate the TLS/SSL handshake.  
 * [NOTE] The communication channel must already have been set and
 * assigned to the <ssl> by setting an underlying BIO. */
int
xSSL_accept ( SSL *ssl )
{
	int ret;

	assert ( ssl != NULL );

	ret = SSL_accept ( ssl );

	/*
	  非ブロックの場合のエラーの識別
	  ( socket 版とうまく対応させる )
	*/
	
	return ret;
}

/* Try to read <num> bytes from the specified <ssl> into the buffer
 * <buf>. */
int
xSSL_read ( SSL *ssl, void *buf, int num )
{
	int ret;

	assert ( ssl != NULL );
	assert ( buf != NULL );

	ret = SSL_read ( ssl, buf, num );

	/* エラーの振り分け */

	return ret;
}

/* Write <num> bytes from the buffer <buf> into the specified <ssl>
 * connection. */
int
xSSL_write ( SSL *ssl, const void *buf, int num )
{
	int ret;

	assert ( ssl != NULL );
	assert ( buf != NULL );

	ret = SSL_write ( ssl, buf, num );

	/* エラーの振り分け */

	return ret;
}

/* Shut down an active TLS/SSL connection. It sends the "close notify"
 * shutdown alert to the peer. */
int
xSSL_shutdown ( SSL *ssl, int how )
{
	int ret;

	ret = SSL_shutdown ( ssl );
	
	if ( ret == -1 )
		return ret;

	ret = SSL_shutdown ( ssl );

	return ret;
}
		
/*
	assert ( ssl != NULL );

	switch ( how ) {
	case SHUT_RD:
		fatal_failure ( "SSL_shutdown_: not supported. ");
		break;

	case SHUT_WR:
		ret = SSL_shutdown ( ssl );
		break;

	case SHUT_RDWR:
		ret = SSL_shutdown ( ssl );

		if ( ret != -1 ) {

			int state;
			state = SSL_get_shutdown ( ssl );

			
			ret = SSL_shutdown ( ssl );
		

		}
		break;
		
	default:
		fatal_failure ( "SSL_shutdown_" );
		break;
	}
*/


/* Retrieve the next index matching <nid> after <lastpos>. 
 * [NOTE] <lastpos> should initially be set to -1. */
int
xX509_NAME_get_index_by_NID ( X509_NAME *name, int nid, int lastpos )
{

	int ret;

	ret = X509_NAME_get_index_by_NID ( name, nid, lastpos );

	if ( ret == -1 ) /* there are no more entries. */
		ssl_failure ( "X509_NAME_get_index_by_NID" );

	return ret;
}
