From 64b4452bca04af433f1c98ab782c0e93cd5c88c0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 19 Feb 2019 18:47:12 +0000 Subject: [efi] Blacklist the Dell Ip4ConfigDxe driver On a Dell OptiPlex 7010, calling DisconnectController() on the LOM device handle will lock up the system. Debugging shows that execution is trapped in an infinite loop that is somehow trying to reconnect drivers (without going via ConnectController()). The problem can be reproduced in the UEFI shell with no iPXE code present, by using the "disconnect" command. Experimentation shows that the only fix is to unload (rather than just disconnect) the "Ip4ConfigDxe" driver. Add the concept of a blacklist of UEFI drivers that will be automatically unloaded when iPXE runs as an application, and add the Dell Ip4ConfigDxe driver to this blacklist. Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi_blacklist.h | 13 ++ src/include/ipxe/errfile.h | 1 + src/interface/efi/efi_blacklist.c | 237 +++++++++++++++++++++++++++++++++++ src/interface/efi/efiprefix.c | 6 + 4 files changed, 257 insertions(+) create mode 100644 src/include/ipxe/efi/efi_blacklist.h create mode 100644 src/interface/efi/efi_blacklist.c diff --git a/src/include/ipxe/efi/efi_blacklist.h b/src/include/ipxe/efi/efi_blacklist.h new file mode 100644 index 000000000..c5a5a61dc --- /dev/null +++ b/src/include/ipxe/efi/efi_blacklist.h @@ -0,0 +1,13 @@ +#ifndef _IPXE_EFI_BLACKLIST_H +#define _IPXE_EFI_BLACKLIST_H + +/** @file + * + * EFI driver blacklist + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern void efi_unload_blacklist ( void ); + +#endif /* _IPXE_EFI_BLACKLIST_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 596491a1f..ce67fc66d 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -375,6 +375,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_cert_cmd ( ERRFILE_OTHER | 0x004f0000 ) #define ERRFILE_acpi_settings ( ERRFILE_OTHER | 0x00500000 ) #define ERRFILE_ntlm ( ERRFILE_OTHER | 0x00510000 ) +#define ERRFILE_efi_blacklist ( ERRFILE_OTHER | 0x00520000 ) /** @} */ diff --git a/src/interface/efi/efi_blacklist.c b/src/interface/efi/efi_blacklist.c new file mode 100644 index 000000000..292b28e8c --- /dev/null +++ b/src/interface/efi/efi_blacklist.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2019 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * EFI driver blacklist + * + */ + +/** A blacklisted driver */ +struct efi_blacklist { + /** Name */ + const char *name; + /** + * Check if driver is blacklisted + * + * @v binding Driver binding protocol + * @v loaded Loaded image protocol + * @v wtf Component name protocol, if present + * @ret blacklisted Driver is the blacklisted driver + */ + int ( * blacklist ) ( EFI_DRIVER_BINDING_PROTOCOL *binding, + EFI_LOADED_IMAGE_PROTOCOL *loaded, + EFI_COMPONENT_NAME_PROTOCOL *wtf ); +}; + +/** + * Blacklist Dell Ip4ConfigDxe driver + * + * @v binding Driver binding protocol + * @v loaded Loaded image protocol + * @v wtf Component name protocol, if present + * @ret blacklisted Driver is the blacklisted driver + */ +static int +efi_blacklist_dell_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, + EFI_LOADED_IMAGE_PROTOCOL *loaded __unused, + EFI_COMPONENT_NAME_PROTOCOL *wtf ) { + static const CHAR16 ip4cfg[] = L"IP4 CONFIG Network Service Driver"; + static const char dell[] = "Dell Inc."; + char manufacturer[ sizeof ( dell ) ]; + CHAR16 *name; + + /* Check driver name */ + if ( ! wtf ) + return 0; + if ( wtf->GetDriverName ( wtf, "eng", &name ) != 0 ) + return 0; + if ( memcmp ( name, ip4cfg, sizeof ( ip4cfg ) ) != 0 ) + return 0; + + /* Check manufacturer */ + fetch_string_setting ( NULL, &manufacturer_setting, manufacturer, + sizeof ( manufacturer ) ); + if ( strcmp ( manufacturer, dell ) != 0 ) + return 0; + + return 1; +} + +/** Blacklisted drivers */ +static struct efi_blacklist efi_blacklists[] = { + { + .name = "Dell Ip4Config", + .blacklist = efi_blacklist_dell_ip4config, + }, +}; + +/** + * Find driver blacklisting, if any + * + * @v driver Driver binding handle + * @ret blacklist Driver blacklisting, or NULL + * @ret rc Return status code + */ +static int efi_blacklist ( EFI_HANDLE driver, + struct efi_blacklist **blacklist ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_DRIVER_BINDING_PROTOCOL *binding; + void *interface; + } binding; + union { + EFI_LOADED_IMAGE_PROTOCOL *loaded; + void *interface; + } loaded; + union { + EFI_COMPONENT_NAME_PROTOCOL *wtf; + void *interface; + } wtf; + unsigned int i; + EFI_HANDLE image; + EFI_STATUS efirc; + int rc; + + DBGC2 ( &efi_blacklists, "EFIBL checking %s\n", + efi_handle_name ( driver ) ); + + /* Mark as not blacklisted */ + *blacklist = NULL; + + /* Open driver binding protocol */ + if ( ( efirc = bs->OpenProtocol ( + driver, &efi_driver_binding_protocol_guid, + &binding.interface, efi_image_handle, driver, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIBL %s could not open driver binding " + "protocol: %s\n", efi_handle_name ( driver ), + strerror ( rc ) ); + goto err_binding; + } + image = binding.binding->ImageHandle; + + /* Open loaded image protocol */ + if ( ( efirc = bs->OpenProtocol ( + image, &efi_loaded_image_protocol_guid, + &loaded.interface, efi_image_handle, image, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIBL %s could not open", + efi_handle_name ( driver ) ); + DBGC ( driver, " %s loaded image protocol: %s\n", + efi_handle_name ( image ), strerror ( rc ) ); + goto err_loaded; + } + + /* Open component name protocol, if present*/ + if ( ( efirc = bs->OpenProtocol ( + driver, &efi_component_name_protocol_guid, + &wtf.interface, efi_image_handle, driver, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + /* Ignore failure; is not required to be present */ + wtf.interface = NULL; + } + + /* Check blacklistings */ + for ( i = 0 ; i < ( sizeof ( efi_blacklists ) / + sizeof ( efi_blacklists[0] ) ) ; i++ ) { + if ( efi_blacklists[i].blacklist ( binding.binding, + loaded.loaded, wtf.wtf ) ) { + *blacklist = &efi_blacklists[i]; + break; + } + } + + /* Success */ + rc = 0; + + /* Close protocols */ + if ( wtf.wtf ) { + bs->CloseProtocol ( driver, &efi_component_name_protocol_guid, + efi_image_handle, driver ); + } + bs->CloseProtocol ( image, &efi_loaded_image_protocol_guid, + efi_image_handle, image ); + err_loaded: + bs->CloseProtocol ( driver, &efi_driver_binding_protocol_guid, + efi_image_handle, driver ); + err_binding: + return rc; +} + +/** + * Unload any blacklisted drivers + * + */ +void efi_unload_blacklist ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_blacklist *blacklist; + EFI_HANDLE *drivers; + EFI_HANDLE driver; + UINTN num_drivers; + unsigned int i; + EFI_STATUS efirc; + int rc; + + /* Locate all driver binding protocol handles */ + if ( ( efirc = bs->LocateHandleBuffer ( + ByProtocol, &efi_driver_binding_protocol_guid, + NULL, &num_drivers, &drivers ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efi_blacklists, "EFIBL could not list all drivers: " + "%s\n", strerror ( rc ) ); + return; + } + + /* Unload any blacklisted drivers */ + for ( i = 0 ; i < num_drivers ; i++ ) { + driver = drivers[i]; + if ( ( rc = efi_blacklist ( driver, &blacklist ) ) != 0 ) { + DBGC ( driver, "EFIBL could not determine " + "blacklisting for %s: %s\n", + efi_handle_name ( driver ), strerror ( rc ) ); + continue; + } + if ( ! blacklist ) + continue; + DBGC ( driver, "EFIBL unloading %s (%s)\n", + efi_handle_name ( driver ), blacklist->name ); + if ( ( efirc = bs->UnloadImage ( driver ) ) != 0 ) { + DBGC ( driver, "EFIBL could not unload %s: %s\n", + efi_handle_name ( driver ), strerror ( rc ) ); + } + } + + /* Free handle list */ + bs->FreePool ( drivers ); +} diff --git a/src/interface/efi/efiprefix.c b/src/interface/efi/efiprefix.c index 18b931e68..de3572c75 100644 --- a/src/interface/efi/efiprefix.c +++ b/src/interface/efi/efiprefix.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** * EFI entry point @@ -75,6 +76,10 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle, */ static int efi_probe ( struct root_device *rootdev __unused ) { + /* Unloaded any blacklisted drivers */ + efi_unload_blacklist(); + + /* Connect our drivers */ return efi_driver_connect_all(); } @@ -85,6 +90,7 @@ static int efi_probe ( struct root_device *rootdev __unused ) { */ static void efi_remove ( struct root_device *rootdev __unused ) { + /* Disconnect our drivers */ efi_driver_disconnect_all(); } -- cgit v1.2.3-55-g7522 From 272fe32529103dd39875a9fbed5cfdf1a059e294 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 21 Feb 2019 11:32:25 +0000 Subject: [tls] Support stateful session resumption Record the session ID (if any) provided by the server and attempt to reuse it for any concurrent connections to the same server. If multiple connections are initiated concurrently (e.g. when using PeerDist) then defer sending the ClientHello for all but the first connection, to allow time for the first connection to potentially obtain a session ID (and thereby speed up the negotiation for all remaining connections). Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 31 +++++++- src/net/tls.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 220 insertions(+), 10 deletions(-) diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index b1e702e18..0375a722a 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -242,13 +242,40 @@ struct md5_sha1_digest { /** MD5+SHA1 digest size */ #define MD5_SHA1_DIGEST_SIZE sizeof ( struct md5_sha1_digest ) -/** A TLS connection */ -struct tls_connection { +/** A TLS session */ +struct tls_session { /** Reference counter */ struct refcnt refcnt; + /** List of sessions */ + struct list_head list; /** Server name */ const char *name; + /** Session ID */ + uint8_t id[32]; + /** Length of session ID */ + size_t id_len; + /** Master secret */ + uint8_t master_secret[48]; + + /** List of connections */ + struct list_head conn; +}; + +/** A TLS connection */ +struct tls_connection { + /** Reference counter */ + struct refcnt refcnt; + + /** Session */ + struct tls_session *session; + /** List of connections within the same session */ + struct list_head list; + /** Session ID */ + uint8_t session_id[32]; + /** Length of session ID */ + size_t session_id_len; + /** Plaintext stream */ struct interface plainstream; /** Ciphertext stream */ diff --git a/src/net/tls.c b/src/net/tls.c index 9d994cd77..a2f1f6528 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -175,6 +175,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); __einfo_uniqify ( EINFO_EPROTO, 0x01, \ "Illegal protocol version upgrade" ) +/** List of TLS session */ +static LIST_HEAD ( tls_sessions ); + +static void tls_tx_resume_all ( struct tls_session *session ); static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, const void *data, size_t len ); static void tls_clear_cipher ( struct tls_connection *tls, @@ -307,6 +311,25 @@ struct rsa_digestinfo_prefix rsa_md5_sha1_prefix __rsa_digestinfo_prefix = { ****************************************************************************** */ +/** + * Free TLS session + * + * @v refcnt Reference counter + */ +static void free_tls_session ( struct refcnt *refcnt ) { + struct tls_session *session = + container_of ( refcnt, struct tls_session, refcnt ); + + /* Sanity check */ + assert ( list_empty ( &session->conn ) ); + + /* Remove from list of sessions */ + list_del ( &session->list ); + + /* Free session */ + free ( session ); +} + /** * Free TLS connection * @@ -315,6 +338,7 @@ struct rsa_digestinfo_prefix rsa_md5_sha1_prefix __rsa_digestinfo_prefix = { static void free_tls ( struct refcnt *refcnt ) { struct tls_connection *tls = container_of ( refcnt, struct tls_connection, refcnt ); + struct tls_session *session = tls->session; struct io_buffer *iobuf; struct io_buffer *tmp; @@ -330,8 +354,12 @@ static void free_tls ( struct refcnt *refcnt ) { x509_put ( tls->cert ); x509_chain_put ( tls->chain ); + /* Drop reference to session */ + assert ( list_empty ( &tls->list ) ); + ref_put ( &session->refcnt ); + /* Free TLS structure itself */ - free ( tls ); + free ( tls ); } /** @@ -353,6 +381,13 @@ static void tls_close ( struct tls_connection *tls, int rc ) { intf_shutdown ( &tls->cipherstream, rc ); intf_shutdown ( &tls->plainstream, rc ); intf_shutdown ( &tls->validator, rc ); + + /* Remove from session */ + list_del ( &tls->list ); + INIT_LIST_HEAD ( &tls->list ); + + /* Resume all other connections, in case we were the lead connection */ + tls_tx_resume_all ( tls->session ); } /****************************************************************************** @@ -928,6 +963,18 @@ static void tls_tx_resume ( struct tls_connection *tls ) { process_add ( &tls->process ); } +/** + * Resume TX state machine for all connections within a session + * + * @v session TLS session + */ +static void tls_tx_resume_all ( struct tls_session *session ) { + struct tls_connection *tls; + + list_for_each_entry ( tls, &session->conn, list ) + tls_tx_resume ( tls ); +} + /** * Transmit Handshake record * @@ -953,11 +1000,14 @@ static int tls_send_handshake ( struct tls_connection *tls, * @ret rc Return status code */ static int tls_send_client_hello ( struct tls_connection *tls ) { + struct tls_session *session = tls->session; + size_t name_len = strlen ( session->name ); struct { uint32_t type_length; uint16_t version; uint8_t random[32]; uint8_t session_id_len; + uint8_t session_id[session->id_len]; uint16_t cipher_suite_len; uint16_t cipher_suites[TLS_NUM_CIPHER_SUITES]; uint8_t compression_methods_len; @@ -971,7 +1021,7 @@ static int tls_send_client_hello ( struct tls_connection *tls ) { struct { uint8_t type; uint16_t len; - uint8_t name[ strlen ( tls->name ) ]; + uint8_t name[name_len]; } __attribute__ (( packed )) list[1]; } __attribute__ (( packed )) server_name; uint16_t max_fragment_length_type; @@ -999,12 +1049,22 @@ static int tls_send_client_hello ( struct tls_connection *tls ) { struct tls_signature_hash_algorithm *sighash; unsigned int i; + /* Record requested session ID and associated master secret */ + memcpy ( tls->session_id, session->id, sizeof ( tls->session_id ) ); + tls->session_id_len = session->id_len; + memcpy ( tls->master_secret, session->master_secret, + sizeof ( tls->master_secret ) ); + + /* Construct record */ memset ( &hello, 0, sizeof ( hello ) ); hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) | htonl ( sizeof ( hello ) - sizeof ( hello.type_length ) ) ); hello.version = htons ( tls->version ); memcpy ( &hello.random, &tls->client_random, sizeof ( hello.random ) ); + hello.session_id_len = tls->session_id_len; + memcpy ( hello.session_id, tls->session_id, + sizeof ( hello.session_id ) ); hello.cipher_suite_len = htons ( sizeof ( hello.cipher_suites ) ); i = 0 ; for_each_table_entry ( suite, TLS_CIPHER_SUITES ) hello.cipher_suites[i++] = suite->code; @@ -1018,7 +1078,7 @@ static int tls_send_client_hello ( struct tls_connection *tls ) { hello.extensions.server_name.list[0].type = TLS_SERVER_NAME_HOST_NAME; hello.extensions.server_name.list[0].len = htons ( sizeof ( hello.extensions.server_name.list[0].name )); - memcpy ( hello.extensions.server_name.list[0].name, tls->name, + memcpy ( hello.extensions.server_name.list[0].name, session->name, sizeof ( hello.extensions.server_name.list[0].name ) ); hello.extensions.max_fragment_length_type = htons ( TLS_MAX_FRAGMENT_LENGTH ); @@ -1513,8 +1573,34 @@ static int tls_new_server_hello ( struct tls_connection *tls, if ( ( rc = tls_select_cipher ( tls, hello_b->cipher_suite ) ) != 0 ) return rc; - /* Generate secrets */ - tls_generate_master_secret ( tls ); + /* Reuse or generate master secret */ + if ( hello_a->session_id_len && + ( hello_a->session_id_len == tls->session_id_len ) && + ( memcmp ( session_id, tls->session_id, + tls->session_id_len ) == 0 ) ) { + + /* Session ID match: reuse master secret */ + DBGC ( tls, "TLS %p resuming session ID:\n", tls ); + DBGC_HDA ( tls, 0, tls->session_id, tls->session_id_len ); + + } else { + + /* Generate new master secret */ + tls_generate_master_secret ( tls ); + + /* Record new session ID, if present */ + if ( hello_a->session_id_len && + ( hello_a->session_id_len <= sizeof ( tls->session_id ))){ + tls->session_id_len = hello_a->session_id_len; + memcpy ( tls->session_id, session_id, + tls->session_id_len ); + DBGC ( tls, "TLS %p new session ID:\n", tls ); + DBGC_HDA ( tls, 0, tls->session_id, + tls->session_id_len ); + } + } + + /* Generate keys */ if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) return rc; @@ -1739,6 +1825,7 @@ static int tls_new_server_hello_done ( struct tls_connection *tls, */ static int tls_new_finished ( struct tls_connection *tls, const void *data, size_t len ) { + struct tls_session *session = tls->session; struct digest_algorithm *digest = tls->handshake_digest; const struct { uint8_t verify_data[ sizeof ( tls->verify.server ) ]; @@ -1767,6 +1854,30 @@ static int tls_new_finished ( struct tls_connection *tls, /* Mark server as finished */ pending_put ( &tls->server_negotiation ); + /* If we are resuming a session (i.e. if the server Finished + * arrives before the client Finished is sent), then schedule + * transmission of Change Cipher and Finished. + */ + if ( is_pending ( &tls->client_negotiation ) ) { + tls->tx_pending |= ( TLS_TX_CHANGE_CIPHER | TLS_TX_FINISHED ); + tls_tx_resume ( tls ); + } + + /* Record session ID and master secret, if applicable */ + if ( tls->session_id_len ) { + session->id_len = tls->session_id_len; + memcpy ( session->id, tls->session_id, sizeof ( session->id ) ); + memcpy ( session->master_secret, tls->master_secret, + sizeof ( session->master_secret ) ); + } + + /* Move to end of session's connection list and allow other + * connections to start making progress. + */ + list_del ( &tls->list ); + list_add_tail ( &tls->list, &session->conn ); + tls_tx_resume_all ( session ); + /* Send notification of a window change */ xfer_window_changed ( &tls->plainstream ); @@ -2608,6 +2719,7 @@ static struct interface_descriptor tls_cipherstream_desc = * @v rc Reason for completion */ static void tls_validator_done ( struct tls_connection *tls, int rc ) { + struct tls_session *session = tls->session; struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; struct x509_certificate *cert; @@ -2628,9 +2740,9 @@ static void tls_validator_done ( struct tls_connection *tls, int rc ) { assert ( cert != NULL ); /* Verify server name */ - if ( ( rc = x509_check_name ( cert, tls->name ) ) != 0 ) { + if ( ( rc = x509_check_name ( cert, session->name ) ) != 0 ) { DBGC ( tls, "TLS %p server certificate does not match %s: %s\n", - tls, tls->name, strerror ( rc ) ); + tls, session->name, strerror ( rc ) ); goto err; } @@ -2682,6 +2794,8 @@ static struct interface_descriptor tls_validator_desc = * @v tls TLS connection */ static void tls_tx_step ( struct tls_connection *tls ) { + struct tls_session *session = tls->session; + struct tls_connection *conn; int rc; /* Wait for cipherstream to become ready */ @@ -2690,6 +2804,17 @@ static void tls_tx_step ( struct tls_connection *tls ) { /* Send first pending transmission */ if ( tls->tx_pending & TLS_TX_CLIENT_HELLO ) { + /* Wait for session ID to become available unless we + * are the lead connection within the session. + */ + if ( session->id_len == 0 ) { + list_for_each_entry ( conn, &session->conn, list ) { + if ( conn == tls ) + break; + if ( is_pending ( &conn->server_negotiation ) ) + return; + } + } /* Send Client Hello */ if ( ( rc = tls_send_client_hello ( tls ) ) != 0 ) { DBGC ( tls, "TLS %p could not send Client Hello: %s\n", @@ -2766,6 +2891,60 @@ static void tls_tx_step ( struct tls_connection *tls ) { static struct process_descriptor tls_process_desc = PROC_DESC_ONCE ( struct tls_connection, process, tls_tx_step ); +/****************************************************************************** + * + * Session management + * + ****************************************************************************** + */ + +/** + * Find or create session for TLS connection + * + * @v tls TLS connection + * @v name Server name + * @ret rc Return status code + */ +static int tls_session ( struct tls_connection *tls, const char *name ) { + struct tls_session *session; + char *name_copy; + int rc; + + /* Find existing matching session, if any */ + list_for_each_entry ( session, &tls_sessions, list ) { + if ( strcmp ( name, session->name ) == 0 ) { + ref_get ( &session->refcnt ); + tls->session = session; + DBGC ( tls, "TLS %p joining session %s\n", tls, name ); + return 0; + } + } + + /* Create new session */ + session = zalloc ( sizeof ( *session ) + strlen ( name ) + + 1 /* NUL */ ); + if ( ! session ) { + rc = -ENOMEM; + goto err_alloc; + } + ref_init ( &session->refcnt, free_tls_session ); + name_copy = ( ( ( void * ) session ) + sizeof ( *session ) ); + strcpy ( name_copy, name ); + session->name = name_copy; + INIT_LIST_HEAD ( &session->conn ); + list_add ( &session->list, &tls_sessions ); + + /* Record session */ + tls->session = session; + + DBGC ( tls, "TLS %p created session %s\n", tls, name ); + return 0; + + ref_put ( &session->refcnt ); + err_alloc: + return rc; +} + /****************************************************************************** * * Instantiator @@ -2786,7 +2965,7 @@ int add_tls ( struct interface *xfer, const char *name, } memset ( tls, 0, sizeof ( *tls ) ); ref_init ( &tls->refcnt, free_tls ); - tls->name = name; + INIT_LIST_HEAD ( &tls->list ); 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 ); @@ -2809,6 +2988,9 @@ int add_tls ( struct interface *xfer, const char *name, ( sizeof ( tls->pre_master_secret.random ) ) ) ) != 0 ) { goto err_random; } + if ( ( rc = tls_session ( tls, name ) ) != 0 ) + goto err_session; + list_add_tail ( &tls->list, &tls->session->conn ); /* Start negotiation */ tls_restart ( tls ); @@ -2819,6 +3001,7 @@ int add_tls ( struct interface *xfer, const char *name, ref_put ( &tls->refcnt ); return 0; + err_session: err_random: ref_put ( &tls->refcnt ); err_alloc: -- cgit v1.2.3-55-g7522 From 799781f1683ef4e0e331a39556e9687c4311ad3e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 21 Feb 2019 23:50:11 +0000 Subject: [tls] Fix incorrectly duplicated error number Signed-off-by: Michael Brown --- src/net/tls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/tls.c b/src/net/tls.c index a2f1f6528..43fe4b4d2 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -104,7 +104,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); "Invalid MAC" ) #define EIO_ALERT __einfo_error ( EINFO_EIO_ALERT ) #define EINFO_EIO_ALERT \ - __einfo_uniqify ( EINFO_EINVAL, 0x01, \ + __einfo_uniqify ( EINFO_EIO, 0x01, \ "Unknown alert level" ) #define ENOMEM_CONTEXT __einfo_error ( EINFO_ENOMEM_CONTEXT ) #define EINFO_ENOMEM_CONTEXT \ -- cgit v1.2.3-55-g7522 From eaba1a22b8552f0410fe1519d7d0b606dc9ef3bb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 6 Mar 2019 15:02:02 +0000 Subject: [tls] Support stateless session resumption Add support for RFC5077 session ticket extensions to allow for stateless TLS session resumption. Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 12 +++++ src/net/tls.c | 129 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 122 insertions(+), 19 deletions(-) diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 0375a722a..4bffde7c2 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -63,6 +63,7 @@ struct tls_header { #define TLS_HELLO_REQUEST 0 #define TLS_CLIENT_HELLO 1 #define TLS_SERVER_HELLO 2 +#define TLS_NEW_SESSION_TICKET 4 #define TLS_CERTIFICATE 11 #define TLS_SERVER_KEY_EXCHANGE 12 #define TLS_CERTIFICATE_REQUEST 13 @@ -108,6 +109,9 @@ struct tls_header { /* TLS signature algorithms extension */ #define TLS_SIGNATURE_ALGORITHMS 13 +/* TLS session ticket extension */ +#define TLS_SESSION_TICKET 35 + /* TLS renegotiation information extension */ #define TLS_RENEGOTIATION_INFO 0xff01 @@ -255,6 +259,10 @@ struct tls_session { uint8_t id[32]; /** Length of session ID */ size_t id_len; + /** Session ticket */ + void *ticket; + /** Length of session ticket */ + size_t ticket_len; /** Master secret */ uint8_t master_secret[48]; @@ -275,6 +283,10 @@ struct tls_connection { uint8_t session_id[32]; /** Length of session ID */ size_t session_id_len; + /** New session ticket */ + void *new_session_ticket; + /** Length of new session ticket */ + size_t new_session_ticket_len; /** Plaintext stream */ struct interface plainstream; diff --git a/src/net/tls.c b/src/net/tls.c index 43fe4b4d2..1cd37e776 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -102,6 +102,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_EINVAL_MAC \ __einfo_uniqify ( EINFO_EINVAL, 0x0d, \ "Invalid MAC" ) +#define EINVAL_TICKET __einfo_error ( EINFO_EINVAL_TICKET ) +#define EINFO_EINVAL_TICKET \ + __einfo_uniqify ( EINFO_EINVAL, 0x0e, \ + "Invalid New Session Ticket record") #define EIO_ALERT __einfo_error ( EINFO_EIO_ALERT ) #define EINFO_EIO_ALERT \ __einfo_uniqify ( EINFO_EIO, 0x01, \ @@ -326,6 +330,9 @@ static void free_tls_session ( struct refcnt *refcnt ) { /* Remove from list of sessions */ list_del ( &session->list ); + /* Free session ticket */ + free ( session->ticket ); + /* Free session */ free ( session ); } @@ -343,6 +350,7 @@ static void free_tls ( struct refcnt *refcnt ) { struct io_buffer *tmp; /* Free dynamically-allocated resources */ + free ( tls->new_session_ticket ); tls_clear_cipher ( tls, &tls->tx_cipherspec ); tls_clear_cipher ( tls, &tls->tx_cipherspec_pending ); tls_clear_cipher ( tls, &tls->rx_cipherspec ); @@ -1007,7 +1015,7 @@ static int tls_send_client_hello ( struct tls_connection *tls ) { uint16_t version; uint8_t random[32]; uint8_t session_id_len; - uint8_t session_id[session->id_len]; + uint8_t session_id[tls->session_id_len]; uint16_t cipher_suite_len; uint16_t cipher_suites[TLS_NUM_CIPHER_SUITES]; uint8_t compression_methods_len; @@ -1043,18 +1051,17 @@ static int tls_send_client_hello ( struct tls_connection *tls ) { uint8_t data[ tls->secure_renegotiation ? sizeof ( tls->verify.client ) :0]; } __attribute__ (( packed )) renegotiation_info; + uint16_t session_ticket_type; + uint16_t session_ticket_len; + struct { + uint8_t data[session->ticket_len]; + } __attribute__ (( packed )) session_ticket; } __attribute__ (( packed )) extensions; } __attribute__ (( packed )) hello; struct tls_cipher_suite *suite; struct tls_signature_hash_algorithm *sighash; unsigned int i; - /* Record requested session ID and associated master secret */ - memcpy ( tls->session_id, session->id, sizeof ( tls->session_id ) ); - tls->session_id_len = session->id_len; - memcpy ( tls->master_secret, session->master_secret, - sizeof ( tls->master_secret ) ); - /* Construct record */ memset ( &hello, 0, sizeof ( hello ) ); hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) | @@ -1102,6 +1109,11 @@ static int tls_send_client_hello ( struct tls_connection *tls ) { = sizeof ( hello.extensions.renegotiation_info.data ); memcpy ( hello.extensions.renegotiation_info.data, tls->verify.client, sizeof ( hello.extensions.renegotiation_info.data ) ); + hello.extensions.session_ticket_type = htons ( TLS_SESSION_TICKET ); + hello.extensions.session_ticket_len + = htons ( sizeof ( hello.extensions.session_ticket ) ); + memcpy ( hello.extensions.session_ticket.data, session->ticket, + sizeof ( hello.extensions.session_ticket.data ) ); return tls_send_handshake ( tls, &hello, sizeof ( hello ) ); } @@ -1631,6 +1643,57 @@ static int tls_new_server_hello ( struct tls_connection *tls, return 0; } +/** + * Receive New Session Ticket handshake record + * + * @v tls TLS connection + * @v data Plaintext handshake record + * @v len Length of plaintext handshake record + * @ret rc Return status code + */ +static int tls_new_session_ticket ( struct tls_connection *tls, + const void *data, size_t len ) { + const struct { + uint32_t lifetime; + uint16_t len; + uint8_t ticket[0]; + } __attribute__ (( packed )) *new_session_ticket = data; + size_t ticket_len; + + /* Parse header */ + if ( sizeof ( *new_session_ticket ) > len ) { + DBGC ( tls, "TLS %p received underlength New Session Ticket\n", + tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL_TICKET; + } + ticket_len = ntohs ( new_session_ticket->len ); + if ( ticket_len > ( len - sizeof ( *new_session_ticket ) ) ) { + DBGC ( tls, "TLS %p received overlength New Session Ticket\n", + tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL_TICKET; + } + + /* Free any unapplied new session ticket */ + free ( tls->new_session_ticket ); + tls->new_session_ticket = NULL; + tls->new_session_ticket_len = 0; + + /* Record ticket */ + tls->new_session_ticket = malloc ( ticket_len ); + if ( ! tls->new_session_ticket ) + return -ENOMEM; + memcpy ( tls->new_session_ticket, new_session_ticket->ticket, + ticket_len ); + tls->new_session_ticket_len = ticket_len; + DBGC ( tls, "TLS %p new session ticket:\n", tls ); + DBGC_HDA ( tls, 0, tls->new_session_ticket, + tls->new_session_ticket_len ); + + return 0; +} + /** * Parse certificate chain * @@ -1863,12 +1926,21 @@ static int tls_new_finished ( struct tls_connection *tls, tls_tx_resume ( tls ); } - /* Record session ID and master secret, if applicable */ + /* Record session ID, ticket, and master secret, if applicable */ + if ( tls->session_id_len || tls->new_session_ticket_len ) { + memcpy ( session->master_secret, tls->master_secret, + sizeof ( session->master_secret ) ); + } if ( tls->session_id_len ) { session->id_len = tls->session_id_len; memcpy ( session->id, tls->session_id, sizeof ( session->id ) ); - memcpy ( session->master_secret, tls->master_secret, - sizeof ( session->master_secret ) ); + } + if ( tls->new_session_ticket_len ) { + free ( session->ticket ); + session->ticket = tls->new_session_ticket; + session->ticket_len = tls->new_session_ticket_len; + tls->new_session_ticket = NULL; + tls->new_session_ticket_len = 0; } /* Move to end of session's connection list and allow other @@ -1933,6 +2005,10 @@ static int tls_new_handshake ( struct tls_connection *tls, case TLS_SERVER_HELLO: rc = tls_new_server_hello ( tls, payload, payload_len ); break; + case TLS_NEW_SESSION_TICKET: + rc = tls_new_session_ticket ( tls, payload, + payload_len ); + break; case TLS_CERTIFICATE: rc = tls_new_certificate ( tls, payload, payload_len ); break; @@ -2804,16 +2880,31 @@ static void tls_tx_step ( struct tls_connection *tls ) { /* Send first pending transmission */ if ( tls->tx_pending & TLS_TX_CLIENT_HELLO ) { - /* Wait for session ID to become available unless we - * are the lead connection within the session. + /* Serialise server negotiations within a session, to + * provide a consistent view of session IDs and + * session tickets. */ - if ( session->id_len == 0 ) { - list_for_each_entry ( conn, &session->conn, list ) { - if ( conn == tls ) - break; - if ( is_pending ( &conn->server_negotiation ) ) - return; - } + list_for_each_entry ( conn, &session->conn, list ) { + if ( conn == tls ) + break; + if ( is_pending ( &conn->server_negotiation ) ) + return; + } + /* Record or generate session ID and associated master secret */ + if ( session->id_len ) { + /* Attempt to resume an existing session */ + memcpy ( tls->session_id, session->id, + sizeof ( tls->session_id ) ); + tls->session_id_len = session->id_len; + memcpy ( tls->master_secret, session->master_secret, + sizeof ( tls->master_secret ) ); + } else { + /* No existing session: use a random session ID */ + assert ( sizeof ( tls->session_id ) == + sizeof ( tls->client_random ) ); + memcpy ( tls->session_id, &tls->client_random, + sizeof ( tls->session_id ) ); + tls->session_id_len = sizeof ( tls->session_id ); } /* Send Client Hello */ if ( ( rc = tls_send_client_hello ( tls ) ) != 0 ) { -- cgit v1.2.3-55-g7522 From 447e5cd4474084eda5db28b467cf407c014ebe33 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 7 Mar 2019 13:47:30 +0000 Subject: [crypto] Use x509_name() in validator debug messages Display a human-readable certificate name in validator debug messages wherever possible. Signed-off-by: Michael Brown --- src/net/validator.c | 105 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 37 deletions(-) diff --git a/src/net/validator.c b/src/net/validator.c index 40f778c7d..25d81bd24 100644 --- a/src/net/validator.c +++ b/src/net/validator.c @@ -72,6 +72,18 @@ struct validator { size_t len ); }; +/** + * Get validator name (for debug messages) + * + * @v validator Certificate validator + * @ret name Validator name + */ +static const char * validator_name ( struct validator *validator ) { + + /* Use name of first certificate in chain */ + return x509_name ( x509_first ( validator->chain ) ); +} + /** * Free certificate validator * @@ -81,7 +93,8 @@ static void validator_free ( struct refcnt *refcnt ) { struct validator *validator = container_of ( refcnt, struct validator, refcnt ); - DBGC2 ( validator, "VALIDATOR %p freed\n", validator ); + DBGC2 ( validator, "VALIDATOR %p \"%s\" freed\n", + validator, validator_name ( validator ) ); x509_chain_put ( validator->chain ); ocsp_put ( validator->ocsp ); xferbuf_free ( &validator->buffer ); @@ -165,8 +178,9 @@ static int validator_append ( struct validator *validator, /* Enter certificateSet */ if ( ( rc = asn1_enter ( &cursor, ASN1_SET ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not enter " - "certificateSet: %s\n", validator, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not enter " + "certificateSet: %s\n", validator, + validator_name ( validator ), strerror ( rc ) ); goto err_certificateset; } @@ -176,15 +190,16 @@ static int validator_append ( struct validator *validator, /* Add certificate to chain */ if ( ( rc = x509_append_raw ( certs, cursor.data, cursor.len ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not append " - "certificate: %s\n", - validator, strerror ( rc) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not " + "append certificate: %s\n", validator, + validator_name ( validator ), strerror ( rc) ); DBGC_HDA ( validator, 0, cursor.data, cursor.len ); return rc; } cert = x509_last ( certs ); - DBGC ( validator, "VALIDATOR %p found certificate %s\n", - validator, x509_name ( cert ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" found certificate ", + validator, validator_name ( validator ) ); + DBGC ( validator, "%s\n", x509_name ( cert ) ); /* Move to next certificate */ asn1_skip_any ( &cursor ); @@ -193,15 +208,17 @@ static int validator_append ( struct validator *validator, /* Append certificates to chain */ last = x509_last ( validator->chain ); if ( ( rc = x509_auto_append ( validator->chain, certs ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not append " - "certificates: %s\n", validator, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not append " + "certificates: %s\n", validator, + validator_name ( validator ), strerror ( rc ) ); goto err_auto_append; } /* Check that at least one certificate has been added */ if ( last == x509_last ( validator->chain ) ) { - DBGC ( validator, "VALIDATOR %p failed to append any " - "applicable certificates\n", validator ); + DBGC ( validator, "VALIDATOR %p \"%s\" failed to append any " + "applicable certificates\n", validator, + validator_name ( validator ) ); rc = -EACCES; goto err_no_progress; } @@ -223,11 +240,12 @@ static int validator_append ( struct validator *validator, * Start download of cross-signing certificate * * @v validator Certificate validator - * @v issuer Required issuer + * @v cert X.509 certificate * @ret rc Return status code */ static int validator_start_download ( struct validator *validator, - const struct asn1_cursor *issuer ) { + struct x509_certificate *cert ) { + const struct asn1_cursor *issuer = &cert->issuer.raw; const char *crosscert; char *crosscert_copy; char *uri_string; @@ -261,8 +279,10 @@ static int validator_start_download ( struct validator *validator, crosscert, crc ); base64_encode ( issuer->data, issuer->len, ( uri_string + len ), ( uri_string_len - len ) ); - DBGC ( validator, "VALIDATOR %p downloading cross-signed certificate " - "from %s\n", validator, uri_string ); + DBGC ( validator, "VALIDATOR %p \"%s\" downloading ", + validator, validator_name ( validator ) ); + DBGC ( validator, "\"%s\" cross-signature from %s\n", + x509_name ( cert ), uri_string ); /* Set completion handler */ validator->done = validator_append; @@ -270,8 +290,9 @@ static int validator_start_download ( struct validator *validator, /* Open URI */ if ( ( rc = xfer_open_uri_string ( &validator->xfer, uri_string ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not open %s: %s\n", - validator, uri_string, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not open %s: " + "%s\n", validator, validator_name ( validator ), + uri_string, strerror ( rc ) ); goto err_open_uri_string; } @@ -307,16 +328,18 @@ static int validator_ocsp_validate ( struct validator *validator, /* Record OCSP response */ if ( ( rc = ocsp_response ( validator->ocsp, data, len ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not record OCSP " - "response: %s\n", validator, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not record OCSP " + "response: %s\n", validator, + validator_name ( validator ),strerror ( rc ) ); return rc; } /* Validate OCSP response */ now = time ( NULL ); if ( ( rc = ocsp_validate ( validator->ocsp, now ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not validate OCSP " - "response: %s\n", validator, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not validate " + "OCSP response: %s\n", validator, + validator_name ( validator ), strerror ( rc ) ); return rc; } @@ -344,8 +367,9 @@ static int validator_start_ocsp ( struct validator *validator, /* Create OCSP check */ assert ( validator->ocsp == NULL ); if ( ( rc = ocsp_check ( cert, issuer, &validator->ocsp ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not create OCSP check: " - "%s\n", validator, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not create OCSP " + "check: %s\n", validator, validator_name ( validator ), + strerror ( rc ) ); return rc; } @@ -354,12 +378,15 @@ static int validator_start_ocsp ( struct validator *validator, /* Open URI */ uri_string = validator->ocsp->uri_string; - DBGC ( validator, "VALIDATOR %p performing OCSP check at %s\n", - validator, uri_string ); + DBGC ( validator, "VALIDATOR %p \"%s\" checking ", + validator, validator_name ( validator ) ); + DBGC ( validator, "\"%s\" via %s\n", + x509_name ( cert ), uri_string ); if ( ( rc = xfer_open_uri_string ( &validator->xfer, uri_string ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not open %s: %s\n", - validator, uri_string, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not open %s: " + "%s\n", validator, validator_name ( validator ), + uri_string, strerror ( rc ) ); return rc; } @@ -385,11 +412,13 @@ static void validator_xfer_close ( struct validator *validator, int rc ) { /* Check for errors */ if ( rc != 0 ) { - DBGC ( validator, "VALIDATOR %p transfer failed: %s\n", - validator, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" transfer failed: %s\n", + validator, validator_name ( validator ), + strerror ( rc ) ); goto err_transfer; } - DBGC2 ( validator, "VALIDATOR %p transfer complete\n", validator ); + DBGC2 ( validator, "VALIDATOR %p \"%s\" transfer complete\n", + validator, validator_name ( validator ) ); /* Process completed download */ assert ( validator->done != NULL ); @@ -426,8 +455,9 @@ static int validator_xfer_deliver ( struct validator *validator, /* Add data to buffer */ if ( ( rc = xferbuf_deliver ( &validator->buffer, iob_disown ( iobuf ), meta ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not receive data: %s\n", - validator, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not receive " + "data: %s\n", validator, validator_name ( validator ), + strerror ( rc ) ); validator_finished ( validator, rc ); return rc; } @@ -471,6 +501,8 @@ static void validator_step ( struct validator *validator ) { now = time ( NULL ); if ( ( rc = x509_validate_chain ( validator->chain, now, NULL, NULL ) ) == 0 ) { + DBGC ( validator, "VALIDATOR %p \"%s\" validated\n", + validator, validator_name ( validator ) ); validator_finished ( validator, 0 ); return; } @@ -514,8 +546,7 @@ static void validator_step ( struct validator *validator ) { /* Otherwise, try to download a suitable cross-signing * certificate. */ - if ( ( rc = validator_start_download ( validator, - &last->issuer.raw ) ) != 0 ) { + if ( ( rc = validator_start_download ( validator, last ) ) != 0 ) { validator_finished ( validator, rc ); return; } @@ -567,8 +598,8 @@ int create_validator ( struct interface *job, struct x509_chain *chain ) { /* Attach parent interface, mortalise self, and return */ intf_plug_plug ( &validator->job, job ); ref_put ( &validator->refcnt ); - DBGC2 ( validator, "VALIDATOR %p validating X509 chain %p\n", - validator, validator->chain ); + DBGC2 ( validator, "VALIDATOR %p \"%s\" validating X509 chain %p\n", + validator, validator_name ( validator ), validator->chain ); return 0; validator_finished ( validator, rc ); -- cgit v1.2.3-55-g7522 From b28ccfc725c9a52401aaa09de0734a44bd44a02d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 7 Mar 2019 15:23:19 +0000 Subject: [tls] Display cross-certificate and OCSP status messages TLS connections will almost always create background connections to perform cross-signed certificate downloads and OCSP checks. There is currently no direct visibility into which checks are taking place, which makes troubleshooting difficult in the absence of either a packet capture or a debug build. Use the job progress message buffer to report the current cross-signed certificate download or OCSP status check, where applicable. Signed-off-by: Michael Brown --- src/net/tls.c | 20 +++++++++++++++ src/net/validator.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/src/net/tls.c b/src/net/tls.c index 1cd37e776..510bef8c4 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -47,6 +47,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /* Disambiguate the various error causes */ @@ -2570,12 +2571,31 @@ static int tls_plainstream_deliver ( struct tls_connection *tls, return rc; } +/** + * Report job progress + * + * @v tls TLS connection + * @v progress Progress report to fill in + * @ret ongoing_rc Ongoing job status code (if known) + */ +static int tls_progress ( struct tls_connection *tls, + struct job_progress *progress ) { + + /* Return cipherstream or validator progress as applicable */ + if ( tls_ready ( tls ) ) { + return job_progress ( &tls->cipherstream, progress ); + } else { + return job_progress ( &tls->validator, progress ); + } +} + /** TLS plaintext stream interface operations */ static struct interface_operation tls_plainstream_ops[] = { INTF_OP ( xfer_deliver, struct tls_connection *, tls_plainstream_deliver ), INTF_OP ( xfer_window, struct tls_connection *, tls_plainstream_window ), + INTF_OP ( job_progress, struct tls_connection *, tls_progress ), INTF_OP ( intf_close, struct tls_connection *, tls_close ), }; diff --git a/src/net/validator.c b/src/net/validator.c index 25d81bd24..f6b03ff41 100644 --- a/src/net/validator.c +++ b/src/net/validator.c @@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -49,6 +50,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +struct validator; + +/** A certificate validator action */ +struct validator_action { + /** Name */ + const char *name; + /** Action to take upon completed transfer */ + int ( * done ) ( struct validator *validator, const void *data, + size_t len ); +}; + /** A certificate validator */ struct validator { /** Reference count */ @@ -67,9 +79,16 @@ struct validator { struct ocsp_check *ocsp; /** Data buffer */ struct xfer_buffer buffer; - /** Action to take upon completed transfer */ - int ( * done ) ( struct validator *validator, const void *data, - size_t len ); + + /** Current action */ + const struct validator_action *action; + /** Current certificate + * + * This will always be present within the certificate chain + * and so this pointer does not hold a reference to the + * certificate. + */ + struct x509_certificate *cert; }; /** @@ -123,8 +142,29 @@ static void validator_finished ( struct validator *validator, int rc ) { * */ +/** + * Report job progress + * + * @v validator Certificate validator + * @v progress Progress report to fill in + * @ret ongoing_rc Ongoing job status code (if known) + */ +static int validator_progress ( struct validator *validator, + struct job_progress *progress ) { + + /* Report current action, if applicable */ + if ( validator->action ) { + snprintf ( progress->message, sizeof ( progress->message ), + "%s %s", validator->action->name, + x509_name ( validator->cert ) ); + } + + return 0; +} + /** Certificate validator job control interface operations */ static struct interface_operation validator_job_operations[] = { + INTF_OP ( job_progress, struct validator *, validator_progress ), INTF_OP ( intf_close, struct validator *, validator_finished ), }; @@ -236,6 +276,12 @@ static int validator_append ( struct validator *validator, return rc; } +/** Cross-signing certificate download validator action */ +static const struct validator_action validator_crosscert = { + .name = "XCRT", + .done = validator_append, +}; + /** * Start download of cross-signing certificate * @@ -285,7 +331,8 @@ static int validator_start_download ( struct validator *validator, x509_name ( cert ), uri_string ); /* Set completion handler */ - validator->done = validator_append; + validator->action = &validator_crosscert; + validator->cert = cert; /* Open URI */ if ( ( rc = xfer_open_uri_string ( &validator->xfer, @@ -350,6 +397,12 @@ static int validator_ocsp_validate ( struct validator *validator, return 0; } +/** OCSP validator action */ +static const struct validator_action validator_ocsp = { + .name = "OCSP", + .done = validator_ocsp_validate, +}; + /** * Start OCSP check * @@ -374,7 +427,8 @@ static int validator_start_ocsp ( struct validator *validator, } /* Set completion handler */ - validator->done = validator_ocsp_validate; + validator->action = &validator_ocsp; + validator->cert = cert; /* Open URI */ uri_string = validator->ocsp->uri_string; @@ -421,9 +475,9 @@ static void validator_xfer_close ( struct validator *validator, int rc ) { validator, validator_name ( validator ) ); /* Process completed download */ - assert ( validator->done != NULL ); - if ( ( rc = validator->done ( validator, validator->buffer.data, - validator->buffer.len ) ) != 0 ) + assert ( validator->action != NULL ); + if ( ( rc = validator->action->done ( validator, validator->buffer.data, + validator->buffer.len ) ) != 0 ) goto err_append; /* Free downloaded data */ -- cgit v1.2.3-55-g7522 From 7b63c1275f33e0fa20c0e59dcc1756899533823c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 10 Mar 2019 17:27:33 +0000 Subject: [tls] Display validator messages only while validation is in progress Allow the cipherstream to report progress status messages during connection establishment. Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 2 ++ src/net/tls.c | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 4bffde7c2..febbdc589 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -335,6 +335,8 @@ struct tls_connection { struct pending_operation client_negotiation; /** Server security negotiation pending operation */ struct pending_operation server_negotiation; + /** Certificate validation pending operation */ + struct pending_operation validation; /** TX sequence number */ uint64_t tx_seq; diff --git a/src/net/tls.c b/src/net/tls.c index 510bef8c4..746274d61 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -382,6 +382,7 @@ static void tls_close ( struct tls_connection *tls, int rc ) { /* Remove pending operations, if applicable */ pending_put ( &tls->client_negotiation ); pending_put ( &tls->server_negotiation ); + pending_put ( &tls->validation ); /* Remove process */ process_del ( &tls->process ); @@ -950,6 +951,7 @@ static void tls_restart ( struct tls_connection *tls ) { assert ( ! tls->tx_pending ); assert ( ! is_pending ( &tls->client_negotiation ) ); assert ( ! is_pending ( &tls->server_negotiation ) ); + assert ( ! is_pending ( &tls->validation ) ); /* (Re)initialise handshake context */ digest_init ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx ); @@ -1875,6 +1877,7 @@ static int tls_new_server_hello_done ( struct tls_connection *tls, "%s\n", tls, strerror ( rc ) ); return rc; } + pending_get ( &tls->validation ); return 0; } @@ -2582,10 +2585,10 @@ static int tls_progress ( struct tls_connection *tls, struct job_progress *progress ) { /* Return cipherstream or validator progress as applicable */ - if ( tls_ready ( tls ) ) { - return job_progress ( &tls->cipherstream, progress ); - } else { + if ( is_pending ( &tls->validation ) ) { return job_progress ( &tls->validator, progress ); + } else { + return job_progress ( &tls->cipherstream, progress ); } } @@ -2820,6 +2823,9 @@ static void tls_validator_done ( struct tls_connection *tls, int rc ) { struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; struct x509_certificate *cert; + /* Mark validation as complete */ + pending_put ( &tls->validation ); + /* Close validator interface */ intf_restart ( &tls->validator, rc ); -- cgit v1.2.3-55-g7522 From f6b2bf9507599709d30bcb74af9bffdb179e5338 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 10 Mar 2019 17:29:06 +0000 Subject: [tcp] Display "connecting" status until connection is established Provide increased visibility into the progress of TCP connections by displaying an explicit "connecting" status message while waiting for the TCP handshake to complete. Signed-off-by: Michael Brown --- src/net/tcp.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/net/tcp.c b/src/net/tcp.c index c445100ad..6bba44282 100644 --- a/src/net/tcp.c +++ b/src/net/tcp.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -1704,10 +1705,30 @@ static int tcp_xfer_deliver ( struct tcp_connection *tcp, return 0; } +/** + * Report job progress + * + * @v tcp TCP connection + * @v progress Progress report to fill in + * @ret ongoing_rc Ongoing job status code (if known) + */ +static int tcp_progress ( struct tcp_connection *tcp, + struct job_progress *progress ) { + + /* Report connection in progress if applicable */ + if ( ! TCP_HAS_BEEN_ESTABLISHED ( tcp->tcp_state ) ) { + snprintf ( progress->message, sizeof ( progress->message ), + "connecting" ); + } + + return 0; +} + /** TCP data transfer interface operations */ static struct interface_operation tcp_xfer_operations[] = { INTF_OP ( xfer_deliver, struct tcp_connection *, tcp_xfer_deliver ), INTF_OP ( xfer_window, struct tcp_connection *, tcp_xfer_window ), + INTF_OP ( job_progress, struct tcp_connection *, tcp_progress ), INTF_OP ( intf_close, struct tcp_connection *, tcp_xfer_close ), }; -- cgit v1.2.3-55-g7522 From b6ffe28a21c53a0946d95751c905d9e0b6c3b630 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 10 Mar 2019 17:58:56 +0000 Subject: [ocsp] Accept response certID with missing hashAlgorithm parameters One of the design goals of ASN.1 DER is to provide a canonical serialization of a data structure, thereby allowing for equality of values to be tested by simply comparing the serialized bytes. Some OCSP servers will modify the request certID to omit the optional (and null) "parameters" portion of the hashAlgorithm. This is arguably legal but breaks the ability to perform a straightforward bitwise comparison on the entire certID field between request and response. Fix by comparing the OID-identified hashAlgorithm separately from the remaining certID fields. Originally-fixed-by: Thilo Fromm Signed-off-by: Michael Brown --- src/crypto/ocsp.c | 42 ++++++++++++++++++++++++++++++------------ src/include/ipxe/ocsp.h | 4 ++-- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/crypto/ocsp.c b/src/crypto/ocsp.c index b83f4c035..2c747fb39 100644 --- a/src/crypto/ocsp.c +++ b/src/crypto/ocsp.c @@ -145,7 +145,7 @@ static void ocsp_free ( struct refcnt *refcnt ) { static int ocsp_request ( struct ocsp_check *ocsp ) { struct digest_algorithm *digest = &ocsp_digest_algorithm; struct asn1_builder *builder = &ocsp->request.builder; - struct asn1_cursor *cert_id = &ocsp->request.cert_id; + struct asn1_cursor *cert_id_tail = &ocsp->request.cert_id_tail; uint8_t digest_ctx[digest->ctxsize]; uint8_t name_digest[digest->digestsize]; uint8_t pubkey_digest[digest->digestsize]; @@ -186,12 +186,14 @@ static int ocsp_request ( struct ocsp_check *ocsp ) { DBGC2_HDA ( ocsp, 0, builder->data, builder->len ); /* Parse certificate ID for comparison with response */ - cert_id->data = builder->data; - cert_id->len = builder->len; - if ( ( rc = ( asn1_enter ( cert_id, ASN1_SEQUENCE ), - asn1_enter ( cert_id, ASN1_SEQUENCE ), - asn1_enter ( cert_id, ASN1_SEQUENCE ), - asn1_enter ( cert_id, ASN1_SEQUENCE ) ) ) != 0 ) { + cert_id_tail->data = builder->data; + cert_id_tail->len = builder->len; + if ( ( rc = ( asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), + asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), + asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), + asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), + asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), + asn1_skip ( cert_id_tail, ASN1_SEQUENCE ) ) ) != 0 ) { DBGC ( ocsp, "OCSP %p \"%s\" could not locate certID: %s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ) ); return rc; @@ -475,15 +477,31 @@ static int ocsp_parse_responder_id ( struct ocsp_check *ocsp, static int ocsp_parse_cert_id ( struct ocsp_check *ocsp, const struct asn1_cursor *raw ) { struct asn1_cursor cursor; + struct asn1_algorithm *algorithm; + int rc; - /* Check certID matches request */ + /* Check certID algorithm */ memcpy ( &cursor, raw, sizeof ( cursor ) ); - asn1_shrink_any ( &cursor ); - if ( asn1_compare ( &cursor, &ocsp->request.cert_id ) != 0 ) { + asn1_enter ( &cursor, ASN1_SEQUENCE ); + if ( ( rc = asn1_digest_algorithm ( &cursor, &algorithm ) ) != 0 ) { + DBGC ( ocsp, "OCSP %p \"%s\" certID unknown algorithm: %s\n", + ocsp, x509_name ( ocsp->cert ), strerror ( rc ) ); + return rc; + } + if ( algorithm->digest != &ocsp_digest_algorithm ) { + DBGC ( ocsp, "OCSP %p \"%s\" certID wrong algorithm %s\n", + ocsp, x509_name ( ocsp->cert ), + algorithm->digest->name ); + return -EACCES_CERT_MISMATCH; + } + + /* Check remaining certID fields */ + asn1_skip ( &cursor, ASN1_SEQUENCE ); + if ( asn1_compare ( &cursor, &ocsp->request.cert_id_tail ) != 0 ) { DBGC ( ocsp, "OCSP %p \"%s\" certID mismatch:\n", ocsp, x509_name ( ocsp->cert ) ); - DBGC_HDA ( ocsp, 0, ocsp->request.cert_id.data, - ocsp->request.cert_id.len ); + DBGC_HDA ( ocsp, 0, ocsp->request.cert_id_tail.data, + ocsp->request.cert_id_tail.len ); DBGC_HDA ( ocsp, 0, cursor.data, cursor.len ); return -EACCES_CERT_MISMATCH; } diff --git a/src/include/ipxe/ocsp.h b/src/include/ipxe/ocsp.h index be0bddc50..9eb70b2cc 100644 --- a/src/include/ipxe/ocsp.h +++ b/src/include/ipxe/ocsp.h @@ -42,8 +42,8 @@ struct ocsp_check; struct ocsp_request { /** Request builder */ struct asn1_builder builder; - /** Certificate ID */ - struct asn1_cursor cert_id; + /** Certificate ID (excluding hashAlgorithm) */ + struct asn1_cursor cert_id_tail; }; /** An OCSP responder */ -- cgit v1.2.3-55-g7522 From ebf2eaf515e46abd43bc798e7e4ba77bfe529218 Mon Sep 17 00:00:00 2001 From: Christian Nilsson Date: Thu, 14 Feb 2019 22:21:55 +0100 Subject: [intel] Add PCI ID for I219-V and -LM 6 to 9 Signed-off-by: Christian Nilsson Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index a2e6d535b..bb0b673b9 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1146,9 +1146,17 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x15b7, "i219lm-2", "I219-LM (2)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15b8, "i219v-2", "I219-V (2)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15b9, "i219lm-3", "I219-LM (3)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15bb, "i219lm-7", "I219-LM (7)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15bc, "i219v-7", "I219-V (7)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15bd, "i219lm-6", "I219-LM (6)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15be, "i219v-6", "I219-V (6)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15d6, "i219v-5", "I219-V (5)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15d7, "i219lm-4", "I219-LM (4)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15d8, "i219v-4", "I219-V (4)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15df, "i219lm-8", "I219-LM (8)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15e0, "i219v-8", "I219-V (8)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15e1, "i219lm-9", "I219-LM (9)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15e2, "i219v-9", "I219-V (9)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15e3, "i219lm-5", "I219-LM (5)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x1f41, "i354", "I354", INTEL_NO_ASDE ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), -- cgit v1.2.3-55-g7522