Reorder cleanup sequence in SSL_CTX_free() to leave ex_data for remove_cb().
[openssl.git] / ssl / ssl_lib.c
index 2b60d7288feb7f0c2607ea6b68c9eeb947a5383e..1ddd3380acb15adace6865967bcaeb2c2f65beb8 100644 (file)
  * Hudson (tjh@cryptsoft.com).
  *
  */
-
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * ECC cipher suite support in OpenSSL originally developed by 
+ * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project.
+ */
 
 #ifdef REF_CHECK
 #  include <assert.h>
 #endif
 #include <stdio.h>
+#include "ssl_locl.h"
+#include "kssl_lcl.h"
 #include <openssl/objects.h>
 #include <openssl/lhash.h>
 #include <openssl/x509v3.h>
-#include "ssl_locl.h"
-#include "kssl_lcl.h"
 
 const char *SSL_version_str=OPENSSL_VERSION_TEXT;
 
@@ -143,6 +147,12 @@ int SSL_clear(SSL *s)
                return(0);
                }
 
+       if (ssl_clear_bad_session(s))
+               {
+               SSL_SESSION_free(s->session);
+               s->session=NULL;
+               }
+
        s->error=0;
        s->hit=0;
        s->shutdown=0;
@@ -162,12 +172,6 @@ int SSL_clear(SSL *s)
 
        s->type=0;
 
-       if (ssl_clear_bad_session(s))
-               {
-               SSL_SESSION_free(s->session);
-               s->session=NULL;
-               }
-
        s->state=SSL_ST_BEFORE|((s->server)?SSL_ST_ACCEPT:SSL_ST_CONNECT);
 
        s->version=s->method->version;
@@ -1405,13 +1409,24 @@ void SSL_CTX_free(SSL_CTX *a)
                abort(); /* ok */
                }
 #endif
-       CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL_CTX, a, &a->ex_data);
 
+       /*
+        * Free internal session cache. However: the remove_cb() may reference
+        * the ex_data of SSL_CTX, thus the ex_data store can only be removed
+        * after the sessions were flushed.
+        * As the ex_data handling routines might also touch the session cache,
+        * the most secure solution seems to be: empty (flush) the cache, then
+        * free ex_data, then finally free the cache.
+        * (See ticket [openssl.org #212].)
+        */
        if (a->sessions != NULL)
-               {
                SSL_CTX_flush_sessions(a,0);
+
+       CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL_CTX, a, &a->ex_data);
+
+       if (a->sessions != NULL)
                lh_free(a->sessions);
-               }
+
        if (a->cert_store != NULL)
                X509_STORE_free(a->cert_store);
        if (a->cipher_list != NULL)
@@ -1443,15 +1458,10 @@ void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx,void *u)
        ctx->default_passwd_callback_userdata=u;
        }
 
-void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx,int (*cb)(),char *arg)
+void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, int (*cb)(X509_STORE_CTX *,void *), void *arg)
        {
-       /* now
-        *     int (*cb)(X509_STORE_CTX *),
-        * but should be
-        *     int (*cb)(X509_STORE_CTX *, void *arg)
-        */
        ctx->app_verify_callback=cb;
-       ctx->app_verify_arg=arg; /* never used */
+       ctx->app_verify_arg=arg;
        }
 
 void SSL_CTX_set_verify(SSL_CTX *ctx,int mode,int (*cb)(int, X509_STORE_CTX *))
@@ -1472,6 +1482,10 @@ void ssl_set_cert_masks(CERT *c, SSL_CIPHER *cipher)
        int rsa_enc_export,dh_rsa_export,dh_dsa_export;
        int rsa_tmp_export,dh_tmp_export,kl;
        unsigned long mask,emask;
+       int have_ecc_cert, have_ecdh_tmp, ecdh_ok, ecdsa_ok, ecc_pkey_size;
+       X509 *x = NULL;
+       EVP_PKEY *ecc_pkey = NULL;
+       int signature_nid = 0;
 
        if (c == NULL) return;
 
@@ -1492,6 +1506,9 @@ void ssl_set_cert_masks(CERT *c, SSL_CIPHER *cipher)
        dh_tmp=dh_tmp_export=0;
 #endif
 
+#ifndef OPENSSL_NO_ECDH
+       have_ecdh_tmp=(c->ecdh_tmp != NULL || c->ecdh_tmp_cb != NULL);
+#endif
        cpk= &(c->pkeys[SSL_PKEY_RSA_ENC]);
        rsa_enc= (cpk->x509 != NULL && cpk->privatekey != NULL);
        rsa_enc_export=(rsa_enc && EVP_PKEY_size(cpk->privatekey)*8 <= kl);
@@ -1506,7 +1523,8 @@ void ssl_set_cert_masks(CERT *c, SSL_CIPHER *cipher)
 /* FIX THIS EAY EAY EAY */
        dh_dsa=  (cpk->x509 != NULL && cpk->privatekey != NULL);
        dh_dsa_export=(dh_dsa && EVP_PKEY_size(cpk->privatekey)*8 <= kl);
-
+       cpk= &(c->pkeys[SSL_PKEY_ECC]);
+       have_ecc_cert= (cpk->x509 != NULL && cpk->privatekey != NULL);
        mask=0;
        emask=0;
 
@@ -1546,7 +1564,7 @@ void ssl_set_cert_masks(CERT *c, SSL_CIPHER *cipher)
        if (rsa_enc || rsa_sign)
                {
                mask|=SSL_aRSA;
-               emask|=SSL_aRSA;
+               mask|=SSL_aRSA;
                }
 
        if (dsa_sign)
@@ -1563,11 +1581,127 @@ void ssl_set_cert_masks(CERT *c, SSL_CIPHER *cipher)
        emask|=SSL_kKRB5|SSL_aKRB5;
 #endif
 
+       /* An ECC certificate may be usable for ECDH and/or
+        * ECDSA cipher suites depending on the key usage extension.
+        */
+       if (have_ecc_cert)
+               {
+                /* This call populates extension flags (ex_flags) */
+               x = (c->pkeys[SSL_PKEY_ECC]).x509;
+               X509_check_purpose(x, -1, 0);
+               ecdh_ok = (x->ex_flags & EXFLAG_KUSAGE) ?
+                   (x->ex_kusage & X509v3_KU_KEY_AGREEMENT) : 1;
+               ecdsa_ok = (x->ex_flags & EXFLAG_KUSAGE) ?
+                   (x->ex_kusage & X509v3_KU_DIGITAL_SIGNATURE) : 1;
+               ecc_pkey = X509_get_pubkey(x);
+               ecc_pkey_size = (ecc_pkey != NULL) ? 
+                   EVP_PKEY_bits(ecc_pkey) : 0;
+               EVP_PKEY_free(ecc_pkey);
+               if ((x->sig_alg) && (x->sig_alg->algorithm))
+                       signature_nid = OBJ_obj2nid(x->sig_alg->algorithm);
+#ifndef OPENSSL_NO_ECDH
+               if (ecdh_ok)
+                       {
+                       if ((signature_nid == NID_md5WithRSAEncryption) ||
+                           (signature_nid == NID_md4WithRSAEncryption) ||
+                           (signature_nid == NID_md2WithRSAEncryption))
+                               {
+                               mask|=SSL_kECDH|SSL_aRSA;
+                               if (ecc_pkey_size <= 163)
+                                       emask|=SSL_kECDH|SSL_aRSA;
+                               }
+                       if (signature_nid == NID_ecdsa_with_SHA1)
+                               {
+                               mask|=SSL_kECDH|SSL_aECDSA;
+                               if (ecc_pkey_size <= 163)
+                                       emask|=SSL_kECDH|SSL_aECDSA;
+                               }
+                       }
+#endif
+#ifndef OPENSSL_NO_ECDSA
+               if (ecdsa_ok)
+                       {
+                       mask|=SSL_aECDSA;
+                       emask|=SSL_aECDSA;
+                       }
+#endif
+               }
+
+#ifndef OPENSSL_NO_ECDH
+       if (have_ecdh_tmp)
+               {
+               mask|=SSL_kECDHE;
+               emask|=SSL_kECDHE;
+               }
+#endif
        c->mask=mask;
        c->export_mask=emask;
        c->valid=1;
        }
 
+/* This handy macro borrowed from crypto/x509v3/v3_purp.c */
+#define ku_reject(x, usage) \
+       (((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage)))
+
+int check_srvr_ecc_cert_and_alg(X509 *x, SSL_CIPHER *cs)
+       {
+       unsigned long alg = cs->algorithms;
+       EVP_PKEY *pkey = NULL;
+       int keysize = 0;
+       int signature_nid = 0;
+
+       if (SSL_C_IS_EXPORT(cs))
+               {
+               /* ECDH key length in export ciphers must be <= 163 bits */
+               pkey = X509_get_pubkey(x);
+               if (pkey == NULL) return 0;
+               keysize = EVP_PKEY_bits(pkey);
+               EVP_PKEY_free(pkey);
+               if (keysize > 163) return 0;
+               }
+
+       /* This call populates the ex_flags field correctly */
+       X509_check_purpose(x, -1, 0);
+       if ((x->sig_alg) && (x->sig_alg->algorithm))
+               signature_nid = OBJ_obj2nid(x->sig_alg->algorithm);
+       if (alg & SSL_kECDH) 
+               {
+               /* key usage, if present, must allow key agreement */
+               if (ku_reject(x, X509v3_KU_KEY_AGREEMENT))
+                       {
+                       return 0;
+                       }
+               if (alg & SSL_aECDSA) 
+                       {
+                       /* signature alg must be ECDSA */
+                       if (signature_nid != NID_ecdsa_with_SHA1)
+                               {
+                               return 0;
+                               }
+                       }
+               if (alg & SSL_aRSA)
+                       {
+                       /* signature alg must be RSA */
+                       if ((signature_nid != NID_md5WithRSAEncryption) &&
+                           (signature_nid != NID_md4WithRSAEncryption) &&
+                           (signature_nid != NID_md2WithRSAEncryption))
+                               {
+                               return 0;
+                               }
+                       }
+               } 
+       else if (alg & SSL_aECDSA)
+               {
+               /* key usage, if present, must allow signing */
+               if (ku_reject(x, X509v3_KU_DIGITAL_SIGNATURE))
+                       {
+                       return 0;
+                       }
+               }
+
+       return 1;  /* all checks are ok */
+       }
+
 /* THIS NEEDS CLEANING UP */
 X509 *ssl_get_server_send_cert(SSL *s)
        {
@@ -1582,7 +1716,26 @@ X509 *ssl_get_server_send_cert(SSL *s)
        mask=is_export?c->export_mask:c->mask;
        kalg=alg&(SSL_MKEY_MASK|SSL_AUTH_MASK);
 
-       if      (kalg & SSL_kDHr)
+       if (kalg & SSL_kECDH)
+               {
+               /* we don't need to look at SSL_kECDHE 
+                * since no certificate is needed for
+                * anon ECDH and for authenticated
+                * ECDHE, the check for the auth 
+                * algorithm will set i correctly
+                * NOTE: For ECDH-RSA, we need an ECC
+                * not an RSA cert but for ECDHE-RSA
+                * we need an RSA cert. Placing the
+                * checks for SSL_kECDH before RSA
+                * checks ensures the correct cert is chosen.
+                */
+               i=SSL_PKEY_ECC;
+               }
+       else if (kalg & SSL_aECDSA)
+               {
+               i=SSL_PKEY_ECC;
+               }
+       else if (kalg & SSL_kDHr)
                i=SSL_PKEY_DH_RSA;
        else if (kalg & SSL_kDHd)
                i=SSL_PKEY_DH_DSA;
@@ -1606,6 +1759,7 @@ X509 *ssl_get_server_send_cert(SSL *s)
                return(NULL);
                }
        if (c->pkeys[i].x509 == NULL) return(NULL);
+
        return(c->pkeys[i].x509);
        }
 
@@ -1629,6 +1783,9 @@ EVP_PKEY *ssl_get_sign_pkey(SSL *s,SSL_CIPHER *cipher)
                else
                        return(NULL);
                }
+       else if ((alg & SSL_aECDSA) &&
+                (c->pkeys[SSL_PKEY_ECC].privatekey != NULL))
+               return(c->pkeys[SSL_PKEY_ECC].privatekey);
        else /* if (alg & SSL_aNULL) */
                {
                SSLerr(SSL_F_SSL_GET_SIGN_PKEY,ERR_R_INTERNAL_ERROR);
@@ -2276,6 +2433,20 @@ void SSL_set_tmp_dh_callback(SSL *ssl,DH *(*dh)(SSL *ssl,int is_export,
        }
 #endif
 
+#ifndef OPENSSL_NO_ECDH
+void SSL_CTX_set_tmp_ecdh_callback(SSL_CTX *ctx,EC_KEY *(*ecdh)(SSL *ssl,int is_export,
+                                                       int keylength))
+       {
+       SSL_CTX_callback_ctrl(ctx,SSL_CTRL_SET_TMP_ECDH_CB,(void (*)())ecdh);
+       }
+
+void SSL_set_tmp_ecdh_callback(SSL *ssl,EC_KEY *(*ecdh)(SSL *ssl,int is_export,
+                                               int keylength))
+       {
+       SSL_callback_ctrl(ssl,SSL_CTRL_SET_TMP_ECDH_CB,(void (*)())ecdh);
+       }
+#endif
+
 
 void SSL_CTX_set_msg_callback(SSL_CTX *ctx, void (*cb)(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg))
        {