From 9296b4bb0e04a10b561da2996d3c9575872f1fa4 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Mon, 24 Jun 2013 12:03:19 -0700 Subject: Add TLS 1.2 digests patch Fixes a bug with handling TLS 1.2 and digest functions for DSA and ECDSA keys. Patch from Adam Langley Change-Id: I11b74472c0df16eca8de3aa36413686603814243 --- include/openssl/ssl3.h | 11 +- openssl.config | 9 + patches/README | 5 + patches/tls12_digests.patch | 411 ++++++++++++++++++++++++++++++++++++++++++++ ssl/s3_clnt.c | 26 ++- ssl/ssl3.h | 11 +- ssl/ssl_cert.c | 20 --- ssl/ssl_lib.c | 35 ++-- ssl/ssl_locl.h | 4 +- ssl/t1_lib.c | 104 ++++------- 10 files changed, 529 insertions(+), 107 deletions(-) create mode 100644 patches/tls12_digests.patch diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h index fee9671..215b985 100644 --- a/include/openssl/ssl3.h +++ b/include/openssl/ssl3.h @@ -550,6 +550,16 @@ typedef struct ssl3_state_st * verified Channel ID from the client: a P256 point, (x,y), where * each are big-endian values. */ unsigned char tlsext_channel_id[64]; + + /* These point to the digest function to use for signatures made with + * each type of public key. A NULL value indicates that the default + * digest should be used, which is SHA1 as of TLS 1.2. + * + * (These should be in the tmp member, but we have to put them here to + * ensure binary compatibility with earlier OpenSSL 1.0.* releases.) */ + const EVP_MD *digest_rsa; + const EVP_MD *digest_dsa; + const EVP_MD *digest_ecdsa; } SSL3_STATE; #endif @@ -699,4 +709,3 @@ typedef struct ssl3_state_st } #endif #endif - diff --git a/openssl.config b/openssl.config index d5c0aa4..e5a71bd 100644 --- a/openssl.config +++ b/openssl.config @@ -991,6 +991,7 @@ jsse.patch \ channelid.patch \ eng_dyn_dirs.patch \ fix_clang_build.patch \ +tls12_digests.patch \ " OPENSSL_PATCHES_progs_SOURCES="\ @@ -1047,3 +1048,11 @@ crypto/bio/b_sock.c \ crypto/x509v3/v3_utl.c \ " +OPENSSL_PATCHES_tls12_digests_SOURCES="\ +ssl/s3_clnt.c \ +ssl/ssl3.h \ +ssl/ssl_cert.c \ +ssl/ssl_lib.c \ +ssl/ssl_locl.h \ +ssl/t1_lib.c \ +" diff --git a/patches/README b/patches/README index 5d9ef03..85f0a4a 100644 --- a/patches/README +++ b/patches/README @@ -26,3 +26,8 @@ Fixes the case of having multiple DIR_ADD commands sent to eng_dyn fix_clang_build.patch Fixes the Clang based build. + +tls12_digests.patch + +Fixes a bug with handling TLS 1.2 and digest functions for DSA and ECDSA +keys. diff --git a/patches/tls12_digests.patch b/patches/tls12_digests.patch new file mode 100644 index 0000000..11f7c27 --- /dev/null +++ b/patches/tls12_digests.patch @@ -0,0 +1,411 @@ +From 3a8c7b1a08b2766a7f8a388eee14442281b4e295 Mon Sep 17 00:00:00 2001 +From: Adam Langley +Date: Thu, 24 Jan 2013 16:27:14 -0500 +Subject: [PATCH 19/36] tls12_digests + +Fixes a bug with handling TLS 1.2 and digest functions for DSA and ECDSA +keys. +--- + ssl/s3_clnt.c | 26 +++++++++++++-- + ssl/ssl3.h | 11 +++++- + ssl/ssl_cert.c | 20 ----------- + ssl/ssl_lib.c | 35 +++++++++++-------- + ssl/ssl_locl.h | 4 +-- + ssl/t1_lib.c | 104 ++++++++++++++++++++------------------------------------- + 6 files changed, 94 insertions(+), 106 deletions(-) + +diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c +index c9196b3..1f3b376 100644 +--- a/ssl/s3_clnt.c ++++ b/ssl/s3_clnt.c +@@ -1990,12 +1990,13 @@ int ssl3_get_certificate_request(SSL *s) + SSLerr(SSL_F_SSL3_GET_CERTIFICATE_REQUEST,SSL_R_DATA_LENGTH_TOO_LONG); + goto err; + } +- if ((llen & 1) || !tls1_process_sigalgs(s, p, llen)) ++ if (llen & 1) + { + ssl3_send_alert(s,SSL3_AL_FATAL,SSL_AD_DECODE_ERROR); + SSLerr(SSL_F_SSL3_GET_CERTIFICATE_REQUEST,SSL_R_SIGNATURE_ALGORITHMS_ERROR); + goto err; + } ++ tls1_process_sigalgs(s, p, llen); + p += llen; + } + +@@ -3017,7 +3018,28 @@ int ssl3_send_client_verify(SSL *s) + { + long hdatalen = 0; + void *hdata; +- const EVP_MD *md = s->cert->key->digest; ++ const EVP_MD *md; ++ switch (ssl_cert_type(NULL, pkey)) ++ { ++ case SSL_PKEY_RSA_ENC: ++ md = s->s3->digest_rsa; ++ break; ++ case SSL_PKEY_DSA_SIGN: ++ md = s->s3->digest_dsa; ++ break; ++ case SSL_PKEY_ECC: ++ md = s->s3->digest_ecdsa; ++ break; ++ default: ++ md = NULL; ++ } ++ if (!md) ++ /* Unlike with the SignatureAlgorithm extension (sent by clients), ++ * there are no default algorithms for the CertificateRequest message ++ * (sent by servers). However, now that we've sent a certificate ++ * for which we don't really know what hash to use for signing, the ++ * best we can do is try a default algorithm. */ ++ md = EVP_sha1(); + hdatalen = BIO_get_mem_data(s->s3->handshake_buffer, + &hdata); + if (hdatalen <= 0 || !tls12_get_sigandhash(p, pkey, md)) +diff --git a/ssl/ssl3.h b/ssl/ssl3.h +index 29098e4..3229995 100644 +--- a/ssl/ssl3.h ++++ b/ssl/ssl3.h +@@ -550,6 +550,16 @@ typedef struct ssl3_state_st + * verified Channel ID from the client: a P256 point, (x,y), where + * each are big-endian values. */ + unsigned char tlsext_channel_id[64]; ++ ++ /* These point to the digest function to use for signatures made with ++ * each type of public key. A NULL value indicates that the default ++ * digest should be used, which is SHA1 as of TLS 1.2. ++ * ++ * (These should be in the tmp member, but we have to put them here to ++ * ensure binary compatibility with earlier OpenSSL 1.0.* releases.) */ ++ const EVP_MD *digest_rsa; ++ const EVP_MD *digest_dsa; ++ const EVP_MD *digest_ecdsa; + } SSL3_STATE; + + #endif +@@ -700,4 +710,3 @@ typedef struct ssl3_state_st + } + #endif + #endif +- +diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c +index 5123a89..bc4150b 100644 +--- a/ssl/ssl_cert.c ++++ b/ssl/ssl_cert.c +@@ -160,21 +160,6 @@ int SSL_get_ex_data_X509_STORE_CTX_idx(void) + return ssl_x509_store_ctx_idx; + } + +-static void ssl_cert_set_default_md(CERT *cert) +- { +- /* Set digest values to defaults */ +-#ifndef OPENSSL_NO_DSA +- cert->pkeys[SSL_PKEY_DSA_SIGN].digest = EVP_sha1(); +-#endif +-#ifndef OPENSSL_NO_RSA +- cert->pkeys[SSL_PKEY_RSA_SIGN].digest = EVP_sha1(); +- cert->pkeys[SSL_PKEY_RSA_ENC].digest = EVP_sha1(); +-#endif +-#ifndef OPENSSL_NO_ECDSA +- cert->pkeys[SSL_PKEY_ECC].digest = EVP_sha1(); +-#endif +- } +- + CERT *ssl_cert_new(void) + { + CERT *ret; +@@ -189,7 +174,6 @@ CERT *ssl_cert_new(void) + + ret->key= &(ret->pkeys[SSL_PKEY_RSA_ENC]); + ret->references=1; +- ssl_cert_set_default_md(ret); + return(ret); + } + +@@ -322,10 +306,6 @@ CERT *ssl_cert_dup(CERT *cert) + * chain is held inside SSL_CTX */ + + ret->references=1; +- /* Set digests to defaults. NB: we don't copy existing values as they +- * will be set during handshake. +- */ +- ssl_cert_set_default_md(ret); + + return(ret); + +diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c +index 5f8b0b0..e360550 100644 +--- a/ssl/ssl_lib.c ++++ b/ssl/ssl_lib.c +@@ -2345,32 +2345,41 @@ EVP_PKEY *ssl_get_sign_pkey(SSL *s,const SSL_CIPHER *cipher, const EVP_MD **pmd) + { + unsigned long alg_a; + CERT *c; +- int idx = -1; + + alg_a = cipher->algorithm_auth; + c=s->cert; + ++ /* SHA1 is the default for all signature algorithms up to TLS 1.2, ++ * except RSA which is handled specially in s3_srvr.c */ ++ if (pmd) ++ *pmd = EVP_sha1(); ++ + if ((alg_a & SSL_aDSS) && +- (c->pkeys[SSL_PKEY_DSA_SIGN].privatekey != NULL)) +- idx = SSL_PKEY_DSA_SIGN; ++ (c->pkeys[SSL_PKEY_DSA_SIGN].privatekey != NULL)) ++ { ++ if (pmd && s->s3 && s->s3->digest_dsa) ++ *pmd = s->s3->digest_dsa; ++ return c->pkeys[SSL_PKEY_DSA_SIGN].privatekey; ++ } + else if (alg_a & SSL_aRSA) + { ++ if (pmd && s->s3 && s->s3->digest_rsa) ++ *pmd = s->s3->digest_rsa; + if (c->pkeys[SSL_PKEY_RSA_SIGN].privatekey != NULL) +- idx = SSL_PKEY_RSA_SIGN; +- else if (c->pkeys[SSL_PKEY_RSA_ENC].privatekey != NULL) +- idx = SSL_PKEY_RSA_ENC; ++ return c->pkeys[SSL_PKEY_RSA_SIGN].privatekey; ++ if (c->pkeys[SSL_PKEY_RSA_ENC].privatekey != NULL) ++ return c->pkeys[SSL_PKEY_RSA_ENC].privatekey; + } + else if ((alg_a & SSL_aECDSA) && + (c->pkeys[SSL_PKEY_ECC].privatekey != NULL)) +- idx = SSL_PKEY_ECC; +- if (idx == -1) + { +- SSLerr(SSL_F_SSL_GET_SIGN_PKEY,ERR_R_INTERNAL_ERROR); +- return(NULL); ++ if (pmd && s->s3 && s->s3->digest_ecdsa) ++ *pmd = s->s3->digest_ecdsa; ++ return c->pkeys[SSL_PKEY_ECC].privatekey; + } +- if (pmd) +- *pmd = c->pkeys[idx].digest; +- return c->pkeys[idx].privatekey; ++ ++ SSLerr(SSL_F_SSL_GET_SIGN_PKEY,ERR_R_INTERNAL_ERROR); ++ return(NULL); + } + + void ssl_update_cache(SSL *s,int mode) +diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h +index 6d38f0f..3e89fcb 100644 +--- a/ssl/ssl_locl.h ++++ b/ssl/ssl_locl.h +@@ -485,8 +485,6 @@ typedef struct cert_pkey_st + { + X509 *x509; + EVP_PKEY *privatekey; +- /* Digest to use when signing */ +- const EVP_MD *digest; + } CERT_PKEY; + + typedef struct cert_st +@@ -1142,7 +1140,7 @@ int ssl_add_clienthello_renegotiate_ext(SSL *s, unsigned char *p, int *len, + int ssl_parse_clienthello_renegotiate_ext(SSL *s, unsigned char *d, int len, + int *al); + long ssl_get_algorithm2(SSL *s); +-int tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize); ++void tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize); + int tls12_get_req_sig_algs(SSL *s, unsigned char *p); + + int ssl_add_clienthello_use_srtp_ext(SSL *s, unsigned char *p, int *len, int maxlen); +diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c +index 26805e4..6af51a9 100644 +--- a/ssl/t1_lib.c ++++ b/ssl/t1_lib.c +@@ -897,6 +897,13 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in + + s->servername_done = 0; + s->tlsext_status_type = -1; ++ ++ /* Reset TLS 1.2 digest functions to defaults because they don't carry ++ * over to a renegotiation. */ ++ s->s3->digest_rsa = NULL; ++ s->s3->digest_dsa = NULL; ++ s->s3->digest_ecdsa = NULL; ++ + #ifndef OPENSSL_NO_NEXTPROTONEG + s->s3->next_proto_neg_seen = 0; + #endif +@@ -1198,11 +1205,7 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in + *al = SSL_AD_DECODE_ERROR; + return 0; + } +- if (!tls1_process_sigalgs(s, data, dsize)) +- { +- *al = SSL_AD_DECODE_ERROR; +- return 0; +- } ++ tls1_process_sigalgs(s, data, dsize); + } + else if (type == TLSEXT_TYPE_status_request && + s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb) +@@ -2354,18 +2357,6 @@ static int tls12_find_id(int nid, tls12_lookup *table, size_t tlen) + } + return -1; + } +-#if 0 +-static int tls12_find_nid(int id, tls12_lookup *table, size_t tlen) +- { +- size_t i; +- for (i = 0; i < tlen; i++) +- { +- if (table[i].id == id) +- return table[i].nid; +- } +- return -1; +- } +-#endif + + int tls12_get_sigandhash(unsigned char *p, const EVP_PKEY *pk, const EVP_MD *md) + { +@@ -2384,6 +2375,8 @@ int tls12_get_sigandhash(unsigned char *p, const EVP_PKEY *pk, const EVP_MD *md) + return 1; + } + ++/* tls12_get_sigid returns the TLS 1.2 SignatureAlgorithm value corresponding ++ * to the given public key, or -1 if not known. */ + int tls12_get_sigid(const EVP_PKEY *pk) + { + return tls12_find_id(pk->type, tls12_sig, +@@ -2403,47 +2396,49 @@ const EVP_MD *tls12_get_hash(unsigned char hash_alg) + return EVP_md5(); + #endif + #ifndef OPENSSL_NO_SHA +- case TLSEXT_hash_sha1: ++ case TLSEXT_hash_sha1: + return EVP_sha1(); + #endif + #ifndef OPENSSL_NO_SHA256 +- case TLSEXT_hash_sha224: ++ case TLSEXT_hash_sha224: + return EVP_sha224(); + +- case TLSEXT_hash_sha256: ++ case TLSEXT_hash_sha256: + return EVP_sha256(); + #endif + #ifndef OPENSSL_NO_SHA512 +- case TLSEXT_hash_sha384: ++ case TLSEXT_hash_sha384: + return EVP_sha384(); + +- case TLSEXT_hash_sha512: ++ case TLSEXT_hash_sha512: + return EVP_sha512(); + #endif +- default: ++ default: + return NULL; + + } + } + +-/* Set preferred digest for each key type */ +- +-int tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize) ++/* tls1_process_sigalgs processes a signature_algorithms extension and sets the ++ * digest functions accordingly for each key type. ++ * ++ * See RFC 5246, section 7.4.1.4.1. ++ * ++ * data: points to the content of the extension, not including type and length ++ * headers. ++ * dsize: the number of bytes of |data|. Must be even. ++ */ ++void tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize) + { +- int i, idx; +- const EVP_MD *md; +- CERT *c = s->cert; ++ int i; ++ const EVP_MD *md, **digest_ptr; + /* Extension ignored for TLS versions below 1.2 */ + if (TLS1_get_version(s) < TLS1_2_VERSION) +- return 1; +- /* Should never happen */ +- if (!c) +- return 0; ++ return; + +- c->pkeys[SSL_PKEY_DSA_SIGN].digest = NULL; +- c->pkeys[SSL_PKEY_RSA_SIGN].digest = NULL; +- c->pkeys[SSL_PKEY_RSA_ENC].digest = NULL; +- c->pkeys[SSL_PKEY_ECC].digest = NULL; ++ s->s3->digest_rsa = NULL; ++ s->s3->digest_dsa = NULL; ++ s->s3->digest_ecdsa = NULL; + + for (i = 0; i < dsize; i += 2) + { +@@ -2453,56 +2448,31 @@ int tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize) + { + #ifndef OPENSSL_NO_RSA + case TLSEXT_signature_rsa: +- idx = SSL_PKEY_RSA_SIGN; ++ digest_ptr = &s->s3->digest_rsa; + break; + #endif + #ifndef OPENSSL_NO_DSA + case TLSEXT_signature_dsa: +- idx = SSL_PKEY_DSA_SIGN; ++ digest_ptr = &s->s3->digest_dsa; + break; + #endif + #ifndef OPENSSL_NO_ECDSA + case TLSEXT_signature_ecdsa: +- idx = SSL_PKEY_ECC; ++ digest_ptr = &s->s3->digest_ecdsa; + break; + #endif + default: + continue; + } + +- if (c->pkeys[idx].digest == NULL) ++ if (*digest_ptr == NULL) + { + md = tls12_get_hash(hash_alg); + if (md) +- { +- c->pkeys[idx].digest = md; +- if (idx == SSL_PKEY_RSA_SIGN) +- c->pkeys[SSL_PKEY_RSA_ENC].digest = md; +- } ++ *digest_ptr = md; + } + + } +- +- +- /* Set any remaining keys to default values. NOTE: if alg is not +- * supported it stays as NULL. +- */ +-#ifndef OPENSSL_NO_DSA +- if (!c->pkeys[SSL_PKEY_DSA_SIGN].digest) +- c->pkeys[SSL_PKEY_DSA_SIGN].digest = EVP_sha1(); +-#endif +-#ifndef OPENSSL_NO_RSA +- if (!c->pkeys[SSL_PKEY_RSA_SIGN].digest) +- { +- c->pkeys[SSL_PKEY_RSA_SIGN].digest = EVP_sha1(); +- c->pkeys[SSL_PKEY_RSA_ENC].digest = EVP_sha1(); +- } +-#endif +-#ifndef OPENSSL_NO_ECDSA +- if (!c->pkeys[SSL_PKEY_ECC].digest) +- c->pkeys[SSL_PKEY_ECC].digest = EVP_sha1(); +-#endif +- return 1; + } + + #endif +-- +1.8.2.1 + diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c index 1d40a2e..88077ea 100644 --- a/ssl/s3_clnt.c +++ b/ssl/s3_clnt.c @@ -2014,12 +2014,13 @@ int ssl3_get_certificate_request(SSL *s) SSLerr(SSL_F_SSL3_GET_CERTIFICATE_REQUEST,SSL_R_DATA_LENGTH_TOO_LONG); goto err; } - if ((llen & 1) || !tls1_process_sigalgs(s, p, llen)) + if (llen & 1) { ssl3_send_alert(s,SSL3_AL_FATAL,SSL_AD_DECODE_ERROR); SSLerr(SSL_F_SSL3_GET_CERTIFICATE_REQUEST,SSL_R_SIGNATURE_ALGORITHMS_ERROR); goto err; } + tls1_process_sigalgs(s, p, llen); p += llen; } @@ -3041,7 +3042,28 @@ int ssl3_send_client_verify(SSL *s) { long hdatalen = 0; void *hdata; - const EVP_MD *md = s->cert->key->digest; + const EVP_MD *md; + switch (ssl_cert_type(NULL, pkey)) + { + case SSL_PKEY_RSA_ENC: + md = s->s3->digest_rsa; + break; + case SSL_PKEY_DSA_SIGN: + md = s->s3->digest_dsa; + break; + case SSL_PKEY_ECC: + md = s->s3->digest_ecdsa; + break; + default: + md = NULL; + } + if (!md) + /* Unlike with the SignatureAlgorithm extension (sent by clients), + * there are no default algorithms for the CertificateRequest message + * (sent by servers). However, now that we've sent a certificate + * for which we don't really know what hash to use for signing, the + * best we can do is try a default algorithm. */ + md = EVP_sha1(); hdatalen = BIO_get_mem_data(s->s3->handshake_buffer, &hdata); if (hdatalen <= 0 || !tls12_get_sigandhash(p, pkey, md)) diff --git a/ssl/ssl3.h b/ssl/ssl3.h index fee9671..215b985 100644 --- a/ssl/ssl3.h +++ b/ssl/ssl3.h @@ -550,6 +550,16 @@ typedef struct ssl3_state_st * verified Channel ID from the client: a P256 point, (x,y), where * each are big-endian values. */ unsigned char tlsext_channel_id[64]; + + /* These point to the digest function to use for signatures made with + * each type of public key. A NULL value indicates that the default + * digest should be used, which is SHA1 as of TLS 1.2. + * + * (These should be in the tmp member, but we have to put them here to + * ensure binary compatibility with earlier OpenSSL 1.0.* releases.) */ + const EVP_MD *digest_rsa; + const EVP_MD *digest_dsa; + const EVP_MD *digest_ecdsa; } SSL3_STATE; #endif @@ -699,4 +709,3 @@ typedef struct ssl3_state_st } #endif #endif - diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c index 5123a89..bc4150b 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c @@ -160,21 +160,6 @@ int SSL_get_ex_data_X509_STORE_CTX_idx(void) return ssl_x509_store_ctx_idx; } -static void ssl_cert_set_default_md(CERT *cert) - { - /* Set digest values to defaults */ -#ifndef OPENSSL_NO_DSA - cert->pkeys[SSL_PKEY_DSA_SIGN].digest = EVP_sha1(); -#endif -#ifndef OPENSSL_NO_RSA - cert->pkeys[SSL_PKEY_RSA_SIGN].digest = EVP_sha1(); - cert->pkeys[SSL_PKEY_RSA_ENC].digest = EVP_sha1(); -#endif -#ifndef OPENSSL_NO_ECDSA - cert->pkeys[SSL_PKEY_ECC].digest = EVP_sha1(); -#endif - } - CERT *ssl_cert_new(void) { CERT *ret; @@ -189,7 +174,6 @@ CERT *ssl_cert_new(void) ret->key= &(ret->pkeys[SSL_PKEY_RSA_ENC]); ret->references=1; - ssl_cert_set_default_md(ret); return(ret); } @@ -322,10 +306,6 @@ CERT *ssl_cert_dup(CERT *cert) * chain is held inside SSL_CTX */ ret->references=1; - /* Set digests to defaults. NB: we don't copy existing values as they - * will be set during handshake. - */ - ssl_cert_set_default_md(ret); return(ret); diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 65b2ef8..6382325 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -2400,32 +2400,41 @@ EVP_PKEY *ssl_get_sign_pkey(SSL *s,const SSL_CIPHER *cipher, const EVP_MD **pmd) { unsigned long alg_a; CERT *c; - int idx = -1; alg_a = cipher->algorithm_auth; c=s->cert; + /* SHA1 is the default for all signature algorithms up to TLS 1.2, + * except RSA which is handled specially in s3_srvr.c */ + if (pmd) + *pmd = EVP_sha1(); + if ((alg_a & SSL_aDSS) && - (c->pkeys[SSL_PKEY_DSA_SIGN].privatekey != NULL)) - idx = SSL_PKEY_DSA_SIGN; + (c->pkeys[SSL_PKEY_DSA_SIGN].privatekey != NULL)) + { + if (pmd && s->s3 && s->s3->digest_dsa) + *pmd = s->s3->digest_dsa; + return c->pkeys[SSL_PKEY_DSA_SIGN].privatekey; + } else if (alg_a & SSL_aRSA) { + if (pmd && s->s3 && s->s3->digest_rsa) + *pmd = s->s3->digest_rsa; if (c->pkeys[SSL_PKEY_RSA_SIGN].privatekey != NULL) - idx = SSL_PKEY_RSA_SIGN; - else if (c->pkeys[SSL_PKEY_RSA_ENC].privatekey != NULL) - idx = SSL_PKEY_RSA_ENC; + return c->pkeys[SSL_PKEY_RSA_SIGN].privatekey; + if (c->pkeys[SSL_PKEY_RSA_ENC].privatekey != NULL) + return c->pkeys[SSL_PKEY_RSA_ENC].privatekey; } else if ((alg_a & SSL_aECDSA) && (c->pkeys[SSL_PKEY_ECC].privatekey != NULL)) - idx = SSL_PKEY_ECC; - if (idx == -1) { - SSLerr(SSL_F_SSL_GET_SIGN_PKEY,ERR_R_INTERNAL_ERROR); - return(NULL); + if (pmd && s->s3 && s->s3->digest_ecdsa) + *pmd = s->s3->digest_ecdsa; + return c->pkeys[SSL_PKEY_ECC].privatekey; } - if (pmd) - *pmd = c->pkeys[idx].digest; - return c->pkeys[idx].privatekey; + + SSLerr(SSL_F_SSL_GET_SIGN_PKEY,ERR_R_INTERNAL_ERROR); + return(NULL); } void ssl_update_cache(SSL *s,int mode) diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 5f21726..917c9f3 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -483,8 +483,6 @@ typedef struct cert_pkey_st X509 *x509; STACK_OF(X509) *cert_chain; EVP_PKEY *privatekey; - /* Digest to use when signing */ - const EVP_MD *digest; } CERT_PKEY; typedef struct cert_st @@ -1140,7 +1138,7 @@ int ssl_add_clienthello_renegotiate_ext(SSL *s, unsigned char *p, int *len, int ssl_parse_clienthello_renegotiate_ext(SSL *s, unsigned char *d, int len, int *al); long ssl_get_algorithm2(SSL *s); -int tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize); +void tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize); int tls12_get_req_sig_algs(SSL *s, unsigned char *p); int ssl_add_clienthello_use_srtp_ext(SSL *s, unsigned char *p, int *len, int maxlen); diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 28d45b3..53b805f 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -897,6 +897,13 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in s->servername_done = 0; s->tlsext_status_type = -1; + + /* Reset TLS 1.2 digest functions to defaults because they don't carry + * over to a renegotiation. */ + s->s3->digest_rsa = NULL; + s->s3->digest_dsa = NULL; + s->s3->digest_ecdsa = NULL; + #ifndef OPENSSL_NO_NEXTPROTONEG s->s3->next_proto_neg_seen = 0; #endif @@ -1194,11 +1201,7 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in *al = SSL_AD_DECODE_ERROR; return 0; } - if (!tls1_process_sigalgs(s, data, dsize)) - { - *al = SSL_AD_DECODE_ERROR; - return 0; - } + tls1_process_sigalgs(s, data, dsize); } else if (type == TLSEXT_TYPE_status_request && s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb) @@ -2350,18 +2353,6 @@ static int tls12_find_id(int nid, tls12_lookup *table, size_t tlen) } return -1; } -#if 0 -static int tls12_find_nid(int id, tls12_lookup *table, size_t tlen) - { - size_t i; - for (i = 0; i < tlen; i++) - { - if (table[i].id == id) - return table[i].nid; - } - return -1; - } -#endif int tls12_get_sigandhash(unsigned char *p, const EVP_PKEY *pk, const EVP_MD *md) { @@ -2380,6 +2371,8 @@ int tls12_get_sigandhash(unsigned char *p, const EVP_PKEY *pk, const EVP_MD *md) return 1; } +/* tls12_get_sigid returns the TLS 1.2 SignatureAlgorithm value corresponding + * to the given public key, or -1 if not known. */ int tls12_get_sigid(const EVP_PKEY *pk) { return tls12_find_id(pk->type, tls12_sig, @@ -2399,47 +2392,49 @@ const EVP_MD *tls12_get_hash(unsigned char hash_alg) return EVP_md5(); #endif #ifndef OPENSSL_NO_SHA - case TLSEXT_hash_sha1: + case TLSEXT_hash_sha1: return EVP_sha1(); #endif #ifndef OPENSSL_NO_SHA256 - case TLSEXT_hash_sha224: + case TLSEXT_hash_sha224: return EVP_sha224(); - case TLSEXT_hash_sha256: + case TLSEXT_hash_sha256: return EVP_sha256(); #endif #ifndef OPENSSL_NO_SHA512 - case TLSEXT_hash_sha384: + case TLSEXT_hash_sha384: return EVP_sha384(); - case TLSEXT_hash_sha512: + case TLSEXT_hash_sha512: return EVP_sha512(); #endif - default: + default: return NULL; } } -/* Set preferred digest for each key type */ - -int tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize) +/* tls1_process_sigalgs processes a signature_algorithms extension and sets the + * digest functions accordingly for each key type. + * + * See RFC 5246, section 7.4.1.4.1. + * + * data: points to the content of the extension, not including type and length + * headers. + * dsize: the number of bytes of |data|. Must be even. + */ +void tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize) { - int i, idx; - const EVP_MD *md; - CERT *c = s->cert; + int i; + const EVP_MD *md, **digest_ptr; /* Extension ignored for TLS versions below 1.2 */ if (TLS1_get_version(s) < TLS1_2_VERSION) - return 1; - /* Should never happen */ - if (!c) - return 0; + return; - c->pkeys[SSL_PKEY_DSA_SIGN].digest = NULL; - c->pkeys[SSL_PKEY_RSA_SIGN].digest = NULL; - c->pkeys[SSL_PKEY_RSA_ENC].digest = NULL; - c->pkeys[SSL_PKEY_ECC].digest = NULL; + s->s3->digest_rsa = NULL; + s->s3->digest_dsa = NULL; + s->s3->digest_ecdsa = NULL; for (i = 0; i < dsize; i += 2) { @@ -2449,56 +2444,31 @@ int tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize) { #ifndef OPENSSL_NO_RSA case TLSEXT_signature_rsa: - idx = SSL_PKEY_RSA_SIGN; + digest_ptr = &s->s3->digest_rsa; break; #endif #ifndef OPENSSL_NO_DSA case TLSEXT_signature_dsa: - idx = SSL_PKEY_DSA_SIGN; + digest_ptr = &s->s3->digest_dsa; break; #endif #ifndef OPENSSL_NO_ECDSA case TLSEXT_signature_ecdsa: - idx = SSL_PKEY_ECC; + digest_ptr = &s->s3->digest_ecdsa; break; #endif default: continue; } - if (c->pkeys[idx].digest == NULL) + if (*digest_ptr == NULL) { md = tls12_get_hash(hash_alg); if (md) - { - c->pkeys[idx].digest = md; - if (idx == SSL_PKEY_RSA_SIGN) - c->pkeys[SSL_PKEY_RSA_ENC].digest = md; - } + *digest_ptr = md; } } - - - /* Set any remaining keys to default values. NOTE: if alg is not - * supported it stays as NULL. - */ -#ifndef OPENSSL_NO_DSA - if (!c->pkeys[SSL_PKEY_DSA_SIGN].digest) - c->pkeys[SSL_PKEY_DSA_SIGN].digest = EVP_sha1(); -#endif -#ifndef OPENSSL_NO_RSA - if (!c->pkeys[SSL_PKEY_RSA_SIGN].digest) - { - c->pkeys[SSL_PKEY_RSA_SIGN].digest = EVP_sha1(); - c->pkeys[SSL_PKEY_RSA_ENC].digest = EVP_sha1(); - } -#endif -#ifndef OPENSSL_NO_ECDSA - if (!c->pkeys[SSL_PKEY_ECC].digest) - c->pkeys[SSL_PKEY_ECC].digest = EVP_sha1(); -#endif - return 1; } #endif -- cgit v1.1 From ee53ab1212ec75db6e1704a6909c45c93dd411c3 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Mon, 24 Jun 2013 12:07:13 -0700 Subject: Add ALPN support patch This change adds support for ALPN[1] in OpenSSL. ALPN is the IETF blessed version of NPN and we'll be supporting both ALPN and NPN for some time yet. [1] https://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-00 Patch from Adam Langley Change-Id: I556b1ee877f398ae8b7f1d4abbaddc44611e5f51 --- apps/s_client.c | 40 +++- include/openssl/ssl.h | 45 ++++ include/openssl/ssl3.h | 10 + include/openssl/tls1.h | 3 + openssl.config | 12 + patches/README | 6 + patches/alpn.patch | 592 +++++++++++++++++++++++++++++++++++++++++++++++++ ssl/s3_lib.c | 13 ++ ssl/ssl.h | 45 ++++ ssl/ssl3.h | 10 + ssl/ssl_lib.c | 87 ++++++++ ssl/t1_lib.c | 167 +++++++++++++- ssl/tls1.h | 3 + 13 files changed, 1030 insertions(+), 3 deletions(-) create mode 100644 patches/alpn.patch diff --git a/apps/s_client.c b/apps/s_client.c index 791e277..cb1efcd 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -359,6 +359,7 @@ static void sc_usage(void) BIO_printf(bio_err," -no_ticket - disable use of RFC4507bis session tickets\n"); # ifndef OPENSSL_NO_NEXTPROTONEG BIO_printf(bio_err," -nextprotoneg arg - enable NPN extension, considering named protocols supported (comma-separated list)\n"); + BIO_printf(bio_err," -alpn arg - enable ALPN extension, considering named protocols supported (comma-separated list)\n"); # endif #endif BIO_printf(bio_err," -cutthrough - enable 1-RTT full-handshake for strong ciphers\n"); @@ -611,6 +612,7 @@ int MAIN(int argc, char **argv) {NULL,0}; # ifndef OPENSSL_NO_NEXTPROTONEG const char *next_proto_neg_in = NULL; + const char *alpn_in = NULL; # endif #endif char *sess_in = NULL; @@ -883,6 +885,11 @@ int MAIN(int argc, char **argv) if (--argc < 1) goto bad; next_proto_neg_in = *(++argv); } + else if (strcmp(*argv,"-alpn") == 0) + { + if (--argc < 1) goto bad; + alpn_in = *(++argv); + } # endif #endif else if (strcmp(*argv,"-cutthrough") == 0) @@ -1157,9 +1164,23 @@ bad: */ if (socket_type == SOCK_DGRAM) SSL_CTX_set_read_ahead(ctx, 1); -#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) +#if !defined(OPENSSL_NO_TLSEXT) +# if !defined(OPENSSL_NO_NEXTPROTONEG) if (next_proto.data) SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto); +# endif + if (alpn_in) + { + unsigned short alpn_len; + unsigned char *alpn = next_protos_parse(&alpn_len, alpn_in); + + if (alpn == NULL) + { + BIO_printf(bio_err, "Error parsing -alpn argument\n"); + goto end; + } + SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len); + } #endif /* Enable handshake cutthrough for client connections using @@ -2077,7 +2098,8 @@ static void print_stuff(BIO *bio, SSL *s, int full) } #endif -#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) +#if !defined(OPENSSL_NO_TLSEXT) +# if !defined(OPENSSL_NO_NEXTPROTONEG) if (next_proto.status != -1) { const unsigned char *proto; unsigned int proto_len; @@ -2086,6 +2108,20 @@ static void print_stuff(BIO *bio, SSL *s, int full) BIO_write(bio, proto, proto_len); BIO_write(bio, "\n", 1); } + { + const unsigned char *proto; + unsigned int proto_len; + SSL_get0_alpn_selected(s, &proto, &proto_len); + if (proto_len > 0) + { + BIO_printf(bio, "ALPN protocol: "); + BIO_write(bio, proto, proto_len); + BIO_write(bio, "\n", 1); + } + else + BIO_printf(bio, "No ALPN negotiated\n"); + } +# endif #endif #ifndef OPENSSL_NO_SRTP diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 61b110f..dac9c3e 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -979,6 +979,31 @@ struct ssl_ctx_st void *arg); void *next_proto_select_cb_arg; # endif + + /* ALPN information + * (we are in the process of transitioning from NPN to ALPN.) */ + + /* For a server, this contains a callback function that allows the + * server to select the protocol for the connection. + * out: on successful return, this must point to the raw protocol + * name (without the length prefix). + * outlen: on successful return, this contains the length of |*out|. + * in: points to the client's list of supported protocols in + * wire-format. + * inlen: the length of |in|. */ + int (*alpn_select_cb)(SSL *s, + const unsigned char **out, + unsigned char *outlen, + const unsigned char* in, + unsigned int inlen, + void *arg); + void *alpn_select_cb_arg; + + /* For a client, this contains the list of supported protocols in wire + * format. */ + unsigned char* alpn_client_proto_list; + unsigned alpn_client_proto_list_len; + /* SRTP profiles we are willing to do from RFC 5764 */ STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles; @@ -1075,6 +1100,21 @@ void SSL_get0_next_proto_negotiated(const SSL *s, #define OPENSSL_NPN_NO_OVERLAP 2 #endif +int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char* protos, + unsigned protos_len); +int SSL_set_alpn_protos(SSL *ssl, const unsigned char* protos, + unsigned protos_len); +void SSL_CTX_set_alpn_select_cb(SSL_CTX* ctx, + int (*cb) (SSL *ssl, + const unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *arg), + void *arg); +void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, + unsigned *len); + #ifndef OPENSSL_NO_PSK /* the maximum length of the buffer given to callbacks containing the * resulting identity/psk */ @@ -1365,6 +1405,11 @@ struct ssl_st char tlsext_channel_id_enabled; /* The client's Channel ID private key. */ EVP_PKEY *tlsext_channel_id_private; + + /* For a client, this contains the list of supported protocols in wire + * format. */ + unsigned char* alpn_client_proto_list; + unsigned alpn_client_proto_list_len; #else #define session_ctx ctx #endif /* OPENSSL_NO_TLSEXT */ diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h index 215b985..4729868 100644 --- a/include/openssl/ssl3.h +++ b/include/openssl/ssl3.h @@ -551,6 +551,16 @@ typedef struct ssl3_state_st * each are big-endian values. */ unsigned char tlsext_channel_id[64]; + /* ALPN information + * (we are in the process of transitioning from NPN to ALPN.) */ + + /* In a server these point to the selected ALPN protocol after the + * ClientHello has been processed. In a client these contain the + * protocol that the server selected once the ServerHello has been + * processed. */ + unsigned char *alpn_selected; + unsigned alpn_selected_len; + /* These point to the digest function to use for signatures made with * each type of public key. A NULL value indicates that the default * digest should be used, which is SHA1 as of TLS 1.2. diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h index 8fc1ff4..c6670f4 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -230,6 +230,9 @@ extern "C" { /* ExtensionType value from RFC5620 */ #define TLSEXT_TYPE_heartbeat 15 +/* ExtensionType value from draft-ietf-tls-applayerprotoneg-00 */ +#define TLSEXT_TYPE_application_layer_protocol_negotiation 16 + /* ExtensionType value from RFC4507 */ #define TLSEXT_TYPE_session_ticket 35 diff --git a/openssl.config b/openssl.config index e5a71bd..00e4ff9 100644 --- a/openssl.config +++ b/openssl.config @@ -992,6 +992,7 @@ channelid.patch \ eng_dyn_dirs.patch \ fix_clang_build.patch \ tls12_digests.patch \ +alpn.patch \ " OPENSSL_PATCHES_progs_SOURCES="\ @@ -1056,3 +1057,14 @@ ssl/ssl_lib.c \ ssl/ssl_locl.h \ ssl/t1_lib.c \ " + +OPENSSL_PATCHES_alpn_SOURCES="\ +apps/s_client.c \ +ssl/s3_lib.c \ +ssl/ssl.h \ +ssl/ssl3.h \ +ssl/ssl_lib.c \ +ssl/t1_lib.c \ +ssl/tls1.h \ +" + diff --git a/patches/README b/patches/README index 85f0a4a..0859abe 100644 --- a/patches/README +++ b/patches/README @@ -31,3 +31,9 @@ tls12_digests.patch Fixes a bug with handling TLS 1.2 and digest functions for DSA and ECDSA keys. + +alpn.patch + +This change adds support for ALPN in OpenSSL. ALPN is the IETF +blessed version of NPN and we'll be supporting both ALPN and NPN for +some time yet. diff --git a/patches/alpn.patch b/patches/alpn.patch new file mode 100644 index 0000000..084ac32 --- /dev/null +++ b/patches/alpn.patch @@ -0,0 +1,592 @@ +From 5ebeb8b5d90f9f47418b6b8d898ace8f1b4d4104 Mon Sep 17 00:00:00 2001 +From: Adam Langley +Date: Mon, 15 Apr 2013 18:07:47 -0400 + +This change adds support for ALPN[1] in OpenSSL. ALPN is the IETF +blessed version of NPN and we'll be supporting both ALPN and NPN for +some time yet. + +[1] https://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-00 +--- + apps/s_client.c | 40 +++++++++++++- + ssl/s3_lib.c | 13 +++++ + ssl/ssl.h | 45 +++++++++++++++ + ssl/ssl3.h | 10 ++++ + ssl/ssl_lib.c | 87 +++++++++++++++++++++++++++++ + ssl/t1_lib.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- + ssl/tls1.h | 3 + + 7 files changed, 362 insertions(+), 3 deletions(-) + +diff --git a/apps/s_client.c b/apps/s_client.c +index 791e277..cb1efcd 100644 +--- a/apps/s_client.c ++++ b/apps/s_client.c +@@ -359,6 +359,7 @@ static void sc_usage(void) + BIO_printf(bio_err," -no_ticket - disable use of RFC4507bis session tickets\n"); + # ifndef OPENSSL_NO_NEXTPROTONEG + BIO_printf(bio_err," -nextprotoneg arg - enable NPN extension, considering named protocols supported (comma-separated list)\n"); ++ BIO_printf(bio_err," -alpn arg - enable ALPN extension, considering named protocols supported (comma-separated list)\n"); + # endif + #endif + BIO_printf(bio_err," -cutthrough - enable 1-RTT full-handshake for strong ciphers\n"); +@@ -611,6 +612,7 @@ int MAIN(int argc, char **argv) + {NULL,0}; + # ifndef OPENSSL_NO_NEXTPROTONEG + const char *next_proto_neg_in = NULL; ++ const char *alpn_in = NULL; + # endif + #endif + char *sess_in = NULL; +@@ -883,6 +885,11 @@ int MAIN(int argc, char **argv) + if (--argc < 1) goto bad; + next_proto_neg_in = *(++argv); + } ++ else if (strcmp(*argv,"-alpn") == 0) ++ { ++ if (--argc < 1) goto bad; ++ alpn_in = *(++argv); ++ } + # endif + #endif + else if (strcmp(*argv,"-cutthrough") == 0) +@@ -1157,9 +1164,23 @@ bad: + */ + if (socket_type == SOCK_DGRAM) SSL_CTX_set_read_ahead(ctx, 1); + +-#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) ++#if !defined(OPENSSL_NO_TLSEXT) ++# if !defined(OPENSSL_NO_NEXTPROTONEG) + if (next_proto.data) + SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto); ++# endif ++ if (alpn_in) ++ { ++ unsigned short alpn_len; ++ unsigned char *alpn = next_protos_parse(&alpn_len, alpn_in); ++ ++ if (alpn == NULL) ++ { ++ BIO_printf(bio_err, "Error parsing -alpn argument\n"); ++ goto end; ++ } ++ SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len); ++ } + #endif + + /* Enable handshake cutthrough for client connections using +@@ -2077,7 +2098,8 @@ static void print_stuff(BIO *bio, SSL *s, int full) + } + #endif + +-#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) ++#if !defined(OPENSSL_NO_TLSEXT) ++# if !defined(OPENSSL_NO_NEXTPROTONEG) + if (next_proto.status != -1) { + const unsigned char *proto; + unsigned int proto_len; +@@ -2086,6 +2108,20 @@ static void print_stuff(BIO *bio, SSL *s, int full) + BIO_write(bio, proto, proto_len); + BIO_write(bio, "\n", 1); + } ++ { ++ const unsigned char *proto; ++ unsigned int proto_len; ++ SSL_get0_alpn_selected(s, &proto, &proto_len); ++ if (proto_len > 0) ++ { ++ BIO_printf(bio, "ALPN protocol: "); ++ BIO_write(bio, proto, proto_len); ++ BIO_write(bio, "\n", 1); ++ } ++ else ++ BIO_printf(bio, "No ALPN negotiated\n"); ++ } ++# endif + #endif + + #ifndef OPENSSL_NO_SRTP +diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c +index 5e46393..2cd1654 100644 +--- a/ssl/s3_lib.c ++++ b/ssl/s3_lib.c +@@ -2996,6 +2996,11 @@ void ssl3_free(SSL *s) + BIO_free(s->s3->handshake_buffer); + } + if (s->s3->handshake_dgst) ssl3_free_digest_list(s); ++#ifndef OPENSSL_NO_TLSEXT ++ if (s->s3->alpn_selected) ++ OPENSSL_free(s->s3->alpn_selected); ++#endif ++ + #ifndef OPENSSL_NO_SRP + SSL_SRP_CTX_free(s); + #endif +@@ -3055,6 +3060,14 @@ void ssl3_clear(SSL *s) + if (s->s3->handshake_dgst) { + ssl3_free_digest_list(s); + } ++ ++#if !defined(OPENSSL_NO_TLSEXT) ++ if (s->s3->alpn_selected) ++ { ++ free(s->s3->alpn_selected); ++ s->s3->alpn_selected = NULL; ++ } ++#endif + memset(s->s3,0,sizeof *s->s3); + s->s3->rbuf.buf = rp; + s->s3->wbuf.buf = wp; +diff --git a/ssl/ssl.h b/ssl/ssl.h +index e8c73fa..612c7aa 100644 +--- a/ssl/ssl.h ++++ b/ssl/ssl.h +@@ -1019,6 +1019,31 @@ struct ssl_ctx_st + void *arg); + void *next_proto_select_cb_arg; + # endif ++ ++ /* ALPN information ++ * (we are in the process of transitioning from NPN to ALPN.) */ ++ ++ /* For a server, this contains a callback function that allows the ++ * server to select the protocol for the connection. ++ * out: on successful return, this must point to the raw protocol ++ * name (without the length prefix). ++ * outlen: on successful return, this contains the length of |*out|. ++ * in: points to the client's list of supported protocols in ++ * wire-format. ++ * inlen: the length of |in|. */ ++ int (*alpn_select_cb)(SSL *s, ++ const unsigned char **out, ++ unsigned char *outlen, ++ const unsigned char* in, ++ unsigned int inlen, ++ void *arg); ++ void *alpn_select_cb_arg; ++ ++ /* For a client, this contains the list of supported protocols in wire ++ * format. */ ++ unsigned char* alpn_client_proto_list; ++ unsigned alpn_client_proto_list_len; ++ + /* SRTP profiles we are willing to do from RFC 5764 */ + STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles; + +@@ -1120,6 +1145,21 @@ void SSL_get0_next_proto_negotiated(const SSL *s, + #define OPENSSL_NPN_NO_OVERLAP 2 + #endif + ++int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char* protos, ++ unsigned protos_len); ++int SSL_set_alpn_protos(SSL *ssl, const unsigned char* protos, ++ unsigned protos_len); ++void SSL_CTX_set_alpn_select_cb(SSL_CTX* ctx, ++ int (*cb) (SSL *ssl, ++ const unsigned char **out, ++ unsigned char *outlen, ++ const unsigned char *in, ++ unsigned int inlen, ++ void *arg), ++ void *arg); ++void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, ++ unsigned *len); ++ + #ifndef OPENSSL_NO_PSK + /* the maximum length of the buffer given to callbacks containing the + * resulting identity/psk */ +@@ -1422,6 +1462,11 @@ struct ssl_st + char tlsext_channel_id_enabled; + /* The client's Channel ID private key. */ + EVP_PKEY *tlsext_channel_id_private; ++ ++ /* For a client, this contains the list of supported protocols in wire ++ * format. */ ++ unsigned char* alpn_client_proto_list; ++ unsigned alpn_client_proto_list_len; + #else + #define session_ctx ctx + #endif /* OPENSSL_NO_TLSEXT */ +diff --git a/ssl/ssl3.h b/ssl/ssl3.h +index 3229995..28c46d5 100644 +--- a/ssl/ssl3.h ++++ b/ssl/ssl3.h +@@ -551,6 +551,16 @@ typedef struct ssl3_state_st + * each are big-endian values. */ + unsigned char tlsext_channel_id[64]; + ++ /* ALPN information ++ * (we are in the process of transitioning from NPN to ALPN.) */ ++ ++ /* In a server these point to the selected ALPN protocol after the ++ * ClientHello has been processed. In a client these contain the ++ * protocol that the server selected once the ServerHello has been ++ * processed. */ ++ unsigned char *alpn_selected; ++ unsigned alpn_selected_len; ++ + /* These point to the digest function to use for signatures made with + * each type of public key. A NULL value indicates that the default + * digest should be used, which is SHA1 as of TLS 1.2. +diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c +index e360550..b472423 100644 +--- a/ssl/ssl_lib.c ++++ b/ssl/ssl_lib.c +@@ -359,6 +359,17 @@ SSL *SSL_new(SSL_CTX *ctx) + # ifndef OPENSSL_NO_NEXTPROTONEG + s->next_proto_negotiated = NULL; + # endif ++ ++ if (s->ctx->alpn_client_proto_list) ++ { ++ s->alpn_client_proto_list = ++ OPENSSL_malloc(s->ctx->alpn_client_proto_list_len); ++ if (s->alpn_client_proto_list == NULL) ++ goto err; ++ memcpy(s->alpn_client_proto_list, s->ctx->alpn_client_proto_list, ++ s->ctx->alpn_client_proto_list_len); ++ s->alpn_client_proto_list_len = s->ctx->alpn_client_proto_list_len; ++ } + #endif + + s->verify_result=X509_V_OK; +@@ -564,6 +575,8 @@ void SSL_free(SSL *s) + OPENSSL_free(s->tlsext_ocsp_resp); + if (s->tlsext_channel_id_private) + EVP_PKEY_free(s->tlsext_channel_id_private); ++ if (s->alpn_client_proto_list) ++ OPENSSL_free(s->alpn_client_proto_list); + #endif + + if (s->client_CA != NULL) +@@ -1615,6 +1628,78 @@ void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, int (*cb) (SSL *s, unsigned + ctx->next_proto_select_cb_arg = arg; + } + # endif ++ ++/* SSL_CTX_set_alpn_protos sets the ALPN protocol list on |ctx| to |protos|. ++ * |protos| must be in wire-format (i.e. a series of non-empty, 8-bit ++ * length-prefixed strings). ++ * ++ * Returns 0 on success. */ ++int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char* protos, ++ unsigned protos_len) ++ { ++ if (ctx->alpn_client_proto_list) ++ OPENSSL_free(ctx->alpn_client_proto_list); ++ ++ ctx->alpn_client_proto_list = OPENSSL_malloc(protos_len); ++ if (!ctx->alpn_client_proto_list) ++ return 1; ++ memcpy(ctx->alpn_client_proto_list, protos, protos_len); ++ ctx->alpn_client_proto_list_len = protos_len; ++ ++ return 0; ++ } ++ ++/* SSL_set_alpn_protos sets the ALPN protocol list on |ssl| to |protos|. ++ * |protos| must be in wire-format (i.e. a series of non-empty, 8-bit ++ * length-prefixed strings). ++ * ++ * Returns 0 on success. */ ++int SSL_set_alpn_protos(SSL *ssl, const unsigned char* protos, ++ unsigned protos_len) ++ { ++ if (ssl->alpn_client_proto_list) ++ OPENSSL_free(ssl->alpn_client_proto_list); ++ ++ ssl->alpn_client_proto_list = OPENSSL_malloc(protos_len); ++ if (!ssl->alpn_client_proto_list) ++ return 1; ++ memcpy(ssl->alpn_client_proto_list, protos, protos_len); ++ ssl->alpn_client_proto_list_len = protos_len; ++ ++ return 0; ++ } ++ ++/* SSL_CTX_set_alpn_select_cb sets a callback function on |ctx| that is called ++ * during ClientHello processing in order to select an ALPN protocol from the ++ * client's list of offered protocols. */ ++void SSL_CTX_set_alpn_select_cb(SSL_CTX* ctx, ++ int (*cb) (SSL *ssl, ++ const unsigned char **out, ++ unsigned char *outlen, ++ const unsigned char *in, ++ unsigned int inlen, ++ void *arg), ++ void *arg) ++ { ++ ctx->alpn_select_cb = cb; ++ ctx->alpn_select_cb_arg = arg; ++ } ++ ++/* SSL_get0_alpn_selected gets the selected ALPN protocol (if any) from |ssl|. ++ * On return it sets |*data| to point to |*len| bytes of protocol name (not ++ * including the leading length-prefix byte). If the server didn't respond with ++ * a negotiated protocol then |*len| will be zero. */ ++void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, ++ unsigned *len) ++ { ++ *data = NULL; ++ if (ssl->s3) ++ *data = ssl->s3->alpn_selected; ++ if (*data == NULL) ++ *len = 0; ++ else ++ *len = ssl->s3->alpn_selected_len; ++ } + #endif + + int SSL_export_keying_material(SSL *s, unsigned char *out, size_t olen, +@@ -1955,6 +2040,8 @@ void SSL_CTX_free(SSL_CTX *a) + #ifndef OPENSSL_NO_TLSEXT + if (a->tlsext_channel_id_private) + EVP_PKEY_free(a->tlsext_channel_id_private); ++ if (a->alpn_client_proto_list != NULL) ++ OPENSSL_free(a->alpn_client_proto_list); + #endif + + OPENSSL_free(a); +diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c +index 1f93a6f..b2e049a 100644 +--- a/ssl/t1_lib.c ++++ b/ssl/t1_lib.c +@@ -659,6 +659,18 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha + s2n(0,ret); + } + ++ if (s->alpn_client_proto_list && !s->s3->tmp.finish_md_len) ++ { ++ if ((size_t)(limit - ret) < 6 + s->alpn_client_proto_list_len) ++ return NULL; ++ s2n(TLSEXT_TYPE_application_layer_protocol_negotiation,ret); ++ s2n(2 + s->alpn_client_proto_list_len,ret); ++ s2n(s->alpn_client_proto_list_len,ret); ++ memcpy(ret, s->alpn_client_proto_list, ++ s->alpn_client_proto_list_len); ++ ret += s->alpn_client_proto_list_len; ++ } ++ + #ifndef OPENSSL_NO_SRTP + if(SSL_get_srtp_profiles(s)) + { +@@ -879,6 +891,21 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha + s2n(0,ret); + } + ++ if (s->s3->alpn_selected) ++ { ++ const unsigned char *selected = s->s3->alpn_selected; ++ unsigned len = s->s3->alpn_selected_len; ++ ++ if ((long)(limit - ret - 4 - 2 - 1 - len) < 0) ++ return NULL; ++ s2n(TLSEXT_TYPE_application_layer_protocol_negotiation,ret); ++ s2n(3 + len,ret); ++ s2n(1 + len,ret); ++ *ret++ = len; ++ memcpy(ret, selected, len); ++ ret += len; ++ } ++ + if ((extdatalen = ret-p-2)== 0) + return p; + +@@ -966,6 +993,76 @@ static void ssl_check_for_safari(SSL *s, const unsigned char *data, const unsign + s->is_probably_safari = 1; + } + ++/* tls1_alpn_handle_client_hello is called to process the ALPN extension in a ++ * ClientHello. ++ * data: the contents of the extension, not including the type and length. ++ * data_len: the number of bytes in |data| ++ * al: a pointer to the alert value to send in the event of a non-zero ++ * return. ++ * ++ * returns: 0 on success. */ ++static int tls1_alpn_handle_client_hello(SSL *s, const unsigned char *data, ++ unsigned data_len, int *al) ++ { ++ unsigned i; ++ unsigned proto_len; ++ const unsigned char *selected; ++ unsigned char selected_len; ++ int r; ++ ++ if (s->ctx->alpn_select_cb == NULL) ++ return 0; ++ ++ if (data_len < 2) ++ goto parse_error; ++ ++ /* data should contain a uint16 length followed by a series of 8-bit, ++ * length-prefixed strings. */ ++ i = ((unsigned) data[0]) << 8 | ++ ((unsigned) data[1]); ++ data_len -= 2; ++ data += 2; ++ if (data_len != i) ++ goto parse_error; ++ ++ if (data_len < 2) ++ goto parse_error; ++ ++ for (i = 0; i < data_len;) ++ { ++ proto_len = data[i]; ++ i++; ++ ++ if (proto_len == 0) ++ goto parse_error; ++ ++ if (i + proto_len < i || i + proto_len > data_len) ++ goto parse_error; ++ ++ i += proto_len; ++ } ++ ++ r = s->ctx->alpn_select_cb(s, &selected, &selected_len, data, data_len, ++ s->ctx->alpn_select_cb_arg); ++ if (r == SSL_TLSEXT_ERR_OK) { ++ if (s->s3->alpn_selected) ++ OPENSSL_free(s->s3->alpn_selected); ++ s->s3->alpn_selected = OPENSSL_malloc(selected_len); ++ if (!s->s3->alpn_selected) ++ { ++ *al = SSL_AD_INTERNAL_ERROR; ++ return -1; ++ } ++ memcpy(s->s3->alpn_selected, selected, selected_len); ++ s->s3->alpn_selected_len = selected_len; ++ } ++ return 0; ++ ++parse_error: ++ *al = SSL_AD_DECODE_ERROR; ++ return -1; ++ } ++ + int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al) + { + unsigned short type; +@@ -988,6 +1085,12 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in + s->s3->next_proto_neg_seen = 0; + #endif + ++ if (s->s3->alpn_selected) ++ { ++ OPENSSL_free(s->s3->alpn_selected); ++ s->s3->alpn_selected = NULL; ++ } ++ + #ifndef OPENSSL_NO_HEARTBEATS + s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED | + SSL_TLSEXT_HB_DONT_SEND_REQUESTS); +@@ -1420,7 +1523,8 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in + #endif + #ifndef OPENSSL_NO_NEXTPROTONEG + else if (type == TLSEXT_TYPE_next_proto_neg && +- s->s3->tmp.finish_md_len == 0) ++ s->s3->tmp.finish_md_len == 0 && ++ s->s3->alpn_selected == NULL) + { + /* We shouldn't accept this extension on a + * renegotiation. +@@ -1444,6 +1548,16 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in + else if (type == TLSEXT_TYPE_channel_id && s->tlsext_channel_id_enabled) + s->s3->tlsext_channel_id_valid = 1; + ++ else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation && ++ s->ctx->alpn_select_cb && ++ s->s3->tmp.finish_md_len == 0) ++ { ++ if (tls1_alpn_handle_client_hello(s, data, size, al) != 0) ++ return 0; ++ /* ALPN takes precedence over NPN. */ ++ s->s3->next_proto_neg_seen = 0; ++ } ++ + /* session ticket processed earlier */ + #ifndef OPENSSL_NO_SRTP + else if (type == TLSEXT_TYPE_use_srtp) +@@ -1508,6 +1622,12 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in + s->s3->next_proto_neg_seen = 0; + #endif + ++ if (s->s3->alpn_selected) ++ { ++ OPENSSL_free(s->s3->alpn_selected); ++ s->s3->alpn_selected = NULL; ++ } ++ + #ifndef OPENSSL_NO_HEARTBEATS + s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED | + SSL_TLSEXT_HB_DONT_SEND_REQUESTS); +@@ -1677,6 +1797,51 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in + else if (type == TLSEXT_TYPE_channel_id) + s->s3->tlsext_channel_id_valid = 1; + ++ else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation) ++ { ++ unsigned len; ++ ++ /* We must have requested it. */ ++ if (s->alpn_client_proto_list == NULL) ++ { ++ *al = TLS1_AD_UNSUPPORTED_EXTENSION; ++ return 0; ++ } ++ if (size < 4) ++ { ++ *al = TLS1_AD_DECODE_ERROR; ++ return 0; ++ } ++ /* The extension data consists of: ++ * uint16 list_length ++ * uint8 proto_length; ++ * uint8 proto[proto_length]; */ ++ len = data[0]; ++ len <<= 8; ++ len |= data[1]; ++ if (len != (unsigned) size - 2) ++ { ++ *al = TLS1_AD_DECODE_ERROR; ++ return 0; ++ } ++ len = data[2]; ++ if (len != (unsigned) size - 3) ++ { ++ *al = TLS1_AD_DECODE_ERROR; ++ return 0; ++ } ++ if (s->s3->alpn_selected) ++ OPENSSL_free(s->s3->alpn_selected); ++ s->s3->alpn_selected = OPENSSL_malloc(len); ++ if (!s->s3->alpn_selected) ++ { ++ *al = TLS1_AD_INTERNAL_ERROR; ++ return 0; ++ } ++ memcpy(s->s3->alpn_selected, data + 3, len); ++ s->s3->alpn_selected_len = len; ++ } ++ + else if (type == TLSEXT_TYPE_renegotiate) + { + if(!ssl_parse_serverhello_renegotiate_ext(s, data, size, al)) +diff --git a/ssl/tls1.h b/ssl/tls1.h +index 8fc1ff4..c6670f4 100644 +--- a/ssl/tls1.h ++++ b/ssl/tls1.h +@@ -230,6 +230,9 @@ extern "C" { + /* ExtensionType value from RFC5620 */ + #define TLSEXT_TYPE_heartbeat 15 + ++/* ExtensionType value from draft-ietf-tls-applayerprotoneg-00 */ ++#define TLSEXT_TYPE_application_layer_protocol_negotiation 16 ++ + /* ExtensionType value from RFC4507 */ + #define TLSEXT_TYPE_session_ticket 35 + +-- +1.8.2.1 + diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 0be87e8..1d3af82 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -2996,6 +2996,11 @@ void ssl3_free(SSL *s) BIO_free(s->s3->handshake_buffer); } if (s->s3->handshake_dgst) ssl3_free_digest_list(s); +#ifndef OPENSSL_NO_TLSEXT + if (s->s3->alpn_selected) + OPENSSL_free(s->s3->alpn_selected); +#endif + #ifndef OPENSSL_NO_SRP SSL_SRP_CTX_free(s); #endif @@ -3055,6 +3060,14 @@ void ssl3_clear(SSL *s) if (s->s3->handshake_dgst) { ssl3_free_digest_list(s); } + +#if !defined(OPENSSL_NO_TLSEXT) + if (s->s3->alpn_selected) + { + free(s->s3->alpn_selected); + s->s3->alpn_selected = NULL; + } +#endif memset(s->s3,0,sizeof *s->s3); s->s3->rbuf.buf = rp; s->s3->wbuf.buf = wp; diff --git a/ssl/ssl.h b/ssl/ssl.h index 61b110f..dac9c3e 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -979,6 +979,31 @@ struct ssl_ctx_st void *arg); void *next_proto_select_cb_arg; # endif + + /* ALPN information + * (we are in the process of transitioning from NPN to ALPN.) */ + + /* For a server, this contains a callback function that allows the + * server to select the protocol for the connection. + * out: on successful return, this must point to the raw protocol + * name (without the length prefix). + * outlen: on successful return, this contains the length of |*out|. + * in: points to the client's list of supported protocols in + * wire-format. + * inlen: the length of |in|. */ + int (*alpn_select_cb)(SSL *s, + const unsigned char **out, + unsigned char *outlen, + const unsigned char* in, + unsigned int inlen, + void *arg); + void *alpn_select_cb_arg; + + /* For a client, this contains the list of supported protocols in wire + * format. */ + unsigned char* alpn_client_proto_list; + unsigned alpn_client_proto_list_len; + /* SRTP profiles we are willing to do from RFC 5764 */ STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles; @@ -1075,6 +1100,21 @@ void SSL_get0_next_proto_negotiated(const SSL *s, #define OPENSSL_NPN_NO_OVERLAP 2 #endif +int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char* protos, + unsigned protos_len); +int SSL_set_alpn_protos(SSL *ssl, const unsigned char* protos, + unsigned protos_len); +void SSL_CTX_set_alpn_select_cb(SSL_CTX* ctx, + int (*cb) (SSL *ssl, + const unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *arg), + void *arg); +void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, + unsigned *len); + #ifndef OPENSSL_NO_PSK /* the maximum length of the buffer given to callbacks containing the * resulting identity/psk */ @@ -1365,6 +1405,11 @@ struct ssl_st char tlsext_channel_id_enabled; /* The client's Channel ID private key. */ EVP_PKEY *tlsext_channel_id_private; + + /* For a client, this contains the list of supported protocols in wire + * format. */ + unsigned char* alpn_client_proto_list; + unsigned alpn_client_proto_list_len; #else #define session_ctx ctx #endif /* OPENSSL_NO_TLSEXT */ diff --git a/ssl/ssl3.h b/ssl/ssl3.h index 215b985..4729868 100644 --- a/ssl/ssl3.h +++ b/ssl/ssl3.h @@ -551,6 +551,16 @@ typedef struct ssl3_state_st * each are big-endian values. */ unsigned char tlsext_channel_id[64]; + /* ALPN information + * (we are in the process of transitioning from NPN to ALPN.) */ + + /* In a server these point to the selected ALPN protocol after the + * ClientHello has been processed. In a client these contain the + * protocol that the server selected once the ServerHello has been + * processed. */ + unsigned char *alpn_selected; + unsigned alpn_selected_len; + /* These point to the digest function to use for signatures made with * each type of public key. A NULL value indicates that the default * digest should be used, which is SHA1 as of TLS 1.2. diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 6382325..1a3ceea 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -360,6 +360,17 @@ SSL *SSL_new(SSL_CTX *ctx) # ifndef OPENSSL_NO_NEXTPROTONEG s->next_proto_negotiated = NULL; # endif + + if (s->ctx->alpn_client_proto_list) + { + s->alpn_client_proto_list = + OPENSSL_malloc(s->ctx->alpn_client_proto_list_len); + if (s->alpn_client_proto_list == NULL) + goto err; + memcpy(s->alpn_client_proto_list, s->ctx->alpn_client_proto_list, + s->ctx->alpn_client_proto_list_len); + s->alpn_client_proto_list_len = s->ctx->alpn_client_proto_list_len; + } #endif s->verify_result=X509_V_OK; @@ -581,6 +592,8 @@ void SSL_free(SSL *s) OPENSSL_free(s->tlsext_ocsp_resp); if (s->tlsext_channel_id_private) EVP_PKEY_free(s->tlsext_channel_id_private); + if (s->alpn_client_proto_list) + OPENSSL_free(s->alpn_client_proto_list); #endif if (s->client_CA != NULL) @@ -1658,6 +1671,78 @@ void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, int (*cb) (SSL *s, unsigned ctx->next_proto_select_cb_arg = arg; } # endif + +/* SSL_CTX_set_alpn_protos sets the ALPN protocol list on |ctx| to |protos|. + * |protos| must be in wire-format (i.e. a series of non-empty, 8-bit + * length-prefixed strings). + * + * Returns 0 on success. */ +int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char* protos, + unsigned protos_len) + { + if (ctx->alpn_client_proto_list) + OPENSSL_free(ctx->alpn_client_proto_list); + + ctx->alpn_client_proto_list = OPENSSL_malloc(protos_len); + if (!ctx->alpn_client_proto_list) + return 1; + memcpy(ctx->alpn_client_proto_list, protos, protos_len); + ctx->alpn_client_proto_list_len = protos_len; + + return 0; + } + +/* SSL_set_alpn_protos sets the ALPN protocol list on |ssl| to |protos|. + * |protos| must be in wire-format (i.e. a series of non-empty, 8-bit + * length-prefixed strings). + * + * Returns 0 on success. */ +int SSL_set_alpn_protos(SSL *ssl, const unsigned char* protos, + unsigned protos_len) + { + if (ssl->alpn_client_proto_list) + OPENSSL_free(ssl->alpn_client_proto_list); + + ssl->alpn_client_proto_list = OPENSSL_malloc(protos_len); + if (!ssl->alpn_client_proto_list) + return 1; + memcpy(ssl->alpn_client_proto_list, protos, protos_len); + ssl->alpn_client_proto_list_len = protos_len; + + return 0; + } + +/* SSL_CTX_set_alpn_select_cb sets a callback function on |ctx| that is called + * during ClientHello processing in order to select an ALPN protocol from the + * client's list of offered protocols. */ +void SSL_CTX_set_alpn_select_cb(SSL_CTX* ctx, + int (*cb) (SSL *ssl, + const unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *arg), + void *arg) + { + ctx->alpn_select_cb = cb; + ctx->alpn_select_cb_arg = arg; + } + +/* SSL_get0_alpn_selected gets the selected ALPN protocol (if any) from |ssl|. + * On return it sets |*data| to point to |*len| bytes of protocol name (not + * including the leading length-prefix byte). If the server didn't respond with + * a negotiated protocol then |*len| will be zero. */ +void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, + unsigned *len) + { + *data = NULL; + if (ssl->s3) + *data = ssl->s3->alpn_selected; + if (*data == NULL) + *len = 0; + else + *len = ssl->s3->alpn_selected_len; + } #endif int SSL_export_keying_material(SSL *s, unsigned char *out, size_t olen, @@ -2010,6 +2095,8 @@ void SSL_CTX_free(SSL_CTX *a) #ifndef OPENSSL_NO_TLSEXT if (a->tlsext_channel_id_private) EVP_PKEY_free(a->tlsext_channel_id_private); + if (a->alpn_client_proto_list != NULL) + OPENSSL_free(a->alpn_client_proto_list); #endif OPENSSL_free(a); diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 53b805f..f170056 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -659,6 +659,18 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha s2n(0,ret); } + if (s->alpn_client_proto_list && !s->s3->tmp.finish_md_len) + { + if ((size_t)(limit - ret) < 6 + s->alpn_client_proto_list_len) + return NULL; + s2n(TLSEXT_TYPE_application_layer_protocol_negotiation,ret); + s2n(2 + s->alpn_client_proto_list_len,ret); + s2n(s->alpn_client_proto_list_len,ret); + memcpy(ret, s->alpn_client_proto_list, + s->alpn_client_proto_list_len); + ret += s->alpn_client_proto_list_len; + } + #ifndef OPENSSL_NO_SRTP if(SSL_get_srtp_profiles(s)) { @@ -879,6 +891,21 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha s2n(0,ret); } + if (s->s3->alpn_selected) + { + const unsigned char *selected = s->s3->alpn_selected; + unsigned len = s->s3->alpn_selected_len; + + if ((long)(limit - ret - 4 - 2 - 1 - len) < 0) + return NULL; + s2n(TLSEXT_TYPE_application_layer_protocol_negotiation,ret); + s2n(3 + len,ret); + s2n(1 + len,ret); + *ret++ = len; + memcpy(ret, selected, len); + ret += len; + } + if ((extdatalen = ret-p-2)== 0) return p; @@ -886,6 +913,76 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha return ret; } +/* tls1_alpn_handle_client_hello is called to process the ALPN extension in a + * ClientHello. + * data: the contents of the extension, not including the type and length. + * data_len: the number of bytes in |data| + * al: a pointer to the alert value to send in the event of a non-zero + * return. + * + * returns: 0 on success. */ +static int tls1_alpn_handle_client_hello(SSL *s, const unsigned char *data, + unsigned data_len, int *al) + { + unsigned i; + unsigned proto_len; + const unsigned char *selected; + unsigned char selected_len; + int r; + + if (s->ctx->alpn_select_cb == NULL) + return 0; + + if (data_len < 2) + goto parse_error; + + /* data should contain a uint16 length followed by a series of 8-bit, + * length-prefixed strings. */ + i = ((unsigned) data[0]) << 8 | + ((unsigned) data[1]); + data_len -= 2; + data += 2; + if (data_len != i) + goto parse_error; + + if (data_len < 2) + goto parse_error; + + for (i = 0; i < data_len;) + { + proto_len = data[i]; + i++; + + if (proto_len == 0) + goto parse_error; + + if (i + proto_len < i || i + proto_len > data_len) + goto parse_error; + + i += proto_len; + } + + r = s->ctx->alpn_select_cb(s, &selected, &selected_len, data, data_len, + s->ctx->alpn_select_cb_arg); + if (r == SSL_TLSEXT_ERR_OK) { + if (s->s3->alpn_selected) + OPENSSL_free(s->s3->alpn_selected); + s->s3->alpn_selected = OPENSSL_malloc(selected_len); + if (!s->s3->alpn_selected) + { + *al = SSL_AD_INTERNAL_ERROR; + return -1; + } + memcpy(s->s3->alpn_selected, selected, selected_len); + s->s3->alpn_selected_len = selected_len; + } + return 0; + +parse_error: + *al = SSL_AD_DECODE_ERROR; + return -1; + } + int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al) { unsigned short type; @@ -908,6 +1005,12 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in s->s3->next_proto_neg_seen = 0; #endif + if (s->s3->alpn_selected) + { + OPENSSL_free(s->s3->alpn_selected); + s->s3->alpn_selected = NULL; + } + #ifndef OPENSSL_NO_HEARTBEATS s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED | SSL_TLSEXT_HB_DONT_SEND_REQUESTS); @@ -1334,7 +1437,8 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in #endif #ifndef OPENSSL_NO_NEXTPROTONEG else if (type == TLSEXT_TYPE_next_proto_neg && - s->s3->tmp.finish_md_len == 0) + s->s3->tmp.finish_md_len == 0 && + s->s3->alpn_selected == NULL) { /* We shouldn't accept this extension on a * renegotiation. @@ -1358,6 +1462,16 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in else if (type == TLSEXT_TYPE_channel_id && s->tlsext_channel_id_enabled) s->s3->tlsext_channel_id_valid = 1; + else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation && + s->ctx->alpn_select_cb && + s->s3->tmp.finish_md_len == 0) + { + if (tls1_alpn_handle_client_hello(s, data, size, al) != 0) + return 0; + /* ALPN takes precedence over NPN. */ + s->s3->next_proto_neg_seen = 0; + } + /* session ticket processed earlier */ #ifndef OPENSSL_NO_SRTP else if (type == TLSEXT_TYPE_use_srtp) @@ -1422,6 +1536,12 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in s->s3->next_proto_neg_seen = 0; #endif + if (s->s3->alpn_selected) + { + OPENSSL_free(s->s3->alpn_selected); + s->s3->alpn_selected = NULL; + } + #ifndef OPENSSL_NO_HEARTBEATS s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED | SSL_TLSEXT_HB_DONT_SEND_REQUESTS); @@ -1591,6 +1711,51 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in else if (type == TLSEXT_TYPE_channel_id) s->s3->tlsext_channel_id_valid = 1; + else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation) + { + unsigned len; + + /* We must have requested it. */ + if (s->alpn_client_proto_list == NULL) + { + *al = TLS1_AD_UNSUPPORTED_EXTENSION; + return 0; + } + if (size < 4) + { + *al = TLS1_AD_DECODE_ERROR; + return 0; + } + /* The extension data consists of: + * uint16 list_length + * uint8 proto_length; + * uint8 proto[proto_length]; */ + len = data[0]; + len <<= 8; + len |= data[1]; + if (len != (unsigned) size - 2) + { + *al = TLS1_AD_DECODE_ERROR; + return 0; + } + len = data[2]; + if (len != (unsigned) size - 3) + { + *al = TLS1_AD_DECODE_ERROR; + return 0; + } + if (s->s3->alpn_selected) + OPENSSL_free(s->s3->alpn_selected); + s->s3->alpn_selected = OPENSSL_malloc(len); + if (!s->s3->alpn_selected) + { + *al = TLS1_AD_INTERNAL_ERROR; + return 0; + } + memcpy(s->s3->alpn_selected, data + 3, len); + s->s3->alpn_selected_len = len; + } + else if (type == TLSEXT_TYPE_renegotiate) { if(!ssl_parse_serverhello_renegotiate_ext(s, data, size, al)) diff --git a/ssl/tls1.h b/ssl/tls1.h index 8fc1ff4..c6670f4 100644 --- a/ssl/tls1.h +++ b/ssl/tls1.h @@ -230,6 +230,9 @@ extern "C" { /* ExtensionType value from RFC5620 */ #define TLSEXT_TYPE_heartbeat 15 +/* ExtensionType value from draft-ietf-tls-applayerprotoneg-00 */ +#define TLSEXT_TYPE_application_layer_protocol_negotiation 16 + /* ExtensionType value from RFC4507 */ #define TLSEXT_TYPE_session_ticket 35 -- cgit v1.1