From 0b362de5f57547b31eddef5f8a0d298c4b7e0fd3 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Wed, 26 Dec 2012 14:25:29 +0000 Subject: [PATCH] Add support for application defined signature algorithms for use with TLS v1.2. These are sent as an extension for clients and during a certificate request for servers. TODO: add support for shared signature algorithms, respect shared algorithms when deciding which ciphersuites and certificates to permit. (backport from HEAD) --- CHANGES | 5 ++ apps/s_client.c | 12 ++++ apps/s_server.c | 21 ++++++ ssl/s3_lib.c | 12 ++++ ssl/s3_srvr.c | 2 +- ssl/ssl.h | 11 ++++ ssl/ssl_cert.c | 25 ++++++-- ssl/ssl_locl.h | 20 ++++-- ssl/t1_lib.c | 165 ++++++++++++++++++++++++++++++++++++++++++------ ssl/tls1.h | 8 +++ 10 files changed, 253 insertions(+), 28 deletions(-) diff --git a/CHANGES b/CHANGES index a1c6e46710..b80ef3694a 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,11 @@ Changes between 1.0.1 and 1.0.2 [xx XXX xxxx] + *) Add new functions to allow customised supported signature algorithms + for SSL and SSL_CTX structures. Add options to s_client and s_server + to support them. + [Steve Henson] + *) New function SSL_certs_clear() to delete all references to certificates from an SSL structure. Before this once a certificate had been added it couldn't be removed. diff --git a/apps/s_client.c b/apps/s_client.c index 7a75888daf..42e0e24127 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -605,6 +605,7 @@ int MAIN(int argc, char **argv) #ifndef OPENSSL_NO_TLSEXT char *servername = NULL; char *curves=NULL; + char *sigalgs=NULL; tlsextctx tlsextcbp = {NULL,0}; # ifndef OPENSSL_NO_NEXTPROTONEG @@ -948,6 +949,11 @@ int MAIN(int argc, char **argv) if (--argc < 1) goto bad; curves= *(++argv); } + else if (strcmp(*argv,"-sigalgs") == 0) + { + if (--argc < 1) goto bad; + sigalgs= *(++argv); + } #endif #ifndef OPENSSL_NO_JPAKE else if (strcmp(*argv,"-jpake") == 0) @@ -1192,6 +1198,12 @@ bad: ERR_print_errors(bio_err); goto end; } + if (sigalgs != NULL) + if(!SSL_CTX_set1_sigalgs_list(ctx,sigalgs)) { + BIO_printf(bio_err,"error setting signature algorithms list\n"); + ERR_print_errors(bio_err); + goto end; + } if (servername != NULL) { tlsextcbp.biodebug = bio_err; diff --git a/apps/s_server.c b/apps/s_server.c index 20f0c221a0..ac88f7c188 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -271,6 +271,7 @@ static const char *s_cert_file=TEST_CERT,*s_key_file=NULL, *s_chain_file=NULL; #ifndef OPENSSL_NO_TLSEXT static const char *s_cert_file2=TEST_CERT2,*s_key_file2=NULL; static char *curves=NULL; +static char *sigalgs=NULL; #endif static char *s_dcert_file=NULL,*s_dkey_file=NULL, *s_dchain_file=NULL; #ifdef FIONBIO @@ -1193,6 +1194,11 @@ int MAIN(int argc, char *argv[]) if (--argc < 1) goto bad; curves= *(++argv); } + else if (strcmp(*argv,"-sigalgs") == 0) + { + if (--argc < 1) goto bad; + sigalgs= *(++argv); + } #endif else if (strcmp(*argv,"-msg") == 0) { s_msg=1; } @@ -1888,6 +1894,21 @@ bad: goto end; } } + if (sigalgs) + { + if(!SSL_CTX_set1_sigalgs_list(ctx,sigalgs)) + { + BIO_printf(bio_err,"error setting signature algorithms\n"); + ERR_print_errors(bio_err); + goto end; + } + if(ctx2 && !SSL_CTX_set1_sigalgs_list(ctx2,sigalgs)) + { + BIO_printf(bio_err,"error setting signature algorithms\n"); + ERR_print_errors(bio_err); + goto end; + } + } #endif SSL_CTX_set_verify(ctx,s_server_verify,verify_callback); SSL_CTX_set_session_id_context(ctx,(void*)&s_server_session_id_context, diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index d520a11939..5e7c590ca1 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -3414,6 +3414,12 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg) s->cert->ecdh_tmp_auto = larg; break; + case SSL_CTRL_SET_SIGALGS: + return tls1_set_sigalgs(s->cert, parg, larg); + + case SSL_CTRL_SET_SIGALGS_LIST: + return tls1_set_sigalgs_list(s->cert, parg); + default: break; } @@ -3691,6 +3697,12 @@ long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) ctx->cert->ecdh_tmp_auto = larg; break; + case SSL_CTRL_SET_SIGALGS: + return tls1_set_sigalgs(ctx->cert, parg, larg); + + case SSL_CTRL_SET_SIGALGS_LIST: + return tls1_set_sigalgs_list(ctx->cert, parg); + case SSL_CTRL_SET_TLSEXT_AUTHZ_SERVER_AUDIT_PROOF_CB_ARG: ctx->tlsext_authz_server_audit_proof_cb_arg = parg; break; diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 29c5b97326..cd623a81b0 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -2069,7 +2069,7 @@ int ssl3_send_certificate_request(SSL *s) if (TLS1_get_version(s) >= TLS1_2_VERSION) { - nl = tls12_get_req_sig_algs(s, p + 2); + nl = tls12_get_sig_algs(s, p + 2); s2n(nl, p); p += nl + 2; n += nl + 2; diff --git a/ssl/ssl.h b/ssl/ssl.h index 7eebdb8354..676e3a1a82 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -1634,6 +1634,8 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION) #define SSL_CTRL_SET_CURVES_LIST 92 #define SSL_CTRL_GET_SHARED_CURVE 93 #define SSL_CTRL_SET_ECDH_AUTO 94 +#define SSL_CTRL_SET_SIGALGS 97 +#define SSL_CTRL_SET_SIGALGS_LIST 98 #define DTLSv1_get_timeout(ssl, arg) \ SSL_ctrl(ssl,DTLS_CTRL_GET_TIMEOUT,0, (void *)arg) @@ -1710,6 +1712,15 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION) #define SSL_set_ecdh_auto(s, onoff) \ SSL_ctrl(s,SSL_CTRL_SET_ECDH_AUTO,onoff,NULL) +#define SSL_CTX_set1_sigalgs(ctx, slist, slistlen) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_SIGALGS,slistlen,(int *)slist) +#define SSL_CTX_set1_sigalgs_list(ctx, s) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_SIGALGS_LIST,0,(char *)s) +#define SSL_set1_sigalgs(ctx, slist, slistlen) \ + SSL_ctrl(ctx,SSL_CTRL_SET_SIGALGS,clistlen,(int *)slist) +#define SSL_set1_sigalgs_list(ctx, s) \ + SSL_ctrl(ctx,SSL_CTRL_SET_SIGALGS_LIST,0,(char *)s) + #ifndef OPENSSL_NO_BIO BIO_METHOD *BIO_f_ssl(void); BIO *BIO_new_ssl(SSL_CTX *ctx,int client); diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c index 9f8b463976..815cfd3c74 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c @@ -357,9 +357,22 @@ CERT *ssl_cert_dup(CERT *cert) * will be set during handshake. */ ssl_cert_set_default_md(ret); - /* Sigalgs set to NULL as we get these from handshake too */ - ret->sigalgs = NULL; - ret->sigalgslen = 0; + /* Peer sigalgs set to NULL as we get these from handshake too */ + ret->peer_sigalgs = NULL; + ret->peer_sigalgslen = 0; + /* Configure sigalgs however we copy across */ + if (cert->conf_sigalgs) + { + ret->conf_sigalgs = OPENSSL_malloc(cert->conf_sigalgslen + * sizeof(TLS_SIGALGS)); + if (!ret->conf_sigalgs) + goto err; + memcpy(ret->conf_sigalgs, cert->conf_sigalgs, + cert->conf_sigalgslen * sizeof(TLS_SIGALGS)); + ret->conf_sigalgslen = cert->conf_sigalgslen; + } + else + ret->conf_sigalgs = NULL; return(ret); @@ -447,8 +460,10 @@ void ssl_cert_free(CERT *c) #endif ssl_cert_clear_certs(c); - if (c->sigalgs) - OPENSSL_free(c->sigalgs); + if (c->peer_sigalgs) + OPENSSL_free(c->peer_sigalgs); + if (c->conf_sigalgs) + OPENSSL_free(c->conf_sigalgs); OPENSSL_free(c); } diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 3a8f6f4ca7..8e338d77a6 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -519,10 +519,19 @@ typedef struct cert_st CERT_PKEY pkeys[SSL_PKEY_NUM]; - /* Array of pairs of NIDs for signature algorithm extension */ - TLS_SIGALGS *sigalgs; + /* signature algorithms peer reports: e.g. supported signature + * algorithms extension for server or as part of a certificate + * request for client. + */ + TLS_SIGALGS *peer_sigalgs; /* Size of above array */ - size_t sigalgslen; + size_t peer_sigalgslen; + /* configured signature algorithms (can be NULL for default). + * sent in signature algorithms extension or certificate request. + */ + TLS_SIGALGS *conf_sigalgs; + /* Size of above array */ + size_t conf_sigalgslen; int references; /* >1 only if SSL_copy_session_id is used */ } CERT; @@ -1164,6 +1173,9 @@ int tls12_get_sigandhash(unsigned char *p, const EVP_PKEY *pk, int tls12_get_sigid(const EVP_PKEY *pk); const EVP_MD *tls12_get_hash(unsigned char hash_alg); +int tls1_set_sigalgs_list(CERT *c, const char *str); +int tls1_set_sigalgs(CERT *c, const int *salg, size_t salglen); + #endif EVP_MD_CTX* ssl_replace_hash(EVP_MD_CTX **hash,const EVP_MD *md) ; void ssl_clear_hash_ctx(EVP_MD_CTX **hash); @@ -1177,7 +1189,7 @@ 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); -int tls12_get_req_sig_algs(SSL *s, unsigned char *p); +size_t tls12_get_sig_algs(SSL *s, unsigned char *p); int ssl_add_clienthello_use_srtp_ext(SSL *s, unsigned char *p, int *len, int maxlen); int ssl_parse_clienthello_use_srtp_ext(SSL *s, unsigned char *d, int len,int *al); diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 9b9fb356f8..a400bcc58c 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -629,9 +629,29 @@ static unsigned char tls12_sigalgs[] = { #endif }; -int tls12_get_req_sig_algs(SSL *s, unsigned char *p) +size_t tls12_get_sig_algs(SSL *s, unsigned char *p) { - size_t slen = sizeof(tls12_sigalgs); + TLS_SIGALGS *sptr = s->cert->conf_sigalgs; + size_t slen; + + /* Use custom signature algorithms if any are set */ + + if (sptr) + { + slen = s->cert->conf_sigalgslen; + if (p) + { + size_t i; + for (i = 0; i < slen; i++, sptr++) + { + *p++ = sptr->rhash; + *p++ = sptr->rsign; + } + } + return slen * 2; + } + + slen = sizeof(tls12_sigalgs); #ifdef OPENSSL_FIPS /* If FIPS mode don't include MD5 which is last */ if (FIPS_mode()) @@ -639,7 +659,7 @@ int tls12_get_req_sig_algs(SSL *s, unsigned char *p) #endif if (p) memcpy(p, tls12_sigalgs, slen); - return (int)slen; + return slen; } /* byte_compare is a compare function for qsort(3) that compares bytes. */ @@ -874,13 +894,15 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha if (TLS1_get_client_version(s) >= TLS1_2_VERSION) { - if ((size_t)(limit - ret) < sizeof(tls12_sigalgs) + 6) + size_t salglen; + salglen = tls12_get_sig_algs(s, NULL); + if ((size_t)(limit - ret) < salglen + 6) return NULL; s2n(TLSEXT_TYPE_signature_algorithms,ret); - s2n(sizeof(tls12_sigalgs) + 2, ret); - s2n(sizeof(tls12_sigalgs), ret); - memcpy(ret, tls12_sigalgs, sizeof(tls12_sigalgs)); - ret += sizeof(tls12_sigalgs); + s2n(salglen + 2, ret); + s2n(salglen, ret); + tls12_get_sig_algs(s, ret); + ret += salglen; } #ifdef TLSEXT_TYPE_opaque_prf_input @@ -2896,14 +2918,14 @@ int tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize) c->pkeys[SSL_PKEY_RSA_ENC].digest = NULL; c->pkeys[SSL_PKEY_ECC].digest = NULL; - if (c->sigalgs) - OPENSSL_free(c->sigalgs); - c->sigalgs = OPENSSL_malloc((dsize/2) * sizeof(TLS_SIGALGS)); - if (!c->sigalgs) + if (c->peer_sigalgs) + OPENSSL_free(c->peer_sigalgs); + c->peer_sigalgs = OPENSSL_malloc((dsize/2) * sizeof(TLS_SIGALGS)); + if (!c->peer_sigalgs) return 0; - c->sigalgslen = dsize/2; + c->peer_sigalgslen = dsize/2; - for (i = 0, sigptr = c->sigalgs; i < dsize; i += 2, sigptr++) + for (i = 0, sigptr = c->peer_sigalgs; i < dsize; i += 2, sigptr++) { sigptr->rhash = data[i]; sigptr->rsign = data[i + 1]; @@ -2977,14 +2999,14 @@ int SSL_get_sigalgs(SSL *s, int idx, int *psign, int *phash, int *psignandhash, unsigned char *rsig, unsigned char *rhash) { - if (s->cert->sigalgs == NULL) + if (s->cert->peer_sigalgs == NULL) return 0; if (idx >= 0) { TLS_SIGALGS *psig; - if (idx >= (int)s->cert->sigalgslen) + if (idx >= (int)s->cert->peer_sigalgslen) return 0; - psig = s->cert->sigalgs + idx; + psig = s->cert->peer_sigalgs + idx; if (psign) *psign = psig->sign_nid; if (phash) @@ -2996,7 +3018,7 @@ int SSL_get_sigalgs(SSL *s, int idx, if (rhash) *rhash = psig->rhash; } - return s->cert->sigalgslen; + return s->cert->peer_sigalgslen; } @@ -3144,3 +3166,110 @@ tls1_heartbeat(SSL *s) return ret; } #endif + +#define MAX_SIGALGLEN (TLSEXT_hash_num * TLSEXT_signature_num *2) + +typedef struct + { + size_t sigalgcnt; + int sigalgs[MAX_SIGALGLEN]; + } sig_cb_st; + +static int sig_cb(const char *elem, int len, void *arg) + { + sig_cb_st *sarg = arg; + size_t i; + char etmp[20], *p; + int sig_alg, hash_alg; + if (sarg->sigalgcnt == MAX_SIGALGLEN) + return 0; + if (len > (int)(sizeof(etmp) - 1)) + return 0; + memcpy(etmp, elem, len); + etmp[len] = 0; + p = strchr(etmp, '+'); + if (!p) + return 0; + *p = 0; + p++; + if (!*p) + return 0; + + if (!strcmp(etmp, "RSA")) + sig_alg = EVP_PKEY_RSA; + else if (!strcmp(etmp, "DSA")) + sig_alg = EVP_PKEY_DSA; + else if (!strcmp(etmp, "ECDSA")) + sig_alg = EVP_PKEY_EC; + else return 0; + + hash_alg = OBJ_sn2nid(p); + if (hash_alg == NID_undef) + hash_alg = OBJ_ln2nid(p); + if (hash_alg == NID_undef) + return 0; + + for (i = 0; i < sarg->sigalgcnt; i+=2) + { + if (sarg->sigalgs[i] == sig_alg + && sarg->sigalgs[i + 1] == hash_alg) + return 0; + } + sarg->sigalgs[sarg->sigalgcnt++] = hash_alg; + sarg->sigalgs[sarg->sigalgcnt++] = sig_alg; + return 1; + } + +/* Set suppored signature algorithms based on a colon separated list + * of the form sig+hash e.g. RSA+SHA512:DSA+SHA512 */ +int tls1_set_sigalgs_list(CERT *c, const char *str) + { + sig_cb_st sig; + sig.sigalgcnt = 0; + if (!CONF_parse_list(str, ':', 1, sig_cb, &sig)) + return 0; + return tls1_set_sigalgs(c, sig.sigalgs, sig.sigalgcnt); + } + +int tls1_set_sigalgs(CERT *c, const int *salg, size_t salglen) + { + TLS_SIGALGS *sigalgs, *sptr; + int rhash, rsign; + size_t i; + if (salglen & 1) + return 0; + salglen /= 2; + sigalgs = OPENSSL_malloc(sizeof(TLS_SIGALGS) * salglen); + if (sigalgs == NULL) + return 0; + for (i = 0, sptr = sigalgs; i < salglen; i++, sptr++) + { + sptr->hash_nid = *salg++; + sptr->sign_nid = *salg++; + rhash = tls12_find_id(sptr->hash_nid, tls12_md, + sizeof(tls12_md)/sizeof(tls12_lookup)); + rsign = tls12_find_id(sptr->sign_nid, tls12_sig, + sizeof(tls12_sig)/sizeof(tls12_lookup)); + + if (rhash == -1 || rsign == -1) + goto err; + + if (!OBJ_find_sigid_by_algs(&sptr->signandhash_nid, + sptr->hash_nid, + sptr->sign_nid)) + sptr->signandhash_nid = NID_undef; + sptr->rhash = rhash; + sptr->rsign = rsign; + } + + if (c->conf_sigalgs) + OPENSSL_free(c->conf_sigalgs); + + c->conf_sigalgs = sigalgs; + c->conf_sigalgslen = salglen; + return 1; + + err: + OPENSSL_free(sigalgs); + return 0; + } diff --git a/ssl/tls1.h b/ssl/tls1.h index dd1b4fb22d..2f6a34c481 100644 --- a/ssl/tls1.h +++ b/ssl/tls1.h @@ -267,6 +267,9 @@ extern "C" { #define TLSEXT_signature_dsa 2 #define TLSEXT_signature_ecdsa 3 +/* Total number of different signature algorithms */ +#define TLSEXT_signature_num 4 + #define TLSEXT_hash_none 0 #define TLSEXT_hash_md5 1 #define TLSEXT_hash_sha1 2 @@ -274,6 +277,11 @@ extern "C" { #define TLSEXT_hash_sha256 4 #define TLSEXT_hash_sha384 5 #define TLSEXT_hash_sha512 6 + +/* Total number of different digest algorithms */ + +#define TLSEXT_hash_num 7 + /* Flag set for unrecognised algorithms */ #define TLSEXT_nid_unknown 0x1000000 -- 2.25.1