From f43a8f8b9f808fb0a8347663abf6efe6908821ed Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 15 Dec 2020 16:11:34 +0000 Subject: [crypto] Allow private key to be specified as a TLS connection parameter Signed-off-by: Michael Brown --- src/crypto/certstore.c | 4 ++-- src/crypto/privkey.c | 35 ++++++++++++++++++++-------- src/include/ipxe/certstore.h | 3 ++- src/include/ipxe/privkey.h | 55 +++++++++++++++++++++++++++++++++++++++++++- src/include/ipxe/tls.h | 7 +++++- src/net/tcp/https.c | 2 +- src/net/tcp/syslogs.c | 2 +- src/net/tls.c | 16 +++++++++---- 8 files changed, 103 insertions(+), 21 deletions(-) diff --git a/src/crypto/certstore.c b/src/crypto/certstore.c index cdf6fb4..2676c7e 100644 --- a/src/crypto/certstore.c +++ b/src/crypto/certstore.c @@ -116,13 +116,13 @@ struct x509_certificate * certstore_find ( struct asn1_cursor *raw ) { * @v key Private key * @ret cert X.509 certificate, or NULL if not found */ -struct x509_certificate * certstore_find_key ( struct asn1_cursor *key ) { +struct x509_certificate * certstore_find_key ( struct private_key *key ) { struct x509_certificate *cert; /* Search for certificate within store */ list_for_each_entry ( cert, &certstore.links, store.list ) { if ( pubkey_match ( cert->signature_algorithm->pubkey, - key->data, key->len, + key->builder.data, key->builder.len, cert->subject.public_key.raw.data, cert->subject.public_key.raw.len ) == 0 ) return certstore_found ( cert ); diff --git a/src/crypto/privkey.c b/src/crypto/privkey.c index 7ef0488..c15edf1 100644 --- a/src/crypto/privkey.c +++ b/src/crypto/privkey.c @@ -64,9 +64,12 @@ __asm__ ( ".section \".rodata\", \"a\", " PROGBITS "\n\t" ".previous\n\t" ); /** Private key */ -struct asn1_cursor private_key = { - .data = private_key_data, - .len = ( ( size_t ) private_key_len ), +struct private_key private_key = { + .refcnt = REF_INIT ( ref_no_free ), + .builder = { + .data = private_key_data, + .len = ( ( size_t ) private_key_len ), + }, }; /** Default private key */ @@ -84,6 +87,19 @@ static struct setting privkey_setting __setting ( SETTING_CRYPTO, privkey ) = { }; /** + * Free private key + * + * @v refcnt Reference counter + */ +void privkey_free ( struct refcnt *refcnt ) { + struct private_key *key = + container_of ( refcnt, struct private_key, refcnt ); + + free ( key->builder.data ); + free ( key ); +} + +/** * Apply private key configuration settings * * @ret rc Return status code @@ -98,23 +114,24 @@ static int privkey_apply_settings ( void ) { if ( ALLOW_KEY_OVERRIDE ) { /* Restore default private key */ - memcpy ( &private_key, &default_private_key, - sizeof ( private_key ) ); + memcpy ( &private_key.builder, &default_private_key, + sizeof ( private_key.builder ) ); /* Fetch new private key, if any */ free ( key_data ); if ( ( len = fetch_raw_setting_copy ( NULL, &privkey_setting, &key_data ) ) >= 0 ) { - private_key.data = key_data; - private_key.len = len; + private_key.builder.data = key_data; + private_key.builder.len = len; } } /* Debug */ - if ( private_key.len ) { + if ( private_key.builder.len ) { DBGC ( &private_key, "PRIVKEY using %s private key:\n", ( key_data ? "external" : "built-in" ) ); - DBGC_HDA ( &private_key, 0, private_key.data, private_key.len ); + DBGC_HDA ( &private_key, 0, private_key.builder.data, + private_key.builder.len ); } else { DBGC ( &private_key, "PRIVKEY has no private key\n" ); } diff --git a/src/include/ipxe/certstore.h b/src/include/ipxe/certstore.h index e4c789c..ce96666 100644 --- a/src/include/ipxe/certstore.h +++ b/src/include/ipxe/certstore.h @@ -11,11 +11,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include extern struct x509_chain certstore; extern struct x509_certificate * certstore_find ( struct asn1_cursor *raw ); -extern struct x509_certificate * certstore_find_key ( struct asn1_cursor *key ); +extern struct x509_certificate * certstore_find_key ( struct private_key *key ); extern void certstore_add ( struct x509_certificate *cert ); extern void certstore_del ( struct x509_certificate *cert ); diff --git a/src/include/ipxe/privkey.h b/src/include/ipxe/privkey.h index 81108b6..a65cf61 100644 --- a/src/include/ipxe/privkey.h +++ b/src/include/ipxe/privkey.h @@ -10,7 +10,60 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include -extern struct asn1_cursor private_key; +/** A private key */ +struct private_key { + /** Reference counter */ + struct refcnt refcnt; + /** ASN.1 object builder */ + struct asn1_builder builder; +}; + +/** + * Get reference to private key + * + * @v key Private key + * @ret key Private key + */ +static inline __attribute__ (( always_inline )) struct private_key * +privkey_get ( struct private_key *key ) { + ref_get ( &key->refcnt ); + return key; +} + +/** + * Drop reference to private key + * + * @v key Private key + */ +static inline __attribute__ (( always_inline )) void +privkey_put ( struct private_key *key ) { + ref_put ( &key->refcnt ); +} + +/** + * Get private key ASN.1 cursor + * + * @v key Private key + * @ret cursor ASN.1 cursor + */ +static inline __attribute__ (( always_inline )) struct asn1_cursor * +privkey_cursor ( struct private_key *key ) { + return asn1_built ( &key->builder ); +} + +extern void privkey_free ( struct refcnt *refcnt ); + +/** + * Initialise empty private key + * + */ +static inline __attribute__ (( always_inline )) void +privkey_init ( struct private_key *key ) { + ref_init ( &key->refcnt, privkey_free ); +} + +extern struct private_key private_key; #endif /* _IPXE_PRIVKEY_H */ diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 8345c9a..8b03579 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -18,6 +18,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -257,6 +258,8 @@ struct tls_session { const char *name; /** Root of trust */ struct x509_root *root; + /** Private key */ + struct private_key *key; /** Session ID */ uint8_t id[32]; @@ -322,6 +325,8 @@ struct tls_connection { struct digest_algorithm *handshake_digest; /** Digest algorithm context used for handshake verification */ uint8_t *handshake_ctx; + /** Private key */ + struct private_key *key; /** Client certificate chain (if used) */ struct x509_chain *certs; /** Secure renegotiation flag */ @@ -384,6 +389,6 @@ struct tls_connection { #define TLS_RX_ALIGN 16 extern int add_tls ( struct interface *xfer, const char *name, - struct x509_root *root ); + struct x509_root *root, struct private_key *key ); #endif /* _IPXE_TLS_H */ diff --git a/src/net/tcp/https.c b/src/net/tcp/https.c index eae8ae5..85f1f12 100644 --- a/src/net/tcp/https.c +++ b/src/net/tcp/https.c @@ -46,7 +46,7 @@ FEATURE ( FEATURE_PROTOCOL, "HTTPS", DHCP_EB_FEATURE_HTTPS, 1 ); */ static int https_filter ( struct http_connection *conn ) { - return add_tls ( &conn->socket, conn->uri->host, NULL ); + return add_tls ( &conn->socket, conn->uri->host, NULL, NULL ); } /** HTTPS URI opener */ diff --git a/src/net/tcp/syslogs.c b/src/net/tcp/syslogs.c index f91864a..f1f70d5 100644 --- a/src/net/tcp/syslogs.c +++ b/src/net/tcp/syslogs.c @@ -246,7 +246,7 @@ static int apply_syslogs_settings ( void ) { } /* Add TLS filter */ - if ( ( rc = add_tls ( &syslogs, server, NULL ) ) != 0 ) { + if ( ( rc = add_tls ( &syslogs, server, NULL, NULL ) ) != 0 ) { DBG ( "SYSLOGS cannot create TLS filter: %s\n", strerror ( rc ) ); goto err_add_tls; diff --git a/src/net/tls.c b/src/net/tls.c index 0463783..3c41444 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -352,6 +352,7 @@ static void free_tls_session ( struct refcnt *refcnt ) { /* Free dynamically-allocated resources */ x509_root_put ( session->root ); + privkey_put ( session->key ); free ( session->ticket ); /* Free session */ @@ -383,6 +384,7 @@ static void free_tls ( struct refcnt *refcnt ) { x509_chain_put ( tls->certs ); x509_chain_put ( tls->chain ); x509_root_put ( tls->root ); + privkey_put ( tls->key ); /* Drop reference to session */ assert ( list_empty ( &tls->list ) ); @@ -1259,6 +1261,7 @@ static int tls_send_certificate_verify ( struct tls_connection *tls ) { struct digest_algorithm *digest = tls->handshake_digest; struct x509_certificate *cert = x509_first ( tls->certs ); struct pubkey_algorithm *pubkey = cert->signature_algorithm->pubkey; + struct asn1_cursor *key = privkey_cursor ( tls->key ); uint8_t digest_out[ digest->digestsize ]; uint8_t ctx[ pubkey->ctxsize ]; struct tls_signature_hash_algorithm *sig_hash = NULL; @@ -1268,8 +1271,7 @@ static int tls_send_certificate_verify ( struct tls_connection *tls ) { tls_verify_handshake ( tls, digest_out ); /* Initialise public-key algorithm */ - if ( ( rc = pubkey_init ( pubkey, ctx, private_key.data, - private_key.len ) ) != 0 ) { + if ( ( rc = pubkey_init ( pubkey, ctx, key->data, key->len ) ) != 0 ) { DBGC ( tls, "TLS %p could not initialise %s client private " "key: %s\n", tls, pubkey->name, strerror ( rc ) ); goto err_pubkey_init; @@ -1876,7 +1878,7 @@ static int tls_new_certificate_request ( struct tls_connection *tls, tls->certs = NULL; /* Determine client certificate to be sent */ - cert = certstore_find_key ( &private_key ); + cert = certstore_find_key ( tls->key ); if ( ! cert ) { DBGC ( tls, "TLS %p could not find certificate corresponding " "to private key\n", tls ); @@ -3100,7 +3102,8 @@ static int tls_session ( struct tls_connection *tls, const char *name ) { /* Find existing matching session, if any */ list_for_each_entry ( session, &tls_sessions, list ) { if ( ( strcmp ( name, session->name ) == 0 ) && - ( tls->root == session->root ) ) { + ( tls->root == session->root ) && + ( tls->key == session->key ) ) { ref_get ( &session->refcnt ); tls->session = session; DBGC ( tls, "TLS %p joining session %s\n", tls, name ); @@ -3120,6 +3123,7 @@ static int tls_session ( struct tls_connection *tls, const char *name ) { strcpy ( name_copy, name ); session->name = name_copy; session->root = x509_root_get ( tls->root ); + session->key = privkey_get ( tls->key ); INIT_LIST_HEAD ( &session->conn ); list_add ( &session->list, &tls_sessions ); @@ -3147,10 +3151,11 @@ static int tls_session ( struct tls_connection *tls, const char *name ) { * @v xfer Data transfer interface * @v name Host name * @v root Root of trust (or NULL to use default) + * @v key Private key (or NULL to use default) * @ret rc Return status code */ int add_tls ( struct interface *xfer, const char *name, - struct x509_root *root ) { + struct x509_root *root, struct private_key *key ) { struct tls_connection *tls; int rc; @@ -3168,6 +3173,7 @@ int add_tls ( struct interface *xfer, const char *name, intf_init ( &tls->validator, &tls_validator_desc, &tls->refcnt ); process_init_stopped ( &tls->process, &tls_process_desc, &tls->refcnt ); + tls->key = privkey_get ( key ? key : &private_key ); tls->root = x509_root_get ( root ? root : &root_certificates ); tls->version = TLS_VERSION_TLS_1_2; tls_clear_cipher ( tls, &tls->tx_cipherspec ); -- cgit v1.1