[tls] Allow provision of a client certificate chain

Use the existing certificate store to automatically append any
available issuing certificates to the selected client certificate.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2020-12-04 15:56:13 +00:00
parent 2b6b02ee7e
commit 25b53afa5b
2 changed files with 79 additions and 32 deletions

View File

@@ -319,8 +319,8 @@ struct tls_connection {
struct digest_algorithm *handshake_digest; struct digest_algorithm *handshake_digest;
/** Digest algorithm context used for handshake verification */ /** Digest algorithm context used for handshake verification */
uint8_t *handshake_ctx; uint8_t *handshake_ctx;
/** Client certificate (if used) */ /** Client certificate chain (if used) */
struct x509_certificate *cert; struct x509_chain *certs;
/** Secure renegotiation flag */ /** Secure renegotiation flag */
int secure_renegotiation; int secure_renegotiation;
/** Verification data */ /** Verification data */

View File

@@ -378,7 +378,7 @@ static void free_tls ( struct refcnt *refcnt ) {
list_del ( &iobuf->list ); list_del ( &iobuf->list );
free_iob ( iobuf ); free_iob ( iobuf );
} }
x509_put ( tls->cert ); x509_chain_put ( tls->certs );
x509_chain_put ( tls->chain ); x509_chain_put ( tls->chain );
/* Drop reference to session */ /* Drop reference to session */
@@ -1147,41 +1147,57 @@ static int tls_send_client_hello ( struct tls_connection *tls ) {
* @ret rc Return status code * @ret rc Return status code
*/ */
static int tls_send_certificate ( struct tls_connection *tls ) { static int tls_send_certificate ( struct tls_connection *tls ) {
struct {
tls24_t length;
uint8_t data[0];
} __attribute__ (( packed )) *certificate;
struct { struct {
uint32_t type_length; uint32_t type_length;
tls24_t length; tls24_t length;
struct { typeof ( *certificate ) certificates[0];
tls24_t length; } __attribute__ (( packed )) *certificates;
uint8_t data[ tls->cert->raw.len ]; struct x509_link *link;
} __attribute__ (( packed )) certificates[1]; struct x509_certificate *cert;
} __attribute__ (( packed )) *certificate; size_t len;
int rc; int rc;
/* Calculate length of client certificates */
len = 0;
list_for_each_entry ( link, &tls->certs->links, list ) {
cert = link->cert;
len += ( sizeof ( *certificate ) + cert->raw.len );
DBGC ( tls, "TLS %p sending client certificate %s\n",
tls, x509_name ( cert ) );
}
/* Allocate storage for Certificate record (which may be too /* Allocate storage for Certificate record (which may be too
* large for the stack). * large for the stack).
*/ */
certificate = zalloc ( sizeof ( *certificate ) ); certificates = zalloc ( sizeof ( *certificates ) + len );
if ( ! certificate ) if ( ! certificates )
return -ENOMEM_CERTIFICATE; return -ENOMEM_CERTIFICATE;
/* Populate record */ /* Populate record */
certificate->type_length = certificates->type_length =
( cpu_to_le32 ( TLS_CERTIFICATE ) | ( cpu_to_le32 ( TLS_CERTIFICATE ) |
htonl ( sizeof ( *certificate ) - htonl ( sizeof ( *certificates ) + len -
sizeof ( certificate->type_length ) ) ); sizeof ( certificates->type_length ) ) );
tls_set_uint24 ( &certificate->length, tls_set_uint24 ( &certificates->length, len );
sizeof ( certificate->certificates ) ); certificate = &certificates->certificates[0];
tls_set_uint24 ( &certificate->certificates[0].length, list_for_each_entry ( link, &tls->certs->links, list ) {
sizeof ( certificate->certificates[0].data ) ); cert = link->cert;
memcpy ( certificate->certificates[0].data, tls_set_uint24 ( &certificate->length, cert->raw.len );
tls->cert->raw.data, memcpy ( certificate->data, cert->raw.data, cert->raw.len );
sizeof ( certificate->certificates[0].data ) ); certificate = ( ( ( void * ) certificate->data ) +
cert->raw.len );
}
/* Transmit record */ /* Transmit record */
rc = tls_send_handshake ( tls, certificate, sizeof ( *certificate ) ); rc = tls_send_handshake ( tls, certificates,
( sizeof ( *certificates ) + len ) );
/* Free record */ /* Free record */
free ( certificate ); free ( certificates );
return rc; return rc;
} }
@@ -1238,7 +1254,7 @@ static int tls_send_client_key_exchange ( struct tls_connection *tls ) {
*/ */
static int tls_send_certificate_verify ( struct tls_connection *tls ) { static int tls_send_certificate_verify ( struct tls_connection *tls ) {
struct digest_algorithm *digest = tls->handshake_digest; struct digest_algorithm *digest = tls->handshake_digest;
struct x509_certificate *cert = tls->cert; struct x509_certificate *cert = x509_first ( tls->certs );
struct pubkey_algorithm *pubkey = cert->signature_algorithm->pubkey; struct pubkey_algorithm *pubkey = cert->signature_algorithm->pubkey;
uint8_t digest_out[ digest->digestsize ]; uint8_t digest_out[ digest->digestsize ];
uint8_t ctx[ pubkey->ctxsize ]; uint8_t ctx[ pubkey->ctxsize ];
@@ -1845,26 +1861,57 @@ static int tls_new_certificate ( struct tls_connection *tls,
static int tls_new_certificate_request ( struct tls_connection *tls, static int tls_new_certificate_request ( struct tls_connection *tls,
const void *data __unused, const void *data __unused,
size_t len __unused ) { size_t len __unused ) {
struct x509_certificate *cert;
int rc;
/* We can only send a single certificate, so there is no point /* We can only send a single certificate, so there is no point
* in parsing the Certificate Request. * in parsing the Certificate Request.
*/ */
/* Free any existing client certificate */ /* Free any existing client certificate chain */
x509_put ( tls->cert ); x509_chain_put ( tls->certs );
tls->certs = NULL;
/* Determine client certificate to be sent */ /* Determine client certificate to be sent */
tls->cert = certstore_find_key ( &private_key ); cert = certstore_find_key ( &private_key );
if ( ! tls->cert ) { if ( ! cert ) {
DBGC ( tls, "TLS %p could not find certificate corresponding " DBGC ( tls, "TLS %p could not find certificate corresponding "
"to private key\n", tls ); "to private key\n", tls );
return -EPERM_CLIENT_CERT; rc = -EPERM_CLIENT_CERT;
goto err_find;
} }
x509_get ( tls->cert ); x509_get ( cert );
DBGC ( tls, "TLS %p sending client certificate %s\n", DBGC ( tls, "TLS %p selected client certificate %s\n",
tls, x509_name ( tls->cert ) ); tls, x509_name ( cert ) );
/* Create client certificate chain */
tls->certs = x509_alloc_chain();
if ( ! tls->certs ) {
rc = -ENOMEM;
goto err_alloc;
}
/* Append client certificate to chain */
if ( ( rc = x509_append ( tls->certs, cert ) ) != 0 )
goto err_append;
/* Append any relevant issuer certificates */
if ( ( rc = x509_auto_append ( tls->certs, &certstore ) ) != 0 )
goto err_auto_append;
/* Drop local reference to client certificate */
x509_put ( cert );
return 0; return 0;
err_auto_append:
err_append:
x509_chain_put ( tls->certs );
tls->certs = NULL;
err_alloc:
x509_put ( cert );
err_find:
return rc;
} }
/** /**
@@ -2880,7 +2927,7 @@ static void tls_validator_done ( struct tls_connection *tls, int rc ) {
tls->tx_pending |= ( TLS_TX_CLIENT_KEY_EXCHANGE | tls->tx_pending |= ( TLS_TX_CLIENT_KEY_EXCHANGE |
TLS_TX_CHANGE_CIPHER | TLS_TX_CHANGE_CIPHER |
TLS_TX_FINISHED ); TLS_TX_FINISHED );
if ( tls->cert ) { if ( tls->certs ) {
tls->tx_pending |= ( TLS_TX_CERTIFICATE | tls->tx_pending |= ( TLS_TX_CERTIFICATE |
TLS_TX_CERTIFICATE_VERIFY ); TLS_TX_CERTIFICATE_VERIFY );
} }