summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES9
-rw-r--r--apps/s_apps.h8
-rw-r--r--apps/s_cb.c243
-rw-r--r--apps/s_server.c13
-rw-r--r--ssl/s3_clnt.c7
-rw-r--r--ssl/s3_srvr.c8
-rw-r--r--ssl/ssl.h3
-rw-r--r--ssl/ssl_cert.c9
-rw-r--r--ssl/ssl_err.c1
-rw-r--r--ssl/ssl_lib.c10
-rw-r--r--ssl/ssl_locl.h11
-rw-r--r--ssl/t1_lib.c5
-rw-r--r--ssl/tls1.h2
13 files changed, 329 insertions, 0 deletions
diff --git a/CHANGES b/CHANGES
index 9ebc5a3f7c..b47ccac9b5 100644
--- a/CHANGES
+++ b/CHANGES
@@ -8,6 +8,15 @@
OID NID.
[Steve Henson]
+ *) 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 an existing
+ certificate and specify the whole chain.
+ [Steve Henson]
+
*) Add new "valid_flags" field to CERT_PKEY structure which determines what
the certificate can be used for (if anything). Set valid_flags field
in new tls1_check_chain function. Simplify ssl_set_cert_masks which used
diff --git a/apps/s_apps.h b/apps/s_apps.h
index 8c644ec7db..3491b1ab69 100644
--- a/apps/s_apps.h
+++ b/apps/s_apps.h
@@ -181,3 +181,11 @@ void MS_CALLBACK tlsext_cb(SSL *s, int client_server, int type,
int MS_CALLBACK generate_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len);
int MS_CALLBACK verify_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned int cookie_len);
+
+typedef struct ssl_excert_st SSL_EXCERT;
+
+void ssl_ctx_set_excert(SSL_CTX *ctx, SSL_EXCERT *exc);
+void ssl_excert_free(SSL_EXCERT *exc);
+int args_excert(char ***pargs, int *pargc,
+ int *badarg, BIO *err, SSL_EXCERT **pexc);
+int load_excert(SSL_EXCERT **pexc, BIO *err);
diff --git a/apps/s_cb.c b/apps/s_cb.c
index bb9064b67a..9a73197286 100644
--- a/apps/s_cb.c
+++ b/apps/s_cb.c
@@ -1039,3 +1039,246 @@ int MS_CALLBACK verify_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned
return 0;
}
+
+/* Example of extended certificate handling. Where the standard support
+ * of one certificate per algorithm is not sufficient an application
+ * can decide which certificate(s) to use at runtime based on whatever
+ * criteria it deems appropriate.
+ */
+
+/* Linked list of certificates, keys and chains */
+struct ssl_excert_st
+ {
+ int certform;
+ const char *certfile;
+ int keyform;
+ const char *keyfile;
+ const char *chainfile;
+ X509 *cert;
+ EVP_PKEY *key;
+ STACK_OF(X509) *chain;
+ struct ssl_excert_st *next, *prev;
+ };
+
+/* Very basic selection callback: just use any certificate chain
+ * reported as valid. More sophisticated could prioritise according
+ * to local policy.
+ */
+static int set_cert_cb(SSL *ssl, void *arg)
+ {
+ SSL_EXCERT *exc = arg;
+ SSL_certs_clear(ssl);
+
+ if (!exc)
+ return 1;
+
+ /* Go to end of list and traverse backwards since we prepend
+ * newer entries this retains the original order.
+ */
+ while (exc->next)
+ exc = exc->next;
+
+ while(exc)
+ {
+ if (SSL_check_chain(ssl, exc->cert, exc->key, exc->chain))
+ {
+ SSL_use_certificate(ssl, exc->cert);
+ SSL_use_PrivateKey(ssl, exc->key);
+ if (exc->chain)
+ SSL_set1_chain(ssl, exc->chain);
+ }
+ exc = exc->prev;
+ }
+ return 1;
+ }
+
+void ssl_ctx_set_excert(SSL_CTX *ctx, SSL_EXCERT *exc)
+ {
+ SSL_CTX_set_cert_cb(ctx, set_cert_cb, exc);
+ }
+
+static int ssl_excert_prepend(SSL_EXCERT **pexc)
+ {
+ SSL_EXCERT *exc;
+ exc = OPENSSL_malloc(sizeof(SSL_EXCERT));
+ if (!exc)
+ return 0;
+ exc->certfile = NULL;
+ exc->keyfile = NULL;
+ exc->chainfile = NULL;
+ exc->cert = NULL;
+ exc->key = NULL;
+ exc->chain = NULL;
+ exc->prev = NULL;
+
+ exc->next = *pexc;
+ *pexc = exc;
+
+ if (exc->next)
+ {
+ exc->certform = exc->next->certform;
+ exc->keyform = exc->next->keyform;
+ exc->next->prev = exc;
+ }
+ else
+ {
+ exc->certform = FORMAT_PEM;
+ exc->keyform = FORMAT_PEM;
+ }
+ return 1;
+
+ }
+
+void ssl_excert_free(SSL_EXCERT *exc)
+ {
+ SSL_EXCERT *curr;
+ while (exc)
+ {
+ if (exc->cert)
+ X509_free(exc->cert);
+ if (exc->key)
+ EVP_PKEY_free(exc->key);
+ if (exc->chain)
+ sk_X509_pop_free(exc->chain, X509_free);
+ curr = exc;
+ exc = exc->next;
+ OPENSSL_free(curr);
+ }
+ }
+
+int load_excert(SSL_EXCERT **pexc, BIO *err)
+ {
+ SSL_EXCERT *exc = *pexc;
+ if (!exc)
+ return 1;
+ /* If nothing in list, free and set to NULL */
+ if (!exc->certfile && !exc->next)
+ {
+ ssl_excert_free(exc);
+ *pexc = NULL;
+ return 1;
+ }
+ for(; exc; exc=exc->next)
+ {
+ if (!exc->certfile)
+ {
+ BIO_printf(err, "Missing filename\n");
+ return 0;
+ }
+ exc->cert = load_cert(err, exc->certfile, exc->certform,
+ NULL, NULL, "Server Certificate");
+ if (!exc->cert)
+ return 0;
+ if (exc->keyfile)
+ exc->keyfile = exc->certfile;
+ exc->key = load_key(err, exc->certfile, exc->certform, 0,
+ NULL, NULL, "Server Certificate");
+ if (!exc->key)
+ return 0;
+ if (exc->chainfile)
+ {
+ exc->chain = load_certs(err,
+ exc->chainfile, FORMAT_PEM,
+ NULL, NULL,
+ "Server Chain");
+ if (!exc->chainfile)
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+
+int args_excert(char ***pargs, int *pargc,
+ int *badarg, BIO *err, SSL_EXCERT **pexc)
+ {
+ char *arg = **pargs, *argn = (*pargs)[1];
+ SSL_EXCERT *exc = *pexc;
+ if (!exc && !ssl_excert_prepend(&exc))
+ {
+ BIO_printf(err, "Error initialising xcert\n");
+ *badarg = 1;
+ goto err;
+ }
+ if (strcmp(arg, "-xcert") == 0)
+ {
+ if (!argn)
+ {
+ *badarg = 1;
+ return 1;
+ }
+ if (exc->certfile && !ssl_excert_prepend(&exc))
+ {
+ BIO_printf(err, "Error adding xcert\n");
+ *badarg = 1;
+ goto err;
+ }
+ exc->certfile = argn;
+ }
+ else if (strcmp(arg,"-xkey") == 0)
+ {
+ if (!argn)
+ {
+ *badarg = 1;
+ return 1;
+ }
+ if (exc->keyfile)
+ {
+ BIO_printf(err, "Key already specified\n");
+ *badarg = 1;
+ return 1;
+ }
+ exc->keyfile = argn;
+ }
+ else if (strcmp(arg,"-xchain") == 0)
+ {
+ if (!argn)
+ {
+ *badarg = 1;
+ return 1;
+ }
+ if (exc->chainfile)
+ {
+ BIO_printf(err, "Chain already specified\n");
+ *badarg = 1;
+ return 1;
+ }
+ exc->chainfile = argn;
+ }
+ else if (strcmp(arg,"-xcertform") == 0)
+ {
+ if (!argn)
+ {
+ *badarg = 1;
+ goto err;
+ }
+ exc->certform = str2fmt(argn);
+ }
+ else if (strcmp(arg,"-xkeyform") == 0)
+ {
+ if (!argn)
+ {
+ *badarg = 1;
+ goto err;
+ }
+ exc->keyform = str2fmt(argn);
+ }
+ else
+ return 0;
+
+ (*pargs) += 2;
+
+ if (pargc)
+ *pargc -= 2;
+
+ *pexc = exc;
+
+ return 1;
+
+ err:
+ ERR_print_errors(err);
+ ssl_excert_free(exc);
+ *pexc = NULL;
+ return 1;
+ }
+
diff --git a/apps/s_server.c b/apps/s_server.c
index b9751bc10e..7016757cf5 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -981,6 +981,7 @@ int MAIN(int argc, char *argv[])
char *srpuserseed = NULL;
char *srp_verifier_file = NULL;
#endif
+ SSL_EXCERT *exc = NULL;
meth=SSLv23_server_method();
local_argc=argc;
@@ -1131,6 +1132,12 @@ int MAIN(int argc, char *argv[])
goto bad;
continue;
}
+ else if (args_excert(&argv, &argc, &badarg, bio_err, &exc))
+ {
+ if (badarg)
+ goto bad;
+ continue;
+ }
else if (strcmp(*argv,"-verify_return_error") == 0)
verify_return_error = 1;
else if (strcmp(*argv,"-serverpref") == 0)
@@ -1433,6 +1440,9 @@ bad:
s_key_file2 = s_cert_file2;
#endif
+ if (!load_excert(&exc, bio_err))
+ goto end;
+
if (nocert == 0)
{
s_key = load_key(bio_err, s_key_file, s_key_format, 0, pass, e,
@@ -1594,6 +1604,7 @@ bad:
if (hack) SSL_CTX_set_options(ctx,SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG);
SSL_CTX_set_options(ctx,off);
if (cert_flags) SSL_CTX_set_cert_flags(ctx, cert_flags);
+ if (exc) ssl_ctx_set_excert(ctx, exc);
/* DTLS: partial reads end up discarding unread UDP bytes :-(
* Setting read ahead solves this problem.
*/
@@ -1666,6 +1677,7 @@ bad:
if (hack) SSL_CTX_set_options(ctx2,SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG);
SSL_CTX_set_options(ctx2,off);
if (cert_flags) SSL_CTX_set_cert_flags(ctx2, cert_flags);
+ if (exc) ssl_ctx_set_excert(ctx2, exc);
/* DTLS: partial reads end up discarding unread UDP bytes :-(
* Setting read ahead solves this problem.
*/
@@ -2009,6 +2021,7 @@ end:
if (authz_in != NULL)
BIO_free(authz_in);
#endif
+ ssl_excert_free(exc);
if (bio_s_out != NULL)
{
BIO_free(bio_s_out);
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 0acaca5a12..8984183c1e 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -3181,6 +3181,13 @@ int ssl3_send_client_certificate(SSL *s)
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 cd623a81b0..f6ab29cd79 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -1358,6 +1358,14 @@ int ssl3_get_client_hello(SSL *s)
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 9ca50b74e0..b3f2108f07 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -1791,6 +1791,7 @@ int (*SSL_get_verify_callback(const SSL *s))(int,X509_STORE_CTX *);
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
@@ -1886,6 +1887,7 @@ void SSL_CTX_set_verify(SSL_CTX *ctx,int mode,
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
@@ -2443,6 +2445,7 @@ void ERR_load_SSL_strings(void);
#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 89a5131119..9aa7b04966 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -379,6 +379,9 @@ CERT *ssl_cert_dup(CERT *cert)
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 @@ int ssl_cert_add1_chain_cert(CERT *c, X509 *x)
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 046b9f9dcd..e78a7ac293 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -357,6 +357,7 @@ static ERR_STRING_DATA SSL_str_reasons[]=
{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 585411afc3..ea5dd0bd69 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2047,6 +2047,16 @@ void SSL_CTX_set_verify_depth(SSL_CTX *ctx,int depth)
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 49ad5fdb89..a81c4e2ab7 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -552,6 +552,16 @@ typedef struct cert_st
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;
@@ -890,6 +900,7 @@ 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);
+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 6e7abc7e97..846dbf7519 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -3551,5 +3551,10 @@ void tls1_set_cert_validity(SSL *s)
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 e2acad4d70..4d087e0a2b 100644
--- a/ssl/tls1.h
+++ b/ssl/tls1.h
@@ -318,6 +318,8 @@ int SSL_get_shared_sigalgs(SSL *s, int idx,
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)