diff options
| author | Simon Rettberg | 2019-03-28 15:47:25 +0100 |
|---|---|---|
| committer | Simon Rettberg | 2019-03-28 15:47:25 +0100 |
| commit | eee6810fbe26652d4cacdf14a19c1c421eea953c (patch) | |
| tree | aad4a6b01220d33c804a28549efae1e39cf185ca /src | |
| parent | [efi] Add very ugly hack to use HW RNG on EFI (diff) | |
| parent | [intel] Add PCI ID for I219-V and -LM 6 to 9 (diff) | |
| download | ipxe-eee6810fbe26652d4cacdf14a19c1c421eea953c.tar.gz ipxe-eee6810fbe26652d4cacdf14a19c1c421eea953c.tar.xz ipxe-eee6810fbe26652d4cacdf14a19c1c421eea953c.zip | |
Merge branch 'master' into openslx
Diffstat (limited to 'src')
| -rw-r--r-- | src/crypto/ocsp.c | 42 | ||||
| -rw-r--r-- | src/drivers/net/intel.c | 8 | ||||
| -rw-r--r-- | src/include/ipxe/efi/efi_blacklist.h | 13 | ||||
| -rw-r--r-- | src/include/ipxe/errfile.h | 1 | ||||
| -rw-r--r-- | src/include/ipxe/ocsp.h | 4 | ||||
| -rw-r--r-- | src/include/ipxe/tls.h | 45 | ||||
| -rw-r--r-- | src/interface/efi/efi_blacklist.c | 237 | ||||
| -rw-r--r-- | src/interface/efi/efiprefix.c | 6 | ||||
| -rw-r--r-- | src/net/tcp.c | 21 | ||||
| -rw-r--r-- | src/net/tls.c | 318 | ||||
| -rw-r--r-- | src/net/validator.c | 175 |
11 files changed, 800 insertions, 70 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/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 ), 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/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 */ diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index b1e702e18..febbdc589 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 @@ -242,13 +246,48 @@ 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; + /** Session ticket */ + void *ticket; + /** Length of session ticket */ + size_t ticket_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; + /** New session ticket */ + void *new_session_ticket; + /** Length of new session ticket */ + size_t new_session_ticket_len; + /** Plaintext stream */ struct interface plainstream; /** Ciphertext stream */ @@ -296,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/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 <mbrown@fensystems.co.uk>. + * + * 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 <stddef.h> +#include <string.h> +#include <errno.h> +#include <ipxe/settings.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/Protocol/DriverBinding.h> +#include <ipxe/efi/Protocol/LoadedImage.h> +#include <ipxe/efi/Protocol/ComponentName.h> +#include <ipxe/efi/efi_blacklist.h> + +/** @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 <ipxe/efi/efi_snp.h> #include <ipxe/efi/efi_autoboot.h> #include <ipxe/efi/efi_watchdog.h> +#include <ipxe/efi/efi_blacklist.h> /** * 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(); } 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 <ipxe/netdevice.h> #include <ipxe/profile.h> #include <ipxe/process.h> +#include <ipxe/job.h> #include <ipxe/tcpip.h> #include <ipxe/tcp.h> @@ -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 ), }; diff --git a/src/net/tls.c b/src/net/tls.c index 9d994cd77..746274d61 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -47,6 +47,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/certstore.h> #include <ipxe/rbg.h> #include <ipxe/validator.h> +#include <ipxe/job.h> #include <ipxe/tls.h> /* Disambiguate the various error causes */ @@ -102,9 +103,13 @@ 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_EINVAL, 0x01, \ + __einfo_uniqify ( EINFO_EIO, 0x01, \ "Unknown alert level" ) #define ENOMEM_CONTEXT __einfo_error ( EINFO_ENOMEM_CONTEXT ) #define EINFO_ENOMEM_CONTEXT \ @@ -175,6 +180,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, @@ -308,6 +317,28 @@ 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 ticket */ + free ( session->ticket ); + + /* Free session */ + free ( session ); +} + +/** * Free TLS connection * * @v refcnt Reference counter @@ -315,10 +346,12 @@ 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; /* 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 ); @@ -330,8 +363,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 ); } /** @@ -345,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 ); @@ -353,6 +391,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 ); } /****************************************************************************** @@ -906,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 ); @@ -929,6 +975,18 @@ static void tls_tx_resume ( struct tls_connection *tls ) { } /** + * 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 * * @v tls TLS connection @@ -953,11 +1011,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[tls->session_id_len]; uint16_t cipher_suite_len; uint16_t cipher_suites[TLS_NUM_CIPHER_SUITES]; uint8_t compression_methods_len; @@ -971,7 +1032,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; @@ -993,18 +1054,27 @@ 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; + /* 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 +1088,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 ); @@ -1042,6 +1112,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 ) ); } @@ -1513,8 +1588,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; @@ -1546,6 +1647,57 @@ static int tls_new_server_hello ( struct tls_connection *tls, } /** + * 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 * * @v tls TLS connection @@ -1725,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; } @@ -1739,6 +1892,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 +1921,39 @@ 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, 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 ) ); + } + 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 + * 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 ); @@ -1822,6 +2009,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; @@ -2383,12 +2574,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 ( is_pending ( &tls->validation ) ) { + return job_progress ( &tls->validator, progress ); + } else { + return job_progress ( &tls->cipherstream, 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 ), }; @@ -2608,10 +2818,14 @@ 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; + /* Mark validation as complete */ + pending_put ( &tls->validation ); + /* Close validator interface */ intf_restart ( &tls->validator, rc ); @@ -2628,9 +2842,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 +2896,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 +2906,32 @@ static void tls_tx_step ( struct tls_connection *tls ) { /* Send first pending transmission */ if ( tls->tx_pending & TLS_TX_CLIENT_HELLO ) { + /* Serialise server negotiations within a session, to + * provide a consistent view of session IDs and + * session tickets. + */ + 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 ) { DBGC ( tls, "TLS %p could not send Client Hello: %s\n", @@ -2768,6 +3010,60 @@ static struct process_descriptor tls_process_desc = /****************************************************************************** * + * 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 +3082,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 +3105,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 +3118,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: diff --git a/src/net/validator.c b/src/net/validator.c index 40f778c7d..f6b03ff41 100644 --- a/src/net/validator.c +++ b/src/net/validator.c @@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/base64.h> #include <ipxe/crc32.h> #include <ipxe/ocsp.h> +#include <ipxe/job.h> #include <ipxe/validator.h> #include <config/crypto.h> @@ -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,12 +79,31 @@ 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; }; /** + * 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 * * @v refcnt Reference count @@ -81,7 +112,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 ); @@ -110,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 ), }; @@ -165,8 +218,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 +230,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 +248,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; } @@ -219,15 +276,22 @@ 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 * * @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,17 +325,21 @@ 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; + validator->action = &validator_crosscert; + validator->cert = cert; /* 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 +375,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; } @@ -327,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 * @@ -344,22 +420,27 @@ 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; } /* Set completion handler */ - validator->done = validator_ocsp_validate; + validator->action = &validator_ocsp; + validator->cert = cert; /* 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,16 +466,18 @@ 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 ); - 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 */ @@ -426,8 +509,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 +555,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 +600,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 +652,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 ); |
