[x509] Record root of trust used when validating a certificate

Record the root of trust used at the point that a certificate is
validated, redefine validation as checking a certificate against a
specific root of trust, and pass an explicit root of trust when
creating a TLS connection.

This allows a custom TLS connection to be used with a custom root of
trust, without causing any validated certificates to be treated as
valid for normal purposes.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
pull/181/head
Michael Brown 2020-12-08 14:58:46 +00:00
parent 6e92d6213d
commit 39f5293492
13 changed files with 60 additions and 33 deletions

View File

@ -284,7 +284,7 @@ int ocsp_check ( struct x509_certificate *cert,
/* Sanity checks */
assert ( cert != NULL );
assert ( issuer != NULL );
assert ( x509_is_valid ( issuer ) );
assert ( issuer->root != NULL );
/* Allocate and initialise check */
*ocsp = zalloc ( sizeof ( **ocsp ) );
@ -915,7 +915,7 @@ int ocsp_validate ( struct ocsp_check *ocsp, time_t time ) {
*/
x509_invalidate ( signer );
if ( ( rc = x509_validate ( signer, ocsp->issuer, time,
NULL ) ) != 0 ) {
ocsp->issuer->root ) ) != 0 ) {
DBGC ( ocsp, "OCSP %p \"%s\" could not validate ",
ocsp, x509_name ( ocsp->cert ) );
DBGC ( ocsp, "signer \"%s\": %s\n",
@ -961,7 +961,7 @@ int ocsp_validate ( struct ocsp_check *ocsp, time_t time ) {
/* Validate certificate against issuer */
if ( ( rc = x509_validate ( ocsp->cert, ocsp->issuer, time,
NULL ) ) != 0 ) {
ocsp->issuer->root ) ) != 0 ) {
DBGC ( ocsp, "OCSP %p \"%s\" could not validate certificate: "
"%s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ));
return rc;

View File

@ -1295,6 +1295,21 @@ int x509_check_time ( struct x509_certificate *cert, time_t time ) {
return 0;
}
/**
* Check if X.509 certificate is valid
*
* @v cert X.509 certificate
* @v root Root certificate list, or NULL to use default
*/
int x509_is_valid ( struct x509_certificate *cert, struct x509_root *root ) {
/* Use default root certificate store if none specified */
if ( ! root )
root = &root_certificates;
return ( cert->root == root );
}
/**
* Validate X.509 certificate
*
@ -1321,7 +1336,7 @@ int x509_validate ( struct x509_certificate *cert,
root = &root_certificates;
/* Return success if certificate has already been validated */
if ( x509_is_valid ( cert ) )
if ( x509_is_valid ( cert, root ) )
return 0;
/* Fail if certificate is invalid at specified time */
@ -1330,7 +1345,7 @@ int x509_validate ( struct x509_certificate *cert,
/* Succeed if certificate is a trusted root certificate */
if ( x509_check_root ( cert, root ) == 0 ) {
cert->flags |= X509_FL_VALIDATED;
cert->root = root;
cert->path_remaining = ( cert->extensions.basic.path_len + 1 );
return 0;
}
@ -1343,7 +1358,7 @@ int x509_validate ( struct x509_certificate *cert,
}
/* Fail unless issuer has already been validated */
if ( ! x509_is_valid ( issuer ) ) {
if ( ! x509_is_valid ( issuer, root ) ) {
DBGC ( cert, "X509 %p \"%s\" ", cert, x509_name ( cert ) );
DBGC ( cert, "issuer %p \"%s\" has not yet been validated\n",
issuer, x509_name ( issuer ) );
@ -1376,7 +1391,7 @@ int x509_validate ( struct x509_certificate *cert,
cert->path_remaining = max_path_remaining;
/* Mark certificate as valid */
cert->flags |= X509_FL_VALIDATED;
cert->root = root;
DBGC ( cert, "X509 %p \"%s\" successfully validated using ",
cert, x509_name ( cert ) );

View File

@ -326,6 +326,8 @@ struct tls_connection {
/** Verification data */
struct tls_verify_data verify;
/** Root of trust (or NULL to use default) */
struct x509_root *root;
/** Server certificate chain */
struct x509_chain *chain;
/** Certificate validator */
@ -378,6 +380,7 @@ struct tls_connection {
/** RX I/O buffer alignment */
#define TLS_RX_ALIGN 16
extern int add_tls ( struct interface *xfer, const char *name );
extern int add_tls ( struct interface *xfer, const char *name,
struct x509_root *root );
#endif /* _IPXE_TLS_H */

View File

@ -12,6 +12,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/interface.h>
#include <ipxe/x509.h>
extern int create_validator ( struct interface *job, struct x509_chain *chain );
extern int create_validator ( struct interface *job, struct x509_chain *chain,
struct x509_root *root );
#endif /* _IPXE_VALIDATOR_H */

View File

@ -191,6 +191,8 @@ struct x509_certificate {
/** Flags */
unsigned int flags;
/** Root against which certificate has been validated (if any) */
struct x509_root *root;
/** Maximum number of subsequent certificates in chain */
unsigned int path_remaining;
@ -218,12 +220,10 @@ struct x509_certificate {
/** X.509 certificate flags */
enum x509_flags {
/** Certificate has been validated */
X509_FL_VALIDATED = 0x0001,
/** Certificate was added at build time */
X509_FL_PERMANENT = 0x0002,
X509_FL_PERMANENT = 0x0001,
/** Certificate was added explicitly at run time */
X509_FL_EXPLICIT = 0x0004,
X509_FL_EXPLICIT = 0x0002,
};
/**
@ -355,6 +355,8 @@ extern int x509_parse ( struct x509_certificate *cert,
const struct asn1_cursor *raw );
extern int x509_certificate ( const void *data, size_t len,
struct x509_certificate **cert );
extern int x509_is_valid ( struct x509_certificate *cert,
struct x509_root *root );
extern int x509_validate ( struct x509_certificate *cert,
struct x509_certificate *issuer,
time_t time, struct x509_root *root );
@ -383,22 +385,13 @@ extern int x509_check_root ( struct x509_certificate *cert,
struct x509_root *root );
extern int x509_check_time ( struct x509_certificate *cert, time_t time );
/**
* Check if X.509 certificate is valid
*
* @v cert X.509 certificate
*/
static inline int x509_is_valid ( struct x509_certificate *cert ) {
return ( cert->flags & X509_FL_VALIDATED );
}
/**
* Invalidate X.509 certificate
*
* @v cert X.509 certificate
*/
static inline void x509_invalidate ( struct x509_certificate *cert ) {
cert->flags &= ~X509_FL_VALIDATED;
cert->root = NULL;
cert->path_remaining = 0;
}

View File

@ -46,7 +46,7 @@ FEATURE ( FEATURE_PROTOCOL, "HTTPS", DHCP_EB_FEATURE_HTTPS, 1 );
*/
static int https_filter ( struct http_connection *conn ) {
return add_tls ( &conn->socket, conn->uri->host );
return add_tls ( &conn->socket, conn->uri->host, NULL );
}
/** HTTPS URI opener */

View File

@ -246,7 +246,7 @@ static int apply_syslogs_settings ( void ) {
}
/* Add TLS filter */
if ( ( rc = add_tls ( &syslogs, server ) ) != 0 ) {
if ( ( rc = add_tls ( &syslogs, server, NULL ) ) != 0 ) {
DBG ( "SYSLOGS cannot create TLS filter: %s\n",
strerror ( rc ) );
goto err_add_tls;

View File

@ -1938,7 +1938,8 @@ static int tls_new_server_hello_done ( struct tls_connection *tls,
}
/* Begin certificate validation */
if ( ( rc = create_validator ( &tls->validator, tls->chain ) ) != 0 ) {
if ( ( rc = create_validator ( &tls->validator, tls->chain,
tls->root ) ) != 0 ) {
DBGC ( tls, "TLS %p could not start certificate validation: "
"%s\n", tls, strerror ( rc ) );
return rc;
@ -3140,9 +3141,11 @@ static int tls_session ( struct tls_connection *tls, const char *name ) {
*
* @v xfer Data transfer interface
* @v name Host name
* @v root Root of trust (or NULL to use default)
* @ret rc Return status code
*/
int add_tls ( struct interface *xfer, const char *name ) {
int add_tls ( struct interface *xfer, const char *name,
struct x509_root *root ) {
struct tls_connection *tls;
int rc;
@ -3160,6 +3163,7 @@ int add_tls ( struct interface *xfer, const char *name ) {
intf_init ( &tls->validator, &tls_validator_desc, &tls->refcnt );
process_init_stopped ( &tls->process, &tls_process_desc,
&tls->refcnt );
tls->root = root;
tls->version = TLS_VERSION_TLS_1_2;
tls_clear_cipher ( tls, &tls->tx_cipherspec );
tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );

View File

@ -73,6 +73,8 @@ struct validator {
/** Process */
struct process process;
/** Root of trust (or NULL to use default) */
struct x509_root *root;
/** X.509 certificate chain */
struct x509_chain *chain;
/** OCSP check */
@ -554,7 +556,7 @@ static void validator_step ( struct validator *validator ) {
*/
now = time ( NULL );
if ( ( rc = x509_validate_chain ( validator->chain, now, NULL,
NULL ) ) == 0 ) {
validator->root ) ) == 0 ) {
DBGC ( validator, "VALIDATOR %p \"%s\" validated\n",
validator, validator_name ( validator ) );
validator_finished ( validator, 0 );
@ -569,7 +571,7 @@ static void validator_step ( struct validator *validator ) {
issuer = link->cert;
if ( ! cert )
continue;
if ( ! x509_is_valid ( issuer ) )
if ( ! x509_is_valid ( issuer, validator->root ) )
continue;
/* The issuer is valid, but this certificate is not
* yet valid. If OCSP is applicable, start it.
@ -621,9 +623,11 @@ static struct process_descriptor validator_process_desc =
*
* @v job Job control interface
* @v chain X.509 certificate chain
* @v root Root of trust, or NULL to use default
* @ret rc Return status code
*/
int create_validator ( struct interface *job, struct x509_chain *chain ) {
int create_validator ( struct interface *job, struct x509_chain *chain,
struct x509_root *root ) {
struct validator *validator;
int rc;
@ -646,6 +650,7 @@ int create_validator ( struct interface *job, struct x509_chain *chain ) {
&validator->refcnt );
process_init ( &validator->process, &validator_process_desc,
&validator->refcnt );
validator->root = root;
validator->chain = x509_chain_get ( chain );
xferbuf_malloc_init ( &validator->buffer );

View File

@ -42,6 +42,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdlib.h>
#include <string.h>
#include <ipxe/x509.h>
#include <ipxe/rootcert.h>
#include <ipxe/ocsp.h>
#include <ipxe/test.h>
@ -110,7 +111,7 @@ static void ocsp_prepare_test ( struct ocsp_test *test ) {
x509_invalidate ( cert );
/* Force-validate issuer certificate */
issuer->flags |= X509_FL_VALIDATED;
issuer->root = &root_certificates;
issuer->path_remaining = ( issuer->extensions.basic.path_len + 1 );
}

View File

@ -943,6 +943,10 @@ static void x509_validate_chain_okx ( struct x509_test_chain *chn, time_t time,
x509_invalidate_chain ( chn->chain );
okx ( x509_validate_chain ( chn->chain, time, store, root ) == 0,
file, line );
okx ( x509_is_valid ( chn->certs[0]->cert, root ),
file, line );
okx ( ! x509_is_valid ( chn->certs[0]->cert, &dummy_root ),
file, line );
}
#define x509_validate_chain_ok( chn, time, store, root ) \
x509_validate_chain_okx ( chn, time, store, root, __FILE__, __LINE__ )

View File

@ -57,7 +57,7 @@ void certstat ( struct x509_certificate *cert ) {
printf ( " [PERMANENT]" );
if ( cert->flags & X509_FL_EXPLICIT )
printf ( " [EXPLICIT]" );
if ( x509_is_valid ( cert ) )
if ( x509_is_valid ( cert, NULL ) )
printf ( " [VALIDATED]" );
printf ( "\n" );
}

View File

@ -77,7 +77,8 @@ int imgverify ( struct image *image, struct image *signature,
/* Complete all certificate chains */
list_for_each_entry ( info, &sig->info, list ) {
if ( ( rc = create_validator ( &monojob, info->chain ) ) != 0 )
if ( ( rc = create_validator ( &monojob, info->chain,
NULL ) ) != 0 )
goto err_create_validator;
if ( ( rc = monojob_wait ( NULL, 0 ) ) != 0 )
goto err_validator_wait;