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.
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index b488cdb..d969379 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c
@@ -3350,6 +3350,21 @@ #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 @@ } 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 b9325fe..3d027af 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h
@@ -1615,6 +1615,9 @@ #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 @@ #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 94b6dea..2f43d2e 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c
@@ -317,11 +317,23 @@ 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 @@ 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 @@ 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 @@ 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 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 @@ X509_STORE_CTX_cleanup(&xs_ctx); } } - /* Thawte special :-) */ - for (i=0; i<sk_X509_num(s->ctx->extra_certs); i++) + for (i=0; i<sk_X509_num(extra_certs); i++) { - x=sk_X509_value(s->ctx->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 6660558..95b531e 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h
@@ -472,6 +472,8 @@ 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 @@ 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);