Add certificate callback. If set this is called whenever a certificate is required by client or server. An application can decide which certificate chain to present based on arbitrary criteria: for example supported signature algorithms. Add very simple example to s_server. This fixes many of the problems and restrictions of the existing client certificate callback: for example you can now clear existing certificates and specify the whole chain.
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c index c51f3d0..8d7bcfe 100644 --- a/ssl/s3_clnt.c +++ b/ssl/s3_clnt.c
@@ -3180,6 +3180,13 @@ if (s->state == SSL3_ST_CW_CERT_A) { + /* Let cert callback update client certificates if required */ + if (s->cert->cert_cb + && s->cert->cert_cb(s, s->cert->cert_cb_arg) <= 0) + { + ssl3_send_alert(s,SSL3_AL_FATAL,SSL_AD_INTERNAL_ERROR); + return 0; + } if (ssl3_check_client_certificate(s)) s->state=SSL3_ST_CW_CERT_C; else
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 6ae2c4d..879d074 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c
@@ -1357,6 +1357,14 @@ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_CIPHERS_PASSED); goto f_err; } + /* Let cert callback update server certificates if required */ + if (s->cert->cert_cb + && s->cert->cert_cb(s, s->cert->cert_cb_arg) <= 0) + { + al=SSL_AD_INTERNAL_ERROR; + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CERT_CB_ERROR); + goto f_err; + } ciphers=NULL; c=ssl3_choose_cipher(s,s->session->ciphers, SSL_get_ciphers(s));
diff --git a/ssl/ssl.h b/ssl/ssl.h index 3675d6e..afeb60d 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h
@@ -1800,6 +1800,7 @@ void SSL_set_verify(SSL *s, int mode, int (*callback)(int ok,X509_STORE_CTX *ctx)); void SSL_set_verify_depth(SSL *s, int depth); +void SSL_set_cert_cb(SSL *s, int (*cb)(SSL *ssl, void *arg), void *arg); #ifndef OPENSSL_NO_RSA int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa); #endif @@ -1895,6 +1896,7 @@ int (*callback)(int, X509_STORE_CTX *)); void SSL_CTX_set_verify_depth(SSL_CTX *ctx,int depth); void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, int (*cb)(X509_STORE_CTX *,void *), void *arg); +void SSL_CTX_set_cert_cb(SSL_CTX *c, int (*cb)(SSL *ssl, void *arg), void *arg); #ifndef OPENSSL_NO_RSA int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa); #endif @@ -2462,6 +2464,7 @@ #define SSL_R_CA_DN_TOO_LONG 132 #define SSL_R_CCS_RECEIVED_EARLY 133 #define SSL_R_CERTIFICATE_VERIFY_FAILED 134 +#define SSL_R_CERT_CB_ERROR 377 #define SSL_R_CERT_LENGTH_MISMATCH 135 #define SSL_R_CHALLENGE_IS_DIFFERENT 136 #define SSL_R_CIPHER_CODE_WRONG_LENGTH 137
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c index 89a5131..9aa7b04 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c
@@ -379,6 +379,9 @@ ret->cert_flags = cert->cert_flags; + ret->cert_cb = cert->cert_cb; + ret->cert_cb_arg = cert->cert_cb_arg; + return(ret); #if !defined(OPENSSL_NO_DH) || !defined(OPENSSL_NO_ECDH) @@ -557,6 +560,12 @@ return 1; } +void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), void *arg) + { + c->cert_cb = cb; + c->cert_cb_arg = arg; + } + SESS_CERT *ssl_sess_cert_new(void) { SESS_CERT *ret;
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 3b32f55..34695e8 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c
@@ -356,6 +356,7 @@ {ERR_REASON(SSL_R_CA_DN_TOO_LONG) ,"ca dn too long"}, {ERR_REASON(SSL_R_CCS_RECEIVED_EARLY) ,"ccs received early"}, {ERR_REASON(SSL_R_CERTIFICATE_VERIFY_FAILED),"certificate verify failed"}, +{ERR_REASON(SSL_R_CERT_CB_ERROR) ,"cert cb error"}, {ERR_REASON(SSL_R_CERT_LENGTH_MISMATCH) ,"cert length mismatch"}, {ERR_REASON(SSL_R_CHALLENGE_IS_DIFFERENT),"challenge is different"}, {ERR_REASON(SSL_R_CIPHER_CODE_WRONG_LENGTH),"cipher code wrong length"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index b3836b7..18e80d4 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c
@@ -2048,6 +2048,16 @@ X509_VERIFY_PARAM_set_depth(ctx->param, depth); } +void SSL_CTX_set_cert_cb(SSL_CTX *c, int (*cb)(SSL *ssl, void *arg), void *arg) + { + ssl_cert_set_cert_cb(c->cert, cb, arg); + } + +void SSL_set_cert_cb(SSL *s, int (*cb)(SSL *ssl, void *arg), void *arg) + { + ssl_cert_set_cert_cb(s->cert, cb, arg); + } + void ssl_set_cert_masks(CERT *c, const SSL_CIPHER *cipher) { CERT_PKEY *cpk;
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index c2547ad..17bbbf5 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h
@@ -550,6 +550,16 @@ TLS_SIGALGS *shared_sigalgs; size_t shared_sigalgslen; + /* Certificate setup callback: if set is called whenever a + * certificate may be required (client or server). the callback + * can then examine any appropriate parameters and setup any + * certificates required. This allows advanced applications + * to select certificates on the fly: for example based on + * supported signature algorithms or curves. + */ + int (*cert_cb)(SSL *ssl, void *arg); + void *cert_cb_arg; + int references; /* >1 only if SSL_copy_session_id is used */ } CERT; @@ -888,6 +898,7 @@ 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); +void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), void *arg); int ssl_verify_cert_chain(SSL *s,STACK_OF(X509) *sk); int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l);
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 46b3a4c..6b0ddf2 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c
@@ -3514,5 +3514,10 @@ tls1_check_chain(s, NULL, NULL, NULL, SSL_PKEY_DH_DSA); tls1_check_chain(s, NULL, NULL, NULL, SSL_PKEY_ECC); } +/* User level utiity function to check a chain is suitable */ +int SSL_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain) + { + return tls1_check_chain(s, x, pk, chain, -1); + } #endif
diff --git a/ssl/tls1.h b/ssl/tls1.h index e2acad4..4d087e0 100644 --- a/ssl/tls1.h +++ b/ssl/tls1.h
@@ -318,6 +318,8 @@ int *psign, int *phash, int *psignandhash, unsigned char *rsig, unsigned char *rhash); +int SSL_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain); + #define SSL_set_tlsext_host_name(s,name) \ SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,(char *)name)