summaryrefslogtreecommitdiffstats
path: root/src/net/tls.c
diff options
context:
space:
mode:
authorMichael Brown2012-05-04 18:12:32 +0200
committerMichael Brown2012-05-04 18:54:31 +0200
commit557f467bab42b47d91b08e936fbe2ffa8e80f2e7 (patch)
treeac81d6db346318baa0048444f2989144b27a0eca /src/net/tls.c
parent[time] Add Linux time source using gettimeofday() (diff)
downloadipxe-557f467bab42b47d91b08e936fbe2ffa8e80f2e7.tar.gz
ipxe-557f467bab42b47d91b08e936fbe2ffa8e80f2e7.tar.xz
ipxe-557f467bab42b47d91b08e936fbe2ffa8e80f2e7.zip
[crypto] Allow certificate chains to be long-lived data structures
At present, certificate chain validation is treated as an instantaneous process that can be carried out using only data that is already in memory. This model does not allow for validation to include non-instantaneous steps, such as downloading a cross-signing certificate, or determining certificate revocation status via OCSP. Redesign the internal representation of certificate chains to allow chains to outlive the scope of the original source of certificates (such as a TLS Certificate record). Allow for certificates to be cached, so that each certificate needs to be validated only once. Signed-off-by: Michael Brown <mcb30@ipxe.org>
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 3d46a2c7..06242152 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;