diff options
Diffstat (limited to 'src/net')
-rw-r--r-- | src/net/80211/wpa.c | 11 | ||||
-rw-r--r-- | src/net/aoe.c | 2 | ||||
-rw-r--r-- | src/net/eap.c | 210 | ||||
-rw-r--r-- | src/net/eap_md5.c | 116 | ||||
-rw-r--r-- | src/net/eap_mschapv2.c | 251 | ||||
-rw-r--r-- | src/net/eapol.c | 204 | ||||
-rw-r--r-- | src/net/fcoe.c | 88 | ||||
-rw-r--r-- | src/net/infiniband/xsigo.c | 4 | ||||
-rw-r--r-- | src/net/ipv6.c | 39 | ||||
-rw-r--r-- | src/net/lldp.c | 95 | ||||
-rw-r--r-- | src/net/neighbour.c | 3 | ||||
-rw-r--r-- | src/net/netdevice.c | 77 | ||||
-rw-r--r-- | src/net/tls.c | 502 | ||||
-rw-r--r-- | src/net/udp/dhcp.c | 7 | ||||
-rw-r--r-- | src/net/udp/ntp.c | 10 | ||||
-rw-r--r-- | src/net/validator.c | 327 | ||||
-rw-r--r-- | src/net/vlan.c | 9 |
17 files changed, 1504 insertions, 451 deletions
diff --git a/src/net/80211/wpa.c b/src/net/80211/wpa.c index 1484d0e8..17c11b8e 100644 --- a/src/net/80211/wpa.c +++ b/src/net/80211/wpa.c @@ -761,13 +761,14 @@ static int wpa_handle_1_of_2 ( struct wpa_common_ctx *ctx, /** * Handle receipt of EAPOL-Key frame for WPA * - * @v iob I/O buffer - * @v netdev Network device - * @v ll_source Source link-layer address + * @v supplicant EAPoL supplicant + * @v iob I/O buffer + * @v ll_source Source link-layer address */ -static int eapol_key_rx ( struct io_buffer *iob, struct net_device *netdev, - const void *ll_source ) +static int eapol_key_rx ( struct eapol_supplicant *supplicant, + struct io_buffer *iob, const void *ll_source ) { + struct net_device *netdev = supplicant->eap.netdev; struct net80211_device *dev = net80211_get ( netdev ); struct eapol_header *eapol; struct eapol_key_pkt *pkt; diff --git a/src/net/aoe.c b/src/net/aoe.c index e785e897..dba4f51b 100644 --- a/src/net/aoe.c +++ b/src/net/aoe.c @@ -374,7 +374,7 @@ static void aoecmd_ata_cmd ( struct aoe_command *aoecmd, struct aoeata *aoeata = &aoehdr->payload[0].ata; /* Sanity check */ - linker_assert ( AOE_FL_DEV_HEAD == ATA_DEV_SLAVE, __fix_ata_h__ ); + static_assert ( AOE_FL_DEV_HEAD == ATA_DEV_SLAVE ); assert ( len == ( sizeof ( *aoehdr ) + sizeof ( *aoeata ) + command->data_out_len ) ); diff --git a/src/net/eap.c b/src/net/eap.c index 8d1d540f..87327d72 100644 --- a/src/net/eap.c +++ b/src/net/eap.c @@ -23,7 +23,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include <stdlib.h> #include <errno.h> +#include <string.h> +#include <byteswap.h> #include <ipxe/netdevice.h> #include <ipxe/eap.h> @@ -34,58 +37,196 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ /** + * Transmit EAP response + * + * @v supplicant EAP supplicant + * @v rsp Response type data + * @v rsp_len Length of response type data + * @ret rc Return status code + */ +int eap_tx_response ( struct eap_supplicant *supplicant, + const void *rsp, size_t rsp_len ) { + struct net_device *netdev = supplicant->netdev; + struct eap_message *msg; + size_t len; + int rc; + + /* Allocate and populate response */ + len = ( sizeof ( *msg ) + rsp_len ); + msg = malloc ( len ); + if ( ! msg ) { + rc = -ENOMEM; + goto err_alloc; + } + msg->hdr.code = EAP_CODE_RESPONSE; + msg->hdr.id = supplicant->id; + msg->hdr.len = htons ( len ); + msg->type = supplicant->type; + memcpy ( msg->data, rsp, rsp_len ); + DBGC ( netdev, "EAP %s Response id %#02x type %d\n", + netdev->name, msg->hdr.id, msg->type ); + + /* Transmit response */ + if ( ( rc = supplicant->tx ( supplicant, msg, len ) ) != 0 ) { + DBGC ( netdev, "EAP %s could not transmit: %s\n", + netdev->name, strerror ( rc ) ); + goto err_tx; + } + + err_tx: + free ( msg ); + err_alloc: + return rc; +} + +/** + * Transmit EAP NAK + * + * @v supplicant EAP supplicant + * @ret rc Return status code + */ +static int eap_tx_nak ( struct eap_supplicant *supplicant ) { + struct net_device *netdev = supplicant->netdev; + unsigned int max = table_num_entries ( EAP_METHODS ); + uint8_t methods[ max + 1 /* potential EAP_TYPE_NONE */ ]; + unsigned int count = 0; + struct eap_method *method; + + /* Populate methods list */ + DBGC ( netdev, "EAP %s Nak offering types {", netdev->name ); + for_each_table_entry ( method, EAP_METHODS ) { + if ( method->type > EAP_TYPE_NAK ) { + DBGC ( netdev, "%s%d", + ( count ? ", " : "" ), method->type ); + methods[count++] = method->type; + } + } + if ( ! count ) + methods[count++] = EAP_TYPE_NONE; + DBGC ( netdev, "}\n" ); + assert ( count <= max ); + + /* Transmit response */ + supplicant->type = EAP_TYPE_NAK; + return eap_tx_response ( supplicant, methods, count ); +} + +/** * Handle EAP Request-Identity * - * @v netdev Network device + * @v supplicant EAP supplicant + * @v req Request type data + * @v req_len Length of request type data * @ret rc Return status code */ -static int eap_rx_request_identity ( struct net_device *netdev ) { +static int eap_rx_identity ( struct eap_supplicant *supplicant, + const void *req, size_t req_len ) { + struct net_device *netdev = supplicant->netdev; + void *rsp; + int rsp_len; + int rc; /* Treat Request-Identity as blocking the link */ DBGC ( netdev, "EAP %s Request-Identity blocking link\n", netdev->name ); + DBGC_HDA ( netdev, 0, req, req_len ); netdev_link_block ( netdev, EAP_BLOCK_TIMEOUT ); - return 0; + /* Mark EAP as in progress */ + supplicant->flags |= EAP_FL_ONGOING; + + /* Construct response, if applicable */ + rsp_len = fetch_raw_setting_copy ( netdev_settings ( netdev ), + &username_setting, &rsp ); + if ( rsp_len < 0 ) { + /* We have no identity to offer, so wait until the + * switch times out and switches to MAC Authentication + * Bypass (MAB). + */ + DBGC2 ( netdev, "EAP %s has no identity\n", netdev->name ); + supplicant->flags |= EAP_FL_PASSIVE; + rc = 0; + goto no_response; + } + + /* Transmit response */ + if ( ( rc = eap_tx_response ( supplicant, rsp, rsp_len ) ) != 0 ) + goto err_tx; + + err_tx: + free ( rsp ); + no_response: + return rc; } +/** EAP Request-Identity method */ +struct eap_method eap_identity_method __eap_method = { + .type = EAP_TYPE_IDENTITY, + .rx = eap_rx_identity, +}; + /** * Handle EAP Request * - * @v netdev Network device - * @v req EAP request + * @v supplicant EAP supplicant + * @v msg EAP request * @v len Length of EAP request * @ret rc Return status code */ -static int eap_rx_request ( struct net_device *netdev, - const struct eap_request *req, size_t len ) { +static int eap_rx_request ( struct eap_supplicant *supplicant, + const struct eap_message *msg, size_t len ) { + struct net_device *netdev = supplicant->netdev; + struct eap_method *method; + const void *req; + size_t req_len; - /* Sanity check */ - if ( len < sizeof ( *req ) ) { + /* Sanity checks */ + if ( len < sizeof ( *msg ) ) { DBGC ( netdev, "EAP %s underlength request:\n", netdev->name ); - DBGC_HDA ( netdev, 0, req, len ); + DBGC_HDA ( netdev, 0, msg, len ); + return -EINVAL; + } + if ( len < ntohs ( msg->hdr.len ) ) { + DBGC ( netdev, "EAP %s truncated request:\n", netdev->name ); + DBGC_HDA ( netdev, 0, msg, len ); return -EINVAL; } + req = msg->data; + req_len = ( ntohs ( msg->hdr.len ) - sizeof ( *msg ) ); + + /* Record request details */ + supplicant->id = msg->hdr.id; + supplicant->type = msg->type; + DBGC ( netdev, "EAP %s Request id %#02x type %d\n", + netdev->name, msg->hdr.id, msg->type ); /* Handle according to type */ - switch ( req->type ) { - case EAP_TYPE_IDENTITY: - return eap_rx_request_identity ( netdev ); - default: - DBGC ( netdev, "EAP %s requested type %d unknown:\n", - netdev->name, req->type ); - DBGC_HDA ( netdev, 0, req, len ); - return -ENOTSUP; + for_each_table_entry ( method, EAP_METHODS ) { + if ( msg->type == method->type ) + return method->rx ( supplicant, req, req_len ); } + DBGC ( netdev, "EAP %s requested type %d unknown:\n", + netdev->name, msg->type ); + DBGC_HDA ( netdev, 0, msg, len ); + + /* Send NAK if applicable */ + if ( msg->type > EAP_TYPE_NAK ) + return eap_tx_nak ( supplicant ); + + return -ENOTSUP; } /** * Handle EAP Success * - * @v netdev Network device + * @v supplicant EAP supplicant * @ret rc Return status code */ -static int eap_rx_success ( struct net_device *netdev ) { +static int eap_rx_success ( struct eap_supplicant *supplicant ) { + struct net_device *netdev = supplicant->netdev; + + /* Mark authentication as complete */ + supplicant->flags = EAP_FL_PASSIVE; /* Mark link as unblocked */ DBGC ( netdev, "EAP %s Success\n", netdev->name ); @@ -97,10 +238,14 @@ static int eap_rx_success ( struct net_device *netdev ) { /** * Handle EAP Failure * - * @v netdev Network device + * @v supplicant EAP supplicant * @ret rc Return status code */ -static int eap_rx_failure ( struct net_device *netdev ) { +static int eap_rx_failure ( struct eap_supplicant *supplicant ) { + struct net_device *netdev = supplicant->netdev; + + /* Mark authentication as complete */ + supplicant->flags = EAP_FL_PASSIVE; /* Record error */ DBGC ( netdev, "EAP %s Failure\n", netdev->name ); @@ -110,12 +255,14 @@ static int eap_rx_failure ( struct net_device *netdev ) { /** * Handle EAP packet * - * @v netdev Network device + * @v supplicant EAP supplicant * @v data EAP packet * @v len Length of EAP packet * @ret rc Return status code */ -int eap_rx ( struct net_device *netdev, const void *data, size_t len ) { +int eap_rx ( struct eap_supplicant *supplicant, const void *data, + size_t len ) { + struct net_device *netdev = supplicant->netdev; const union eap_packet *eap = data; /* Sanity check */ @@ -128,11 +275,14 @@ int eap_rx ( struct net_device *netdev, const void *data, size_t len ) { /* Handle according to code */ switch ( eap->hdr.code ) { case EAP_CODE_REQUEST: - return eap_rx_request ( netdev, &eap->req, len ); + return eap_rx_request ( supplicant, &eap->msg, len ); + case EAP_CODE_RESPONSE: + DBGC2 ( netdev, "EAP %s ignoring response\n", netdev->name ); + return 0; case EAP_CODE_SUCCESS: - return eap_rx_success ( netdev ); + return eap_rx_success ( supplicant ); case EAP_CODE_FAILURE: - return eap_rx_failure ( netdev ); + return eap_rx_failure ( supplicant ); default: DBGC ( netdev, "EAP %s unsupported code %d\n", netdev->name, eap->hdr.code ); @@ -140,3 +290,9 @@ int eap_rx ( struct net_device *netdev, const void *data, size_t len ) { return -ENOTSUP; } } + +/* Drag in objects via eap_rx() */ +REQUIRING_SYMBOL ( eap_rx ); + +/* Drag in EAP configuration */ +REQUIRE_OBJECT ( config_eap ); diff --git a/src/net/eap_md5.c b/src/net/eap_md5.c new file mode 100644 index 00000000..0664174f --- /dev/null +++ b/src/net/eap_md5.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2024 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ipxe/md5.h> +#include <ipxe/chap.h> +#include <ipxe/eap.h> + +/** @file + * + * EAP MD5-Challenge authentication method + * + */ + +/** + * Handle EAP MD5-Challenge + * + * @v supplicant EAP supplicant + * @v req Request type data + * @v req_len Length of request type data + * @ret rc Return status code + */ +static int eap_rx_md5 ( struct eap_supplicant *supplicant, + const void *req, size_t req_len ) { + struct net_device *netdev = supplicant->netdev; + const struct eap_md5 *md5req = req; + struct { + uint8_t len; + uint8_t value[MD5_DIGEST_SIZE]; + } __attribute__ (( packed )) md5rsp; + struct chap_response chap; + void *secret; + int secret_len; + int rc; + + /* Sanity checks */ + if ( req_len < sizeof ( *md5req ) ) { + DBGC ( netdev, "EAP %s underlength MD5-Challenge:\n", + netdev->name ); + DBGC_HDA ( netdev, 0, req, req_len ); + rc = -EINVAL; + goto err_sanity; + } + if ( ( req_len - sizeof ( *md5req ) ) < md5req->len ) { + DBGC ( netdev, "EAP %s truncated MD5-Challenge:\n", + netdev->name ); + DBGC_HDA ( netdev, 0, req, req_len ); + rc = -EINVAL; + goto err_sanity; + } + + /* Construct response */ + if ( ( rc = chap_init ( &chap, &md5_algorithm ) ) != 0 ) { + DBGC ( netdev, "EAP %s could not initialise CHAP: %s\n", + netdev->name, strerror ( rc ) ); + goto err_chap; + } + chap_set_identifier ( &chap, supplicant->id ); + secret_len = fetch_raw_setting_copy ( netdev_settings ( netdev ), + &password_setting, &secret ); + if ( secret_len < 0 ) { + rc = secret_len; + DBGC ( netdev, "EAP %s has no secret: %s\n", + netdev->name, strerror ( rc ) ); + goto err_secret; + } + chap_update ( &chap, secret, secret_len ); + chap_update ( &chap, md5req->value, md5req->len ); + chap_respond ( &chap ); + assert ( chap.response_len == sizeof ( md5rsp.value ) ); + md5rsp.len = sizeof ( md5rsp.value ); + memcpy ( md5rsp.value, chap.response, sizeof ( md5rsp.value ) ); + + /* Transmit response */ + if ( ( rc = eap_tx_response ( supplicant, &md5rsp, + sizeof ( md5rsp ) ) ) != 0 ) + goto err_tx; + + err_tx: + free ( secret ); + err_secret: + chap_finish ( &chap ); + err_chap: + err_sanity: + return rc; +} + +/** EAP MD5-Challenge method */ +struct eap_method eap_md5_method __eap_method = { + .type = EAP_TYPE_MD5, + .rx = eap_rx_md5, +}; diff --git a/src/net/eap_mschapv2.c b/src/net/eap_mschapv2.c new file mode 100644 index 00000000..0be62ed5 --- /dev/null +++ b/src/net/eap_mschapv2.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2024 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/mschapv2.h> +#include <ipxe/eap.h> + +/** @file + * + * EAP MS-CHAPv2 authentication method + * + * EAP-MSCHAPv2 was described in a draft RFC first published in 2002 + * (draft-kamath-pppext-eap-mschapv2-02.txt). The draft eventually + * expired in 2007 without becoming an official RFC, quite possibly + * because the protocol design was too ugly to be called an IETF + * standard. It is, however, fairly widely used. + */ + +/** An EAP MS-CHAPv2 request message */ +struct eap_mschapv2_request { + /** EAP-MSCHAPv2 header */ + struct eap_mschapv2 hdr; + /** MS-CHAPv2 challenge length (fixed value) */ + uint8_t len; + /** MS-CHAPv2 challenge */ + struct mschapv2_challenge msg; +} __attribute__ (( packed )); + +/** An EAP MS-CHAPv2 response message */ +struct eap_mschapv2_response { + /** EAP-MSCHAPv2 header */ + struct eap_mschapv2 hdr; + /** MS-CHAPv2 response length (fixed value) */ + uint8_t len; + /** MS-CHAPv2 response */ + struct mschapv2_response msg; + /** User name */ + char name[0]; +} __attribute__ (( packed )); + +/** An EAP MS-CHAPv2 success request message */ +struct eap_mschapv2_success_request { + /** EAP-MSCHAPv2 header */ + struct eap_mschapv2 hdr; + /** Message */ + char message[0]; +} __attribute__ (( packed )); + +/** An EAP MS-CHAPv2 success response message */ +struct eap_mschapv2_success_response { + /** Opcode */ + uint8_t code; +} __attribute__ (( packed )); + +/** + * Handle EAP MS-CHAPv2 request + * + * @v supplicant EAP supplicant + * @v hdr EAP-MSCHAPv2 header + * @v len Message length + * @ret rc Return status code + */ +static int eap_rx_mschapv2_request ( struct eap_supplicant *supplicant, + const struct eap_mschapv2 *hdr, + size_t len ) { + struct net_device *netdev = supplicant->netdev; + struct settings *settings = netdev_settings ( netdev ); + const struct eap_mschapv2_request *msreq = + container_of ( hdr, struct eap_mschapv2_request, hdr ); + struct eap_mschapv2_response *msrsp; + struct mschapv2_challenge peer; + char *username; + char *password; + int username_len; + int password_len; + size_t msrsp_len; + unsigned int i; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *msreq ) ) { + DBGC ( netdev, "EAP %s underlength MS-CHAPv2 request\n", + netdev->name ); + DBGC_HDA ( netdev, 0, hdr, len ); + rc = -EINVAL; + goto err_sanity; + } + + /* Fetch username and password */ + username_len = fetch_string_setting_copy ( settings, &username_setting, + &username ); + if ( username_len < 0 ) { + rc = username_len; + DBGC ( netdev, "EAP %s has no username: %s\n", + netdev->name, strerror ( rc ) ); + goto err_username; + } + password_len = fetch_string_setting_copy ( settings, &password_setting, + &password ); + if ( password_len < 0 ) { + rc = password_len; + DBGC ( netdev, "EAP %s has no password: %s\n", + netdev->name, strerror ( rc ) ); + goto err_password; + } + + /* Construct a peer challenge. We do not perform mutual + * authentication, so this does not need to be strong. + */ + for ( i = 0 ; i < ( sizeof ( peer.byte ) / + sizeof ( peer.byte[0] ) ) ; i++ ) { + peer.byte[i] = random(); + } + + /* Allocate response */ + msrsp_len = ( sizeof ( *msrsp ) + username_len ); + msrsp = malloc ( msrsp_len ); + if ( ! msrsp ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Construct response */ + msrsp->hdr.code = EAP_CODE_RESPONSE; + msrsp->hdr.id = msreq->hdr.id; + msrsp->hdr.len = htons ( msrsp_len ); + msrsp->len = sizeof ( msrsp->msg ); + mschapv2_response ( username, password, &msreq->msg, &peer, + &msrsp->msg ); + memcpy ( msrsp->name, username, username_len ); + + /* Send response */ + if ( ( rc = eap_tx_response ( supplicant, msrsp, msrsp_len ) ) != 0 ) + goto err_tx; + + err_tx: + free ( msrsp ); + err_alloc: + free ( password ); + err_password: + free ( username ); + err_username: + err_sanity: + return rc; +} + +/** + * Handle EAP MS-CHAPv2 success request + * + * @v supplicant EAP supplicant + * @v hdr EAP-MSCHAPv2 header + * @v len Message length + * @ret rc Return status code + */ +static int eap_rx_mschapv2_success ( struct eap_supplicant *supplicant, + const struct eap_mschapv2 *hdr, + size_t len ) { + const struct eap_mschapv2_success_request *msreq = + container_of ( hdr, struct eap_mschapv2_success_request, hdr ); + static const struct eap_mschapv2_success_response msrsp = { + .code = EAP_CODE_SUCCESS, + }; + + /* Sanity check */ + assert ( len >= sizeof ( *msreq ) ); + + /* The success request contains the MS-CHAPv2 authenticator + * response, which could potentially be used to verify that + * the EAP authenticator also knew the password (or, at least, + * the MD4 hash of the password). + * + * Our model for EAP does not encompass mutual authentication: + * we will starting sending plaintext packets (e.g. DHCP + * requests) over the link even before EAP completes, and our + * only use for an EAP success is to mark the link as + * unblocked. + * + * We therefore ignore the content of the success request and + * just send back a success response, so that the EAP + * authenticator will complete the process and send through + * the real EAP success packet (which will, in turn, cause us + * to unblock the link). + */ + return eap_tx_response ( supplicant, &msrsp, sizeof ( msrsp ) ); +} + +/** + * Handle EAP MS-CHAPv2 + * + * @v supplicant EAP supplicant + * @v req Request type data + * @v req_len Length of request type data + * @ret rc Return status code + */ +static int eap_rx_mschapv2 ( struct eap_supplicant *supplicant, + const void *req, size_t req_len ) { + struct net_device *netdev = supplicant->netdev; + const struct eap_mschapv2 *hdr = req; + + /* Sanity check */ + if ( req_len < sizeof ( *hdr ) ) { + DBGC ( netdev, "EAP %s underlength MS-CHAPv2:\n", + netdev->name ); + DBGC_HDA ( netdev, 0, req, req_len ); + return -EINVAL; + } + + /* Handle according to opcode */ + switch ( hdr->code ) { + case EAP_CODE_REQUEST: + return eap_rx_mschapv2_request ( supplicant, hdr, req_len ); + case EAP_CODE_SUCCESS: + return eap_rx_mschapv2_success ( supplicant, hdr, req_len ); + default: + DBGC ( netdev, "EAP %s unsupported MS-CHAPv2 opcode %d\n", + netdev->name, hdr->code ); + DBGC_HDA ( netdev, 0, req, req_len ); + return -ENOTSUP; + } +} + +/** EAP MS-CHAPv2 method */ +struct eap_method eap_mschapv2_method __eap_method = { + .type = EAP_TYPE_MSCHAPV2, + .rx = eap_rx_mschapv2, +}; diff --git a/src/net/eapol.c b/src/net/eapol.c index 3578f0e3..8b09ca23 100644 --- a/src/net/eapol.c +++ b/src/net/eapol.c @@ -28,7 +28,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <byteswap.h> #include <ipxe/iobuf.h> #include <ipxe/if_ether.h> +#include <ipxe/if_arp.h> #include <ipxe/netdevice.h> +#include <ipxe/vlan.h> +#include <ipxe/retry.h> #include <ipxe/eap.h> #include <ipxe/eapol.h> @@ -38,6 +41,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +struct net_driver eapol_driver __net_driver; + +/** EAPoL destination MAC address */ +static const uint8_t eapol_mac[ETH_ALEN] = { + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 +}; + /** * Process EAPoL packet * @@ -51,12 +61,25 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static int eapol_rx ( struct io_buffer *iobuf, struct net_device *netdev, const void *ll_dest __unused, const void *ll_source, unsigned int flags __unused ) { + struct eapol_supplicant *supplicant; struct eapol_header *eapol; struct eapol_handler *handler; size_t remaining; size_t len; int rc; + /* Find matching supplicant */ + supplicant = netdev_priv ( netdev, &eapol_driver ); + + /* Ignore non-EAPoL devices */ + if ( ! supplicant->eap.netdev ) { + DBGC ( netdev, "EAPOL %s is not an EAPoL device\n", + netdev->name ); + DBGC_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -ENOTTY; + goto drop; + } + /* Sanity checks */ if ( iob_len ( iobuf ) < sizeof ( *eapol ) ) { DBGC ( netdev, "EAPOL %s underlength header:\n", @@ -83,7 +106,7 @@ static int eapol_rx ( struct io_buffer *iobuf, struct net_device *netdev, /* Handle according to type */ for_each_table_entry ( handler, EAPOL_HANDLERS ) { if ( handler->type == eapol->type ) { - return handler->rx ( iob_disown ( iobuf ) , netdev, + return handler->rx ( supplicant, iob_disown ( iobuf ), ll_source ); } } @@ -107,12 +130,14 @@ struct net_protocol eapol_protocol __net_protocol = { /** * Process EAPoL-encapsulated EAP packet * - * @v netdev Network device + * @v supplicant EAPoL supplicant * @v ll_source Link-layer source address * @ret rc Return status code */ -static int eapol_eap_rx ( struct io_buffer *iobuf, struct net_device *netdev, +static int eapol_eap_rx ( struct eapol_supplicant *supplicant, + struct io_buffer *iobuf, const void *ll_source __unused ) { + struct net_device *netdev = supplicant->eap.netdev; struct eapol_header *eapol; int rc; @@ -123,12 +148,28 @@ static int eapol_eap_rx ( struct io_buffer *iobuf, struct net_device *netdev, eapol = iob_pull ( iobuf, sizeof ( *eapol ) ); /* Process EAP packet */ - if ( ( rc = eap_rx ( netdev, iobuf->data, iob_len ( iobuf ) ) ) != 0 ) { + if ( ( rc = eap_rx ( &supplicant->eap, iobuf->data, + iob_len ( iobuf ) ) ) != 0 ) { DBGC ( netdev, "EAPOL %s v%d EAP failed: %s\n", netdev->name, eapol->version, strerror ( rc ) ); goto drop; } + /* Update EAPoL-Start transmission timer */ + if ( supplicant->eap.flags & EAP_FL_PASSIVE ) { + /* Stop sending EAPoL-Start */ + if ( timer_running ( &supplicant->timer ) ) { + DBGC ( netdev, "EAPOL %s becoming passive\n", + netdev->name ); + } + stop_timer ( &supplicant->timer ); + } else if ( supplicant->eap.flags & EAP_FL_ONGOING ) { + /* Delay EAPoL-Start until after next expected packet */ + DBGC ( netdev, "EAPOL %s deferring Start\n", netdev->name ); + start_timer_fixed ( &supplicant->timer, EAP_WAIT_TIMEOUT ); + supplicant->count = 0; + } + drop: free_iob ( iobuf ); return rc; @@ -139,3 +180,158 @@ struct eapol_handler eapol_eap __eapol_handler = { .type = EAPOL_TYPE_EAP, .rx = eapol_eap_rx, }; + +/** + * Transmit EAPoL packet + * + * @v supplicant EAPoL supplicant + * @v type Packet type + * @v data Packet body + * @v len Length of packet body + * @ret rc Return status code + */ +static int eapol_tx ( struct eapol_supplicant *supplicant, unsigned int type, + const void *data, size_t len ) { + struct net_device *netdev = supplicant->eap.netdev; + struct io_buffer *iobuf; + struct eapol_header *eapol; + int rc; + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *eapol ) + len ); + if ( ! iobuf ) + return -ENOMEM; + iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); + + /* Construct EAPoL header */ + eapol = iob_put ( iobuf, sizeof ( *eapol ) ); + eapol->version = EAPOL_VERSION_2001; + eapol->type = type; + eapol->len = htons ( len ); + + /* Append packet body */ + memcpy ( iob_put ( iobuf, len ), data, len ); + + /* Transmit packet */ + if ( ( rc = net_tx ( iob_disown ( iobuf ), netdev, &eapol_protocol, + &eapol_mac, netdev->ll_addr ) ) != 0 ) { + DBGC ( netdev, "EAPOL %s could not transmit type %d: %s\n", + netdev->name, type, strerror ( rc ) ); + DBGC_HDA ( netdev, 0, data, len ); + return rc; + } + + return 0; +} + +/** + * Transmit EAPoL-encapsulated EAP packet + * + * @v supplicant EAPoL supplicant + * @v ll_source Link-layer source address + * @ret rc Return status code + */ +static int eapol_eap_tx ( struct eap_supplicant *eap, const void *data, + size_t len ) { + struct eapol_supplicant *supplicant = + container_of ( eap, struct eapol_supplicant, eap ); + + /* Transmit encapsulated packet */ + return eapol_tx ( supplicant, EAPOL_TYPE_EAP, data, len ); +} + +/** + * (Re)transmit EAPoL-Start packet + * + * @v timer EAPoL-Start timer + * @v expired Failure indicator + */ +static void eapol_expired ( struct retry_timer *timer, int fail __unused ) { + struct eapol_supplicant *supplicant = + container_of ( timer, struct eapol_supplicant, timer ); + struct net_device *netdev = supplicant->eap.netdev; + + /* Stop transmitting after maximum number of attempts */ + if ( supplicant->count++ >= EAPOL_START_COUNT ) { + DBGC ( netdev, "EAPOL %s giving up\n", netdev->name ); + return; + } + + /* Schedule next transmission */ + start_timer_fixed ( timer, EAPOL_START_INTERVAL ); + + /* Transmit EAPoL-Start, ignoring errors */ + DBGC2 ( netdev, "EAPOL %s transmitting Start\n", netdev->name ); + eapol_tx ( supplicant, EAPOL_TYPE_START, NULL, 0 ); +} + +/** + * Create EAPoL supplicant + * + * @v netdev Network device + * @v priv Private data + * @ret rc Return status code + */ +static int eapol_probe ( struct net_device *netdev, void *priv ) { + struct eapol_supplicant *supplicant = priv; + struct ll_protocol *ll_protocol = netdev->ll_protocol; + + /* Ignore non-EAPoL devices */ + if ( ll_protocol->ll_proto != htons ( ARPHRD_ETHER ) ) + return 0; + if ( vlan_tag ( netdev ) ) + return 0; + + /* Initialise structure */ + supplicant->eap.netdev = netdev; + supplicant->eap.tx = eapol_eap_tx; + timer_init ( &supplicant->timer, eapol_expired, &netdev->refcnt ); + + return 0; +} + +/** + * Handle EAPoL supplicant state change + * + * @v netdev Network device + * @v priv Private data + */ +static void eapol_notify ( struct net_device *netdev, void *priv ) { + struct eapol_supplicant *supplicant = priv; + + /* Ignore non-EAPoL devices */ + if ( ! supplicant->eap.netdev ) + return; + + /* Terminate and reset EAP when link goes down */ + if ( ! ( netdev_is_open ( netdev ) && netdev_link_ok ( netdev ) ) ) { + if ( timer_running ( &supplicant->timer ) ) { + DBGC ( netdev, "EAPOL %s shutting down\n", + netdev->name ); + } + supplicant->eap.flags = 0; + stop_timer ( &supplicant->timer ); + return; + } + + /* Do nothing if EAP is already in progress */ + if ( timer_running ( &supplicant->timer ) ) + return; + + /* Do nothing if EAP has already finished transmitting */ + if ( supplicant->eap.flags & EAP_FL_PASSIVE ) + return; + + /* Otherwise, start sending EAPoL-Start */ + start_timer_nodelay ( &supplicant->timer ); + supplicant->count = 0; + DBGC ( netdev, "EAPOL %s starting up\n", netdev->name ); +} + +/** EAPoL driver */ +struct net_driver eapol_driver __net_driver = { + .name = "EAPoL", + .priv_len = sizeof ( struct eapol_supplicant ), + .probe = eapol_probe, + .notify = eapol_notify, +}; diff --git a/src/net/fcoe.c b/src/net/fcoe.c index f910eeea..9f3ddf88 100644 --- a/src/net/fcoe.c +++ b/src/net/fcoe.c @@ -69,10 +69,6 @@ FEATURE ( FEATURE_PROTOCOL, "FCoE", DHCP_EB_FEATURE_FCOE, 1 ); /** An FCoE port */ struct fcoe_port { - /** Reference count */ - struct refcnt refcnt; - /** List of FCoE ports */ - struct list_head list; /** Transport interface */ struct interface transport; /** Network device */ @@ -115,6 +111,7 @@ enum fcoe_flags { FCOE_VLAN_TIMED_OUT = 0x0020, }; +struct net_driver fcoe_driver __net_driver; struct net_protocol fcoe_protocol __net_protocol; struct net_protocol fip_protocol __net_protocol; @@ -152,9 +149,6 @@ static uint8_t default_fcf_mac[ETH_ALEN] = /** Maximum number of missing discovery advertisements */ #define FCOE_MAX_FIP_MISSING_KEEPALIVES 4 -/** List of FCoE ports */ -static LIST_HEAD ( fcoe_ports ); - /****************************************************************************** * * FCoE protocol @@ -163,22 +157,6 @@ static LIST_HEAD ( fcoe_ports ); */ /** - * Identify FCoE port by network device - * - * @v netdev Network device - * @ret fcoe FCoE port, or NULL - */ -static struct fcoe_port * fcoe_demux ( struct net_device *netdev ) { - struct fcoe_port *fcoe; - - list_for_each_entry ( fcoe, &fcoe_ports, list ) { - if ( fcoe->netdev == netdev ) - return fcoe; - } - return NULL; -} - -/** * Reset FCoE port * * @v fcoe FCoE port @@ -348,7 +326,8 @@ static int fcoe_rx ( struct io_buffer *iobuf, struct net_device *netdev, int rc; /* Identify FCoE port */ - if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) { + fcoe = netdev_priv ( netdev, &fcoe_driver ); + if ( ! fcoe->netdev ) { DBG ( "FCoE received frame for net device %s missing FCoE " "port\n", netdev->name ); rc = -ENOTCONN; @@ -448,9 +427,6 @@ static void fcoe_close ( struct fcoe_port *fcoe, int rc ) { stop_timer ( &fcoe->timer ); intf_shutdown ( &fcoe->transport, rc ); - netdev_put ( fcoe->netdev ); - list_del ( &fcoe->list ); - ref_put ( &fcoe->refcnt ); } /** @@ -947,7 +923,8 @@ static int fcoe_fip_rx ( struct io_buffer *iobuf, int rc; /* Identify FCoE port */ - if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) { + fcoe = netdev_priv ( netdev, &fcoe_driver ); + if ( ! fcoe->netdev ) { DBG ( "FCoE received FIP frame for net device %s missing FCoE " "port\n", netdev->name ); rc = -ENOTCONN; @@ -1110,31 +1087,24 @@ static void fcoe_expired ( struct retry_timer *timer, int over __unused ) { * Create FCoE port * * @v netdev Network device + * @v priv Private data * @ret rc Return status code */ -static int fcoe_probe ( struct net_device *netdev ) { +static int fcoe_probe ( struct net_device *netdev, void *priv ) { struct ll_protocol *ll_protocol = netdev->ll_protocol; - struct fcoe_port *fcoe; - int rc; + struct fcoe_port *fcoe = priv; /* Sanity check */ if ( ll_protocol->ll_proto != htons ( ARPHRD_ETHER ) ) { /* Not an error; simply skip this net device */ DBG ( "FCoE skipping non-Ethernet device %s\n", netdev->name ); - rc = 0; - goto err_non_ethernet; + return 0; } - /* Allocate and initialise structure */ - fcoe = zalloc ( sizeof ( *fcoe ) ); - if ( ! fcoe ) { - rc = -ENOMEM; - goto err_zalloc; - } - ref_init ( &fcoe->refcnt, NULL ); - intf_init ( &fcoe->transport, &fcoe_transport_desc, &fcoe->refcnt ); - timer_init ( &fcoe->timer, fcoe_expired, &fcoe->refcnt ); - fcoe->netdev = netdev_get ( netdev ); + /* Initialise structure */ + intf_init ( &fcoe->transport, &fcoe_transport_desc, &netdev->refcnt ); + timer_init ( &fcoe->timer, fcoe_expired, &netdev->refcnt ); + fcoe->netdev = netdev; /* Construct node and port names */ fcoe->node_wwn.fcoe.authority = htons ( FCOE_AUTHORITY_IEEE ); @@ -1148,30 +1118,21 @@ static int fcoe_probe ( struct net_device *netdev ) { fc_ntoa ( &fcoe->node_wwn.fc ) ); DBGC ( fcoe, " port %s\n", fc_ntoa ( &fcoe->port_wwn.fc ) ); - /* Transfer reference to port list */ - list_add ( &fcoe->list, &fcoe_ports ); return 0; - - netdev_put ( fcoe->netdev ); - err_zalloc: - err_non_ethernet: - return rc; } /** * Handle FCoE port device or link state change * * @v netdev Network device + * @v priv Private data */ -static void fcoe_notify ( struct net_device *netdev ) { - struct fcoe_port *fcoe; +static void fcoe_notify ( struct net_device *netdev, void *priv ) { + struct fcoe_port *fcoe = priv; - /* Sanity check */ - if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) { - DBG ( "FCoE notification for net device %s missing FCoE " - "port\n", netdev->name ); + /* Skip non-FCoE net devices */ + if ( ! fcoe->netdev ) return; - } /* Reset the FCoE link if necessary */ if ( ! ( netdev_is_open ( netdev ) && @@ -1185,16 +1146,14 @@ static void fcoe_notify ( struct net_device *netdev ) { * Destroy FCoE port * * @v netdev Network device + * @v priv Private data */ -static void fcoe_remove ( struct net_device *netdev ) { - struct fcoe_port *fcoe; +static void fcoe_remove ( struct net_device *netdev __unused, void *priv ) { + struct fcoe_port *fcoe = priv; - /* Sanity check */ - if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) { - DBG ( "FCoE removal of net device %s missing FCoE port\n", - netdev->name ); + /* Skip non-FCoE net devices */ + if ( ! fcoe->netdev ) return; - } /* Close FCoE device */ fcoe_close ( fcoe, 0 ); @@ -1203,6 +1162,7 @@ static void fcoe_remove ( struct net_device *netdev ) { /** FCoE driver */ struct net_driver fcoe_driver __net_driver = { .name = "FCoE", + .priv_len = sizeof ( struct fcoe_port ), .probe = fcoe_probe, .notify = fcoe_notify, .remove = fcoe_remove, diff --git a/src/net/infiniband/xsigo.c b/src/net/infiniband/xsigo.c index 4f5c618d..5e805fa0 100644 --- a/src/net/infiniband/xsigo.c +++ b/src/net/infiniband/xsigo.c @@ -1829,8 +1829,10 @@ struct ib_driver xsigo_ib_driver __ib_driver = { * Handle device or link status change * * @v netdev Network device + * @v priv Private data */ -static void xsigo_net_notify ( struct net_device *netdev ) { +static void xsigo_net_notify ( struct net_device *netdev, + void *priv __unused ) { struct xsigo_device *xdev; struct ib_device *ibdev; struct xsigo_manager *xcm; diff --git a/src/net/ipv6.c b/src/net/ipv6.c index ef5e51da..8ee0804d 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -1212,50 +1212,33 @@ static struct settings_operations ipv6_settings_operations = { .fetch = ipv6_fetch, }; -/** IPv6 link-local address settings */ -struct ipv6_settings { - /** Reference counter */ - struct refcnt refcnt; - /** Settings interface */ - struct settings settings; -}; - /** * Register IPv6 link-local address settings * * @v netdev Network device + * @v priv Private data * @ret rc Return status code */ -static int ipv6_register_settings ( struct net_device *netdev ) { +static int ipv6_register_settings ( struct net_device *netdev, void *priv ) { struct settings *parent = netdev_settings ( netdev ); - struct ipv6_settings *ipv6set; + struct settings *settings = priv; int rc; - /* Allocate and initialise structure */ - ipv6set = zalloc ( sizeof ( *ipv6set ) ); - if ( ! ipv6set ) { - rc = -ENOMEM; - goto err_alloc; - } - ref_init ( &ipv6set->refcnt, NULL ); - settings_init ( &ipv6set->settings, &ipv6_settings_operations, - &ipv6set->refcnt, &ipv6_settings_scope ); - ipv6set->settings.order = IPV6_ORDER_LINK_LOCAL; - - /* Register settings */ - if ( ( rc = register_settings ( &ipv6set->settings, parent, + /* Initialise and register settings */ + settings_init ( settings, &ipv6_settings_operations, + &netdev->refcnt, &ipv6_settings_scope ); + settings->order = IPV6_ORDER_LINK_LOCAL; + if ( ( rc = register_settings ( settings, parent, IPV6_SETTINGS_NAME ) ) != 0 ) - goto err_register; + return rc; - err_register: - ref_put ( &ipv6set->refcnt ); - err_alloc: - return rc; + return 0; } /** IPv6 network device driver */ struct net_driver ipv6_driver __net_driver = { .name = "IPv6", + .priv_len = sizeof ( struct settings ), .probe = ipv6_register_settings, }; diff --git a/src/net/lldp.c b/src/net/lldp.c index 72e3ecdf..a854d0ac 100644 --- a/src/net/lldp.c +++ b/src/net/lldp.c @@ -40,12 +40,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** An LLDP settings block */ struct lldp_settings { - /** Reference counter */ - struct refcnt refcnt; /** Settings interface */ struct settings settings; - /** List of LLDP settings blocks */ - struct list_head list; /** Name */ const char *name; /** LLDP data */ @@ -54,45 +50,12 @@ struct lldp_settings { size_t len; }; +/* Forward declaration */ +struct net_driver lldp_driver __net_driver; + /** LLDP settings scope */ static const struct settings_scope lldp_settings_scope; -/** List of LLDP settings blocks */ -static LIST_HEAD ( lldp_settings ); - -/** - * Free LLDP settings block - * - * @v refcnt Reference counter - */ -static void lldp_free ( struct refcnt *refcnt ) { - struct lldp_settings *lldpset = - container_of ( refcnt, struct lldp_settings, refcnt ); - - DBGC ( lldpset, "LLDP %s freed\n", lldpset->name ); - list_del ( &lldpset->list ); - free ( lldpset->data ); - free ( lldpset ); -} - -/** - * Find LLDP settings block - * - * @v netdev Network device - * @ret lldpset LLDP settings block - */ -static struct lldp_settings * lldp_find ( struct net_device *netdev ) { - struct lldp_settings *lldpset; - - /* Find matching LLDP settings block */ - list_for_each_entry ( lldpset, &lldp_settings, list ) { - if ( netdev_settings ( netdev ) == lldpset->settings.parent ) - return lldpset; - } - - return NULL; -} - /** * Check applicability of LLDP setting * @@ -246,13 +209,7 @@ static int lldp_rx ( struct io_buffer *iobuf, struct net_device *netdev, int rc; /* Find matching LLDP settings block */ - lldpset = lldp_find ( netdev ); - if ( ! lldpset ) { - DBGC ( netdev, "LLDP %s has no \"%s\" settings block\n", - netdev->name, LLDP_SETTINGS_NAME ); - rc = -ENOENT; - goto err_find; - } + lldpset = netdev_priv ( netdev, &lldp_driver ); /* Create trimmed copy of received LLDP data */ len = iob_len ( iobuf ); @@ -280,7 +237,6 @@ static int lldp_rx ( struct io_buffer *iobuf, struct net_device *netdev, free ( data ); err_alloc: - err_find: free_iob ( iobuf ); return rc; } @@ -296,26 +252,21 @@ struct net_protocol lldp_protocol __net_protocol = { * Create LLDP settings block * * @v netdev Network device + * @v priv Private data * @ret rc Return status code */ -static int lldp_probe ( struct net_device *netdev ) { - struct lldp_settings *lldpset; +static int lldp_probe ( struct net_device *netdev, void *priv ) { + struct lldp_settings *lldpset = priv; int rc; - /* Allocate LLDP settings block */ - lldpset = zalloc ( sizeof ( *lldpset ) ); - if ( ! lldpset ) { - rc = -ENOMEM; - goto err_alloc; - } - ref_init ( &lldpset->refcnt, lldp_free ); + /* Initialise LLDP settings block */ settings_init ( &lldpset->settings, &lldp_settings_operations, - &lldpset->refcnt, &lldp_settings_scope ); - list_add_tail ( &lldpset->list, &lldp_settings ); + &netdev->refcnt, &lldp_settings_scope ); lldpset->name = netdev->name; /* Register settings */ - if ( ( rc = register_settings ( &lldpset->settings, netdev_settings ( netdev ), + if ( ( rc = register_settings ( &lldpset->settings, + netdev_settings ( netdev ), LLDP_SETTINGS_NAME ) ) != 0 ) { DBGC ( lldpset, "LLDP %s could not register settings: %s\n", lldpset->name, strerror ( rc ) ); @@ -323,18 +274,36 @@ static int lldp_probe ( struct net_device *netdev ) { } DBGC ( lldpset, "LLDP %s registered\n", lldpset->name ); - ref_put ( &lldpset->refcnt ); return 0; unregister_settings ( &lldpset->settings ); err_register: - ref_put ( &lldpset->refcnt ); - err_alloc: + assert ( lldpset->data == NULL ); return rc; } +/** + * Remove LLDP settings block + * + * @v netdev Network device + * @v priv Private data + */ +static void lldp_remove ( struct net_device *netdev __unused, void *priv ) { + struct lldp_settings *lldpset = priv; + + /* Unregister settings */ + unregister_settings ( &lldpset->settings ); + DBGC ( lldpset, "LLDP %s unregistered\n", lldpset->name ); + + /* Free any LLDP data */ + free ( lldpset->data ); + lldpset->data = NULL; +} + /** LLDP driver */ struct net_driver lldp_driver __net_driver = { .name = "LLDP", + .priv_len = sizeof ( struct lldp_settings ), .probe = lldp_probe, + .remove = lldp_remove, }; diff --git a/src/net/neighbour.c b/src/net/neighbour.c index 7f66d999..13a8bc3b 100644 --- a/src/net/neighbour.c +++ b/src/net/neighbour.c @@ -383,8 +383,9 @@ int neighbour_define ( struct net_device *netdev, * Update neighbour cache on network device state change or removal * * @v netdev Network device + * @v priv Private data */ -static void neighbour_flush ( struct net_device *netdev ) { +static void neighbour_flush ( struct net_device *netdev, void *priv __unused ) { struct neighbour *neighbour; struct neighbour *tmp; diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 07961bf2..a9ed1813 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -110,16 +110,63 @@ static int netdev_has_ll_addr ( struct net_device *netdev ) { } /** + * Get offset of network device driver private data + * + * @v driver Upper-layer driver, or NULL for device driver + * @ret offset Offset of driver private data + */ +static size_t netdev_priv_offset ( struct net_driver *driver ) { + struct net_device *netdev; + unsigned int num_configs; + size_t offset; + + /* Allow space for network device */ + offset = sizeof ( *netdev ); + + /* Allow space for configurations */ + num_configs = table_num_entries ( NET_DEVICE_CONFIGURATORS ); + offset += ( num_configs * sizeof ( netdev->configs[0] ) ); + + /* Place variable-length device driver private data at end */ + if ( ! driver ) + driver = table_end ( NET_DRIVERS ); + + /* Allow space for preceding upper-layer drivers' private data */ + for_each_table_entry_continue_reverse ( driver, NET_DRIVERS ) { + offset += driver->priv_len; + } + + /* Sanity check */ + assert ( ( offset & ( sizeof ( void * ) - 1 ) ) == 0 ); + + return offset; +} + +/** + * Get network device driver private data + * + * @v netdev Network device + * @v driver Upper-layer driver, or NULL for device driver + * @ret priv Driver private data + */ +void * netdev_priv ( struct net_device *netdev, struct net_driver *driver ) { + + return ( ( ( void * ) netdev ) + netdev_priv_offset ( driver ) ); +} + +/** * Notify drivers of network device or link state change * * @v netdev Network device */ static void netdev_notify ( struct net_device *netdev ) { struct net_driver *driver; + void *priv; for_each_table_entry ( driver, NET_DRIVERS ) { + priv = netdev_priv ( netdev, driver ); if ( driver->notify ) - driver->notify ( netdev ); + driver->notify ( netdev, priv ); } } @@ -656,7 +703,7 @@ static void free_netdev ( struct refcnt *refcnt ) { struct net_device *netdev = container_of ( refcnt, struct net_device, refcnt ); - stop_timer ( &netdev->link_block ); + assert ( ! timer_running ( &netdev->link_block ) ); netdev_tx_flush ( netdev ); netdev_rx_flush ( netdev ); clear_settings ( netdev_settings ( netdev ) ); @@ -675,14 +722,8 @@ struct net_device * alloc_netdev ( size_t priv_len ) { struct net_device *netdev; struct net_device_configurator *configurator; struct net_device_configuration *config; - unsigned int num_configs; - size_t confs_len; - size_t total_len; - num_configs = table_num_entries ( NET_DEVICE_CONFIGURATORS ); - confs_len = ( num_configs * sizeof ( netdev->configs[0] ) ); - total_len = ( sizeof ( *netdev ) + confs_len + priv_len ); - netdev = zalloc ( total_len ); + netdev = zalloc ( netdev_priv_offset ( NULL ) + priv_len ); if ( netdev ) { ref_init ( &netdev->refcnt, free_netdev ); netdev->link_rc = -EUNKNOWN_LINK_STATUS; @@ -701,8 +742,7 @@ struct net_device * alloc_netdev ( size_t priv_len ) { &netdev->refcnt ); config++; } - netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) + - confs_len ); + netdev->priv = netdev_priv ( netdev, NULL ); } return netdev; } @@ -722,6 +762,7 @@ int register_netdev ( struct net_device *netdev ) { struct net_device *duplicate; unsigned int i; uint32_t seed; + void *priv; int rc; /* Set initial link-layer address, if not already set */ @@ -784,7 +825,9 @@ int register_netdev ( struct net_device *netdev ) { /* Probe device */ for_each_table_entry ( driver, NET_DRIVERS ) { - if ( driver->probe && ( rc = driver->probe ( netdev ) ) != 0 ) { + priv = netdev_priv ( netdev, driver ); + if ( driver->probe && + ( rc = driver->probe ( netdev, priv ) ) != 0 ) { DBGC ( netdev, "NETDEV %s could not add %s device: " "%s\n", netdev->name, driver->name, strerror ( rc ) ); @@ -796,8 +839,9 @@ int register_netdev ( struct net_device *netdev ) { err_probe: for_each_table_entry_continue_reverse ( driver, NET_DRIVERS ) { + priv = netdev_priv ( netdev, driver ); if ( driver->remove ) - driver->remove ( netdev ); + driver->remove ( netdev, priv ); } clear_settings ( netdev_settings ( netdev ) ); unregister_settings ( netdev_settings ( netdev ) ); @@ -879,6 +923,9 @@ void netdev_close ( struct net_device *netdev ) { /* Close the device */ netdev->op->close ( netdev ); + /* Stop link block timer */ + stop_timer ( &netdev->link_block ); + /* Flush TX and RX queues */ netdev_tx_flush ( netdev ); netdev_rx_flush ( netdev ); @@ -893,14 +940,16 @@ void netdev_close ( struct net_device *netdev ) { */ void unregister_netdev ( struct net_device *netdev ) { struct net_driver *driver; + void *priv; /* Ensure device is closed */ netdev_close ( netdev ); /* Remove device */ for_each_table_entry_reverse ( driver, NET_DRIVERS ) { + priv = netdev_priv ( netdev, driver ); if ( driver->remove ) - driver->remove ( netdev ); + driver->remove ( netdev, priv ); } /* Unregister per-netdev configuration settings */ diff --git a/src/net/tls.c b/src/net/tls.c index 000a8a78..5f89be45 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -158,6 +158,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_ENOTSUP_VERSION \ __einfo_uniqify ( EINFO_ENOTSUP, 0x04, \ "Unsupported protocol version" ) +#define ENOTSUP_CURVE __einfo_error ( EINFO_ENOTSUP_CURVE ) +#define EINFO_ENOTSUP_CURVE \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x05, \ + "Unsupported elliptic curve" ) #define EPERM_ALERT __einfo_error ( EINFO_EPERM_ALERT ) #define EINFO_EPERM_ALERT \ __einfo_uniqify ( EINFO_EPERM, 0x01, \ @@ -1044,6 +1048,35 @@ tls_signature_hash_digest ( struct tls_signature_hash_id code ) { /****************************************************************************** * + * Ephemeral Elliptic Curve Diffie-Hellman key exchange + * + ****************************************************************************** + */ + +/** Number of supported named curves */ +#define TLS_NUM_NAMED_CURVES table_num_entries ( TLS_NAMED_CURVES ) + +/** + * Identify named curve + * + * @v named_curve Named curve specification + * @ret curve Named curve, or NULL + */ +static struct tls_named_curve * +tls_find_named_curve ( unsigned int named_curve ) { + struct tls_named_curve *curve; + + /* Identify named curve */ + for_each_table_entry ( curve, TLS_NAMED_CURVES ) { + if ( curve->code == named_curve ) + return curve; + } + + return NULL; +} + +/****************************************************************************** + * * Record handling * ****************************************************************************** @@ -1122,6 +1155,67 @@ static int tls_client_hello ( struct tls_connection *tls, struct tls_session *session = tls->session; size_t name_len = strlen ( session->name ); struct { + uint16_t type; + uint16_t len; + struct { + uint16_t len; + struct { + uint8_t type; + uint16_t len; + uint8_t name[name_len]; + } __attribute__ (( packed )) list[1]; + } __attribute__ (( packed )) data; + } __attribute__ (( packed )) *server_name_ext; + struct { + uint16_t type; + uint16_t len; + struct { + uint8_t max; + } __attribute__ (( packed )) data; + } __attribute__ (( packed )) *max_fragment_length_ext; + struct { + uint16_t type; + uint16_t len; + struct { + uint16_t len; + struct tls_signature_hash_id + code[TLS_NUM_SIG_HASH_ALGORITHMS]; + } __attribute__ (( packed )) data; + } __attribute__ (( packed )) *signature_algorithms_ext; + struct { + uint16_t type; + uint16_t len; + struct { + uint8_t len; + uint8_t data[ tls->secure_renegotiation ? + sizeof ( tls->verify.client ) :0 ]; + } __attribute__ (( packed )) data; + } __attribute__ (( packed )) *renegotiation_info_ext; + struct { + uint16_t type; + uint16_t len; + struct { + uint8_t data[session->ticket_len]; + } __attribute__ (( packed )) data; + } __attribute__ (( packed )) *session_ticket_ext; + struct { + uint16_t type; + uint16_t len; + struct { + uint16_t len; + uint16_t code[TLS_NUM_NAMED_CURVES]; + } __attribute__ (( packed )) data; + } __attribute__ (( packed )) *named_curve_ext; + struct { + typeof ( *server_name_ext ) server_name; + typeof ( *max_fragment_length_ext ) max_fragment_length; + typeof ( *signature_algorithms_ext ) signature_algorithms; + typeof ( *renegotiation_info_ext ) renegotiation_info; + typeof ( *session_ticket_ext ) session_ticket; + typeof ( *named_curve_ext ) + named_curve[TLS_NUM_NAMED_CURVES ? 1 : 0]; + } __attribute__ (( packed )) *extensions; + struct { uint32_t type_length; uint16_t version; uint8_t random[32]; @@ -1132,45 +1226,11 @@ static int tls_client_hello ( struct tls_connection *tls, uint8_t compression_methods_len; uint8_t compression_methods[1]; uint16_t extensions_len; - struct { - uint16_t server_name_type; - uint16_t server_name_len; - struct { - uint16_t len; - struct { - uint8_t type; - uint16_t len; - uint8_t name[name_len]; - } __attribute__ (( packed )) list[1]; - } __attribute__ (( packed )) server_name; - uint16_t max_fragment_length_type; - uint16_t max_fragment_length_len; - struct { - uint8_t max; - } __attribute__ (( packed )) max_fragment_length; - uint16_t signature_algorithms_type; - uint16_t signature_algorithms_len; - struct { - uint16_t len; - struct tls_signature_hash_id - code[TLS_NUM_SIG_HASH_ALGORITHMS]; - } __attribute__ (( packed )) signature_algorithms; - uint16_t renegotiation_info_type; - uint16_t renegotiation_info_len; - struct { - uint8_t len; - 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; + typeof ( *extensions ) extensions; } __attribute__ (( packed )) hello; struct tls_cipher_suite *suite; struct tls_signature_hash_algorithm *sighash; + struct tls_named_curve *curve; unsigned int i; /* Construct record */ @@ -1188,43 +1248,66 @@ static int tls_client_hello ( struct tls_connection *tls, hello.cipher_suites[i++] = suite->code; hello.compression_methods_len = sizeof ( hello.compression_methods ); hello.extensions_len = htons ( sizeof ( hello.extensions ) ); - hello.extensions.server_name_type = htons ( TLS_SERVER_NAME ); - hello.extensions.server_name_len - = htons ( sizeof ( hello.extensions.server_name ) ); - hello.extensions.server_name.len - = htons ( sizeof ( hello.extensions.server_name.list ) ); - 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, session->name, - sizeof ( hello.extensions.server_name.list[0].name ) ); - hello.extensions.max_fragment_length_type - = htons ( TLS_MAX_FRAGMENT_LENGTH ); - hello.extensions.max_fragment_length_len - = htons ( sizeof ( hello.extensions.max_fragment_length ) ); - hello.extensions.max_fragment_length.max - = TLS_MAX_FRAGMENT_LENGTH_4096; - hello.extensions.signature_algorithms_type - = htons ( TLS_SIGNATURE_ALGORITHMS ); - hello.extensions.signature_algorithms_len - = htons ( sizeof ( hello.extensions.signature_algorithms ) ); - hello.extensions.signature_algorithms.len - = htons ( sizeof ( hello.extensions.signature_algorithms.code)); + extensions = &hello.extensions; + + /* Construct server name extension */ + server_name_ext = &extensions->server_name; + server_name_ext->type = htons ( TLS_SERVER_NAME ); + server_name_ext->len = htons ( sizeof ( server_name_ext->data ) ); + server_name_ext->data.len + = htons ( sizeof ( server_name_ext->data.list ) ); + server_name_ext->data.list[0].type = TLS_SERVER_NAME_HOST_NAME; + server_name_ext->data.list[0].len + = htons ( sizeof ( server_name_ext->data.list[0].name ) ); + memcpy ( server_name_ext->data.list[0].name, session->name, + sizeof ( server_name_ext->data.list[0].name ) ); + + /* Construct maximum fragment length extension */ + max_fragment_length_ext = &extensions->max_fragment_length; + max_fragment_length_ext->type = htons ( TLS_MAX_FRAGMENT_LENGTH ); + max_fragment_length_ext->len + = htons ( sizeof ( max_fragment_length_ext->data ) ); + max_fragment_length_ext->data.max = TLS_MAX_FRAGMENT_LENGTH_4096; + + /* Construct supported signature algorithms extension */ + signature_algorithms_ext = &extensions->signature_algorithms; + signature_algorithms_ext->type = htons ( TLS_SIGNATURE_ALGORITHMS ); + signature_algorithms_ext->len + = htons ( sizeof ( signature_algorithms_ext->data ) ); + signature_algorithms_ext->data.len + = htons ( sizeof ( signature_algorithms_ext->data.code ) ); i = 0 ; for_each_table_entry ( sighash, TLS_SIG_HASH_ALGORITHMS ) - hello.extensions.signature_algorithms.code[i++] = sighash->code; - hello.extensions.renegotiation_info_type - = htons ( TLS_RENEGOTIATION_INFO ); - hello.extensions.renegotiation_info_len - = htons ( sizeof ( hello.extensions.renegotiation_info ) ); - hello.extensions.renegotiation_info.len - = 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 ) ); + signature_algorithms_ext->data.code[i++] = sighash->code; + + /* Construct renegotiation information extension */ + renegotiation_info_ext = &extensions->renegotiation_info; + renegotiation_info_ext->type = htons ( TLS_RENEGOTIATION_INFO ); + renegotiation_info_ext->len + = htons ( sizeof ( renegotiation_info_ext->data ) ); + renegotiation_info_ext->data.len + = sizeof ( renegotiation_info_ext->data.data ); + memcpy ( renegotiation_info_ext->data.data, tls->verify.client, + sizeof ( renegotiation_info_ext->data.data ) ); + + /* Construct session ticket extension */ + session_ticket_ext = &extensions->session_ticket; + session_ticket_ext->type = htons ( TLS_SESSION_TICKET ); + session_ticket_ext->len + = htons ( sizeof ( session_ticket_ext->data ) ); + memcpy ( session_ticket_ext->data.data, session->ticket, + sizeof ( session_ticket_ext->data.data ) ); + + /* Construct named curves extension, if applicable */ + if ( sizeof ( extensions->named_curve ) ) { + named_curve_ext = &extensions->named_curve[0]; + named_curve_ext->type = htons ( TLS_NAMED_CURVE ); + named_curve_ext->len + = htons ( sizeof ( named_curve_ext->data ) ); + named_curve_ext->data.len + = htons ( sizeof ( named_curve_ext->data.code ) ); + i = 0 ; for_each_table_entry ( curve, TLS_NAMED_CURVES ) + named_curve_ext->data.code[i++] = curve->code; + } return action ( tls, &hello, sizeof ( hello ) ); } @@ -1336,13 +1419,6 @@ static int tls_send_client_key_exchange_pubkey ( struct tls_connection *tls ) { tls_generate_master_secret ( tls, &pre_master_secret, sizeof ( pre_master_secret ) ); - /* Generate keys */ - if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) { - DBGC ( tls, "TLS %p could not generate keys: %s\n", - tls, strerror ( rc ) ); - return rc; - } - /* Encrypt pre-master secret using server's public key */ memset ( &key_xchg, 0, sizeof ( key_xchg ) ); len = pubkey_encrypt ( pubkey, cipherspec->pubkey_ctx, @@ -1374,21 +1450,18 @@ struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm = { }; /** - * Transmit Client Key Exchange record using DHE key exchange + * Verify Diffie-Hellman parameter signature * * @v tls TLS connection + * @v param_len Diffie-Hellman parameter length * @ret rc Return status code */ -static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { +static int tls_verify_dh_params ( struct tls_connection *tls, + size_t param_len ) { struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; struct pubkey_algorithm *pubkey; struct digest_algorithm *digest; int use_sig_hash = tls_version ( tls, TLS_VERSION_TLS_1_2 ); - uint8_t private[ sizeof ( tls->client_random.random ) ]; - const struct { - uint16_t len; - uint8_t data[0]; - } __attribute__ (( packed )) *dh_val[3]; const struct { struct tls_signature_hash_id sig_hash[use_sig_hash]; uint16_t signature_len; @@ -1396,29 +1469,14 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { } __attribute__ (( packed )) *sig; const void *data; size_t remaining; - size_t frag_len; - unsigned int i; int rc; - /* Parse ServerKeyExchange */ - data = tls->server_key; - remaining = tls->server_key_len; - for ( i = 0 ; i < ( sizeof ( dh_val ) / sizeof ( dh_val[0] ) ) ; i++ ){ - dh_val[i] = data; - if ( ( sizeof ( *dh_val[i] ) > remaining ) || - ( ntohs ( dh_val[i]->len ) > ( remaining - - sizeof ( *dh_val[i] ) ) )){ - DBGC ( tls, "TLS %p received underlength " - "ServerKeyExchange\n", tls ); - DBGC_HDA ( tls, 0, tls->server_key, - tls->server_key_len ); - rc = -EINVAL_KEY_EXCHANGE; - goto err_header; - } - frag_len = ( sizeof ( *dh_val[i] ) + ntohs ( dh_val[i]->len )); - data += frag_len; - remaining -= frag_len; - } + /* Signature follows parameters */ + assert ( param_len <= tls->server_key_len ); + data = ( tls->server_key + param_len ); + remaining = ( tls->server_key_len - param_len ); + + /* Parse signature from ServerKeyExchange */ sig = data; if ( ( sizeof ( *sig ) > remaining ) || ( ntohs ( sig->signature_len ) > ( remaining - @@ -1426,8 +1484,7 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { DBGC ( tls, "TLS %p received underlength ServerKeyExchange\n", tls ); DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len ); - rc = -EINVAL_KEY_EXCHANGE; - goto err_header; + return -EINVAL_KEY_EXCHANGE; } /* Identify signature and hash algorithm */ @@ -1437,15 +1494,13 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { if ( ( ! pubkey ) || ( ! digest ) ) { DBGC ( tls, "TLS %p ServerKeyExchange unsupported " "signature and hash algorithm\n", tls ); - rc = -ENOTSUP_SIG_HASH; - goto err_sig_hash; + return -ENOTSUP_SIG_HASH; } if ( pubkey != cipherspec->suite->pubkey ) { DBGC ( tls, "TLS %p ServerKeyExchange incorrect " "signature algorithm %s (expected %s)\n", tls, pubkey->name, cipherspec->suite->pubkey->name ); - rc = -EPERM_KEY_EXCHANGE; - goto err_sig_hash; + return -EPERM_KEY_EXCHANGE; } } else { pubkey = cipherspec->suite->pubkey; @@ -1465,8 +1520,7 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { sizeof ( tls->client_random ) ); digest_update ( digest, ctx, tls->server_random, sizeof ( tls->server_random ) ); - digest_update ( digest, ctx, tls->server_key, - ( tls->server_key_len - remaining ) ); + digest_update ( digest, ctx, tls->server_key, param_len ); digest_final ( digest, ctx, hash ); /* Verify signature */ @@ -1477,10 +1531,56 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { "verification\n", tls ); DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len ); - rc = -EPERM_KEY_EXCHANGE; - goto err_verify; + return -EPERM_KEY_EXCHANGE; + } + } + + return 0; +} + +/** + * Transmit Client Key Exchange record using DHE key exchange + * + * @v tls TLS connection + * @ret rc Return status code + */ +static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { + uint8_t private[ sizeof ( tls->client_random.random ) ]; + const struct { + uint16_t len; + uint8_t data[0]; + } __attribute__ (( packed )) *dh_val[3]; + const void *data; + size_t remaining; + size_t frag_len; + size_t param_len; + unsigned int i; + int rc; + + /* Parse ServerKeyExchange */ + data = tls->server_key; + remaining = tls->server_key_len; + for ( i = 0 ; i < ( sizeof ( dh_val ) / sizeof ( dh_val[0] ) ) ; i++ ){ + dh_val[i] = data; + if ( ( sizeof ( *dh_val[i] ) > remaining ) || + ( ntohs ( dh_val[i]->len ) > ( remaining - + sizeof ( *dh_val[i] ) ) )){ + DBGC ( tls, "TLS %p received underlength " + "ServerKeyExchange\n", tls ); + DBGC_HDA ( tls, 0, tls->server_key, + tls->server_key_len ); + rc = -EINVAL_KEY_EXCHANGE; + goto err_header; } + frag_len = ( sizeof ( *dh_val[i] ) + ntohs ( dh_val[i]->len )); + data += frag_len; + remaining -= frag_len; } + param_len = ( tls->server_key_len - remaining ); + + /* Verify parameter signature */ + if ( ( rc = tls_verify_dh_params ( tls, param_len ) ) != 0 ) + goto err_verify; /* Generate Diffie-Hellman private key */ if ( ( rc = tls_generate_random ( tls, private, @@ -1540,13 +1640,6 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { /* Generate master secret */ tls_generate_master_secret ( tls, pre_master_secret, len ); - /* Generate keys */ - if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) { - DBGC ( tls, "TLS %p could not generate keys: %s\n", - tls, strerror ( rc ) ); - goto err_generate_keys; - } - /* Transmit Client Key Exchange record */ if ( ( rc = tls_send_handshake ( tls, key_xchg, sizeof ( *key_xchg ) ) ) !=0){ @@ -1554,14 +1647,12 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { } err_send_handshake: - err_generate_keys: err_dhe_key: free ( dynamic ); } err_alloc: err_random: err_verify: - err_sig_hash: err_header: return rc; } @@ -1573,6 +1664,119 @@ struct tls_key_exchange_algorithm tls_dhe_exchange_algorithm = { }; /** + * Transmit Client Key Exchange record using ECDHE key exchange + * + * @v tls TLS connection + * @ret rc Return status code + */ +static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) { + struct tls_named_curve *curve; + const struct { + uint8_t curve_type; + uint16_t named_curve; + uint8_t public_len; + uint8_t public[0]; + } __attribute__ (( packed )) *ecdh; + size_t param_len; + int rc; + + /* Parse ServerKeyExchange record */ + ecdh = tls->server_key; + if ( ( sizeof ( *ecdh ) > tls->server_key_len ) || + ( ecdh->public_len > ( tls->server_key_len - sizeof ( *ecdh ) ))){ + DBGC ( tls, "TLS %p received underlength ServerKeyExchange\n", + tls ); + DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len ); + return -EINVAL_KEY_EXCHANGE; + } + param_len = ( sizeof ( *ecdh ) + ecdh->public_len ); + + /* Verify parameter signature */ + if ( ( rc = tls_verify_dh_params ( tls, param_len ) ) != 0 ) + return rc; + + /* Identify named curve */ + if ( ecdh->curve_type != TLS_NAMED_CURVE_TYPE ) { + DBGC ( tls, "TLS %p unsupported curve type %d\n", + tls, ecdh->curve_type ); + DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len ); + return -ENOTSUP_CURVE; + } + curve = tls_find_named_curve ( ecdh->named_curve ); + if ( ! curve ) { + DBGC ( tls, "TLS %p unsupported named curve %d\n", + tls, ntohs ( ecdh->named_curve ) ); + DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len ); + return -ENOTSUP_CURVE; + } + + /* Check key length */ + if ( ecdh->public_len != curve->curve->keysize ) { + DBGC ( tls, "TLS %p invalid %s key\n", + tls, curve->curve->name ); + DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len ); + return -EINVAL_KEY_EXCHANGE; + } + + /* Construct pre-master secret and ClientKeyExchange record */ + { + size_t len = curve->curve->keysize; + uint8_t private[len]; + uint8_t pre_master_secret[len]; + struct { + uint32_t type_length; + uint8_t public_len; + uint8_t public[len]; + } __attribute__ (( packed )) key_xchg; + + /* Generate ephemeral private key */ + if ( ( rc = tls_generate_random ( tls, private, + sizeof ( private ) ) ) != 0){ + return rc; + } + + /* Calculate pre-master secret */ + if ( ( rc = elliptic_multiply ( curve->curve, + ecdh->public, private, + pre_master_secret ) ) != 0 ) { + DBGC ( tls, "TLS %p could not exchange ECDHE key: %s\n", + tls, strerror ( rc ) ); + return rc; + } + + /* Generate master secret */ + tls_generate_master_secret ( tls, pre_master_secret, len ); + + /* Generate Client Key Exchange record */ + key_xchg.type_length = + ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) | + htonl ( sizeof ( key_xchg ) - + sizeof ( key_xchg.type_length ) ) ); + key_xchg.public_len = len; + if ( ( rc = elliptic_multiply ( curve->curve, NULL, private, + key_xchg.public ) ) != 0 ) { + DBGC ( tls, "TLS %p could not generate ECDHE key: %s\n", + tls, strerror ( rc ) ); + return rc; + } + + /* Transmit Client Key Exchange record */ + if ( ( rc = tls_send_handshake ( tls, &key_xchg, + sizeof ( key_xchg ) ) ) !=0){ + return rc; + } + } + + return 0; +} + +/** Ephemeral Elliptic Curve Diffie-Hellman key exchange algorithm */ +struct tls_key_exchange_algorithm tls_ecdhe_exchange_algorithm = { + .name = "ecdhe", + .exchange = tls_send_client_key_exchange_ecdhe, +}; + +/** * Transmit Client Key Exchange record * * @v tls TLS connection @@ -1581,9 +1785,23 @@ struct tls_key_exchange_algorithm tls_dhe_exchange_algorithm = { static int tls_send_client_key_exchange ( struct tls_connection *tls ) { struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; struct tls_cipher_suite *suite = cipherspec->suite; + int rc; /* Transmit Client Key Exchange record via key exchange algorithm */ - return suite->exchange->exchange ( tls ); + if ( ( rc = suite->exchange->exchange ( tls ) ) != 0 ) { + DBGC ( tls, "TLS %p could not exchange keys: %s\n", + tls, strerror ( rc ) ); + return rc; + } + + /* Generate keys from master secret */ + if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) { + DBGC ( tls, "TLS %p could not generate keys: %s\n", + tls, strerror ( rc ) ); + return rc; + } + + return 0; } /** @@ -2727,9 +2945,9 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, } __attribute__ (( packed )) iv; struct tls_auth_header authhdr; struct tls_header *tlshdr; - void *plaintext = NULL; - size_t plaintext_len = len; - struct io_buffer *ciphertext = NULL; + void *plaintext; + size_t plaintext_len; + struct io_buffer *ciphertext; size_t ciphertext_len; size_t padding_len; uint8_t mac[digest->digestsize]; @@ -2738,7 +2956,10 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, /* Construct initialisation vector */ memcpy ( iv.fixed, cipherspec->fixed_iv, sizeof ( iv.fixed ) ); - tls_generate_random ( tls, iv.record, sizeof ( iv.record ) ); + if ( ( rc = tls_generate_random ( tls, iv.record, + sizeof ( iv.record ) ) ) != 0 ) { + goto err_random; + } /* Construct authentication data */ authhdr.seq = cpu_to_be64 ( tls->tx_seq ); @@ -2747,7 +2968,7 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, authhdr.header.length = htons ( len ); /* Calculate padding length */ - plaintext_len += suite->mac_len; + plaintext_len = ( len + suite->mac_len ); if ( is_block_cipher ( cipher ) ) { padding_len = ( ( ( cipher->blocksize - 1 ) & -( plaintext_len + 1 ) ) + 1 ); @@ -2762,7 +2983,7 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, DBGC ( tls, "TLS %p could not allocate %zd bytes for " "plaintext\n", tls, plaintext_len ); rc = -ENOMEM_TX_PLAINTEXT; - goto done; + goto err_plaintext; } /* Assemble plaintext */ @@ -2796,7 +3017,7 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, DBGC ( tls, "TLS %p could not allocate %zd bytes for " "ciphertext\n", tls, ciphertext_len ); rc = -ENOMEM_TX_CIPHERTEXT; - goto done; + goto err_ciphertext; } /* Assemble ciphertext */ @@ -2821,15 +3042,22 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, iob_disown ( ciphertext ) ) ) != 0 ) { DBGC ( tls, "TLS %p could not deliver ciphertext: %s\n", tls, strerror ( rc ) ); - goto done; + goto err_deliver; } /* Update TX state machine to next record */ tls->tx_seq += 1; - done: - free ( plaintext ); + assert ( plaintext == NULL ); + assert ( ciphertext == NULL ); + return 0; + + err_deliver: free_iob ( ciphertext ); + err_ciphertext: + free ( plaintext ); + err_plaintext: + err_random: return rc; } diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index bd2c4a19..8e2e97f1 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -91,9 +91,10 @@ static uint8_t dhcp_request_options_data[] = { DHCP_PARAMETER_REQUEST_LIST, DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS, DHCP_LOG_SERVERS, DHCP_HOST_NAME, DHCP_DOMAIN_NAME, - DHCP_ROOT_PATH, DHCP_MTU, DHCP_VENDOR_ENCAP, - DHCP_VENDOR_CLASS_ID, DHCP_TFTP_SERVER_NAME, - DHCP_BOOTFILE_NAME, DHCP_DOMAIN_SEARCH, + DHCP_ROOT_PATH, DHCP_MTU, DHCP_NTP_SERVERS, + DHCP_VENDOR_ENCAP, DHCP_VENDOR_CLASS_ID, + DHCP_TFTP_SERVER_NAME, DHCP_BOOTFILE_NAME, + DHCP_DOMAIN_SEARCH, 128, 129, 130, 131, 132, 133, 134, 135, /* for PXE */ DHCP_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ), DHCP_END diff --git a/src/net/udp/ntp.c b/src/net/udp/ntp.c index 11f8ccc0..55923357 100644 --- a/src/net/udp/ntp.c +++ b/src/net/udp/ntp.c @@ -36,6 +36,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/timer.h> #include <ipxe/time.h> #include <ipxe/tcpip.h> +#include <ipxe/dhcp.h> +#include <ipxe/settings.h> #include <ipxe/ntp.h> /** @file @@ -273,3 +275,11 @@ int start_ntp ( struct interface *job, const char *hostname ) { err_alloc: return rc; } + +/** IPv4 NTP server setting */ +const struct setting ntp_setting __setting ( SETTING_IP4_EXTRA, ntp ) = { + .name = "ntp", + .description = "NTP server", + .tag = DHCP_NTP_SERVERS, + .type = &setting_type_ipv4, +}; diff --git a/src/net/validator.c b/src/net/validator.c index 693d4464..69c0df33 100644 --- a/src/net/validator.c +++ b/src/net/validator.c @@ -57,8 +57,7 @@ struct validator_action { /** Name */ const char *name; /** Action to take upon completed transfer */ - int ( * done ) ( struct validator *validator, const void *data, - size_t len ); + void ( * done ) ( struct validator *validator, int rc ); }; /** A certificate validator */ @@ -72,6 +71,40 @@ struct validator { /** Process */ struct process process; + /** Most relevant status code + * + * The cross-signed certificate mechanism may attempt several + * downloads as it works its way up the provided partial chain + * to locate a suitable cross-signed certificate with which to + * complete the chain. + * + * Some of these download or validation attempts may fail for + * uninteresting reasons (i.e. because a cross-signed + * certificate has never existed for that link in the chain). + * + * We must therefore keep track of the most relevant error + * that has occurred, in order to be able to report a + * meaningful overall status to the user. + * + * As a concrete example: consider the case of an expired OCSP + * signer for an intermediate certificate. This will cause + * OCSP validation to fail for that intermediate certificate, + * and this is the error that should eventually be reported to + * the user. We do not want to instead report the + * uninteresting fact that no cross-signed certificate was + * found for the remaining links in the chain, nor do we want + * to report just a generic "OCSP required" error. + * + * We record the most relevant status code whenever a + * definitely relevant error occurs, and clear it whenever we + * successfully make forward progress (e.g. by completing + * OCSP, or by adding new cross-signed certificates). + * + * When we subsequently attempt to validate the chain, we + * report the most relevant error status code (if recorded), + * otherwise we report the validation error itself. + */ + int rc; /** Root of trust (or NULL to use default) */ struct x509_root *root; @@ -84,13 +117,15 @@ struct validator { /** Current action */ const struct validator_action *action; - /** Current certificate + /** Current certificate (for progress reporting) * * 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; + /** Current link within certificate chain */ + struct x509_link *link; }; /** @@ -196,17 +231,36 @@ static const char crosscert_default[] = CROSSCERT; * Append cross-signing certificates to certificate chain * * @v validator Certificate validator - * @v data Raw cross-signing certificate data - * @v len Length of raw data + * @v rc Completion status code * @ret rc Return status code */ -static int validator_append ( struct validator *validator, - const void *data, size_t len ) { +static void validator_append ( struct validator *validator, int rc ) { struct asn1_cursor cursor; struct x509_chain *certs; struct x509_certificate *cert; - struct x509_certificate *last; - int rc; + struct x509_link *link; + struct x509_link *prev; + + /* Check for errors */ + if ( rc != 0 ) { + DBGC ( validator, "VALIDATOR %p \"%s\" could not download ", + validator, validator_name ( validator ) ); + DBGC ( validator, "\"%s\" cross-signature: %s\n", + x509_name ( validator->cert ), strerror ( rc ) ); + /* If the overall validation is going to fail, then we + * will end up attempting multiple downloads for + * non-existent cross-signed certificates as we work + * our way up the certificate chain. Do not record + * these as relevant errors, since we want to + * eventually report whichever much more relevant + * error occurred previously. + */ + goto err_irrelevant; + } + DBGC ( validator, "VALIDATOR %p \"%s\" downloaded ", + validator, validator_name ( validator ) ); + DBGC ( validator, "\"%s\" cross-signature\n", + x509_name ( validator->cert ) ); /* Allocate certificate list */ certs = x509_alloc_chain(); @@ -216,8 +270,8 @@ static int validator_append ( struct validator *validator, } /* Initialise cursor */ - cursor.data = data; - cursor.len = len; + cursor.data = validator->buffer.data; + cursor.len = validator->buffer.len; /* Enter certificateSet */ if ( ( rc = asn1_enter ( &cursor, ASN1_SET ) ) != 0 ) { @@ -230,14 +284,14 @@ static int validator_append ( struct validator *validator, /* Add each certificate to list */ while ( cursor.len ) { - /* Add certificate to chain */ + /* Add certificate to list */ if ( ( rc = x509_append_raw ( certs, cursor.data, cursor.len ) ) != 0 ) { 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; + goto err_append_raw; } cert = x509_last ( certs ); DBGC ( validator, "VALIDATOR %p \"%s\" found certificate ", @@ -248,8 +302,12 @@ static int validator_append ( struct validator *validator, asn1_skip_any ( &cursor ); } + /* Truncate existing certificate chain at current link */ + link = validator->link; + assert ( link->flags & X509_LINK_FL_CROSSED ); + x509_truncate ( validator->chain, link ); + /* Append certificates to chain */ - last = x509_last ( validator->chain ); if ( ( rc = x509_auto_append ( validator->chain, certs ) ) != 0 ) { DBGC ( validator, "VALIDATOR %p \"%s\" could not append " "certificates: %s\n", validator, @@ -257,26 +315,31 @@ static int validator_append ( struct validator *validator, goto err_auto_append; } - /* Check that at least one certificate has been added */ - if ( last == x509_last ( validator->chain ) ) { - DBGC ( validator, "VALIDATOR %p \"%s\" failed to append any " - "applicable certificates\n", validator, - validator_name ( validator ) ); - rc = -EACCES; - goto err_no_progress; + /* Record that a cross-signed certificate download has already + * been performed for all but the last of the appended + * certificates. (It may be necessary to perform a further + * download to complete the chain, if this download did not + * extend all the way to a root of trust.) + */ + prev = NULL; + list_for_each_entry_continue ( link, &validator->chain->links, list ) { + if ( prev ) + prev->flags |= X509_LINK_FL_CROSSED; + prev = link; } - /* Drop reference to certificate list */ - x509_chain_put ( certs ); - - return 0; + /* Success */ + rc = 0; - err_no_progress: err_auto_append: + err_append_raw: err_certificateset: x509_chain_put ( certs ); err_alloc_certs: - return rc; + validator->rc = rc; + err_irrelevant: + /* Do not record irrelevant errors */ + return; } /** Cross-signing certificate download validator action */ @@ -289,11 +352,12 @@ static const struct validator_action validator_crosscert = { * Start download of cross-signing certificate * * @v validator Certificate validator - * @v cert X.509 certificate + * @v link Link in certificate chain * @ret rc Return status code */ static int validator_start_download ( struct validator *validator, - struct x509_certificate *cert ) { + struct x509_link *link ) { + struct x509_certificate *cert = link->cert; const struct asn1_cursor *issuer = &cert->issuer.raw; const char *crosscert; char *crosscert_copy; @@ -336,6 +400,7 @@ static int validator_start_download ( struct validator *validator, /* Set completion handler */ validator->action = &validator_crosscert; validator->cert = cert; + validator->link = link; /* Open URI */ if ( ( rc = xfer_open_uri_string ( &validator->xfer, @@ -346,14 +411,20 @@ static int validator_start_download ( struct validator *validator, goto err_open_uri_string; } + /* Free temporary allocations */ + free ( uri_string ); + free ( crosscert_copy ); + /* Success */ - rc = 0; + return 0; + intf_restart ( &validator->xfer, rc ); err_open_uri_string: free ( uri_string ); err_alloc_uri_string: err_check_uri_string: free ( crosscert_copy ); + validator->rc = rc; return rc; } @@ -367,21 +438,27 @@ static int validator_start_download ( struct validator *validator, * Validate OCSP response * * @v validator Certificate validator - * @v data Raw OCSP response - * @v len Length of raw data - * @ret rc Return status code + * @v rc Completion status code */ -static int validator_ocsp_validate ( struct validator *validator, - const void *data, size_t len ) { +static void validator_ocsp_validate ( struct validator *validator, int rc ) { + const void *data = validator->buffer.data; + size_t len = validator->buffer.len; time_t now; - int rc; + + /* Check for errors */ + if ( rc != 0 ) { + DBGC ( validator, "VALIDATOR %p \"%s\" could not fetch OCSP " + "response: %s\n", validator, + validator_name ( validator ), strerror ( rc ) ); + goto err_status; + } /* Record OCSP response */ if ( ( rc = ocsp_response ( validator->ocsp, data, len ) ) != 0 ) { DBGC ( validator, "VALIDATOR %p \"%s\" could not record OCSP " "response: %s\n", validator, - validator_name ( validator ),strerror ( rc ) ); - return rc; + validator_name ( validator ), strerror ( rc ) ); + goto err_response; } /* Validate OCSP response */ @@ -390,14 +467,20 @@ static int validator_ocsp_validate ( struct validator *validator, DBGC ( validator, "VALIDATOR %p \"%s\" could not validate " "OCSP response: %s\n", validator, validator_name ( validator ), strerror ( rc ) ); - return rc; + goto err_validate; } - /* Drop reference to OCSP check */ + /* Success */ + DBGC ( validator, "VALIDATOR %p \"%s\" checked ", + validator, validator_name ( validator ) ); + DBGC ( validator, "\"%s\" via OCSP\n", x509_name ( validator->cert ) ); + + err_validate: + err_response: + err_status: ocsp_put ( validator->ocsp ); validator->ocsp = NULL; - - return 0; + validator->rc = rc; } /** OCSP validator action */ @@ -426,7 +509,7 @@ static int validator_start_ocsp ( struct validator *validator, DBGC ( validator, "VALIDATOR %p \"%s\" could not create OCSP " "check: %s\n", validator, validator_name ( validator ), strerror ( rc ) ); - return rc; + goto err_check; } /* Set completion handler */ @@ -444,10 +527,18 @@ static int validator_start_ocsp ( struct validator *validator, DBGC ( validator, "VALIDATOR %p \"%s\" could not open %s: " "%s\n", validator, validator_name ( validator ), uri_string, strerror ( rc ) ); - return rc; + goto err_open; } return 0; + + intf_restart ( &validator->xfer, rc ); + err_open: + ocsp_put ( validator->ocsp ); + validator->ocsp = NULL; + err_check: + validator->rc = rc; + return rc; } /**************************************************************************** @@ -466,34 +557,18 @@ static void validator_xfer_close ( struct validator *validator, int rc ) { /* Close data transfer interface */ intf_restart ( &validator->xfer, rc ); - - /* Check for errors */ - if ( rc != 0 ) { - DBGC ( validator, "VALIDATOR %p \"%s\" transfer failed: %s\n", - validator, validator_name ( validator ), - strerror ( rc ) ); - goto err_transfer; - } DBGC2 ( validator, "VALIDATOR %p \"%s\" transfer complete\n", validator, validator_name ( validator ) ); /* Process completed download */ assert ( validator->action != NULL ); - if ( ( rc = validator->action->done ( validator, validator->buffer.data, - validator->buffer.len ) ) != 0 ) - goto err_append; + validator->action->done ( validator, rc ); /* Free downloaded data */ xferbuf_free ( &validator->buffer ); /* Resume validation process */ process_add ( &validator->process ); - - return; - - err_append: - err_transfer: - validator_finished ( validator, rc ); } /** @@ -515,7 +590,7 @@ static int validator_xfer_deliver ( struct validator *validator, DBGC ( validator, "VALIDATOR %p \"%s\" could not receive " "data: %s\n", validator, validator_name ( validator ), strerror ( rc ) ); - validator_finished ( validator, rc ); + validator_xfer_close ( validator, rc ); return rc; } @@ -544,10 +619,10 @@ static struct interface_descriptor validator_xfer_desc = * @v validator Certificate validator */ static void validator_step ( struct validator *validator ) { + struct x509_chain *chain = validator->chain; struct x509_link *link; + struct x509_link *prev; struct x509_certificate *cert; - struct x509_certificate *issuer = NULL; - struct x509_certificate *last; time_t now; int rc; @@ -556,57 +631,109 @@ static void validator_step ( struct validator *validator ) { * previously. */ now = time ( NULL ); - if ( ( rc = x509_validate_chain ( validator->chain, now, NULL, + if ( ( rc = x509_validate_chain ( chain, now, NULL, validator->root ) ) == 0 ) { DBGC ( validator, "VALIDATOR %p \"%s\" validated\n", validator, validator_name ( validator ) ); validator_finished ( validator, 0 ); return; } + DBGC ( validator, "VALIDATOR %p \"%s\" not yet valid: %s\n", + validator, validator_name ( validator ), strerror ( rc ) ); - /* If there is a certificate that could be validated using - * OCSP, try it. + /* Record as the most relevant error, if no more relevant + * error has already been recorded. */ - list_for_each_entry ( link, &validator->chain->links, list ) { - cert = issuer; - issuer = link->cert; - if ( ! cert ) - continue; - if ( ! x509_is_valid ( issuer, validator->root ) ) - continue; - /* The issuer is valid, but this certificate is not - * yet valid. If OCSP is applicable, start it. - */ - if ( ocsp_required ( cert ) ) { - /* Start OCSP */ - if ( ( rc = validator_start_ocsp ( validator, cert, - issuer ) ) != 0 ) { - validator_finished ( validator, rc ); - return; - } - return; - } - /* Otherwise, this is a permanent failure */ - validator_finished ( validator, rc ); - return; + if ( validator->rc == 0 ) + validator->rc = rc; + + /* Find the first valid link in the chain, if any + * + * There is no point in attempting OCSP or cross-signed + * certificate downloads for certificates after the first + * valid link in the chain, since they cannot make a + * difference to the overall validation of the chain. + */ + prev = NULL; + list_for_each_entry ( link, &chain->links, list ) { + + /* Dump link information (for debugging) */ + DBGC ( validator, "VALIDATOR %p \"%s\" has link ", + validator, validator_name ( validator ) ); + DBGC ( validator, "\"%s\"%s%s%s%s%s\n", + x509_name ( link->cert ), + ( ocsp_required ( link->cert ) ? " [NEEDOCSP]" : "" ), + ( ( link->flags & X509_LINK_FL_OCSPED ) ? + " [OCSPED]" : "" ), + ( ( link->flags & X509_LINK_FL_CROSSED ) ? + " [CROSSED]" : "" ), + ( x509_is_self_signed ( link->cert ) ? " [SELF]" : "" ), + ( x509_is_valid ( link->cert, validator->root ) ? + " [VALID]" : "" ) ); + + /* Stop at first valid link */ + if ( x509_is_valid ( link->cert, validator->root ) ) + break; + prev = link; } - /* If chain ends with a self-issued certificate, then there is - * nothing more to do. + /* If this link is the issuer for a certificate that is + * pending an OCSP check attempt, then start OCSP to validate + * that certificate. + * + * If OCSP is not required for the issued certificate, or has + * already been attempted, or if we were unable to start OCSP + * for any reason, then proceed to attempting a cross-signed + * certificate download (which may end up replacing this + * issuer anyway). */ - last = x509_last ( validator->chain ); - if ( asn1_compare ( &last->issuer.raw, &last->subject.raw ) == 0 ) { - validator_finished ( validator, rc ); - return; + if ( ( ! list_is_head_entry ( link, &chain->links, list ) ) && + ( ! ( link->flags & X509_LINK_FL_OCSPED ) ) && + ( prev != NULL ) && ocsp_required ( prev->cert ) ) { + + /* Mark OCSP as attempted with this issuer */ + link->flags |= X509_LINK_FL_OCSPED; + + /* Start OCSP */ + if ( ( rc = validator_start_ocsp ( validator, prev->cert, + link->cert ) ) == 0 ) { + /* Sleep until OCSP is complete */ + return; + } } - /* Otherwise, try to download a suitable cross-signing - * certificate. + /* Work back up the chain (starting from the already + * identified first valid link, if any) to find a not-yet + * valid certificate for which we could attempt to download a + * cross-signed certificate chain. */ - if ( ( rc = validator_start_download ( validator, last ) ) != 0 ) { - validator_finished ( validator, rc ); - return; + list_for_each_entry_continue_reverse ( link, &chain->links, list ) { + cert = link->cert; + + /* Sanity check */ + assert ( ! x509_is_valid ( cert, validator->root ) ); + + /* Skip self-signed certificates (cannot be cross-signed) */ + if ( x509_is_self_signed ( cert ) ) + continue; + + /* Skip previously attempted cross-signed downloads */ + if ( link->flags & X509_LINK_FL_CROSSED ) + continue; + + /* Mark cross-signed certificate download as attempted */ + link->flags |= X509_LINK_FL_CROSSED; + + /* Start cross-signed certificate download */ + if ( ( rc = validator_start_download ( validator, + link ) ) == 0 ) { + /* Sleep until download is complete */ + return; + } } + + /* Nothing more to try: fail the validation */ + validator_finished ( validator, validator->rc ); } /** Certificate validator process descriptor */ diff --git a/src/net/vlan.c b/src/net/vlan.c index d73a9571..c61bb850 100644 --- a/src/net/vlan.c +++ b/src/net/vlan.c @@ -470,9 +470,10 @@ void vlan_auto ( const void *ll_addr, unsigned int tag ) { * Create automatic VLAN device * * @v trunk Trunk network device + * @v priv Private data * @ret rc Return status code */ -static int vlan_probe ( struct net_device *trunk ) { +static int vlan_probe ( struct net_device *trunk, void *priv __unused ) { int rc; /* Do nothing unless an automatic VLAN exists */ @@ -498,8 +499,9 @@ static int vlan_probe ( struct net_device *trunk ) { * Handle trunk network device link state change * * @v trunk Trunk network device + * @v priv Private data */ -static void vlan_notify ( struct net_device *trunk ) { +static void vlan_notify ( struct net_device *trunk, void *priv __unused ) { struct net_device *netdev; struct vlan_device *vlan; @@ -538,8 +540,9 @@ static int vlan_remove_first ( struct net_device *trunk ) { * Destroy all VLAN devices for a given trunk * * @v trunk Trunk network device + * @v priv Private data */ -static void vlan_remove ( struct net_device *trunk ) { +static void vlan_remove ( struct net_device *trunk, void *priv __unused ) { /* Remove all VLAN devices attached to this trunk, safe * against arbitrary net device removal. |