From f71c6e52f769af0d2d40ed7e1dcb4fff837837a0 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Tue, 31 Jan 2012 14:00:10 +0000 Subject: [PATCH] Add support for distinct certificate chains per key type and per SSL structure. Before this the only way to add a custom chain was in the parent SSL_CTX (which is shared by all key types and SSL structures) or rely on auto chain building (which is performed on each handshake) from the trust store. --- CHANGES | 4 +++ ssl/s3_lib.c | 29 ++++++++++++++++ ssl/ssl.h | 21 ++++++++++++ ssl/ssl_cert.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++---- ssl/ssl_locl.h | 7 ++++ 5 files changed, 145 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index 39fa10f292..86b2f92583 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,10 @@ Changes between 1.0.1 and 1.1.0 [xx XXX xxxx] + *) Enhance SSL/TLS certificate chain handling to support different + chains for each certificate instead of one chain in the parent SSL_CTX. + [Steve Henson] + *) Support for fixed DH ciphersuite client authentication: where both server and client use DH certificates with common parameters. [Steve Henson] diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index b488cdbb35..d969379157 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -3350,6 +3350,21 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg) #endif #endif /* !OPENSSL_NO_TLSEXT */ + + case SSL_CTRL_CHAIN: + if (larg) + return ssl_cert_set1_chain(s->cert, + (STACK_OF (X509) *)parg); + else + return ssl_cert_set0_chain(s->cert, + (STACK_OF (X509) *)parg); + + case SSL_CTRL_CHAIN_CERT: + if (larg) + return ssl_cert_add1_chain_cert(s->cert, (X509 *)parg); + else + return ssl_cert_add0_chain_cert(s->cert, (X509 *)parg); + default: break; } @@ -3642,6 +3657,20 @@ long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) } break; + case SSL_CTRL_CHAIN: + if (larg) + return ssl_cert_set1_chain(ctx->cert, + (STACK_OF (X509) *)parg); + else + return ssl_cert_set0_chain(ctx->cert, + (STACK_OF (X509) *)parg); + + case SSL_CTRL_CHAIN_CERT: + if (larg) + return ssl_cert_add1_chain_cert(ctx->cert, (X509 *)parg); + else + return ssl_cert_add0_chain_cert(ctx->cert, (X509 *)parg); + default: return(0); } diff --git a/ssl/ssl.h b/ssl/ssl.h index b9325feb41..3d027af3cc 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -1615,6 +1615,9 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION) #define SSL_CTRL_GET_EXTRA_CHAIN_CERTS 82 #define SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS 83 +#define SSL_CTRL_CHAIN 88 +#define SSL_CTRL_CHAIN_CERT 89 + #define DTLSv1_get_timeout(ssl, arg) \ SSL_ctrl(ssl,DTLS_CTRL_GET_TIMEOUT,0, (void *)arg) #define DTLSv1_handle_timeout(ssl) \ @@ -1656,6 +1659,24 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION) #define SSL_CTX_clear_extra_chain_certs(ctx) \ SSL_CTX_ctrl(ctx,SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS,0,NULL) +#define SSL_CTX_set0_chain(ctx,sk) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_CHAIN,0,(char *)sk) +#define SSL_CTX_set1_chain(ctx,sk) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_CHAIN,1,(char *)sk) +#define SSL_CTX_add0_chain_cert(ctx,x509) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_CHAIN_CERT,0,(char *)x509) +#define SSL_CTX_add1_chain_cert(ctx,x509) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_CHAIN_CERT,1,(char *)x509) + +#define SSL_set0_chain(ctx,sk) \ + SSL_ctrl(ctx,SSL_CTRL_CHAIN,0,(char *)sk) +#define SSL_set1_chain(ctx,sk) \ + SSL_ctrl(ctx,SSL_CTRL_CHAIN,1,(char *)sk) +#define SSL_add0_chain_cert(ctx,x509) \ + SSL_ctrl(ctx,SSL_CTRL_CHAIN_CERT,0,(char *)x509) +#define SSL_add1_chain_cert(ctx,x509) \ + SSL_ctrl(ctx,SSL_CTRL_CHAIN_CERT,1,(char *)x509) + #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 94b6deadc1..2f43d2ede1 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c @@ -317,11 +317,23 @@ CERT *ssl_cert_dup(CERT *cert) SSLerr(SSL_F_SSL_CERT_DUP, SSL_R_LIBRARY_BUG); } } + + if (cpk->chain) + { + rpk->chain = sk_X509_dup(cpk->chain); + if (!rpk->chain) + { + SSLerr(SSL_F_SSL_CERT_DUP, ERR_R_MALLOC_FAILURE); + goto err; + } + for (i = 0; i < sk_X509_num(rpk->chain); i++) + { + X509 *x = sk_X509_value(rpk->chain, i); + CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); + } + } } - /* ret->extra_certs *should* exist, but currently the own certificate - * 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. @@ -353,6 +365,8 @@ err: X509_free(rpk->x509); if (rpk->privatekey != NULL) EVP_PKEY_free(rpk->privatekey); + if (rpk->chain) + sk_X509_pop_free(rpk->chain, X509_free); } @@ -397,6 +411,8 @@ void ssl_cert_free(CERT *c) X509_free(cpk->x509); if (cpk->privatekey != NULL) EVP_PKEY_free(cpk->privatekey); + if (cpk->chain) + sk_X509_pop_free(cpk->chain, X509_free); #if 0 if (c->pkeys[i].publickey != NULL) EVP_PKEY_free(c->pkeys[i].publickey); @@ -433,6 +449,59 @@ int ssl_cert_inst(CERT **o) return(1); } +int ssl_cert_set0_chain(CERT *c, STACK_OF(X509) *chain) + { + CERT_PKEY *cpk = c->key; + if (!cpk) + return 0; + if (cpk->chain) + sk_X509_pop_free(cpk->chain, X509_free); + cpk->chain = chain; + return 1; + } + +int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) *chain) + { + STACK_OF(X509) *dchain; + X509 *x; + int i; + if (!chain) + return ssl_cert_set0_chain(c, NULL); + dchain = sk_X509_dup(chain); + if (!dchain) + return 0; + for (i = 0; i < sk_X509_num(dchain); i++) + { + x = sk_X509_value(dchain, i); + CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); + } + if (!ssl_cert_set0_chain(c, dchain)) + { + sk_X509_pop_free(dchain, X509_free); + return 0; + } + return 1; + } + +int ssl_cert_add0_chain_cert(CERT *c, X509 *x) + { + CERT_PKEY *cpk = c->key; + if (!cpk) + return 0; + if (!cpk->chain) + cpk->chain = sk_X509_new_null(); + if (!cpk->chain || !sk_X509_push(cpk->chain, x)) + return 0; + return 1; + } + +int ssl_cert_add1_chain_cert(CERT *c, X509 *x) + { + if (!ssl_cert_add0_chain_cert(c, x)) + return 0; + CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); + return 1; + } SESS_CERT *ssl_sess_cert_new(void) { @@ -884,13 +953,22 @@ int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l) int i; X509 *x; + STACK_OF(X509) *extra_certs; if (cpk) x = cpk->x509; else x = NULL; - if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || s->ctx->extra_certs) + /* If we have a certificate specific chain use it, else use + * parent ctx. + */ + if (cpk && cpk->chain) + extra_certs = cpk->chain; + else + extra_certs = s->ctx->extra_certs; + + if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || extra_certs) no_chain = 1; else no_chain = 0; @@ -933,10 +1011,9 @@ int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l) X509_STORE_CTX_cleanup(&xs_ctx); } } - /* Thawte special :-) */ - for (i=0; ictx->extra_certs); i++) + for (i=0; ictx->extra_certs,i); + x=sk_X509_value(extra_certs,i); if (!ssl_add_cert_to_buf(buf, l, x)) return 0; } diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 66605586ac..95b531e832 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -472,6 +472,8 @@ typedef struct cert_pkey_st EVP_PKEY *privatekey; /* Digest to use when signing */ const EVP_MD *digest; + /* Chain for this certificate */ + STACK_OF(X509) *chain; } CERT_PKEY; typedef struct cert_st @@ -824,6 +826,11 @@ void ssl_update_cache(SSL *s, int mode); int ssl_cipher_get_evp(const SSL_SESSION *s,const EVP_CIPHER **enc, const EVP_MD **md,int *mac_pkey_type,int *mac_secret_size, SSL_COMP **comp); int ssl_get_handshake_digest(int i,long *mask,const EVP_MD **md); +int ssl_cert_set0_chain(CERT *c, STACK_OF(X509) *chain); +int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) *chain); +int ssl_cert_add0_chain_cert(CERT *c, X509 *x); +int ssl_cert_add1_chain_cert(CERT *c, X509 *x); + int ssl_verify_cert_chain(SSL *s,STACK_OF(X509) *sk); int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l); int ssl_undefined_function(SSL *s); -- 2.25.1