Support retries in certificate callback
[openssl.git] / ssl / s2_clnt.c
index 46e14d82cd3de1f0e5d8d80451789d2ea7ac9733..299389addc05183301dd7225d017b573ae036161 100644 (file)
  * copied and put under another distribution licence
  * [including the GNU Public Licence.]
  */
+/* ====================================================================
+ * Copyright (c) 1998-2001 The OpenSSL Project.  All rights reserved.
+ *
+ * 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 above 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 acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
 
-#include <stdio.h>
-#include "rand.h"
-#include "buffer.h"
-#include "objects.h"
 #include "ssl_locl.h"
-#include "evp.h"
+#ifndef OPENSSL_NO_SSL2
+#include <stdio.h>
+#include <openssl/rand.h>
+#include <openssl/buffer.h>
+#include <openssl/objects.h>
+#include <openssl/evp.h>
 
-#ifndef NOPROTO
-static SSL_METHOD *ssl2_get_client_method(int ver);
+static const SSL_METHOD *ssl2_get_client_method(int ver);
 static int get_server_finished(SSL *s);
 static int get_server_verify(SSL *s);
 static int get_server_hello(SSL *s);
@@ -72,24 +125,11 @@ 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 ssl_rsa_public_encrypt(CERT *c, int len, unsigned char *from,
+static int ssl_rsa_public_encrypt(SESS_CERT *sc, int len, unsigned char *from,
        unsigned char *to,int padding);
-#else
-static SSL_METHOD *ssl2_get_client_method();
-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 ssl_rsa_public_encrypt();
-#endif
-
 #define BREAK  break
 
-static SSL_METHOD *ssl2_get_client_method(ver)
-int ver;
+static const SSL_METHOD *ssl2_get_client_method(int ver)
        {
        if (ver == SSL2_VERSION)
                return(SSLv2_client_method());
@@ -97,32 +137,20 @@ int ver;
                return(NULL);
        }
 
-SSL_METHOD *SSLv2_client_method()
-       {
-       static int init=1;
-       static SSL_METHOD SSLv2_client_data;
-
-       if (init)
-               {
-               init=0;
-               memcpy((char *)&SSLv2_client_data,(char *)sslv2_base_method(),
-                       sizeof(SSL_METHOD));
-               SSLv2_client_data.ssl_connect=ssl2_connect;
-               SSLv2_client_data.get_ssl_method=ssl2_get_client_method;
-               }
-       return(&SSLv2_client_data);
-       }
+IMPLEMENT_ssl2_meth_func(SSLv2_client_method,
+                       ssl_undefined_function,
+                       ssl2_connect,
+                       ssl2_get_client_method)
 
-int ssl2_connect(s)
-SSL *s;
+int ssl2_connect(SSL *s)
        {
-       unsigned long l=time(NULL);
+       unsigned long l=(unsigned long)time(NULL);
        BUF_MEM *buf=NULL;
        int ret= -1;
-       void (*cb)()=NULL;
+       void (*cb)(const SSL *ssl,int type,int val)=NULL;
        int new_state,state;
 
-       RAND_seed(&l,sizeof(l));
+       RAND_add(&l,sizeof(l),0);
        ERR_clear_error();
        clear_sys_error();
 
@@ -132,8 +160,8 @@ SSL *s;
                cb=s->ctx->info_callback;
 
        /* init things to blank */
-       if (!SSL_in_init(s) || SSL_in_before(s)) SSL_clear(s);
        s->in_handshake++;
+       if (!SSL_in_init(s) || SSL_in_before(s)) SSL_clear(s);
 
        for (;;)
                {
@@ -161,10 +189,13 @@ SSL *s;
                        if (!BUF_MEM_grow(buf,
                                SSL2_MAX_RECORD_LENGTH_3_BYTE_HEADER))
                                {
+                               if (buf == s->init_buf)
+                                       buf=NULL;
                                ret= -1;
                                goto end;
                                }
                        s->init_buf=buf;
+                       buf=NULL;
                        s->init_num=0;
                        s->state=SSL2_ST_SEND_CLIENT_HELLO_A;
                        s->ctx->stats.sess_connect++;
@@ -259,7 +290,7 @@ SSL *s;
                /*      ERR_clear_error();*/
 
                        /* If we want to cache session-ids in the client
-                        * and we sucessfully add the session-id to the
+                        * and we successfully 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.
                         */
@@ -291,18 +322,20 @@ SSL *s;
                }
 end:
        s->in_handshake--;
+       if (buf != NULL)
+               BUF_MEM_free(buf);
        if (cb != NULL) 
                cb(s,SSL_CB_CONNECT_EXIT,ret);
        return(ret);
        }
 
-static int get_server_hello(s)
-SSL *s;
+static int get_server_hello(SSL *s)
        {
        unsigned char *buf;
        unsigned char *p;
        int i,j;
-       STACK_OF(SSL_CIPHER) *sk=NULL,*cl;
+       unsigned long len;
+       STACK_OF(SSL_CIPHER) *sk=NULL,*cl, *prio, *allow;
 
        buf=(unsigned char *)s->init_buf->data;
        p=buf;
@@ -311,6 +344,7 @@ SSL *s;
                i=ssl2_read(s,(char *)&(buf[s->init_num]),11-s->init_num);
                if (i < (11-s->init_num)) 
                        return(ssl2_part_read(s,SSL_F_GET_SERVER_HELLO,i));
+               s->init_num = 11;
 
                if (*(p++) != SSL2_MT_SERVER_HELLO)
                        {
@@ -325,7 +359,15 @@ SSL *s;
                                        SSL_R_PEER_ERROR);
                        return(-1);
                        }
+#if 0
                s->hit=(*(p++))?1:0;
+               /* Some [PPC?] compilers fail to increment p in above
+                  statement, e.g. one provided with Rhapsody 5.5, but
+                  most recent example XL C 11.1 for AIX, even without
+                  optimization flag... */
+#else
+               s->hit=(*p)?1:0; p++;
+#endif
                s->s2->tmp.cert_type= *(p++);
                n2s(p,i);
                if (i < s->version) s->version=i;
@@ -333,18 +375,24 @@ SSL *s;
                n2s(p,i); s->s2->tmp.csl=i;
                n2s(p,i); s->s2->tmp.conn_id_length=i;
                s->state=SSL2_ST_GET_SERVER_HELLO_B;
-               s->init_num=0;
                }
 
        /* SSL2_ST_GET_SERVER_HELLO_B */
-       j=s->s2->tmp.cert_length+s->s2->tmp.csl+s->s2->tmp.conn_id_length
-               - s->init_num;
-       i=ssl2_read(s,(char *)&(buf[s->init_num]),j);
+       len = 11 + (unsigned long)s->s2->tmp.cert_length + (unsigned long)s->s2->tmp.csl + (unsigned long)s->s2->tmp.conn_id_length;
+       if (len > SSL2_MAX_RECORD_LENGTH_3_BYTE_HEADER)
+               {
+               SSLerr(SSL_F_GET_SERVER_HELLO,SSL_R_MESSAGE_TOO_LONG);
+               return -1;
+               }
+       j = (int)len - s->init_num;
+       i = ssl2_read(s,(char *)&(buf[s->init_num]),j);
        if (i != j) return(ssl2_part_read(s,SSL_F_GET_SERVER_HELLO,i));
+       if (s->msg_callback)
+               s->msg_callback(0, s->version, 0, buf, (size_t)len, s, s->msg_callback_arg); /* SERVER-HELLO */
 
        /* things are looking good */
 
-       p=buf;
+       p = buf + 11;
        if (s->hit)
                {
                if (s->s2->tmp.cert_length != 0) 
@@ -377,7 +425,7 @@ SSL *s;
                */
 #endif
 
-               /* we need to do this incase we were trying to reuse a 
+               /* we need to do this in case 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 */
@@ -420,49 +468,81 @@ SSL *s;
                        return(-1);
                        }
 
-               sk_SSL_CIPHER_set_cmp_func(sk,ssl_cipher_ptr_id_cmp);
+               (void)sk_SSL_CIPHER_set_cmp_func(sk,ssl_cipher_ptr_id_cmp);
 
                /* get the array of ciphers we will accept */
-               cl=ssl_get_ciphers_by_id(s);
-
+               cl=SSL_get_ciphers(s);
+               (void)sk_SSL_CIPHER_set_cmp_func(cl,ssl_cipher_ptr_id_cmp);
+
+               /*
+                * If server preference flag set, choose the first
+                * (highest priority) cipher the server sends, otherwise
+                * client preference has priority.
+                */
+               if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE)
+                   {
+                   prio = sk;
+                   allow = cl;
+                   }
+               else
+                   {
+                   prio = cl;
+                   allow = sk;
+                   }
                /* In theory we could have ciphers sent back that we
                 * don't want to use but that does not matter since we
-                * will check against the list we origionally sent and
+                * will check against the list we originally sent and
                 * for performance reasons we should not bother to match
                 * the two lists up just to check. */
-               for (i=0; i<sk_SSL_CIPHER_num(cl); i++)
+               for (i=0; i<sk_SSL_CIPHER_num(prio); i++)
                        {
-                       if (sk_SSL_CIPHER_find(sk,
-                                              sk_SSL_CIPHER_value(cl,i)) >= 0)
+                       if (sk_SSL_CIPHER_find(allow,
+                                            sk_SSL_CIPHER_value(prio,i)) >= 0)
                                break;
                        }
 
-               if (i >= sk_SSL_CIPHER_num(cl))
+               if (i >= sk_SSL_CIPHER_num(prio))
                        {
                        ssl2_return_error(s,SSL2_PE_NO_CIPHER);
                        SSLerr(SSL_F_GET_SERVER_HELLO,SSL_R_NO_CIPHER_MATCH);
                        return(-1);
                        }
-               s->session->cipher=sk_SSL_CIPHER_value(cl,i);
-               }
+               s->session->cipher=sk_SSL_CIPHER_value(prio,i);
+
 
-       if ((s->session != NULL) && (s->session->peer != NULL))
-               X509_free(s->session->peer);
+               if (s->session->peer != NULL) /* can't happen*/
+                       {
+                       ssl2_return_error(s, SSL2_PE_UNDEFINED_ERROR);
+                       SSLerr(SSL_F_GET_SERVER_HELLO, ERR_R_INTERNAL_ERROR);
+                       return(-1);
+                       }
 
-       /* 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->key->x509;
-       CRYPTO_add(&s->session->peer->references,1,CRYPTO_LOCK_X509);
-       CRYPTO_w_unlock(CRYPTO_LOCK_X509);
+               s->session->peer = s->session->sess_cert->peer_key->x509;
+               /* peer_key->x509 has been set by ssl2_set_certificate. */
+               CRYPTO_add(&s->session->peer->references, 1, CRYPTO_LOCK_X509);
+               }
 
+       if (s->session->sess_cert == NULL 
+      || s->session->peer != s->session->sess_cert->peer_key->x509)
+               /* can't happen */
+               {
+               ssl2_return_error(s, SSL2_PE_UNDEFINED_ERROR);
+               SSLerr(SSL_F_GET_SERVER_HELLO, ERR_R_INTERNAL_ERROR);
+               return(-1);
+               }
+               
        s->s2->conn_id_length=s->s2->tmp.conn_id_length;
+       if (s->s2->conn_id_length > sizeof s->s2->conn_id)
+               {
+               ssl2_return_error(s, SSL2_PE_UNDEFINED_ERROR);
+               SSLerr(SSL_F_GET_SERVER_HELLO, SSL_R_SSL2_CONNECTION_ID_TOO_LONG);
+               return -1;
+               }
        memcpy(s->s2->conn_id,p,s->s2->tmp.conn_id_length);
        return(1);
        }
 
-static int client_hello(s)
-SSL *s;
+static int client_hello(SSL *s)
        {
        unsigned char *buf;
        unsigned char *p,*d;
@@ -489,7 +569,7 @@ SSL *s;
                s2n(SSL2_VERSION,p);                    /* version */
                n=j=0;
 
-               n=ssl_cipher_list_to_bytes(s,SSL_get_ciphers(s),d);
+               n=ssl_cipher_list_to_bytes(s,SSL_get_ciphers(s),d,0);
                d+=n;
 
                if (n == 0)
@@ -517,7 +597,8 @@ SSL *s;
                s->s2->challenge_length=SSL2_CHALLENGE_LENGTH;
                s2n(SSL2_CHALLENGE_LENGTH,p);           /* challenge length */
                /*challenge id data*/
-               RAND_bytes(s->s2->challenge,SSL2_CHALLENGE_LENGTH);
+               if (RAND_pseudo_bytes(s->s2->challenge,SSL2_CHALLENGE_LENGTH) <= 0)
+                       return -1;
                memcpy(d,s->s2->challenge,SSL2_CHALLENGE_LENGTH);
                d+=SSL2_CHALLENGE_LENGTH;
 
@@ -529,21 +610,20 @@ SSL *s;
        return(ssl2_do_write(s));
        }
 
-static int client_master_key(s)
-SSL *s;
+static int client_master_key(SSL *s)
        {
        unsigned char *buf;
        unsigned char *p,*d;
        int clear,enc,karg,i;
        SSL_SESSION *sess;
-       EVP_CIPHER *c;
-       EVP_MD *md;
+       const EVP_CIPHER *c;
+       const EVP_MD *md;
 
        buf=(unsigned char *)s->init_buf->data;
        if (s->state == SSL2_ST_SEND_CLIENT_MASTER_KEY_A)
                {
 
-               if (!ssl_cipher_get_evp(s->session,&c,&md,NULL))
+               if (!ssl_cipher_get_evp(s->session,&c,&md,NULL,NULL,NULL, 0))
                        {
                        ssl2_return_error(s,SSL2_PE_NO_CIPHER);
                        SSLerr(SSL_F_CLIENT_MASTER_KEY,SSL_R_PROBLEMS_MAPPING_CIPHER_FUNCTIONS);
@@ -560,12 +640,33 @@ SSL *s;
                /* make key_arg data */
                i=EVP_CIPHER_iv_length(c);
                sess->key_arg_length=i;
-               if (i > 0) RAND_bytes(sess->key_arg,i);
+               if (i > SSL_MAX_KEY_ARG_LENGTH)
+                       {
+                       ssl2_return_error(s, SSL2_PE_UNDEFINED_ERROR);
+                       SSLerr(SSL_F_CLIENT_MASTER_KEY, ERR_R_INTERNAL_ERROR);
+                       return -1;
+                       }
+               if (i > 0)
+                       if (RAND_pseudo_bytes(sess->key_arg,i) <= 0)
+                               return -1;
 
                /* make a master key */
                i=EVP_CIPHER_key_length(c);
                sess->master_key_length=i;
-               if (i > 0) RAND_bytes(sess->master_key,i);
+               if (i > 0)
+                       {
+                       if (i > (int)sizeof(sess->master_key))
+                               {
+                               ssl2_return_error(s, SSL2_PE_UNDEFINED_ERROR);
+                               SSLerr(SSL_F_CLIENT_MASTER_KEY, ERR_R_INTERNAL_ERROR);
+                               return -1;
+                               }
+                       if (RAND_bytes(sess->master_key,i) <= 0)
+                               {
+                               ssl2_return_error(s,SSL2_PE_UNDEFINED_ERROR);
+                               return(-1);
+                               }
+                       }
 
                if (sess->cipher->algorithm2 & SSL2_CF_8_BYTE_ENC)
                        enc=8;
@@ -574,7 +675,7 @@ SSL *s;
                else
                        enc=i;
 
-               if (i < enc)
+               if ((int)i < enc)
                        {
                        ssl2_return_error(s,SSL2_PE_UNDEFINED_ERROR);
                        SSLerr(SSL_F_CLIENT_MASTER_KEY,SSL_R_CIPHER_TABLE_SRC_ERROR);
@@ -585,7 +686,7 @@ SSL *s;
                memcpy(d,sess->master_key,(unsigned int)clear);
                d+=clear;
 
-               enc=ssl_rsa_public_encrypt(sess->cert,enc,
+               enc=ssl_rsa_public_encrypt(sess->sess_cert,enc,
                        &(sess->master_key[clear]),d,
                        (s->s2->ssl2_rollback)?RSA_SSLV23_PADDING:RSA_PKCS1_PADDING);
                if (enc <= 0)
@@ -603,6 +704,12 @@ SSL *s;
                d+=enc;
                karg=sess->key_arg_length;      
                s2n(karg,p); /* key arg size */
+               if (karg > (int)sizeof(sess->key_arg))
+                       {
+                       ssl2_return_error(s,SSL2_PE_UNDEFINED_ERROR);
+                       SSLerr(SSL_F_CLIENT_MASTER_KEY, ERR_R_INTERNAL_ERROR);
+                       return -1;
+                       }
                memcpy(d,sess->key_arg,(unsigned int)karg);
                d+=karg;
 
@@ -615,8 +722,7 @@ SSL *s;
        return(ssl2_do_write(s));
        }
 
-static int client_finished(s)
-SSL *s;
+static int client_finished(SSL *s)
        {
        unsigned char *p;
 
@@ -624,6 +730,11 @@ SSL *s;
                {
                p=(unsigned char *)s->init_buf->data;
                *(p++)=SSL2_MT_CLIENT_FINISHED;
+               if (s->s2->conn_id_length > sizeof s->s2->conn_id)
+                       {
+                       SSLerr(SSL_F_CLIENT_FINISHED, ERR_R_INTERNAL_ERROR);
+                       return -1;
+                       }
                memcpy(p,s->s2->conn_id,(unsigned int)s->s2->conn_id_length);
 
                s->state=SSL2_ST_SEND_CLIENT_FINISHED_B;
@@ -634,18 +745,16 @@ SSL *s;
        }
 
 /* read the data and then respond */
-static int client_certificate(s)
-SSL *s;
+static int client_certificate(SSL *s)
        {
        unsigned char *buf;
        unsigned char *p,*d;
        int i;
        unsigned int n;
-       int cert_ch_len=0;
+       int cert_ch_len;
        unsigned char *cert_ch;
 
        buf=(unsigned char *)s->init_buf->data;
-       cert_ch= &(buf[2]);
 
        /* We have a cert associated with the SSL, so attach it to
         * the session if it does not have one */
@@ -653,9 +762,12 @@ SSL *s;
        if (s->state == SSL2_ST_SEND_CLIENT_CERTIFICATE_A)
                {
                i=ssl2_read(s,(char *)&(buf[s->init_num]),
-                       SSL2_MAX_CERT_CHALLENGE_LENGTH+1-s->init_num);
-               if (i<(SSL2_MIN_CERT_CHALLENGE_LENGTH+1-s->init_num))
+                       SSL2_MAX_CERT_CHALLENGE_LENGTH+2-s->init_num);
+               if (i<(SSL2_MIN_CERT_CHALLENGE_LENGTH+2-s->init_num))
                        return(ssl2_part_read(s,SSL_F_CLIENT_CERTIFICATE,i));
+               s->init_num += i;
+               if (s->msg_callback)
+                       s->msg_callback(0, s->version, 0, buf, (size_t)s->init_num, s, s->msg_callback_arg); /* REQUEST-CERTIFICATE */
 
                /* type=buf[0]; */
                /* type eq x509 */
@@ -665,7 +777,6 @@ SSL *s;
                        SSLerr(SSL_F_CLIENT_CERTIFICATE,SSL_R_BAD_AUTHENTICATION_TYPE);
                        return(-1);
                        }
-               cert_ch_len=i-1;
 
                if ((s->cert == NULL) ||
                        (s->cert->key->x509 == NULL) ||
@@ -677,6 +788,9 @@ SSL *s;
                        s->state=SSL2_ST_SEND_CLIENT_CERTIFICATE_C;
                }
 
+       cert_ch = buf + 2;
+       cert_ch_len = s->init_num - 2;
+
        if (s->state == SSL2_ST_X509_GET_CLIENT_CERTIFICATE)
                {
                X509 *x509=NULL;
@@ -746,12 +860,15 @@ SSL *s;
                /* ok, now we calculate the checksum
                 * do it first so we can reuse buf :-) */
                p=buf;
-               EVP_SignInit(&ctx,s->ctx->rsa_md5);
+               EVP_MD_CTX_init(&ctx);
+               EVP_SignInit_ex(&ctx,s->ctx->rsa_md5, NULL);
                EVP_SignUpdate(&ctx,s->s2->key_material,
-                       (unsigned int)s->s2->key_material_length);
+                              s->s2->key_material_length);
                EVP_SignUpdate(&ctx,cert_ch,(unsigned int)cert_ch_len);
-               n=i2d_X509(s->session->cert->key->x509,&p);
-               EVP_SignUpdate(&ctx,buf,(unsigned int)n);
+               i=i2d_X509(s->session->sess_cert->peer_key->x509,&p);
+               /* Don't update the signature if it fails - FIXME: probably should handle this better */
+               if(i > 0)
+                       EVP_SignUpdate(&ctx,buf,(unsigned int)i);
 
                p=buf;
                d=p+6;
@@ -764,10 +881,10 @@ SSL *s;
                        {
                        /* 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
+                        * We will continue with a 0 length signature
                         */
                        }
-               memset(&ctx,0,sizeof(ctx));
+               EVP_MD_CTX_cleanup(&ctx);
                s2n(n,p);
                d+=n;
 
@@ -779,11 +896,10 @@ SSL *s;
        return(ssl2_do_write(s));
        }
 
-static int get_server_verify(s)
-SSL *s;
+static int get_server_verify(SSL *s)
        {
        unsigned char *p;
-       int i;
+       int i, n, len;
 
        p=(unsigned char *)s->init_buf->data;
        if (s->state == SSL2_ST_GET_SERVER_VERIFY_A)
@@ -791,9 +907,9 @@ SSL *s;
                i=ssl2_read(s,(char *)&(p[s->init_num]),1-s->init_num);
                if (i < (1-s->init_num)) 
                        return(ssl2_part_read(s,SSL_F_GET_SERVER_VERIFY,i));
+               s->init_num += i;
 
                s->state= SSL2_ST_GET_SERVER_VERIFY_B;
-               s->init_num=0;
                if (*p != SSL2_MT_SERVER_VERIFY)
                        {
                        if (p[0] != SSL2_MT_ERROR)
@@ -803,18 +919,27 @@ SSL *s;
                                        SSL_R_READ_WRONG_PACKET_TYPE);
                                }
                        else
-                               SSLerr(SSL_F_GET_SERVER_VERIFY,
-                                       SSL_R_PEER_ERROR);
+                               {
+                               SSLerr(SSL_F_GET_SERVER_VERIFY,SSL_R_PEER_ERROR);
+                               /* try to read the error message */
+                               i=ssl2_read(s,(char *)&(p[s->init_num]),3-s->init_num);
+                               return ssl2_part_read(s,SSL_F_GET_SERVER_VERIFY,i);
+                               }
                        return(-1);
                        }
                }
        
        p=(unsigned char *)s->init_buf->data;
-       i=ssl2_read(s,(char *)&(p[s->init_num]),
-               (unsigned int)s->s2->challenge_length-s->init_num);
-       if (i < ((int)s->s2->challenge_length-s->init_num))
+       len = 1 + s->s2->challenge_length;
+       n =  len - s->init_num;
+       i = ssl2_read(s,(char *)&(p[s->init_num]),n);
+       if (i < n)
                return(ssl2_part_read(s,SSL_F_GET_SERVER_VERIFY,i));
-       if (memcmp(p,s->s2->challenge,(unsigned int)s->s2->challenge_length) != 0)
+       if (s->msg_callback)
+               s->msg_callback(0, s->version, 0, p, len, s, s->msg_callback_arg); /* SERVER-VERIFY */
+       p += 1;
+
+       if (CRYPTO_memcmp(p,s->s2->challenge,s->s2->challenge_length) != 0)
                {
                ssl2_return_error(s,SSL2_PE_UNDEFINED_ERROR);
                SSLerr(SSL_F_GET_SERVER_VERIFY,SSL_R_CHALLENGE_IS_DIFFERENT);
@@ -823,12 +948,11 @@ SSL *s;
        return(1);
        }
 
-static int get_server_finished(s)
-SSL *s;
+static int get_server_finished(SSL *s)
        {
        unsigned char *buf;
        unsigned char *p;
-       int i;
+       int i, n, len;
 
        buf=(unsigned char *)s->init_buf->data;
        p=buf;
@@ -837,7 +961,8 @@ SSL *s;
                i=ssl2_read(s,(char *)&(buf[s->init_num]),1-s->init_num);
                if (i < (1-s->init_num))
                        return(ssl2_part_read(s,SSL_F_GET_SERVER_FINISHED,i));
-               s->init_num=i;
+               s->init_num += i;
+
                if (*p == SSL2_MT_REQUEST_CERTIFICATE)
                        {
                        s->state=SSL2_ST_SEND_CLIENT_CERTIFICATE_A;
@@ -851,17 +976,25 @@ SSL *s;
                                SSLerr(SSL_F_GET_SERVER_FINISHED,SSL_R_READ_WRONG_PACKET_TYPE);
                                }
                        else
+                               {
                                SSLerr(SSL_F_GET_SERVER_FINISHED,SSL_R_PEER_ERROR);
+                               /* try to read the error message */
+                               i=ssl2_read(s,(char *)&(p[s->init_num]),3-s->init_num);
+                               return ssl2_part_read(s,SSL_F_GET_SERVER_VERIFY,i);
+                               }
                        return(-1);
                        }
-               s->state=SSL_ST_OK;
-               s->init_num=0;
+               s->state=SSL2_ST_GET_SERVER_FINISHED_B;
                }
 
-       i=ssl2_read(s,(char *)&(buf[s->init_num]),
-               SSL2_SSL_SESSION_ID_LENGTH-s->init_num);
-       if (i < (SSL2_SSL_SESSION_ID_LENGTH-s->init_num))
+       len = 1 + SSL2_SSL_SESSION_ID_LENGTH;
+       n = len - s->init_num;
+       i = ssl2_read(s,(char *)&(buf[s->init_num]), n);
+       if (i < n) /* XXX could be shorter than SSL2_SSL_SESSION_ID_LENGTH, that's the maximum */
                return(ssl2_part_read(s,SSL_F_GET_SERVER_FINISHED,i));
+       s->init_num += i;
+       if (s->msg_callback)
+               s->msg_callback(0, s->version, 0, buf, (size_t)s->init_num, s, s->msg_callback_arg); /* SERVER-FINISHED */
 
        if (!s->hit) /* new session */
                {
@@ -870,14 +1003,15 @@ SSL *s;
                 * or bad things can happen */
                /* ZZZZZZZZZZZZZ */
                s->session->session_id_length=SSL2_SSL_SESSION_ID_LENGTH;
-               memcpy(s->session->session_id,p,SSL2_SSL_SESSION_ID_LENGTH);
+               memcpy(s->session->session_id,p+1,SSL2_SSL_SESSION_ID_LENGTH);
                }
        else
                {
                if (!(s->options & SSL_OP_MICROSOFT_SESS_ID_BUG))
                        {
-                       if (memcmp(buf,s->session->session_id,
-                               (unsigned int)s->session->session_id_length) != 0)
+                       if ((s->session->session_id_length > sizeof s->session->session_id)
+                           || (0 != memcmp(buf + 1, s->session->session_id,
+                                           (unsigned int)s->session->session_id_length)))
                                {
                                ssl2_return_error(s,SSL2_PE_UNDEFINED_ERROR);
                                SSLerr(SSL_F_GET_SERVER_FINISHED,SSL_R_SSL_SESSION_ID_IS_DIFFERENT);
@@ -885,19 +1019,16 @@ SSL *s;
                                }
                        }
                }
+       s->state = SSL_ST_OK;
        return(1);
        }
 
 /* loads in the certificate from the server */
-int ssl2_set_certificate(s, type, len, data)
-SSL *s;
-int type;
-int len;
-unsigned char *data;
+int ssl2_set_certificate(SSL *s, int type, int len, const unsigned char *data)
        {
        STACK_OF(X509) *sk=NULL;
        EVP_PKEY *pkey=NULL;
-       CERT *c=NULL;
+       SESS_CERT *sc=NULL;
        int i;
        X509 *x509=NULL;
        int ret=0;
@@ -917,28 +1048,26 @@ unsigned char *data;
 
        i=ssl_verify_cert_chain(s,sk);
                
-       if ((s->verify_mode != SSL_VERIFY_NONE) && (!i))
+       if ((s->verify_mode != SSL_VERIFY_NONE) && (i <= 0))
                {
                SSLerr(SSL_F_SSL2_SET_CERTIFICATE,SSL_R_CERTIFICATE_VERIFY_FAILED);
                goto err;
                }
+       ERR_clear_error(); /* but we keep s->verify_result */
+       s->session->verify_result = s->verify_result;
 
-       /* cert for ssl */
-       c=ssl_cert_new();
-       if (c == NULL)
+       /* server's cert for this session */
+       sc=ssl_sess_cert_new();
+       if (sc == NULL)
                {
                ret= -1;
                goto err;
                }
+       if (s->session->sess_cert) ssl_sess_cert_free(s->session->sess_cert);
+       s->session->sess_cert=sc;
 
-       /* cert for session */
-       if (s->session->cert) ssl_cert_free(s->session->cert);
-       s->session->cert=c;
-
-/*     c->cert_type=type; */
-
-       c->pkeys[SSL_PKEY_RSA_ENC].x509=x509;
-       c->key= &(c->pkeys[SSL_PKEY_RSA_ENC]);
+       sc->peer_pkeys[SSL_PKEY_RSA_ENC].x509=x509;
+       sc->peer_key= &(sc->peer_pkeys[SSL_PKEY_RSA_ENC]);
 
        pkey=X509_get_pubkey(x509);
        x509=NULL;
@@ -953,7 +1082,7 @@ unsigned char *data;
                goto err;
                }
 
-       if (!ssl_set_cert_type(c,SSL2_CT_X509_CERTIFICATE))
+       if (!ssl_set_peer_cert_type(sc,SSL2_CT_X509_CERTIFICATE))
                goto err;
        ret=1;
 err:
@@ -963,18 +1092,14 @@ err:
        return(ret);
        }
 
-static int ssl_rsa_public_encrypt(c, len, from, to, padding)
-CERT *c;
-int len;
-unsigned char *from;
-unsigned char *to;
-int padding;
+static int ssl_rsa_public_encrypt(SESS_CERT *sc, int len, unsigned char *from,
+            unsigned char *to, int padding)
        {
        EVP_PKEY *pkey=NULL;
        int i= -1;
 
-       if ((c == NULL) || (c->key->x509 == NULL) ||
-               ((pkey=X509_get_pubkey(c->key->x509)) == NULL))
+       if ((sc == NULL) || (sc->peer_key->x509 == NULL) ||
+               ((pkey=X509_get_pubkey(sc->peer_key->x509)) == NULL))
                {
                SSLerr(SSL_F_SSL_RSA_PUBLIC_ENCRYPT,SSL_R_NO_PUBLICKEY);
                return(-1);
@@ -993,4 +1118,10 @@ end:
        EVP_PKEY_free(pkey);
        return(i);
        }
+#else /* !OPENSSL_NO_SSL2 */
 
+# if PEDANTIC
+static void *dummy=&dummy;
+# endif
+
+#endif