mirror of
https://github.com/xcat2/xNBA.git
synced 2025-02-16 18:48:12 +00:00
[tls] Use asynchronous certificate validator
Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
parent
29dcb0631b
commit
f19565f58f
@ -237,6 +237,13 @@ struct tls_session {
|
||||
|
||||
/** Server certificate chain */
|
||||
struct x509_chain *chain;
|
||||
/** Certificate validator */
|
||||
struct interface validator;
|
||||
|
||||
/** Client has finished security negotiation */
|
||||
unsigned int client_finished;
|
||||
/** Server has finished security negotiation */
|
||||
unsigned int server_finished;
|
||||
|
||||
/** TX sequence number */
|
||||
uint64_t tx_seq;
|
||||
@ -244,8 +251,6 @@ struct tls_session {
|
||||
unsigned int tx_pending;
|
||||
/** TX process */
|
||||
struct process process;
|
||||
/** TX ready for plaintext data */
|
||||
int tx_ready;
|
||||
|
||||
/** RX sequence number */
|
||||
uint64_t rx_seq;
|
||||
|
159
src/net/tls.c
159
src/net/tls.c
@ -43,6 +43,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
||||
#include <ipxe/x509.h>
|
||||
#include <ipxe/clientcert.h>
|
||||
#include <ipxe/rbg.h>
|
||||
#include <ipxe/validator.h>
|
||||
#include <ipxe/tls.h>
|
||||
|
||||
/* Disambiguate the various error causes */
|
||||
@ -93,6 +94,16 @@ static void tls_set_uint24 ( uint8_t field24[3], unsigned long value ) {
|
||||
*field32 |= cpu_to_be32 ( value << 8 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if TLS session is ready for application data
|
||||
*
|
||||
* @v tls TLS session
|
||||
* @ret is_ready TLS session is ready
|
||||
*/
|
||||
static int tls_ready ( struct tls_session *tls ) {
|
||||
return ( tls->client_finished && tls->server_finished );
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Hybrid MD5+SHA1 hash as used by TLSv1.1 and earlier
|
||||
@ -196,10 +207,11 @@ static void tls_close ( struct tls_session *tls, int rc ) {
|
||||
|
||||
/* Remove process */
|
||||
process_del ( &tls->process );
|
||||
|
||||
/* Close ciphertext and plaintext streams */
|
||||
|
||||
/* Close all interfaces */
|
||||
intf_shutdown ( &tls->cipherstream, rc );
|
||||
intf_shutdown ( &tls->plainstream, rc );
|
||||
intf_shutdown ( &tls->validator, rc );
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
@ -1111,7 +1123,9 @@ static int tls_send_finished ( struct tls_session *tls ) {
|
||||
uint8_t verify_data[12];
|
||||
} __attribute__ (( packed )) finished;
|
||||
uint8_t digest_out[ digest->digestsize ];
|
||||
int rc;
|
||||
|
||||
/* Construct record */
|
||||
memset ( &finished, 0, sizeof ( finished ) );
|
||||
finished.type_length = ( cpu_to_le32 ( TLS_FINISHED ) |
|
||||
htonl ( sizeof ( finished ) -
|
||||
@ -1121,7 +1135,15 @@ static int tls_send_finished ( struct tls_session *tls ) {
|
||||
finished.verify_data, sizeof ( finished.verify_data ),
|
||||
"client finished", digest_out, sizeof ( digest_out ) );
|
||||
|
||||
return tls_send_handshake ( tls, &finished, sizeof ( finished ) );
|
||||
/* Transmit record */
|
||||
if ( ( rc = tls_send_handshake ( tls, &finished,
|
||||
sizeof ( finished ) ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Mark client as finished */
|
||||
tls->client_finished = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1354,10 +1376,6 @@ static int tls_new_certificate ( struct tls_session *tls,
|
||||
} __attribute__ (( packed )) *certificate = data;
|
||||
size_t certificates_len = tls_uint24 ( certificate->length );
|
||||
const void *end = ( certificate->certificates + certificates_len );
|
||||
struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
|
||||
struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey;
|
||||
struct x509_certificate *cert;
|
||||
time_t now;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
@ -1373,35 +1391,6 @@ static int tls_new_certificate ( struct tls_session *tls,
|
||||
certificates_len ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Validate certificate chain */
|
||||
now = time ( NULL );
|
||||
if ( ( rc = x509_validate_chain ( tls->chain, now, NULL ) ) != 0 ) {
|
||||
DBGC ( tls, "TLS %p could not validate certificate chain: %s\n",
|
||||
tls, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Extract first certificate */
|
||||
cert = x509_first ( tls->chain );
|
||||
assert ( cert != NULL );
|
||||
|
||||
/* Verify server name */
|
||||
if ( ( cert->subject.name == NULL ) ||
|
||||
( strcmp ( cert->subject.name, tls->name ) != 0 ) ) {
|
||||
DBGC ( tls, "TLS %p server name incorrect (expected %s, got "
|
||||
"%s)\n", tls, tls->name, cert->subject.name );
|
||||
return -EACCES_WRONG_NAME;
|
||||
}
|
||||
|
||||
/* Initialise public key algorithm */
|
||||
if ( ( rc = pubkey_init ( pubkey, cipherspec->pubkey_ctx,
|
||||
cert->subject.public_key.raw.data,
|
||||
cert->subject.public_key.raw.len ) ) != 0 ) {
|
||||
DBGC ( tls, "TLS %p cannot initialise public key: %s\n",
|
||||
tls, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1442,6 +1431,7 @@ static int tls_new_server_hello_done ( struct tls_session *tls,
|
||||
char next[0];
|
||||
} __attribute__ (( packed )) *hello_done = data;
|
||||
const void *end = hello_done->next;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
if ( end != ( data + len ) ) {
|
||||
@ -1451,11 +1441,12 @@ static int tls_new_server_hello_done ( struct tls_session *tls,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Schedule Client Key Exchange, Change Cipher, and Finished */
|
||||
tls->tx_pending |= ( TLS_TX_CLIENT_KEY_EXCHANGE |
|
||||
TLS_TX_CHANGE_CIPHER |
|
||||
TLS_TX_FINISHED );
|
||||
tls_tx_resume ( tls );
|
||||
/* Begin certificate validation */
|
||||
if ( ( rc = create_validator ( &tls->validator, tls->chain ) ) != 0 ) {
|
||||
DBGC ( tls, "TLS %p could not start certificate validation: "
|
||||
"%s\n", tls, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1497,8 +1488,8 @@ static int tls_new_finished ( struct tls_session *tls,
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/* Mark session as ready to transmit plaintext data */
|
||||
tls->tx_ready = 1;
|
||||
/* Mark server as finished */
|
||||
tls->server_finished = 1;
|
||||
|
||||
/* Send notification of a window change */
|
||||
xfer_window_changed ( &tls->plainstream );
|
||||
@ -1601,6 +1592,8 @@ static int tls_new_record ( struct tls_session *tls, unsigned int type,
|
||||
case TLS_TYPE_HANDSHAKE:
|
||||
return tls_new_handshake ( tls, data, len );
|
||||
case TLS_TYPE_DATA:
|
||||
if ( ! tls_ready ( tls ) )
|
||||
return -ENOTCONN;
|
||||
return xfer_deliver_raw ( &tls->plainstream, data, len );
|
||||
default:
|
||||
/* RFC4346 says that we should just ignore unknown
|
||||
@ -2015,7 +2008,7 @@ static int tls_new_ciphertext ( struct tls_session *tls,
|
||||
static size_t tls_plainstream_window ( struct tls_session *tls ) {
|
||||
|
||||
/* Block window unless we are ready to accept data */
|
||||
if ( ! tls->tx_ready )
|
||||
if ( ! tls_ready ( tls ) )
|
||||
return 0;
|
||||
|
||||
return xfer_window ( &tls->cipherstream );
|
||||
@ -2035,7 +2028,7 @@ static int tls_plainstream_deliver ( struct tls_session *tls,
|
||||
int rc;
|
||||
|
||||
/* Refuse unless we are ready to accept data */
|
||||
if ( ! tls->tx_ready ) {
|
||||
if ( ! tls_ready ( tls ) ) {
|
||||
rc = -ENOTCONN;
|
||||
goto done;
|
||||
}
|
||||
@ -2192,6 +2185,79 @@ static struct interface_descriptor tls_cipherstream_desc =
|
||||
INTF_DESC_PASSTHRU ( struct tls_session, cipherstream,
|
||||
tls_cipherstream_ops, plainstream );
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Certificate validator
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handle certificate validation completion
|
||||
*
|
||||
* @v tls TLS session
|
||||
* @v rc Reason for completion
|
||||
*/
|
||||
static void tls_validator_done ( struct tls_session *tls, int rc ) {
|
||||
struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
|
||||
struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey;
|
||||
struct x509_certificate *cert;
|
||||
|
||||
/* Close validator interface */
|
||||
intf_restart ( &tls->validator, rc );
|
||||
|
||||
/* Check for validation failure */
|
||||
if ( rc != 0 ) {
|
||||
DBGC ( tls, "TLS %p certificate validation failed: %s\n",
|
||||
tls, strerror ( rc ) );
|
||||
goto err;
|
||||
}
|
||||
DBGC ( tls, "TLS %p certificate validation succeeded\n", tls );
|
||||
|
||||
/* Extract first certificate */
|
||||
cert = x509_first ( tls->chain );
|
||||
assert ( cert != NULL );
|
||||
|
||||
/* Verify server name */
|
||||
if ( ( cert->subject.name == NULL ) ||
|
||||
( strcmp ( cert->subject.name, tls->name ) != 0 ) ) {
|
||||
DBGC ( tls, "TLS %p server name incorrect (expected %s, got "
|
||||
"%s)\n", tls, tls->name, cert->subject.name );
|
||||
rc = -EACCES_WRONG_NAME;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Initialise public key algorithm */
|
||||
if ( ( rc = pubkey_init ( pubkey, cipherspec->pubkey_ctx,
|
||||
cert->subject.public_key.raw.data,
|
||||
cert->subject.public_key.raw.len ) ) != 0 ) {
|
||||
DBGC ( tls, "TLS %p cannot initialise public key: %s\n",
|
||||
tls, strerror ( rc ) );
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Schedule Client Key Exchange, Change Cipher, and Finished */
|
||||
tls->tx_pending |= ( TLS_TX_CLIENT_KEY_EXCHANGE |
|
||||
TLS_TX_CHANGE_CIPHER |
|
||||
TLS_TX_FINISHED );
|
||||
tls_tx_resume ( tls );
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
tls_close ( tls, rc );
|
||||
return;
|
||||
}
|
||||
|
||||
/** TLS certificate validator interface operations */
|
||||
static struct interface_operation tls_validator_ops[] = {
|
||||
INTF_OP ( intf_close, struct tls_session *, tls_validator_done ),
|
||||
};
|
||||
|
||||
/** TLS certificate validator interface descriptor */
|
||||
static struct interface_descriptor tls_validator_desc =
|
||||
INTF_DESC ( struct tls_session, validator, tls_validator_ops );
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Controlling process
|
||||
@ -2307,6 +2373,8 @@ int add_tls ( struct interface *xfer, const char *name,
|
||||
tls->name = name;
|
||||
intf_init ( &tls->plainstream, &tls_plainstream_desc, &tls->refcnt );
|
||||
intf_init ( &tls->cipherstream, &tls_cipherstream_desc, &tls->refcnt );
|
||||
intf_init ( &tls->validator, &tls_validator_desc, &tls->refcnt );
|
||||
process_init ( &tls->process, &tls_process_desc, &tls->refcnt );
|
||||
tls->version = TLS_VERSION_TLS_1_2;
|
||||
tls_clear_cipher ( tls, &tls->tx_cipherspec );
|
||||
tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
|
||||
@ -2327,7 +2395,6 @@ int add_tls ( struct interface *xfer, const char *name,
|
||||
tls->handshake_digest = &sha256_algorithm;
|
||||
tls->handshake_ctx = tls->handshake_sha256_ctx;
|
||||
tls->tx_pending = TLS_TX_CLIENT_HELLO;
|
||||
process_init ( &tls->process, &tls_process_desc, &tls->refcnt );
|
||||
|
||||
/* Attach to parent interface, mortalise self, and return */
|
||||
intf_plug_plug ( &tls->plainstream, xfer );
|
||||
|
Loading…
x
Reference in New Issue
Block a user