Various custom extension fixes.
[openssl.git] / ssl / t1_lib.c
index 984d4bbf7a3579dec48f29eabbe9573b78c98ff8..17ce8b3f8c96d5045e981fa4616ad5961dd896a8 100644 (file)
@@ -525,6 +525,8 @@ int tls1_set_curves_list(unsigned char **pext, size_t *pextlen,
        ncb.nidcnt = 0;
        if (!CONF_parse_list(str, ':', 1, nid_cb, &ncb))
                return 0;
+       if (pext == NULL)
+               return 1;
        return tls1_set_curves(pext, pextlen, ncb.nid_arr, ncb.nidcnt);
        }
 /* For an EC key set TLS id and required compression based on parameters */
@@ -707,6 +709,11 @@ int tls1_check_ec_tmp_key(SSL *s, unsigned long cid)
        {
        unsigned char curve_id[2];
        EC_KEY *ec = s->cert->ecdh_tmp;
+#ifdef OPENSSL_SSL_DEBUG_BROKEN_PROTOCOL
+       /* Allow any curve: not just those peer supports */
+       if (s->cert->cert_flags & SSL_CERT_FLAG_BROKEN_PROTOCOL)
+               return 1;
+#endif
        /* If Suite B, AES128 MUST use P-256 and AES256 MUST use P-384,
         * no other curves permitted.
         */
@@ -764,6 +771,13 @@ int tls1_check_ec_tmp_key(SSL *s, unsigned long cid)
 #endif
        }
 
+#else
+
+static int tls1_check_cert_param(SSL *s, X509 *x, int set_ee_md)
+       {
+       return 1;
+       }
+
 #endif /* OPENSSL_NO_EC */
 
 #ifndef OPENSSL_NO_TLSEXT
@@ -811,17 +825,18 @@ static unsigned char tls12_sigalgs[] = {
        tlsext_sigalg_rsa(TLSEXT_hash_md5)
 #endif
 };
-
+#ifndef OPENSSL_NO_ECDSA
 static unsigned char suiteb_sigalgs[] = {
        tlsext_sigalg_ecdsa(TLSEXT_hash_sha256)
        tlsext_sigalg_ecdsa(TLSEXT_hash_sha384)
 };
-
+#endif
 size_t tls12_get_psigalgs(SSL *s, const unsigned char **psigs)
        {
        /* If Suite B mode use Suite B sigalgs only, ignore any other
         * preferences.
         */
+#ifndef OPENSSL_NO_EC
        switch (tls1_suiteb(s))
                {
        case SSL_CERT_FLAG_SUITEB_128_LOS:
@@ -836,7 +851,7 @@ size_t tls12_get_psigalgs(SSL *s, const unsigned char **psigs)
                *psigs = suiteb_sigalgs + 2;
                return 2;
                }
-
+#endif
        /* If server use client authentication sigalgs if not NULL */
        if (s->server && s->cert->client_sigalgs)
                {
@@ -878,6 +893,7 @@ int tls12_check_peer_sigalg(const EVP_MD **pmd, SSL *s,
                SSLerr(SSL_F_TLS12_CHECK_PEER_SIGALG,SSL_R_WRONG_SIGNATURE_TYPE);
                return 0;
                }
+#ifndef OPENSSL_NO_EC
        if (pkey->type == EVP_PKEY_EC)
                {
                unsigned char curve_id[2], comp_id;
@@ -885,7 +901,10 @@ int tls12_check_peer_sigalg(const EVP_MD **pmd, SSL *s,
                if (!tls1_set_ec_id(curve_id, &comp_id, pkey->pkey.ec))
                        return 0;
                if (!s->server && !tls1_check_ec_key(s, curve_id, &comp_id))
+                       {
+                       SSLerr(SSL_F_TLS12_CHECK_PEER_SIGALG,SSL_R_WRONG_CURVE);
                        return 0;
+                       }
                /* If Suite B only P-384+SHA384 or P-256+SHA-256 allowed */
                if (tls1_suiteb(s))
                        {
@@ -915,6 +934,7 @@ int tls12_check_peer_sigalg(const EVP_MD **pmd, SSL *s,
                }
        else if (tls1_suiteb(s))
                return 0;
+#endif
 
        /* Check signature matches a type we sent */
        sent_sigslen = tls12_get_psigalgs(s, &sent_sigs);
@@ -1379,11 +1399,11 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
        /* 1 byte for the list (we only support audit proofs) */
        if (s->ctx->tlsext_authz_server_audit_proof_cb != NULL)
                {
-               size_t lenmax;
                 const unsigned short ext_len = 2;
                 const unsigned char list_len = 1;
 
-               if ((lenmax = limit - ret - 6) < 0) return NULL;
+               if (limit < ret + 6)
+                       return NULL;
 
                s2n(TLSEXT_TYPE_server_authz, ret);
                 /* Extension length: 2 bytes */
@@ -1392,6 +1412,40 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
                *(ret++) = TLSEXT_AUTHZDATAFORMAT_audit_proof;
                }
 
+       /* Add custom TLS Extensions to ClientHello */
+       if (s->ctx->custom_cli_ext_records_count)
+               {
+               size_t i;
+               custom_cli_ext_record* record;
+
+               for (i = 0; i < s->ctx->custom_cli_ext_records_count; i++)
+                       {
+                       const unsigned char* out = NULL;
+                       unsigned short outlen = 0;
+
+                       record = &s->ctx->custom_cli_ext_records[i];
+                       /* NULL callback sends empty extension */ 
+                       /* -1 from callback omits extension */
+                       if (record->fn1)
+                               {
+                               int cb_retval = 0;
+                               cb_retval = record->fn1(s, record->ext_type,
+                                                       &out, &outlen,
+                                                       record->arg);
+                               if (cb_retval == 0)
+                                       return NULL; /* error */
+                               if (cb_retval == -1)
+                                       continue; /* skip this extension */
+                               }
+                       if (limit < ret + 4 + outlen)
+                               return NULL;
+                       s2n(record->ext_type, ret);
+                       s2n(outlen, ret);
+                       memcpy(ret, out, outlen);
+                       ret += outlen;
+                       }
+               }
+
        if ((extdatalen = ret-p-2) == 0)
                return p;
 
@@ -1406,11 +1460,12 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
 #ifndef OPENSSL_NO_NEXTPROTONEG
        int next_proto_neg_seen;
 #endif
+#ifndef OPENSSL_NO_EC
        unsigned long alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
        unsigned long alg_a = s->s3->tmp.new_cipher->algorithm_auth;
        int using_ecc = (alg_k & (SSL_kEECDH|SSL_kECDHr|SSL_kECDHe)) || (alg_a & SSL_aECDSA);
        using_ecc = using_ecc && (s->session->tlsext_ecpointformatlist != NULL);
-
+#endif
        /* don't add extensions for SSLv3, unless doing secure renegotiation */
        if (s->version == SSL3_VERSION && !s->s3->send_connection_binding)
                return p;
@@ -1659,6 +1714,47 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
                        }
                }
 
+       /* If custom types were sent in ClientHello, add ServerHello responses */
+       if (s->s3->tlsext_custom_types_count)
+               {
+               size_t i;
+
+               for (i = 0; i < s->s3->tlsext_custom_types_count; i++)
+                       {
+                       size_t j;
+                       custom_srv_ext_record *record;
+
+                       for (j = 0; j < s->ctx->custom_srv_ext_records_count; j++)
+                               {
+                               record = &s->ctx->custom_srv_ext_records[j];
+                               if (s->s3->tlsext_custom_types[i] == record->ext_type)
+                                       {
+                                       const unsigned char *out = NULL;
+                                       unsigned short outlen = 0;
+                                       int cb_retval = 0;
+
+                                       /* NULL callback or -1 omits extension */
+                                       if (!record->fn2)
+                                               break;
+                                       cb_retval = record->fn2(s, record->ext_type,
+                                                               &out, &outlen,
+                                                               record->arg);
+                                       if (cb_retval == 0)
+                                               return NULL; /* error */
+                                       if (cb_retval == -1)
+                                               break; /* skip this extension */
+                                       if (limit < ret + 4 + outlen)
+                                               return NULL;
+                                       s2n(record->ext_type, ret);
+                                       s2n(outlen, ret);
+                                       memcpy(ret, out, outlen);
+                                       ret += outlen;
+                                       break;
+                                       }
+                               }
+                       }
+               }
+
        if ((extdatalen = ret-p-2)== 0) 
                return p;
 
@@ -1666,6 +1762,89 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
        return ret;
        }
 
+#ifndef OPENSSL_NO_EC
+/* ssl_check_for_safari attempts to fingerprint Safari using OS X
+ * SecureTransport using the TLS extension block in |d|, of length |n|.
+ * Safari, since 10.6, sends exactly these extensions, in this order:
+ *   SNI,
+ *   elliptic_curves
+ *   ec_point_formats
+ *
+ * We wish to fingerprint Safari because they broke ECDHE-ECDSA support in 10.8,
+ * but they advertise support. So enabling ECDHE-ECDSA ciphers breaks them.
+ * Sadly we cannot differentiate 10.6, 10.7 and 10.8.4 (which work), from
+ * 10.8..10.8.3 (which don't work).
+ */
+static void ssl_check_for_safari(SSL *s, const unsigned char *data, const unsigned char *d, int n) {
+       unsigned short type, size;
+       static const unsigned char kSafariExtensionsBlock[] = {
+               0x00, 0x0a,  /* elliptic_curves extension */
+               0x00, 0x08,  /* 8 bytes */
+               0x00, 0x06,  /* 6 bytes of curve ids */
+               0x00, 0x17,  /* P-256 */
+               0x00, 0x18,  /* P-384 */
+               0x00, 0x19,  /* P-521 */
+
+               0x00, 0x0b,  /* ec_point_formats */
+               0x00, 0x02,  /* 2 bytes */
+               0x01,        /* 1 point format */
+               0x00,        /* uncompressed */
+       };
+
+       /* The following is only present in TLS 1.2 */
+       static const unsigned char kSafariTLS12ExtensionsBlock[] = {
+               0x00, 0x0d,  /* signature_algorithms */
+               0x00, 0x0c,  /* 12 bytes */
+               0x00, 0x0a,  /* 10 bytes */
+               0x05, 0x01,  /* SHA-384/RSA */
+               0x04, 0x01,  /* SHA-256/RSA */
+               0x02, 0x01,  /* SHA-1/RSA */
+               0x04, 0x03,  /* SHA-256/ECDSA */
+               0x02, 0x03,  /* SHA-1/ECDSA */
+       };
+
+       if (data >= (d+n-2))
+               return;
+       data += 2;
+
+       if (data > (d+n-4))
+               return;
+       n2s(data,type);
+       n2s(data,size);
+
+       if (type != TLSEXT_TYPE_server_name)
+               return;
+
+       if (data+size > d+n)
+               return;
+       data += size;
+
+       if (TLS1_get_client_version(s) >= TLS1_2_VERSION)
+               {
+               const size_t len1 = sizeof(kSafariExtensionsBlock);
+               const size_t len2 = sizeof(kSafariTLS12ExtensionsBlock);
+
+               if (data + len1 + len2 != d+n)
+                       return;
+               if (memcmp(data, kSafariExtensionsBlock, len1) != 0)
+                       return;
+               if (memcmp(data + len1, kSafariTLS12ExtensionsBlock, len2) != 0)
+                       return;
+               }
+       else
+               {
+               const size_t len = sizeof(kSafariExtensionsBlock);
+
+               if (data + len != d+n)
+                       return;
+               if (memcmp(data, kSafariExtensionsBlock, len) != 0)
+                       return;
+               }
+
+       s->s3->is_probably_safari = 1;
+}
+#endif /* !OPENSSL_NO_EC */
+
 static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al) 
        {       
        unsigned short type;
@@ -1681,10 +1860,24 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
        s->s3->next_proto_neg_seen = 0;
 #endif
 
+       /* Clear observed custom extensions */
+       s->s3->tlsext_custom_types_count = 0;
+       if (s->s3->tlsext_custom_types != NULL)
+               {
+               OPENSSL_free(s->s3->tlsext_custom_types);
+               s->s3->tlsext_custom_types = NULL;
+               }               
+
 #ifndef OPENSSL_NO_HEARTBEATS
        s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED |
                               SSL_TLSEXT_HB_DONT_SEND_REQUESTS);
 #endif
+
+#ifndef OPENSSL_NO_EC
+       if (s->options & SSL_OP_SAFARI_ECDHE_ECDSA_BUG)
+               ssl_check_for_safari(s, data, d, n);
+#endif /* !OPENSSL_NO_EC */
+
        /* Clear any signature algorithms extension received */
        if (s->cert->peer_sigalgs)
                {
@@ -1863,7 +2056,8 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
                        unsigned char *sdata = data;
                        int ecpointformatlist_length = *(sdata++);
 
-                       if (ecpointformatlist_length != size - 1)
+                       if (ecpointformatlist_length != size - 1 || 
+                               ecpointformatlist_length < 1)
                                {
                                *al = TLS1_AD_DECODE_ERROR;
                                return 0;
@@ -2226,6 +2420,54 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
                                }
                        }
 
+               /* If this ClientHello extension was unhandled and this is 
+                * a nonresumed connection, check whether the extension is a 
+                * custom TLS Extension (has a custom_srv_ext_record), and if
+                * so call the callback and record the extension number so that
+                * an appropriate ServerHello may be later returned.
+                */
+               else if (!s->hit && s->ctx->custom_srv_ext_records_count)
+                       {
+                       custom_srv_ext_record *record;
+
+                       for (i=0; i < s->ctx->custom_srv_ext_records_count; i++)
+                               {
+                               record = &s->ctx->custom_srv_ext_records[i];
+                               if (type == record->ext_type)
+                                       {
+                                       size_t j;
+
+                                       /* Error on duplicate TLS Extensions */
+                                       for (j = 0; j < s->s3->tlsext_custom_types_count; j++)
+                                               {
+                                               if (type == s->s3->tlsext_custom_types[j])
+                                                       {
+                                                       *al = TLS1_AD_DECODE_ERROR;
+                                                       return 0;
+                                                       }
+                                               }
+
+                                       /* NULL callback still notes the extension */ 
+                                       if (record->fn1 && !record->fn1(s, type, data, size, al, record->arg))
+                                               return 0;
+                                               
+                                       /* Add the (non-duplicated) entry */
+                                       s->s3->tlsext_custom_types_count++;
+                                       s->s3->tlsext_custom_types = OPENSSL_realloc(
+                                                       s->s3->tlsext_custom_types,
+                                                       s->s3->tlsext_custom_types_count * 2);
+                                       if (s->s3->tlsext_custom_types == NULL)
+                                               {
+                                               s->s3->tlsext_custom_types = 0;
+                                               *al = TLS1_AD_INTERNAL_ERROR;
+                                               return 0;
+                                               }
+                                       s->s3->tlsext_custom_types[
+                                                       s->s3->tlsext_custom_types_count - 1] = type;
+                                       }                                               
+                               }
+                       }
+
                data+=size;
                }
 
@@ -2344,8 +2586,7 @@ static int ssl_scan_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char
                        unsigned char *sdata = data;
                        int ecpointformatlist_length = *(sdata++);
 
-                       if (ecpointformatlist_length != size - 1 || 
-                               ecpointformatlist_length < 1)
+                       if (ecpointformatlist_length != size - 1)
                                {
                                *al = TLS1_AD_DECODE_ERROR;
                                return 0;
@@ -2439,7 +2680,7 @@ static int ssl_scan_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char
                        unsigned char selected_len;
 
                        /* We must have requested it. */
-                       if ((s->ctx->next_proto_select_cb == NULL))
+                       if (s->ctx->next_proto_select_cb == NULL)
                                {
                                *al = TLS1_AD_UNSUPPORTED_EXTENSION;
                                return 0;
@@ -2534,6 +2775,26 @@ static int ssl_scan_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char
 
                        s->s3->tlsext_authz_server_promised = 1;
                        }
+
+               /* If this extension type was not otherwise handled, but 
+                * matches a custom_cli_ext_record, then send it to the c
+                * callback */
+               else if (s->ctx->custom_cli_ext_records_count)
+                       {
+                       size_t i;
+                       custom_cli_ext_record* record;
+
+                       for (i = 0; i < s->ctx->custom_cli_ext_records_count; i++)
+                               {
+                               record = &s->ctx->custom_cli_ext_records[i];
+                               if (record->ext_type == type)
+                                       {
+                                       if (record->fn2 && !record->fn2(s, type, data, size, al, record->arg))
+                                               return 0;
+                                       break;
+                                       }
+                               }                       
+                       }
  
                data += size;
                }
@@ -2783,11 +3044,11 @@ int ssl_check_clienthello_tlsext_late(SSL *s)
        switch (ret)
                {
                case SSL_TLSEXT_ERR_ALERT_FATAL:
-                       ssl3_send_alert(s, SSL3_AL_FATAL,al);
+                       ssl3_send_alert(s, SSL3_AL_FATAL, al);
                        return -1;
 
                case SSL_TLSEXT_ERR_ALERT_WARNING:
-                       ssl3_send_alert(s, SSL3_AL_WARNING,al);
+                       ssl3_send_alert(s, SSL3_AL_WARNING, al);
                        return 1; 
 
                default:
@@ -3115,7 +3376,7 @@ static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen,
        HMAC_Update(&hctx, etick, eticklen);
        HMAC_Final(&hctx, tick_hmac, NULL);
        HMAC_CTX_cleanup(&hctx);
-       if (memcmp(tick_hmac, etick + eticklen, mlen))
+       if (CRYPTO_memcmp(tick_hmac, etick + eticklen, mlen))
                return 2;
        /* Attempt to decrypt session data */
        /* Move p after IV to start of encrypted ticket, update length */
@@ -3421,6 +3682,32 @@ int tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize)
 
        tls1_set_shared_sigalgs(s);
 
+#ifdef OPENSSL_SSL_DEBUG_BROKEN_PROTOCOL
+       if (s->cert->cert_flags & SSL_CERT_FLAG_BROKEN_PROTOCOL)
+               {
+               /* Use first set signature preference to force message
+                * digest, ignoring any peer preferences.
+                */
+               const unsigned char *sigs = NULL;
+               if (s->server)
+                       sigs = c->conf_sigalgs;
+               else
+                       sigs = c->client_sigalgs;
+               if (sigs)
+                       {
+                       idx = tls12_get_pkey_idx(sigs[1]);
+                       md = tls12_get_hash(sigs[0]);
+                       c->pkeys[idx].digest = md;
+                       c->pkeys[idx].valid_flags = CERT_PKEY_EXPLICIT_SIGN;
+                       if (idx == SSL_PKEY_RSA_SIGN)
+                               {
+                               c->pkeys[SSL_PKEY_RSA_ENC].valid_flags = CERT_PKEY_EXPLICIT_SIGN;
+                               c->pkeys[SSL_PKEY_RSA_ENC].digest = md;
+                               }
+                       }
+               }
+#endif
+
        for (i = 0, sigptr = c->shared_sigalgs;
                        i < c->shared_sigalgslen; i++, sigptr++)
                {
@@ -3716,6 +4003,8 @@ int tls1_set_sigalgs_list(CERT *c, const char *str, int client)
        sig.sigalgcnt = 0;
        if (!CONF_parse_list(str, ':', 1, sig_cb, &sig))
                return 0;
+       if (c == NULL)
+               return 1;
        return tls1_set_sigalgs(c, sig.sigalgs, sig.sigalgcnt, client);
        }
 
@@ -3834,6 +4123,15 @@ int tls1_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain,
                /* If no cert or key, forget it */
                if (!x || !pk)
                        goto end;
+#ifdef OPENSSL_SSL_DEBUG_BROKEN_PROTOCOL
+               /* Allow any certificate to pass test */
+               if (s->cert->cert_flags & SSL_CERT_FLAG_BROKEN_PROTOCOL)
+                       {
+                       rv = CERT_PKEY_STRICT_FLAGS|CERT_PKEY_EXPLICIT_SIGN|CERT_PKEY_VALID|CERT_PKEY_SIGN;
+                       cpk->valid_flags = rv;
+                       return rv;
+                       }
+#endif
                }
        else
                {