[tls] Validate server certificate
Validate the server certificate against the trusted root certificate store. The server must provide a complete certificate chain, up to and including the trusted root certificate that is embedded into iPXE. Note that the date and time are not yet validated. Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
parent
aee3a064f2
commit
f3a791c6de
111
src/net/tls.c
111
src/net/tls.c
|
@ -43,6 +43,16 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
#include <ipxe/rbg.h>
|
#include <ipxe/rbg.h>
|
||||||
#include <ipxe/tls.h>
|
#include <ipxe/tls.h>
|
||||||
|
|
||||||
|
/* Disambiguate the various error causes */
|
||||||
|
#define EACCES_UNTRUSTED \
|
||||||
|
__einfo_error ( EINFO_EACCES_UNTRUSTED )
|
||||||
|
#define EINFO_EACCES_UNTRUSTED \
|
||||||
|
__einfo_uniqify ( EINFO_EACCES, 0x01, "Untrusted certificate chain" )
|
||||||
|
#define EACCES_WRONG_NAME \
|
||||||
|
__einfo_error ( EINFO_EACCES_WRONG_NAME )
|
||||||
|
#define EINFO_EACCES_WRONG_NAME \
|
||||||
|
__einfo_uniqify ( EINFO_EACCES, 0x02, "Incorrect server name" )
|
||||||
|
|
||||||
static int tls_send_plaintext ( struct tls_session *tls, unsigned int type,
|
static int tls_send_plaintext ( struct tls_session *tls, unsigned int type,
|
||||||
const void *data, size_t len );
|
const void *data, size_t len );
|
||||||
static void tls_clear_cipher ( struct tls_session *tls,
|
static void tls_clear_cipher ( struct tls_session *tls,
|
||||||
|
@ -1003,6 +1013,63 @@ static int tls_new_server_hello ( struct tls_session *tls,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** TLS certificate chain context */
|
||||||
|
struct tls_certificate_context {
|
||||||
|
/** TLS session */
|
||||||
|
struct tls_session *tls;
|
||||||
|
/** Current certificate */
|
||||||
|
const void *current;
|
||||||
|
/** End of certificates */
|
||||||
|
const void *end;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse next certificate in TLS certificate list
|
||||||
|
*
|
||||||
|
* @v cert X.509 certificate to fill in
|
||||||
|
* @v ctx Context
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int tls_parse_next ( struct x509_certificate *cert, void *ctx ) {
|
||||||
|
struct tls_certificate_context *context = ctx;
|
||||||
|
struct tls_session *tls = context->tls;
|
||||||
|
const struct {
|
||||||
|
uint8_t length[3];
|
||||||
|
uint8_t certificate[0];
|
||||||
|
} __attribute__ (( packed )) *current = context->current;
|
||||||
|
const void *data;
|
||||||
|
const void *next;
|
||||||
|
size_t len;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Return error at end of chain */
|
||||||
|
if ( context->current >= context->end ) {
|
||||||
|
DBGC ( tls, "TLS %p reached end of certificate chain\n", tls );
|
||||||
|
return -EACCES_UNTRUSTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extract current certificate and update context */
|
||||||
|
data = current->certificate;
|
||||||
|
len = tls_uint24 ( current->length );
|
||||||
|
next = ( data + len );
|
||||||
|
if ( next > context->end ) {
|
||||||
|
DBGC ( tls, "TLS %p overlength certificate\n", tls );
|
||||||
|
DBGC_HDA ( tls, 0, context->current,
|
||||||
|
( context->end - context->current ) );
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
context->current = next;
|
||||||
|
|
||||||
|
/* Parse current certificate */
|
||||||
|
if ( ( rc = x509_parse ( cert, data, len ) ) != 0 ) {
|
||||||
|
DBGC ( tls, "TLS %p could not parse certificate: %s\n",
|
||||||
|
tls, strerror ( rc ) );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receive new Certificate handshake record
|
* Receive new Certificate handshake record
|
||||||
*
|
*
|
||||||
|
@ -1017,19 +1084,14 @@ static int tls_new_certificate ( struct tls_session *tls,
|
||||||
uint8_t length[3];
|
uint8_t length[3];
|
||||||
uint8_t certificates[0];
|
uint8_t certificates[0];
|
||||||
} __attribute__ (( packed )) *certificate = data;
|
} __attribute__ (( packed )) *certificate = data;
|
||||||
const struct {
|
|
||||||
uint8_t length[3];
|
|
||||||
uint8_t certificate[0];
|
|
||||||
} __attribute__ (( packed )) *element =
|
|
||||||
( ( void * ) certificate->certificates );
|
|
||||||
size_t elements_len = tls_uint24 ( certificate->length );
|
size_t elements_len = tls_uint24 ( certificate->length );
|
||||||
const void *end = ( certificate->certificates + elements_len );
|
const void *end = ( certificate->certificates + elements_len );
|
||||||
struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
|
struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
|
||||||
struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey;
|
struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey;
|
||||||
|
struct tls_certificate_context context;
|
||||||
struct x509_certificate cert;
|
struct x509_certificate cert;
|
||||||
|
struct x509_name *name = &cert.subject.name;
|
||||||
struct x509_public_key *key = &cert.subject.public_key;
|
struct x509_public_key *key = &cert.subject.public_key;
|
||||||
const void *cert_data;
|
|
||||||
size_t cert_len;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
|
@ -1040,38 +1102,33 @@ static int tls_new_certificate ( struct tls_session *tls,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Traverse certificate chain */
|
/* Parse first certificate and validate certificate chain */
|
||||||
do {
|
context.tls = tls;
|
||||||
cert_data = element->certificate;
|
context.current = certificate->certificates;
|
||||||
cert_len = tls_uint24 ( element->length );
|
context.end = end;
|
||||||
if ( ( cert_data + cert_len ) > end ) {
|
if ( ( rc = x509_validate_chain ( tls_parse_next, &context,
|
||||||
DBGC ( tls, "TLS %p received corrupt Server "
|
NULL, &cert ) ) != 0 ) {
|
||||||
"Certificate\n", tls );
|
DBGC ( tls, "TLS %p could not validate certificate chain: %s\n",
|
||||||
DBGC_HD ( tls, data, len );
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// HACK
|
|
||||||
|
|
||||||
/* Parse certificate */
|
|
||||||
if ( ( rc = x509_parse ( &cert, cert_data, cert_len ) ) != 0 ) {
|
|
||||||
DBGC ( tls, "TLS %p cannot parse certificate: %s\n",
|
|
||||||
tls, strerror ( rc ) );
|
tls, strerror ( rc ) );
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Verify server name */
|
||||||
|
if ( ( name->len != strlen ( tls->name ) ) ||
|
||||||
|
( memcmp ( name->data, tls->name, name->len ) != 0 ) ) {
|
||||||
|
DBGC ( tls, "TLS %p server name incorrect\n", tls );
|
||||||
|
return -EACCES_WRONG_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialise public key algorithm */
|
/* Initialise public key algorithm */
|
||||||
if ( ( rc = pubkey_init ( pubkey, cipherspec->pubkey_ctx,
|
if ( ( rc = pubkey_init ( pubkey, cipherspec->pubkey_ctx,
|
||||||
key->raw.data, key->raw.len ) ) != 0){
|
key->raw.data, key->raw.len ) ) != 0 ) {
|
||||||
DBGC ( tls, "TLS %p cannot initialise public key: %s\n",
|
DBGC ( tls, "TLS %p cannot initialise public key: %s\n",
|
||||||
tls, strerror ( rc ) );
|
tls, strerror ( rc ) );
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} while ( element != end );
|
|
||||||
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Reference in New Issue