From 623a5e24cbec899d21a0cc90f74071e511072c30 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Wed, 26 Dec 2012 14:43:51 +0000 Subject: 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. (backport from HEAD) --- apps/s_apps.h | 8 ++ apps/s_cb.c | 243 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ apps/s_server.c | 13 +++ 3 files changed, 264 insertions(+) (limited to 'apps') 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); -- cgit v1.2.3