summaryrefslogtreecommitdiffstats
path: root/src/net
diff options
context:
space:
mode:
authorSimon Rettberg2024-04-12 14:00:15 +0200
committerSimon Rettberg2024-04-12 14:00:15 +0200
commit98dc341428e247141f120d05fac48c4e144a4c0f (patch)
tree3ebacb37927e338383ac64c2e20eb0b2f820cb85 /src/net
parentMerge branch 'master' into openslx (diff)
parentMerge branch 'ipxe:master' into aqc1xx (diff)
downloadipxe-98dc341428e247141f120d05fac48c4e144a4c0f.tar.gz
ipxe-98dc341428e247141f120d05fac48c4e144a4c0f.tar.xz
ipxe-98dc341428e247141f120d05fac48c4e144a4c0f.zip
Merge branch 'aqc1xx' into openslx
Diffstat (limited to 'src/net')
-rw-r--r--src/net/aoe.c2
-rw-r--r--src/net/eap.c181
-rw-r--r--src/net/eap_md5.c116
-rw-r--r--src/net/eap_mschapv2.c251
-rw-r--r--src/net/eapol.c80
-rw-r--r--src/net/tls.c502
-rw-r--r--src/net/validator.c327
7 files changed, 1165 insertions, 294 deletions
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 beaeb61d..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,54 +37,183 @@ 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 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 eap_supplicant *supplicant ) {
+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 supplicant EAP supplicant
- * @v req EAP request
+ * @v msg EAP request
* @v len Length of EAP request
* @ret rc Return status code
*/
static int eap_rx_request ( struct eap_supplicant *supplicant,
- const struct eap_request *req, size_t len ) {
+ 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 ) );
- /* Mark authentication as incomplete */
- supplicant->done = 0;
+ /* 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 ( supplicant );
- 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;
}
/**
@@ -94,7 +226,7 @@ static int eap_rx_success ( struct eap_supplicant *supplicant ) {
struct net_device *netdev = supplicant->netdev;
/* Mark authentication as complete */
- supplicant->done = 1;
+ supplicant->flags = EAP_FL_PASSIVE;
/* Mark link as unblocked */
DBGC ( netdev, "EAP %s Success\n", netdev->name );
@@ -113,7 +245,7 @@ static int eap_rx_failure ( struct eap_supplicant *supplicant ) {
struct net_device *netdev = supplicant->netdev;
/* Mark authentication as complete */
- supplicant->done = 1;
+ supplicant->flags = EAP_FL_PASSIVE;
/* Record error */
DBGC ( netdev, "EAP %s Failure\n", netdev->name );
@@ -143,7 +275,10 @@ int eap_rx ( struct eap_supplicant *supplicant, const void *data,
/* Handle according to code */
switch ( eap->hdr.code ) {
case EAP_CODE_REQUEST:
- return eap_rx_request ( supplicant, &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 ( supplicant );
case EAP_CODE_FAILURE:
@@ -155,3 +290,9 @@ int eap_rx ( struct eap_supplicant *supplicant, const void *data,
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 1b843e89..8b09ca23 100644
--- a/src/net/eapol.c
+++ b/src/net/eapol.c
@@ -49,37 +49,6 @@ static const uint8_t eapol_mac[ETH_ALEN] = {
};
/**
- * Update EAPoL supplicant state
- *
- * @v supplicant EAPoL supplicant
- * @v timeout Timer ticks until next EAPoL-Start (if applicable)
- */
-static void eapol_update ( struct eapol_supplicant *supplicant,
- unsigned long timeout ) {
- struct net_device *netdev = supplicant->eap.netdev;
-
- /* Check device and EAP state */
- if ( netdev_is_open ( netdev ) && netdev_link_ok ( netdev ) ) {
- if ( supplicant->eap.done ) {
-
- /* EAP has completed: stop sending EAPoL-Start */
- stop_timer ( &supplicant->timer );
-
- } else if ( ! timer_running ( &supplicant->timer ) ) {
-
- /* EAP has not yet begun: start sending EAPoL-Start */
- start_timer_fixed ( &supplicant->timer, timeout );
- }
-
- } else {
-
- /* Not ready: clear completion and stop sending EAPoL-Start */
- supplicant->eap.done = 0;
- stop_timer ( &supplicant->timer );
- }
-}
-
-/**
* Process EAPoL packet
*
* @v iobuf I/O buffer
@@ -186,8 +155,20 @@ static int eapol_eap_rx ( struct eapol_supplicant *supplicant,
goto drop;
}
- /* Update supplicant state */
- eapol_update ( supplicant, EAPOL_START_INTERVAL );
+ /* 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 );
@@ -270,6 +251,12 @@ static void eapol_expired ( struct retry_timer *timer, int fail __unused ) {
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 );
@@ -309,15 +296,36 @@ static int eapol_probe ( struct net_device *netdev, void *priv ) {
* @v netdev Network device
* @v priv Private data
*/
-static void eapol_notify ( struct net_device *netdev __unused, void *priv ) {
+static void eapol_notify ( struct net_device *netdev, void *priv ) {
struct eapol_supplicant *supplicant = priv;
/* Ignore non-EAPoL devices */
if ( ! supplicant->eap.netdev )
return;
- /* Update supplicant state */
- eapol_update ( supplicant, 0 );
+ /* 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 */
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/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 */