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
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);
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;
+ }
+
char *srpuserseed = NULL;
char *srp_verifier_file = NULL;
#endif
+ SSL_EXCERT *exc = NULL;
meth=SSLv23_server_method();
local_argc=argc;
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)
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,
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.
*/
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.
*/
if (authz_in != NULL)
BIO_free(authz_in);
#endif
+ ssl_excert_free(exc);
if (bio_s_out != NULL)
{
BIO_free(bio_s_out);
--- /dev/null
+
+# 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
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
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));
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
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
#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
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)
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;
{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"},
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;
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;
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);
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
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)