Fix SSL handshake functions and SSL_clear() such that SSL_clear()
[openssl.git] / ssl / s2_clnt.c
index 0f188ecaa402d15825f70fad506ffa484454e191..8cb7388ef957180c78eac513245bff5649d16de4 100644 (file)
  * [including the GNU Public Licence.]
  */
 
-#ifndef NO_RSA
+#include "ssl_locl.h"
+#ifndef OPENSSL_NO_SSL2
 #include <stdio.h>
 #include <openssl/rand.h>
 #include <openssl/buffer.h>
 #include <openssl/objects.h>
-#include "ssl_locl.h"
 #include <openssl/evp.h>
 
 static SSL_METHOD *ssl2_get_client_method(int ver);
@@ -72,7 +72,7 @@ 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);
 #define BREAK  break
 
@@ -108,7 +108,7 @@ int ssl2_connect(SSL *s)
        void (*cb)()=NULL;
        int new_state,state;
 
-       RAND_seed(&l,sizeof(l));
+       RAND_add(&l,sizeof(l),0);
        ERR_clear_error();
        clear_sys_error();
 
@@ -118,8 +118,8 @@ int ssl2_connect(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 (;;)
                {
@@ -245,7 +245,7 @@ int ssl2_connect(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.
                         */
@@ -287,7 +287,7 @@ static int get_server_hello(SSL *s)
        unsigned char *buf;
        unsigned char *p;
        int i,j;
-       STACK_OF(SSL_CIPHER) *sk=NULL,*cl;
+       STACK_OF(SSL_CIPHER) *sk=NULL,*cl, *prio, *allow;
 
        buf=(unsigned char *)s->init_buf->data;
        p=buf;
@@ -310,7 +310,13 @@ static int get_server_hello(SSL *s)
                                        SSL_R_PEER_ERROR);
                        return(-1);
                        }
+#ifdef __APPLE_CC__
+               /* The Rhapsody 5.5 (a.k.a. MacOS X) compiler bug
+                * workaround. <appro@fy.chalmers.se> */
+               s->hit=(i=*(p++))?1:0;
+#else
                s->hit=(*(p++))?1:0;
+#endif
                s->s2->tmp.cert_type= *(p++);
                n2s(p,i);
                if (i < s->version) s->version=i;
@@ -362,7 +368,7 @@ static int get_server_hello(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 */
@@ -408,41 +414,65 @@ static int get_server_hello(SSL *s)
                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);
+               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);
 
-       /* 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->sess_cert->key->x509;
-       /* Shouldn't do this: already locked */
-       /*CRYPTO_add(&s->session->peer->references,1,CRYPTO_LOCK_X509);*/
-       s->session->peer->references++;
-       CRYPTO_w_unlock(CRYPTO_LOCK_X509);
+               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);
+                       }
 
+               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->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;
        memcpy(s->s2->conn_id,p,s->s2->tmp.conn_id_length);
        return(1);
@@ -503,7 +533,7 @@ static int client_hello(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);
+               RAND_pseudo_bytes(s->s2->challenge,SSL2_CHALLENGE_LENGTH);
                memcpy(d,s->s2->challenge,SSL2_CHALLENGE_LENGTH);
                d+=SSL2_CHALLENGE_LENGTH;
 
@@ -545,12 +575,19 @@ static int client_master_key(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 > 0) RAND_pseudo_bytes(sess->key_arg,i);
 
                /* 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 (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;
@@ -729,11 +766,12 @@ static int client_certificate(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);
                EVP_SignUpdate(&ctx,cert_ch,(unsigned int)cert_ch_len);
-               n=i2d_X509(s->session->sess_cert->key->x509,&p);
+               n=i2d_X509(s->session->sess_cert->peer_key->x509,&p);
                EVP_SignUpdate(&ctx,buf,(unsigned int)n);
 
                p=buf;
@@ -747,10 +785,10 @@ static int client_certificate(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;
 
@@ -874,7 +912,7 @@ int ssl2_set_certificate(SSL *s, int type, int len, 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;
@@ -899,23 +937,21 @@ int ssl2_set_certificate(SSL *s, int type, int len, unsigned char *data)
                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->sess_cert) ssl_cert_free(s->session->sess_cert);
-       s->session->sess_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;
@@ -930,7 +966,7 @@ int ssl2_set_certificate(SSL *s, int type, int len, 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:
@@ -940,14 +976,14 @@ err:
        return(ret);
        }
 
-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)
        {
        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);
@@ -966,4 +1002,10 @@ end:
        EVP_PKEY_free(pkey);
        return(i);
        }
+#else /* !OPENSSL_NO_SSL2 */
+
+# if PEDANTIC
+static void *dummy=&dummy;
+# endif
+
 #endif