/* ssl/s2_clnt.c */
/* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au)
 * All rights reserved.
 *
 * This package is an SSL implementation written
 * by Eric Young (eay@mincom.oz.au).
 * The implementation was written so as to conform with Netscapes SSL.
 * 
 * This library is free for commercial and non-commercial use as long as
 * the following conditions are aheared to.  The following conditions
 * apply to all code found in this distribution, be it the RC4, RSA,
 * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
 * included with this distribution is covered by the same copyright terms
 * except that the holder is Tim Hudson (tjh@mincom.oz.au).
 * 
 * Copyright remains Eric Young's, and as such any Copyright notices in
 * the code are not to be removed.
 * If this package is used in a product, Eric Young should be given attribution
 * as the author of the parts of the library used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    "This product includes cryptographic software written by
 *     Eric Young (eay@mincom.oz.au)"
 *    The word 'cryptographic' can be left out if the rouines from the library
 *    being used are not cryptographic related :-).
 * 4. If you include any Windows specific code (or a derivative thereof) from 
 *    the apps directory (application code) you must include an acknowledgement:
 *    "This product includes software written by Tim Hudson (tjh@mincom.oz.au)"
 * 
 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * 
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */

#include <stdio.h>
#ifdef NO_MD5
#include <md5.h>
#else
#include "md5.h"
#endif
#include "rand.h"
#include "buffer.h"
#include "objects.h"
#include "ssl_locl.h"
#include "envelope.h"

#ifndef NOPROTO
static int get_server_finished(SSL *s);
static int get_server_verify(SSL *s);
static int get_server_hello(SSL *s);
static int client_hello(SSL *s); 
static int client_master_key(SSL *s);
static int client_finished(SSL *s);
static int client_certificate(SSL *s);
static int choose_cipher(SSL *s);
#else
static int get_server_finished();
static int get_server_verify();
static int get_server_hello();
static int client_hello(); 
static int client_master_key();
static int client_finished();
static int client_certificate();
static int choose_cipher();
#endif

#define BREAK	break

int SSL2_connect(s)
SSL *s;
	{
	unsigned long l=time(NULL);
	BUF_MEM *buf=NULL;
	int ret= -1;
	void (*cb)()=NULL;

	RAND_seed((unsigned char *)&l,sizeof(l));
	ERR_clear_error();
	errno=0;

	if (s->info_callback != NULL)
		cb=s->info_callback;
	else if (s->ctx->info_callback != NULL)
		cb=s->ctx->info_callback;

	/* init things to blank */
	if (!SSL_in_init(s)) SSL_clear(s);

	for (;;)
		{
		int state;

#ifdef SSL_DEBUG
		SSL_TRACE(SSL_ERR,"state=%02X %d %s\n",
			s->state,s->s2.init_num,SSL_state_string(s));
#endif

		state=s->state;

		switch (s->state)
			{
		case SSL_ST_BEFORE:
		case SSL_ST_CONNECT:
			if ((buf=BUF_MEM_new()) == NULL)
				{
				ret= -1;
				goto end;
				}
			if (!BUF_MEM_grow(buf,
				SSL_MAX_RECORD_LENGTH_3_BYTE_HEADER))
				{
				ret= -1;
				goto end;
				}
			if (s->s2.init_buf) Free((char *)s->s2.init_buf);
			s->s2.init_buf=(unsigned char *)buf->data;
			Free((char *)buf);
			s->s2.init_num=0;
			s->state=SSL2_ST_SEND_CLIENT_HELLO_A;
			s->ctx->sess_connect++;
			BREAK;

		case SSL2_ST_SEND_CLIENT_HELLO_A:
		case SSL2_ST_SEND_CLIENT_HELLO_B:
			ret=client_hello(s);
			if (ret <= 0) goto end;
			s->s2.init_num=0;
			s->state=SSL2_ST_GET_SERVER_HELLO_A;
			BREAK;
		
		case SSL2_ST_GET_SERVER_HELLO_A:
		case SSL2_ST_GET_SERVER_HELLO_B:
			ret=get_server_hello(s);
			if (ret <= 0) goto end;
			s->s2.init_num=0;
			if (!s->hit) /* new session */
				{
				s->state=SSL2_ST_SEND_CLIENT_MASTER_KEY_A;
				BREAK; 
				}
			else
				{
				s->state=SSL2_ST_CLIENT_START_ENCRYPTION;
				break;
				}
	
		case SSL2_ST_SEND_CLIENT_MASTER_KEY_A:
		case SSL2_ST_SEND_CLIENT_MASTER_KEY_B:
			ret=client_master_key(s);
			if (ret <= 0) goto end;
			s->s2.init_num=0;
			s->state=SSL2_ST_CLIENT_START_ENCRYPTION;
			break;

		case SSL2_ST_CLIENT_START_ENCRYPTION:
			/* Ok, we now have all the stuff needed to
			 * start encrypting, so lets fire it up :-) */
			if (!ssl_enc_init(s,1))
				{
				ret= -1;
				goto end;
				}
			s->s2.clear_text=0;
			s->state=SSL2_ST_SEND_CLIENT_FINISHED_A;
			break;

		case SSL2_ST_SEND_CLIENT_FINISHED_A:
		case SSL2_ST_SEND_CLIENT_FINISHED_B:
			ret=client_finished(s);
			if (ret <= 0) goto end;
			s->s2.init_num=0;
			s->state=SSL2_ST_GET_SERVER_VERIFY_A;
			break;

		case SSL2_ST_GET_SERVER_VERIFY_A:
		case SSL2_ST_GET_SERVER_VERIFY_B:
			ret=get_server_verify(s);
			if (ret <= 0) goto end;
			s->s2.init_num=0;
			s->state=SSL2_ST_GET_SERVER_FINISHED_A;
			break;

		case SSL2_ST_GET_SERVER_FINISHED_A:
		case SSL2_ST_GET_SERVER_FINISHED_B:
			ret=get_server_finished(s);
			if (ret <= 0) goto end;
			break;

		case SSL2_ST_SEND_CLIENT_CERTIFICATE_A:
		case SSL2_ST_SEND_CLIENT_CERTIFICATE_B:
		case SSL2_ST_SEND_CLIENT_CERTIFICATE_C:
		case SSL2_ST_SEND_CLIENT_CERTIFICATE_D:
		case SSL2_ST_X509_GET_CLIENT_CERTIFICATE:
			ret=client_certificate(s);
			if (ret <= 0) goto end;
			s->s2.init_num=0;
			s->state=SSL2_ST_GET_SERVER_FINISHED_A;
			break;

		case SSL_ST_OK:
			Free(s->s2.init_buf);
			s->s2.init_buf=NULL;
			s->s2.init_num=0;
		/*	ERR_clear_error();*/

			/* If we want to cache session-ids in the client
			 * and we sucessfully add the session-id to the
			 * cache, and there is a callback, then pass it out.
			 * 26/11/96 - eay - only add if not a re-used session.
			 */

			if (!s->hit && (s->ctx->session_cache_mode &
				SSL_SESS_CACHE_CLIENT))
				{
				SSL_add_session(s->ctx,s->session);
				if (s->ctx->new_session_cb != NULL)
					{
/*					CRYPTO_add(&s->session->references,1,
						CRYPTO_LOCK_SSL_SESSION);*/

					if (!s->ctx->new_session_cb(
						s,s->session))
						{
						SSL_SESSION_free(s->session);
						}
					}
				}

			/* auto flush every 255 connections */
			if ((!(s->ctx->session_cache_mode &
				SSL_SESS_CACHE_NO_AUTO_CLEAR)) &&
				((s->ctx->sess_connect_good & 0xff) == 0))
				SSL_flush_sessions(s->ctx,time(NULL));

			ret=1;
			s->ctx->sess_connect_good++;
			goto end;
			break;
		default:
			SSLerr(SSL_F_SSL2_CONNECT,SSL_R_UNKNOWN_STATE);
			return(-1);
			/* break; */
			}

		if ((cb != NULL) && (s->state != state))
			cb(s,SSL_CB_CONNECT_LOOP,1);
		}
end:
	if (cb != NULL) 
		cb(s,SSL_CB_CONNECT_EXIT,ret);
	return(ret);
	}

static int get_server_hello(s)
SSL *s;
	{
	unsigned char *buf;
	unsigned char *p;
	CIPHER **cipher;
	CIPHER *tmp_c[SSL_NUM_CIPHERS];
	int i,j,num_c,nnum_c;

	buf=s->s2.init_buf;
	p=buf;
	if (s->state == SSL2_ST_GET_SERVER_HELLO_A)
		{
		i=SSL2_read(s,(char *)&(buf[s->s2.init_num]),11-s->s2.init_num);
		if (i < (11-s->s2.init_num)) 
			return(ssl_part_read(s,SSL_F_GET_SERVER_HELLO,i));

		if (*(p++) != SSL_MT_SERVER_HELLO)
			{
			if (p[-1] != SSL_MT_ERROR)
				{
				ssl_return_error(s,SSL_PE_UNDEFINED_ERROR);
				SSLerr(SSL_F_GET_SERVER_HELLO,
					SSL_R_READ_WRONG_PACKET_TYPE);
				}
			else
				SSLerr(SSL_F_GET_SERVER_HELLO,
					SSL_R_PEER_ERROR);
			return(-1);
			}
		s->hit=(*(p++))?1:0;
		s->tmp.cert_type= *(p++);
		n2s(p,i);
		if (i < s->version) s->version=i;
		n2s(p,i); s->tmp.cert_length=i;
		n2s(p,i); s->tmp.csl=i;
		n2s(p,i); s->tmp.conn_id_length=i;
		s->state=SSL2_ST_GET_SERVER_HELLO_B;
		s->s2.init_num=0;
		}

	/* SSL2_ST_GET_SERVER_HELLO_B */
	j=s->tmp.cert_length+s->tmp.csl+s->tmp.conn_id_length
		- s->s2.init_num;
	i=SSL2_read(s,(char *)&(buf[s->s2.init_num]),j);
	if (i != j) return(ssl_part_read(s,SSL_F_GET_SERVER_HELLO,i));

	/* things are looking good */

	p=buf;
	if (s->hit)
		{
		if ((s->tmp.cert_length != 0) ||
			/*(s->tmp.cert_type != 0) || */ (s->tmp.csl != 0))
			{
			/*return(-1);*/
			/* ignore these errors :-(
			 * SSLref does return a non-zero cert_type */
			}
		}
	else
		{
#ifdef undef
		/* very bad */
		memset(s->session->session_id,0,
			SSL_MAX_SSL_SESSION_ID_LENGTH_IN_BYTES);
		s->session->session_id_length=0;
		*/
#endif

		/* we need to do this incase we were trying to reuse a 
		 * client session but others are already reusing it.
		 * If this was a new 'blank' session ID, the session-id
		 * length will still be 0 */
		if (s->session->session_id_length > 0)
			{
			if (!ssl_get_new_session(s,0))
				{
				ssl_return_error(s,SSL_PE_UNDEFINED_ERROR);
				return(-1);
				}
			for (j=i=0; i<SSL_NUM_CIPHERS; i++)
				{
				if (ssl_ciphers[i].valid)
					s->session->ciphers[j++]=
						&(ssl_ciphers[i]);
				}
			s->session->num_ciphers=j;
			}

		if (ssl_set_certificate(s,s->tmp.cert_type,
			s->tmp.cert_length,p) <= 0)
			{
			ssl_return_error(s,SSL_PE_BAD_CERTIFICATE);
			return(-1);
			}
		p+=s->tmp.cert_length;

		if (s->tmp.csl == 0)
			{
			ssl_return_error(s,SSL_PE_NO_CIPHER);
			SSLerr(SSL_F_GET_SERVER_HELLO,SSL_R_NO_CIPHER_LIST);
			return(-1);
			}

		/* what we will do is copy the ciphers we are happy to
		 * use to a temp area and then check the server ones
		 * are put those that are in the session structure again. */
		cipher= &(s->session->ciphers[0]);
		memcpy(&(tmp_c[0]),cipher,sizeof(CIPHER *)*SSL_NUM_CIPHERS);
		num_c=s->session->num_ciphers;
		nnum_c=0;
		for (i=0; i<(int)s->tmp.csl; i+=3)
			{
			CIPHER c,*cp= &c,**cpp;

			c.c1=p[i];
			c.c2=p[i+1];
			c.c3=p[i+2];
			
			cpp=(CIPHER **)OBJ_bsearch((char *)&cp,
				(char *)&(cipher[0]),num_c,sizeof(CIPHER *),
				(int (*)())ssl_cipher_ptr_cmp);
			if ((cpp != NULL) && ((*cpp)->valid))
				{
				s->session->ciphers[nnum_c++]= *cpp;
				}
			if (nnum_c == SSL_NUM_CIPHERS) break;
			}
		p+=s->tmp.csl;
		s->session->num_ciphers=nnum_c;

		if (nnum_c == 0)
			{
			ssl_return_error(s,SSL_PE_NO_CIPHER);
			SSLerr(SSL_F_GET_SERVER_HELLO,SSL_R_NO_CIPHER_MATCH);
			return(-1);
			}
		qsort((char *)&(cipher[0]),nnum_c,sizeof(CIPHER *),
			(int (*)(P_CC_CC))ssl_cipher_ptr_cmp);

		/* we now have the sorted cipher in s->session->ciphers; */

		/* pick a cipher - which one? */
		if (!choose_cipher(s))
			{
			ssl_return_error(s,SSL_PE_NO_CIPHER);
			SSLerr(SSL_F_GET_SERVER_HELLO,SSL_R_NO_CIPHER_WE_TRUST);
			return(-1);
			}
		}

	if ((s->session != NULL) && (s->session->peer != NULL))
		X509_free(s->session->peer);

	/* hmmm, can we have the problem of the other session with this
	 * cert, Free's it before we increment the reference count. */
	CRYPTO_w_lock(CRYPTO_LOCK_X509);
	s->session->peer=s->session->cert->x509;
	s->session->peer->references++;
	CRYPTO_w_unlock(CRYPTO_LOCK_X509);

	/* get conn_id */
	if (s->conn_id_length != s->tmp.conn_id_length)
		{
		if (s->conn_id) Free(s->conn_id);
		s->conn_id=(unsigned char *)Malloc(s->tmp.conn_id_length);
		if (s->conn_id == NULL)
			{
			ssl_return_error(s,SSL_PE_UNDEFINED_ERROR);
			SSLerr(SSL_F_GET_SERVER_HELLO,ERR_R_MALLOC_FAILURE);
			return(-1);
			}
		}
	s->conn_id_length=s->tmp.conn_id_length;
	memcpy(s->conn_id,p,s->tmp.conn_id_length);
	return(1);
	}

static int client_hello(s)
SSL *s;
	{
	unsigned char *buf;
	unsigned char *p,*d;
	CIPHER **cipher;
	int i,n,j;

	buf=s->s2.init_buf;
	if (s->state == SSL2_ST_SEND_CLIENT_HELLO_A)
		{
		if (s->session == NULL)
			if (!ssl_get_new_session(s,0))
				{
				ssl_return_error(s,SSL_PE_UNDEFINED_ERROR);
				return(-1);
				}
		/* else use the pre-loaded session */

		cipher= &(s->session->ciphers[0]);
		p=buf;					/* header */
		d=p+9;					/* data section */
		*(p++)=SSL_MT_CLIENT_HELLO;		/* type */
		s2n(SSL_CLIENT_VERSION,p);		/* version */
		n=j=0;
		for (i=0; i<SSL_NUM_CIPHERS; i++)
			{
			if (ssl_ciphers[i].valid)
				{
				cipher[j++]= &(ssl_ciphers[i]);
				*(d++)=ssl_ciphers[i].c1;
				*(d++)=ssl_ciphers[i].c2;
				*(d++)=ssl_ciphers[i].c3;
				n+=3;
				}
			}
		s->session->num_ciphers=j;
		s2n(n,p);			/* cipher spec num bytes */

		if ((s->session->session_id_length > 0) &&
			(s->session->session_id_length <=
			SSL2_MAX_SSL_SESSION_ID_LENGTH_IN_BYTES))
			{
			i=s->session->session_id_length;
			s2n(i,p);		/* session id length */
			memcpy(d,s->session->session_id,(unsigned int)i);
			d+=i;
			}
		else
			{
			s2n(0,p);
			}

		s->challenge_length=CHALLENGE_LENGTH;
		s2n(CHALLENGE_LENGTH,p);		/* challenge length */
		if (s->challenge != NULL) Free(s->challenge);
		s->challenge=(unsigned char *)Malloc(CHALLENGE_LENGTH);
		if (s->challenge == NULL)
			{
			ssl_return_error(s,SSL_PE_UNDEFINED_ERROR);
			SSLerr(SSL_F_CLIENT_HELLO,ERR_R_MALLOC_FAILURE);
			return(-1);
			}
		if (s->ctx->reverse)
			{
			memcpy(s->challenge,s->ctx->challenge,
				s->ctx->challenge_length);
			}
		else
			/*challenge id data*/
			RAND_bytes(s->challenge,CHALLENGE_LENGTH);
		memcpy(d,s->challenge,CHALLENGE_LENGTH);
		d+=CHALLENGE_LENGTH;

		s->state=SSL2_ST_SEND_CLIENT_HELLO_B;
		s->s2.init_num=d-buf;
		s->s2.init_off=0;
		}
	/* SSL2_ST_SEND_CLIENT_HELLO_B */
	return(ssl_do_write(s));
	}

static int client_master_key(s)
SSL *s;
	{
	unsigned char *buf;
	unsigned char *p,*d;
	int clear,enc,karg,i;

	buf=s->s2.init_buf;
	if (s->state == SSL2_ST_SEND_CLIENT_MASTER_KEY_A)
		{
		p=buf;
		d=p+10;
		*(p++)=SSL_MT_CLIENT_MASTER_KEY;/* type */
		*(p++)=s->session->cipher->c1;	/* cipher type - byte 1 */
		*(p++)=s->session->cipher->c2;	/* cipher type - byte 2 */
		*(p++)=s->session->cipher->c3;	/* cipher type - byte 3 */

		/* make a master key */
		i=s->session->master_key_length=(unsigned char)
			s->session->cipher->key_size;
		s->session->master_key=(unsigned char *)Malloc((unsigned int)i+1);
		if (s->session->master_key == NULL)
			{
			ssl_return_error(s,SSL_PE_UNDEFINED_ERROR);
			SSLerr(SSL_F_CLIENT_MASTER_KEY,ERR_R_MALLOC_FAILURE);
			return(-1);
			}
		if (s->ctx->reverse)
			{
			if ((int)s->ctx->master_key_length != i)
				{
				SSLerr(SSL_F_CLIENT_MASTER_KEY,SSL_R_REVERSE_MASTER_KEY_LENGTH_IS_WRONG);
				return(-1);
				}
			memcpy(s->session->master_key,s->ctx->master_key,i);
			}
		else
			RAND_bytes(s->session->master_key,i);

		/* make key_arg data */
		i=s->session->key_arg_length=s->session->cipher->key_arg_size;
		if (i == 0)
			s->session->key_arg=NULL;
		else
			{
			s->session->key_arg=(unsigned char *)
				Malloc((unsigned int)i);
			if (s->session->key_arg == NULL)
				{
				ssl_return_error(s,SSL_PE_UNDEFINED_ERROR);
				SSLerr(SSL_F_CLIENT_MASTER_KEY,ERR_R_MALLOC_FAILURE);
				return(-1);
				}
			if (s->ctx->reverse)
				{
				if ((int)s->ctx->key_arg_length != i)
					{
					SSLerr(SSL_F_CLIENT_MASTER_KEY,SSL_R_REVERSE_KEY_ARG_LENGTH_IS_WRONG);
					return(-1);
					}
				memcpy(s->session->key_arg,s->ctx->key_arg,i);
				}
			else
				RAND_bytes(s->session->key_arg,i);
			}

		i=s->session->cipher->key_size*8;
		enc=s->session->cipher->enc_bits;
		if (enc == 0) enc=i;
		if (i < enc)
			{
			ssl_return_error(s,SSL_PE_UNDEFINED_ERROR);
			SSLerr(SSL_F_CLIENT_MASTER_KEY,SSL_R_CIPHER_TABLE_SRC_ERROR);
			return(-1);
			}
		clear=i-enc;
		/* bytes or bits? */
		clear/=8; /* clear */
		s2n(clear,p);
		memcpy(d,s->session->master_key,(unsigned int)clear);
		d+=clear;

		enc/=8;
		enc=s->session->cert->public_encrypt(s->session->cert,enc,
			&(s->session->master_key[clear]),d);
		if (enc <= 0)
			{
			ssl_return_error(s,SSL_PE_UNDEFINED_ERROR);
			SSLerr(SSL_F_CLIENT_MASTER_KEY,SSL_R_PUBLIC_KEY_ENCRYPT_ERROR);
			return(-1);
			}
		s2n(enc,p);
		d+=enc;
		karg=s->session->cipher->key_arg_size;	
		s2n(karg,p); /* key arg size */
		memcpy(d,s->session->key_arg,(unsigned int)karg);
		d+=karg;

		s->state=SSL2_ST_SEND_CLIENT_MASTER_KEY_B;
		s->s2.init_num=d-buf;
		s->s2.init_off=0;
		}

	/* SSL2_ST_SEND_CLIENT_MASTER_KEY_B */
	return(ssl_do_write(s));
	}

static int client_finished(s)
SSL *s;
	{
	unsigned char *p;

	if (s->state == SSL2_ST_SEND_CLIENT_FINISHED_A)
		{
		p=s->s2.init_buf;
		*(p++)=SSL_MT_CLIENT_FINISHED;
		memcpy(p,s->conn_id,(unsigned int)s->conn_id_length);

		s->state=SSL2_ST_SEND_CLIENT_FINISHED_B;
		s->s2.init_num=s->conn_id_length+1;
		s->s2.init_off=0;
		}
	return(ssl_do_write(s));
	}

/* read the data and then respond */
static int client_certificate(s)
SSL *s;
	{
	unsigned char *buf;
	unsigned char *p,*d;
	int i;
	unsigned int n;
	int cert_ch_len=0;
	unsigned char *cert_ch;

	buf=s->s2.init_buf;
	cert_ch= &(buf[2]);

	/* We have a cert associated with the SSL, so attach it to
	 * the session if it does not have one */

	if (s->state == SSL2_ST_SEND_CLIENT_CERTIFICATE_A)
		{
		i=SSL2_read(s,(char *)&(buf[s->s2.init_num]),
			MAX_CERT_CHALLENGE_LENGTH+1-s->s2.init_num);
		if (i<(MIN_CERT_CHALLENGE_LENGTH+1-s->s2.init_num))
			return(ssl_part_read(s,SSL_F_CLIENT_CERTIFICATE,i));

		/* type=buf[0]; */
		/* type eq x509 */
		if (buf[1] != SSL_AT_MD5_WITH_RSA_ENCRYPTION)
			{
			ssl_return_error(s,SSL_PE_UNSUPPORTED_CERTIFICATE_TYPE);
			SSLerr(SSL_F_CLIENT_CERTIFICATE,SSL_R_BAD_AUTHENTICATION_TYPE);
			return(-1);
			}
		cert_ch_len=i-1;

		if ((s->cert == NULL) || (s->cert->x509 == NULL) ||
			(s->cert->privatekey == NULL))
			{
			s->state=SSL2_ST_X509_GET_CLIENT_CERTIFICATE;
			}
		else
			s->state=SSL2_ST_SEND_CLIENT_CERTIFICATE_C;
		}

	if (s->state == SSL2_ST_X509_GET_CLIENT_CERTIFICATE)
		{
		X509 *x509=NULL;
		EVP_PKEY *pkey=NULL;

		/* If we get an error we need to
		 * ssl->rwstate=SSL_X509_LOOKUP;
		 * return(error);
		 * We should then be retried when things are ok and we
		 * can get a cert or not */

		i=0;
		if (s->ctx->client_cert_cb != NULL)
			{
			i=s->ctx->client_cert_cb(s,&(x509),&(pkey));
			}

		if (i < 0)
			{
			s->rwstate=SSL_X509_LOOKUP;
			return(-1);
			}

		if ((i == 1) && (pkey != NULL) && (x509 != NULL))
			{
			s->state=SSL2_ST_SEND_CLIENT_CERTIFICATE_C;
			if (	!SSL_use_certificate(s,x509) || 
				!SSL_use_PrivateKey(s,pkey))
				{
				i=0;
				}
			X509_free(x509);
			EVP_PKEY_free(pkey);
			}
		else if (i == 1)
			{
			if (x509 != NULL) X509_free(x509);
			if (pkey != NULL) EVP_PKEY_free(pkey);
			SSLerr(SSL_F_CLIENT_CERTIFICATE,SSL_R_BAD_DATA_RETURNED_BY_CALLBACK);
			i=0;
			}

		if (i == 0)
			{
			/* We have no client certificate to respond with
			 * so send the correct error message back */
			s->state=SSL2_ST_SEND_CLIENT_CERTIFICATE_B;
			p=buf;
			*(p++)=SSL_MT_ERROR;
			s2n(SSL_PE_NO_CERTIFICATE,p);
			s->s2.init_off=0;
			s->s2.init_num=3;
			/* Write is done at the end */
			}
		}

	if (s->state == SSL2_ST_SEND_CLIENT_CERTIFICATE_B)
		{
		return(ssl_do_write(s));
		}

	if (s->state == SSL2_ST_SEND_CLIENT_CERTIFICATE_C)
		{
		EVP_MD_CTX ctx;

		/* ok, now we calculate the checksum
		 * do it first so we can reuse buf :-) */
		p=buf;
		EVP_SignInit(&ctx,EVP_md5());
		EVP_SignUpdate(&ctx,s->key_material,
			(unsigned int)s->key_material_length);
		EVP_SignUpdate(&ctx,cert_ch,(unsigned int)cert_ch_len);
		n=i2d_X509(s->session->cert->x509,&p);
		EVP_SignUpdate(&ctx,buf,(unsigned int)n);

		p=buf;
		d=p+6;
		*(p++)=SSL_MT_CLIENT_CERTIFICATE;
		*(p++)=SSL_CT_X509_CERTIFICATE;
		n=i2d_X509(s->cert->x509,&d);
		s2n(n,p);

		if (!EVP_SignFinal(&ctx,d,&n,s->cert->privatekey))
			{
			/* this is not good.  If things have failed it
			 * means there so something wrong with the key.
			 * We will contiune with a 0 length signature
			 */
			}
		memset(&ctx,0,sizeof(ctx));
		s2n(n,p);
		d+=n;

		s->state=SSL2_ST_SEND_CLIENT_CERTIFICATE_D;
		s->s2.init_num=d-buf;
		s->s2.init_off=0;
		}
	/* if (s->state == SSL2_ST_SEND_CLIENT_CERTIFICATE_D) */
	return(ssl_do_write(s));
	}

static int get_server_verify(s)
SSL *s;
	{
	unsigned char *p;
	int i;

	p=s->s2.init_buf;
	if (s->state == SSL2_ST_GET_SERVER_VERIFY_A)
		{
		i=SSL2_read(s,(char *)&(p[s->s2.init_num]),1-s->s2.init_num);
		if (i < (1-s->s2.init_num)) 
			return(ssl_part_read(s,SSL_F_GET_SERVER_VERIFY,i));

		s->state= SSL2_ST_GET_SERVER_VERIFY_B;
		s->s2.init_num=0;
		if (*p != SSL_MT_SERVER_VERIFY)
			{
			if (p[0] != SSL_MT_ERROR)
				{
				ssl_return_error(s,SSL_PE_UNDEFINED_ERROR);
				SSLerr(SSL_F_GET_SERVER_VERIFY,
					SSL_R_READ_WRONG_PACKET_TYPE);
				}
			else
				SSLerr(SSL_F_GET_SERVER_VERIFY,
					SSL_R_PEER_ERROR);
			return(-1);
			}
		}
	
	p=s->s2.init_buf;
	i=SSL2_read(s,(char *)&(p[s->s2.init_num]),
		(unsigned int)s->challenge_length-s->s2.init_num);
	if (i < ((int)s->challenge_length-s->s2.init_num))
		return(ssl_part_read(s,SSL_F_GET_SERVER_VERIFY,i));
	if (memcmp(p,s->challenge,(unsigned int)s->challenge_length) != 0)
		{
		ssl_return_error(s,SSL_PE_UNDEFINED_ERROR);
		SSLerr(SSL_F_GET_SERVER_VERIFY,SSL_R_CHALLENGE_IS_DIFFERENT);
		return(-1);
		}
	return(1);
	}

static int get_server_finished(s)
SSL *s;
	{
	unsigned char *buf;
	unsigned char *p;
	int i;

	buf=s->s2.init_buf;
	p=buf;
	if (s->state == SSL2_ST_GET_SERVER_FINISHED_A)
		{
		i=SSL2_read(s,(char *)&(buf[s->s2.init_num]),1-s->s2.init_num);
		if (i < (1-s->s2.init_num))
			return(ssl_part_read(s,SSL_F_GET_SERVER_FINISHED,i));
		s->s2.init_num=i;
		if (*p == SSL_MT_REQUEST_CERTIFICATE)
			{
			s->state=SSL2_ST_SEND_CLIENT_CERTIFICATE_A;
			return(1);
			}
		else if (*p != SSL_MT_SERVER_FINISHED)
			{
			if (p[0] != SSL_MT_ERROR)
				{
				ssl_return_error(s,SSL_PE_UNDEFINED_ERROR);
				SSLerr(SSL_F_GET_SERVER_FINISHED,SSL_R_READ_WRONG_PACKET_TYPE);
				}
			else
				SSLerr(SSL_F_GET_SERVER_FINISHED,SSL_R_PEER_ERROR);
			return(-1);
			}
		s->state=SSL_ST_OK;
		s->s2.init_num=0;
		}

	i=SSL2_read(s,(char *)&(buf[s->s2.init_num]),
		SSL_SESSION_ID_LENGTH-s->s2.init_num);
	if (i < (SSL_SESSION_ID_LENGTH-s->s2.init_num))
		return(ssl_part_read(s,SSL_F_GET_SERVER_FINISHED,i));

	if (!s->hit) /* new session */
		{
		/* new session-id */
		s->session->session_id_length=SSL_SESSION_ID_LENGTH;
		memcpy(s->session->session_id,p,SSL_SESSION_ID_LENGTH);
		}
	else
		{
		if (memcmp(buf,s->session->session_id,
			(unsigned int)s->session->session_id_length) != 0)
			{
			ssl_return_error(s,SSL_PE_UNDEFINED_ERROR);
			SSLerr(SSL_F_GET_SERVER_FINISHED,SSL_R_SSL_SESSION_ID_IS_DIFFERENT);
			return(-1);
			}
		}
	return(1);
	}

static int choose_cipher(s)
SSL *s;
	{
	int j,i;
	char *p;

	if (s->ctx->reverse)
		{
		for (j=0; j < s->session->num_ciphers; j++)
			{
			if (strcmp(s->ctx->cipher,
				s->session->ciphers[j]->name) == 0)
				{
				s->session->cipher=s->session->ciphers[j];
				return(1);
				}
			}
		return(0);
		}
	i=0;
	for (;;)
		{
		p=SSL_get_cipher_list(s,i++);
		if (p == NULL)
			{
			s->session->cipher=NULL;
			return(0);
			}
		for (j=0; j < s->session->num_ciphers; j++)
			{
			if (strcmp(p,s->session->ciphers[j]->name) == 0)
				{
				s->session->cipher=s->session->ciphers[j];
				return(1);
				}
			}
		}
	}

