On 06/11/13 17:27, Dr. Stephen Henson wrote:
On Wed, Nov 06, 2013, Rob Stradling wrote:

These 2 #defines exist for SSL_CTX->extra_certs:
   SSL_CTX_add_extra_chain_cert
   SSL_CTX_get_extra_chain_certs
   SSL_CTX_clear_extra_chain_certs

In 1.0.2-dev, the #defines such as SSL_CTX_add0_chain_cert allow me
to specify different chains for different certificate types, but
AFAICT there are no associated get() or clear() functions.

I can't see a way to squeeze a standalone SSL_CTX_get_chain_certs
function into SSL_CTX_ctrl().  There's only 1 pointer argument
available, so I can't pass in an X509* (to indicate which cert I
want the chain for) and get back a STACK_OF(X509)* (the chain).

One option would be to have another SSL_CTX_ctrl #define called
SSL_CTX_get_cert_type, which would accept an X509* and return the
index of that cert (i.e. SSL_CTX->CERT->pkeys[index]->x509), or -1
if not found.  That index could then be passed to
SSL_CTX_get_chain_certs in the larg argument.
However, since the SSL_PKEY_* #defines are private (in ssl_locl.h),
I'm unsure whether exposing these values in the public APIs would be
acceptable.

The other option would be to write SSL_CTX_get_chain_certs() as a
proper function (instead of a SSL_CTX_ctrl #define), but I'm unsure
whether or not that would be better than the first option.

Any preference?

The index for certificates could change in future so I'd rather not expose it
in a public API.

Agreed. I can imagine some future version of OpenSSL being able to accommodate several ECC certs, each with a key on a different named curve, for example.

OpenSSL has the concept of a "current certificate" which could be used here.
This refers to the last certificate set. So you'd have (for example) a way to
retrieve extra chain certificates for the current certificate.

For that to work properly you'd also have to have a way to set the current
certtificate, without the risk of disturbing the existing structure.

So perhaps something like:

int SSL_set_current_cert(SSL *ssl, X509 *x);

Works for me.  :-)

I think the word "select" instead of "set" in that function name might help to make its purpose slightly clearer. (I can imagine someone who doesn't like reading man pages thinking this function will do the same thing as SSL_use_certificate() ).

Which returns 1 and sets the current certificate to one containing 'x' if a
match is found and returns 0 and does nothing if no match is found. Also with
an SSL_CTX version.

Does the attached patch (against the master branch) look acceptable?

Thanks.

--
Rob Stradling
Senior Research & Development Scientist
COMODO - Creating Trust Online

diff --git a/doc/ssl/SSL_CTX_add1_chain_cert.pod b/doc/ssl/SSL_CTX_add1_chain_cert.pod
index 04f7526..2d2161a 100644
--- a/doc/ssl/SSL_CTX_add1_chain_cert.pod
+++ b/doc/ssl/SSL_CTX_add1_chain_cert.pod
@@ -3,9 +3,11 @@
 =head1 NAME
 
 SSL_CTX_set0_chain, SSL_CTX_set1_chain, SSL_CTX_add0_chain_cert,
-SSL_CTX_add1_chain_cert, SSL_set0_chain, SSL_set1_chain,
-SSL_add0_chain_cert, SSL_add1_chain_cert, SSL_CTX_build_cert_chain,
-SSL_build_cert_chain - extra chain certificate processing
+SSL_CTX_add1_chain_cert, SSL_CTX_get0_chain_certs, SSL_CTX_clear_chain_certs,
+SSL_set0_chain, SSL_set1_chain, SSL_add0_chain_cert, SSL_add1_chain_cert,
+SSL_get0_chain_certs, SSL_clear_chain_certs, SSL_CTX_build_cert_chain,
+SSL_build_cert_chain, SSL_CTX_select_current_cert,
+SSL_select_current_cert - extra chain certificate processing
 
 =head1 SYNOPSIS
 
@@ -13,36 +15,58 @@ SSL_build_cert_chain - extra chain certificate processing
 
  int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *sk);
  int SSL_CTX_set1_chain(SSL_CTX *ctx, STACK_OF(X509) *sk);
- int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, STACK_OF(X509) *x509);
+ int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509);
  int SSL_CTX_add1_chain_cert(SSL_CTX *ctx, X509 *x509);
+ int SSL_CTX_get0_chain_certs(SSL_CTX *ctx, STACK_OF(X509) **sk);
+ int SSL_CTX_clear_chain_certs(SSL_CTX *ctx);
 
  int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *sk);
  int SSL_set1_chain(SSL *ssl, STACK_OF(X509) *sk);
- int SSL_add0_chain_cert(SSL *ssl, STACK_OF(X509) *x509);
+ int SSL_add0_chain_cert(SSL *ssl, X509 *x509);
  int SSL_add1_chain_cert(SSL *ssl, X509 *x509);
+ int SSL_get0_chain_certs(SSL *ssl, STACK_OF(X509) **sk);
+ int SSL_clear_chain_certs(SSL *ssl);
 
  int SSL_CTX_build_cert_chain(SSL_CTX *ctx, flags);
- int SSL_build_cert_chain(SSL_CTX *ctx, flags);
+ int SSL_build_cert_chain(SSL *ssl, flags);
+
+ int SSL_CTX_select_current_cert(SSL_CTX *ctx, X509 *x509);
+ int SSL_select_current_cert(SSL *ssl, X509 *x509);
 
 =head1 DESCRIPTION
 
 SSL_CTX_set0_chain() and SSL_CTX_set1_chain() set the certificate chain
-associated with the current certificate of B<ctx> to B<sk>. If B<sk> is set
-to B<NULL> any existing chain is cleared.
+associated with the current certificate of B<ctx> to B<sk>.
 
 SSL_CTX_add0_chain_cert() and SSL_CTX_add1_chain_cert() append the single
 certificate B<x509> to the chain associated with the current certificate of
 B<ctx>.
 
+SSL_CTX_get0_chain_certs() retrieves the chain associated with the current
+certificate of B<ctx>.
+
+SSL_CTX_clear_chain_certs() clears any existing chain associated with the
+current certificate of B<ctx>.  (This is implemented by calling
+SSL_CTX_set0_chain() with B<sk> set to B<NULL>).
+
 SSL_CTX_build_cert_chain() builds the certificate chain for B<ctx> using the
 chain store. Any existing chain certificates are used as untrusted CAs.
 If the function is successful the built chain will replace any existing chain.
 The B<flags> parameter can be set to B<SSL_BUILD_CHAIN_FLAG_NO_ROOT> to omit
 the root CA from the built chain.
 
-SSL_set0_chain(), SSL_set1_chain(), SSL_add0_chain_cert(), SSL_add0_chain_cert()
-and SSL_build_cert_chain() are similar except they apply to SSL structure
-B<ssl>.
+Each of these functions operates on the I<current> end entity
+(i.e. server or client) certificate. This is the last certificate loaded or
+selected on the corresponding B<ctx> structure.
+
+SSL_CTX_select_current_cert() selects B<x509> as the current end entity
+certificate, but only if B<x509> has already been loaded into B<ctx> using a
+function such as SSL_CTX_use_certificate().
+
+SSL_set0_chain(), SSL_set1_chain(), SSL_add0_chain_cert(),
+SSL_add1_chain_cert(), SSL_get0_chain_certs(), SSL_clear_chain_certs(),
+SSL_build_cert_chain() and SSL_select_current_cert() are similar except they
+apply to SSL structure B<ssl>.
 
 All these functions are implemented as macros. Those containing a B<1>
 increment the reference count of the supplied certificate or chain so it must
@@ -56,10 +80,6 @@ The chains associate with an SSL_CTX structure are copied to any SSL
 structures when SSL_new() is called. SSL structures will not be affected
 by any chains subsequently changed in the parent SSL_CTX.
 
-Each of these functions operates on the I<current> end entity
-(i.e. server or client) certificate. This is the last certificate set
-on the corresponding B<ctx> or B<ssl> structure.
-
 One chain can be set for each key type supported by a server. So, for example,
 an RSA and a DSA certificate can (and often will) have different chains.
 
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 2205337..7114304 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -3424,6 +3424,13 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
 		else
 			return ssl_cert_add0_chain_cert(s->cert, (X509 *)parg);
 
+	case SSL_CTRL_GET_CHAIN_CERTS:
+		*(STACK_OF(X509) **)parg = s->cert->key->chain;
+		break;
+
+	case SSL_CTRL_SELECT_CURRENT_CERT:
+		return ssl_cert_select_current(s->cert, (X509 *)parg);
+
 #ifndef OPENSSL_NO_EC
 	case SSL_CTRL_GET_CURVES:
 		{
@@ -3923,6 +3930,13 @@ long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg)
 		else
 			return ssl_cert_add0_chain_cert(ctx->cert, (X509 *)parg);
 
+	case SSL_CTRL_GET_CHAIN_CERTS:
+		*(STACK_OF(X509) **)parg = ctx->cert->key->chain;
+		break;
+
+	case SSL_CTRL_SELECT_CURRENT_CERT:
+		return ssl_cert_select_current(ctx->cert, (X509 *)parg);
+
 	default:
 		return(0);
 		}
diff --git a/ssl/ssl.h b/ssl/ssl.h
index 013345e..be33b66 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -1935,6 +1935,9 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 #define SSL_CTRL_GET_RAW_CIPHERLIST		110
 #define SSL_CTRL_GET_EC_POINT_FORMATS		111
 
+#define SSL_CTRL_GET_CHAIN_CERTS		115
+#define SSL_CTRL_SELECT_CURRENT_CERT		116
+
 #define DTLSv1_get_timeout(ssl, arg) \
 	SSL_ctrl(ssl,DTLS_CTRL_GET_TIMEOUT,0, (void *)arg)
 #define DTLSv1_handle_timeout(ssl) \
@@ -1984,8 +1987,14 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 	SSL_CTX_ctrl(ctx,SSL_CTRL_CHAIN_CERT,0,(char *)x509)
 #define SSL_CTX_add1_chain_cert(ctx,x509) \
 	SSL_CTX_ctrl(ctx,SSL_CTRL_CHAIN_CERT,1,(char *)x509)
+#define SSL_CTX_get0_chain_certs(ctx,px509) \
+	SSL_CTX_ctrl(ctx,SSL_CTRL_GET_CHAIN_CERTS,0,px509)
+#define SSL_CTX_clear_chain_certs(ctx) \
+	SSL_CTX_set0_chain(ctx,NULL)
 #define SSL_CTX_build_cert_chain(ctx, flags) \
 	SSL_CTX_ctrl(ctx,SSL_CTRL_BUILD_CERT_CHAIN, flags, NULL)
+#define SSL_CTX_select_current_cert(ctx,x509) \
+	SSL_CTX_ctrl(ctx,SSL_CTRL_SELECT_CURRENT_CERT,0,(char *)x509)
 
 #define SSL_CTX_set0_verify_cert_store(ctx,st) \
 	SSL_CTX_ctrl(ctx,SSL_CTRL_SET_VERIFY_CERT_STORE,0,(char *)st)
@@ -2004,8 +2013,15 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 	SSL_ctrl(ctx,SSL_CTRL_CHAIN_CERT,0,(char *)x509)
 #define SSL_add1_chain_cert(ctx,x509) \
 	SSL_ctrl(ctx,SSL_CTRL_CHAIN_CERT,1,(char *)x509)
+#define SSL_get0_chain_certs(ctx,px509) \
+	SSL_ctrl(ctx,SSL_CTRL_GET_CHAIN_CERTS,0,px509)
+#define SSL_clear_chain_certs(ctx) \
+	SSL_set0_chain(ctx,NULL)
 #define SSL_build_cert_chain(s, flags) \
 	SSL_ctrl(s,SSL_CTRL_BUILD_CERT_CHAIN, flags, NULL)
+#define SSL_select_current_cert(ctx,x509) \
+	SSL_ctrl(ctx,SSL_CTRL_SELECT_CURRENT_CERT,0,(char *)x509)
+
 #define SSL_set0_verify_cert_store(s,st) \
 	SSL_ctrl(s,SSL_CTRL_SET_VERIFY_CERT_STORE,0,(char *)st)
 #define SSL_set1_verify_cert_store(s,st) \
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index d442e54..9d77ef7 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -599,6 +599,20 @@ int ssl_cert_add1_chain_cert(CERT *c, X509 *x)
 	return 1;
 	}
 
+int ssl_cert_select_current(CERT *c, X509 *x)
+	{
+	int i;
+	for (i = 0; i < SSL_PKEY_NUM; i++)
+		{
+		if (c->pkeys[i].x509 == x)
+			{
+			c->key = &c->pkeys[i];
+			return 1;
+			}
+		}
+	return 0;
+	}
+
 void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), void *arg)
 	{
 	c->cert_cb = cb;
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index ce4c348..51e8891 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -996,6 +996,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);
+int ssl_cert_select_current(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);

Reply via email to