[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
129
src/net/tls.c
129
src/net/tls.c
|
@ -43,6 +43,16 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
|||
#include <ipxe/rbg.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,
|
||||
const void *data, size_t len );
|
||||
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;
|
||||
}
|
||||
|
||||
/** 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
|
||||
*
|
||||
|
@ -1017,19 +1084,14 @@ static int tls_new_certificate ( struct tls_session *tls,
|
|||
uint8_t length[3];
|
||||
uint8_t certificates[0];
|
||||
} __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 );
|
||||
const void *end = ( certificate->certificates + elements_len );
|
||||
struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
|
||||
struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey;
|
||||
struct tls_certificate_context context;
|
||||
struct x509_certificate cert;
|
||||
struct x509_name *name = &cert.subject.name;
|
||||
struct x509_public_key *key = &cert.subject.public_key;
|
||||
const void *cert_data;
|
||||
size_t cert_len;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
|
@ -1040,38 +1102,33 @@ static int tls_new_certificate ( struct tls_session *tls,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Traverse certificate chain */
|
||||
do {
|
||||
cert_data = element->certificate;
|
||||
cert_len = tls_uint24 ( element->length );
|
||||
if ( ( cert_data + cert_len ) > end ) {
|
||||
DBGC ( tls, "TLS %p received corrupt Server "
|
||||
"Certificate\n", tls );
|
||||
DBGC_HD ( tls, data, len );
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Parse first certificate and validate certificate chain */
|
||||
context.tls = tls;
|
||||
context.current = certificate->certificates;
|
||||
context.end = end;
|
||||
if ( ( rc = x509_validate_chain ( tls_parse_next, &context,
|
||||
NULL, &cert ) ) != 0 ) {
|
||||
DBGC ( tls, "TLS %p could not validate certificate chain: %s\n",
|
||||
tls, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
// HACK
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Parse certificate */
|
||||
if ( ( rc = x509_parse ( &cert, cert_data, cert_len ) ) != 0 ) {
|
||||
DBGC ( tls, "TLS %p cannot parse certificate: %s\n",
|
||||
tls, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
/* Initialise public key algorithm */
|
||||
if ( ( rc = pubkey_init ( pubkey, cipherspec->pubkey_ctx,
|
||||
key->raw.data, key->raw.len ) ) != 0 ) {
|
||||
DBGC ( tls, "TLS %p cannot initialise public key: %s\n",
|
||||
tls, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Initialise public key algorithm */
|
||||
if ( ( rc = pubkey_init ( pubkey, cipherspec->pubkey_ctx,
|
||||
key->raw.data, key->raw.len ) ) != 0){
|
||||
DBGC ( tls, "TLS %p cannot initialise public key: %s\n",
|
||||
tls, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} while ( element != end );
|
||||
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Reference in New Issue