Commit 18d71588 authored by Dr. Stephen Henson's avatar Dr. Stephen Henson
Browse files

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.
parent 0f39bab0
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -4,6 +4,15 @@

 Changes between 1.0.1 and 1.1.0  [xx XXX xxxx]

  *) 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
+8 −0
Original line number Diff line number Diff line
@@ -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);
+243 −0
Original line number Diff line number Diff line
@@ -1037,3 +1037,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;
	}
+13 −0
Original line number Diff line number Diff line
@@ -991,6 +991,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;
@@ -1143,6 +1144,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)
@@ -1456,6 +1463,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,
@@ -1618,6 +1628,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.
	 */
@@ -1692,6 +1703,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.
		 */
@@ -2035,6 +2047,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);
+29 −0
Original line number Diff line number Diff line

# Create certificates using various algorithms to test multi-certificate
# functionality.

OPENSSL=../../../apps/openssl
CN="OpenSSL Test RSA SHA-1 cert" $OPENSSL req \
	-config apps.cnf -extensions usr_cert -x509 -nodes \
	-keyout tsha1.pem -out tsha1.pem -new -days 3650 -sha1
CN="OpenSSL Test RSA SHA-256 cert" $OPENSSL req \
	-config apps.cnf -extensions usr_cert -x509 -nodes \
	-keyout tsha256.pem -out tsha256.pem -new -days 3650 -sha256
CN="OpenSSL Test RSA SHA-512 cert" $OPENSSL req \
	-config apps.cnf -extensions usr_cert -x509 -nodes \
	-keyout tsha512.pem -out tsha512.pem -new -days 3650 -sha512

# Create EC parameters 

$OPENSSL ecparam -name P-256 -out ecp256.pem
$OPENSSL ecparam -name P-384 -out ecp384.pem

CN="OpenSSL Test P-256 SHA-256 cert" $OPENSSL req \
	-config apps.cnf -extensions usr_cert -x509 -nodes \
	-nodes -keyout tecp256.pem -out tecp256.pem -newkey ec:ecp256.pem \
	-days 3650 -sha256

CN="OpenSSL Test P-384 SHA-384 cert" $OPENSSL req \
	-config apps.cnf -extensions usr_cert -x509 -nodes \
	-nodes -keyout tecp384.pem -out tecp384.pem -newkey ec:ecp384.pem \
	-days 3650 -sha384
Loading