summaryrefslogtreecommitdiffstats
path: root/src/net/tls.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/tls.c')
-rw-r--r--src/net/tls.c161
1 files changed, 94 insertions, 67 deletions
diff --git a/src/net/tls.c b/src/net/tls.c
index 3d46a2c7a..062421524 100644
--- a/src/net/tls.c
+++ b/src/net/tls.c
@@ -46,10 +46,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/tls.h>
/* Disambiguate the various error causes */
-#define EACCES_INCOMPLETE \
- __einfo_error ( EINFO_EACCES_INCOMPLETE )
-#define EINFO_EACCES_INCOMPLETE \
- __einfo_uniqify ( EINFO_EACCES, 0x01, "Incomplete certificate chain" )
#define EACCES_WRONG_NAME \
__einfo_error ( EINFO_EACCES_WRONG_NAME )
#define EINFO_EACCES_WRONG_NAME \
@@ -184,6 +180,7 @@ static void free_tls ( struct refcnt *refcnt ) {
tls_clear_cipher ( tls, &tls->rx_cipherspec );
tls_clear_cipher ( tls, &tls->rx_cipherspec_pending );
free ( tls->rx_data );
+ x509_chain_put ( tls->chain );
/* Free TLS structure itself */
free ( tls );
@@ -899,7 +896,7 @@ static int tls_send_certificate ( struct tls_session *tls ) {
uint8_t data[ client_certificate.len ];
} __attribute__ (( packed )) certificates[num_certificates];
} __attribute__ (( packed )) *certificate;
- struct x509_certificate cert;
+ struct x509_certificate *cert;
int rc;
/* If we have a certificate to send, determine the applicable
@@ -909,13 +906,16 @@ static int tls_send_certificate ( struct tls_session *tls ) {
if ( num_certificates ) {
/* Parse certificate to determine public-key algorithm */
- if ( ( rc = x509_parse ( &cert, client_certificate.data,
- client_certificate.len ) ) != 0 ) {
+ if ( ( rc = x509_certificate ( client_certificate.data,
+ client_certificate.len,
+ &cert ) ) != 0 ) {
DBGC ( tls, "TLS %p could not parse client "
"certificate: %s\n", tls, strerror ( rc ) );
return rc;
}
- tls->verify_pubkey = cert.signature_algorithm->pubkey;
+ tls->verify_pubkey = cert->signature_algorithm->pubkey;
+ x509_put ( cert );
+ cert = NULL;
/* Schedule CertificateVerify transmission */
tls->tx_pending |= TLS_TX_CERTIFICATE_VERIFY;
@@ -1267,64 +1267,88 @@ 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
+ * Parse certificate chain
*
- * @v cert X.509 certificate to fill in
- * @v previous Previous X.509 certificate, or NULL
- * @v ctx Context
+ * @v tls TLS session
+ * @v data Certificate chain
+ * @v len Length of certificate chain
* @ret rc Return status code
*/
-static int tls_parse_next ( struct x509_certificate *cert,
- const struct x509_certificate *previous __unused,
- void *ctx ) {
- struct tls_certificate_context *context = ctx;
- struct tls_session *tls = context->tls;
+static int tls_parse_chain ( struct tls_session *tls,
+ const void *data, size_t len ) {
+ const void *end = ( data + len );
const struct {
uint8_t length[3];
- uint8_t certificate[0];
- } __attribute__ (( packed )) *current = context->current;
- const void *data;
+ uint8_t data[0];
+ } __attribute__ (( packed )) *certificate;
+ size_t certificate_len;
+ struct x509_certificate *cert;
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_INCOMPLETE;
- }
+ /* Free any existing certificate chain */
+ x509_chain_put ( tls->chain );
+ tls->chain = NULL;
- /* 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;
+ /* Create certificate chain */
+ tls->chain = x509_alloc_chain();
+ if ( ! tls->chain ) {
+ rc = -ENOMEM;
+ goto err_alloc_chain;
}
- 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;
+ /* Add certificates to chain */
+ while ( data < end ) {
+
+ /* Extract raw certificate data */
+ certificate = data;
+ certificate_len = tls_uint24 ( certificate->length );
+ next = ( certificate->data + certificate_len );
+ if ( next > end ) {
+ DBGC ( tls, "TLS %p overlength certificate:\n", tls );
+ DBGC_HDA ( tls, 0, data, ( end - data ) );
+ rc = -EINVAL;
+ goto err_overlength;
+ }
+
+ /* Parse certificate */
+ if ( ( rc = x509_certificate ( certificate->data,
+ certificate_len,
+ &cert ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not parse certificate: %s\n",
+ tls, strerror ( rc ) );
+ DBGC_HDA ( tls, 0, data, ( end - data ) );
+ goto err_parse;
+ }
+ DBGC ( tls, "TLS %p found certificate %s\n",
+ tls, cert->subject.name );
+
+ /* Append certificate to chain */
+ if ( ( rc = x509_append ( tls->chain, cert ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not append certificate: %s\n",
+ tls, strerror ( rc ) );
+ goto err_append;
+ }
+
+ /* Drop reference to certificate */
+ x509_put ( cert );
+ cert = NULL;
+
+ /* Move to next certificate in list */
+ data = next;
}
return 0;
+
+ err_append:
+ x509_put ( cert );
+ err_parse:
+ err_overlength:
+ x509_chain_put ( tls->chain );
+ tls->chain = NULL;
+ err_alloc_chain:
+ return rc;
}
/**
@@ -1341,14 +1365,11 @@ static int tls_new_certificate ( struct tls_session *tls,
uint8_t length[3];
uint8_t certificates[0];
} __attribute__ (( packed )) *certificate = data;
- size_t elements_len = tls_uint24 ( certificate->length );
- const void *end = ( certificate->certificates + elements_len );
+ 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 tls_certificate_context context;
- struct x509_certificate cert;
- struct x509_string *name = &cert.subject.name;
- struct x509_public_key *key = &cert.subject.public_key;
+ struct x509_certificate *cert;
time_t now;
int rc;
@@ -1360,28 +1381,34 @@ static int tls_new_certificate ( struct tls_session *tls,
return -EINVAL;
}
- /* Parse first certificate and validate certificate chain */
- context.tls = tls;
- context.current = certificate->certificates;
- context.end = end;
+ /* Parse certificate chain */
+ if ( ( rc = tls_parse_chain ( tls, certificate->certificates,
+ certificates_len ) ) != 0 )
+ return rc;
+
+ /* Validate certificate chain */
now = time ( NULL );
- if ( ( rc = x509_validate_chain ( tls_parse_next, &context,
- now, NULL, &cert ) ) != 0 ) {
+ 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 ( ( name->len != strlen ( tls->name ) ) ||
- ( memcmp ( name->data, tls->name, name->len ) != 0 ) ) {
- DBGC ( tls, "TLS %p server name incorrect\n", tls );
+ if ( strcmp ( tls->name, cert->subject.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,
- key->raw.data, key->raw.len ) ) != 0 ) {
+ 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;