summaryrefslogtreecommitdiffstats
path: root/src/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/net')
-rw-r--r--src/net/80211/net80211.c1
-rw-r--r--src/net/80211/rc80211.c1
-rw-r--r--src/net/80211/sec80211.c1
-rw-r--r--src/net/80211/wep.c1
-rw-r--r--src/net/80211/wpa.c1
-rw-r--r--src/net/80211/wpa_ccmp.c1
-rw-r--r--src/net/80211/wpa_psk.c1
-rw-r--r--src/net/80211/wpa_tkip.c1
-rw-r--r--src/net/aoe.c8
-rw-r--r--src/net/arp.c1
-rw-r--r--src/net/dhcpopts.c1
-rw-r--r--src/net/dhcppkt.c1
-rw-r--r--src/net/eap.c1
-rw-r--r--src/net/eap_md5.c1
-rw-r--r--src/net/eapol.c2
-rw-r--r--src/net/eth_slow.c1
-rw-r--r--src/net/ethernet.c1
-rw-r--r--src/net/fakedhcp.c1
-rw-r--r--src/net/fcoe.c1
-rw-r--r--src/net/fcp.c6
-rw-r--r--src/net/fragment.c1
-rw-r--r--src/net/icmp.c1
-rw-r--r--src/net/icmpv4.c1
-rw-r--r--src/net/icmpv6.c1
-rw-r--r--src/net/iobpad.c1
-rw-r--r--src/net/ipv4.c274
-rw-r--r--src/net/ipv6.c5
-rw-r--r--src/net/lldp.c2
-rw-r--r--src/net/ndp.c3
-rw-r--r--src/net/neighbour.c141
-rw-r--r--src/net/netdev_settings.c3
-rw-r--r--src/net/netdevice.c5
-rw-r--r--src/net/nullnet.c1
-rw-r--r--src/net/oncrpc/mount.c2
-rw-r--r--src/net/oncrpc/nfs.c2
-rw-r--r--src/net/oncrpc/nfs_open.c2
-rw-r--r--src/net/oncrpc/nfs_uri.c2
-rw-r--r--src/net/oncrpc/oncrpc_iob.c2
-rw-r--r--src/net/oncrpc/portmap.c2
-rw-r--r--src/net/pccrc.c7
-rw-r--r--src/net/pccrd.c1
-rw-r--r--src/net/peerblk.c2
-rw-r--r--src/net/peerdisc.c1
-rw-r--r--src/net/peerdist.c1
-rw-r--r--src/net/peermux.c9
-rw-r--r--src/net/ping.c1
-rw-r--r--src/net/retry.c1
-rw-r--r--src/net/rndis.c1
-rw-r--r--src/net/socket.c1
-rw-r--r--src/net/stp.c1
-rw-r--r--src/net/tcp.c23
-rw-r--r--src/net/tcp/http.c1
-rw-r--r--src/net/tcp/httpauth.c1
-rw-r--r--src/net/tcp/httpbasic.c1
-rw-r--r--src/net/tcp/httpblock.c7
-rw-r--r--src/net/tcp/httpconn.c1
-rw-r--r--src/net/tcp/httpcore.c66
-rw-r--r--src/net/tcp/httpdigest.c1
-rw-r--r--src/net/tcp/httpntlm.c1
-rw-r--r--src/net/tcp/https.c1
-rw-r--r--src/net/tcp/iscsi.c8
-rw-r--r--src/net/tcp/oncrpc.c2
-rw-r--r--src/net/tcp/syslogs.c2
-rw-r--r--src/net/tcpip.c1
-rw-r--r--src/net/tls.c1044
-rw-r--r--src/net/udp.c1
-rw-r--r--src/net/udp/dhcp.c3
-rw-r--r--src/net/udp/dhcpv6.c112
-rw-r--r--src/net/udp/dns.c1
-rw-r--r--src/net/udp/ntp.c1
-rw-r--r--src/net/udp/syslog.c2
-rw-r--r--src/net/udp/tftp.c1
-rw-r--r--src/net/validator.c7
-rw-r--r--src/net/vlan.c1
74 files changed, 1204 insertions, 595 deletions
diff --git a/src/net/80211/net80211.c b/src/net/80211/net80211.c
index 482000102..4391b4296 100644
--- a/src/net/80211/net80211.c
+++ b/src/net/80211/net80211.c
@@ -20,6 +20,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( FORBIDDEN );
#include <string.h>
#include <byteswap.h>
diff --git a/src/net/80211/rc80211.c b/src/net/80211/rc80211.c
index eea3bc908..d416867fa 100644
--- a/src/net/80211/rc80211.c
+++ b/src/net/80211/rc80211.c
@@ -20,6 +20,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( FORBIDDEN );
#include <stdlib.h>
#include <ipxe/net80211.h>
diff --git a/src/net/80211/sec80211.c b/src/net/80211/sec80211.c
index d1bc75e90..500dec9f5 100644
--- a/src/net/80211/sec80211.c
+++ b/src/net/80211/sec80211.c
@@ -18,6 +18,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( FORBIDDEN );
#include <stdlib.h>
#include <string.h>
diff --git a/src/net/80211/wep.c b/src/net/80211/wep.c
index e22ac8998..053cf1417 100644
--- a/src/net/80211/wep.c
+++ b/src/net/80211/wep.c
@@ -18,6 +18,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( FORBIDDEN );
#include <ipxe/net80211.h>
#include <ipxe/sec80211.h>
diff --git a/src/net/80211/wpa.c b/src/net/80211/wpa.c
index 17c11b8ed..33358221b 100644
--- a/src/net/80211/wpa.c
+++ b/src/net/80211/wpa.c
@@ -18,6 +18,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( FORBIDDEN );
#include <ipxe/net80211.h>
#include <ipxe/sec80211.h>
diff --git a/src/net/80211/wpa_ccmp.c b/src/net/80211/wpa_ccmp.c
index 0abd217e7..f4906d473 100644
--- a/src/net/80211/wpa_ccmp.c
+++ b/src/net/80211/wpa_ccmp.c
@@ -18,6 +18,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( FORBIDDEN );
#include <string.h>
#include <ipxe/net80211.h>
diff --git a/src/net/80211/wpa_psk.c b/src/net/80211/wpa_psk.c
index 71190b139..d86204026 100644
--- a/src/net/80211/wpa_psk.c
+++ b/src/net/80211/wpa_psk.c
@@ -18,6 +18,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( FORBIDDEN );
#include <string.h>
#include <ipxe/net80211.h>
diff --git a/src/net/80211/wpa_tkip.c b/src/net/80211/wpa_tkip.c
index 3bd651512..39a6391a8 100644
--- a/src/net/80211/wpa_tkip.c
+++ b/src/net/80211/wpa_tkip.c
@@ -18,6 +18,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( FORBIDDEN );
#include <string.h>
#include <ipxe/net80211.h>
diff --git a/src/net/aoe.c b/src/net/aoe.c
index dba4f51b5..edeb81867 100644
--- a/src/net/aoe.c
+++ b/src/net/aoe.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stddef.h>
#include <string.h>
@@ -33,7 +34,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/list.h>
#include <ipxe/if_ether.h>
#include <ipxe/iobuf.h>
-#include <ipxe/uaccess.h>
#include <ipxe/netdevice.h>
#include <ipxe/features.h>
#include <ipxe/interface.h>
@@ -391,8 +391,7 @@ static void aoecmd_ata_cmd ( struct aoe_command *aoecmd,
if ( ! command->cb.lba48 )
aoeata->lba.bytes[3] |=
( command->cb.device & ATA_DEV_MASK );
- copy_from_user ( aoeata->data, command->data_out, 0,
- command->data_out_len );
+ memcpy ( aoeata->data, command->data_out, command->data_out_len );
DBGC2 ( aoedev, "AoE %s/%08x ATA cmd %02x:%02x:%02x:%02x:%08llx",
aoedev_name ( aoedev ), aoecmd->tag, aoeata->aflags,
@@ -452,8 +451,7 @@ static int aoecmd_ata_rsp ( struct aoe_command *aoecmd, const void *data,
}
/* Copy out data payload */
- copy_to_user ( command->data_in, 0, aoeata->data,
- command->data_in_len );
+ memcpy ( command->data_in, aoeata->data, command->data_in_len );
return 0;
}
diff --git a/src/net/arp.c b/src/net/arp.c
index c9b4109a9..2bf3c12ec 100644
--- a/src/net/arp.c
+++ b/src/net/arp.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/net/dhcpopts.c b/src/net/dhcpopts.c
index cdb632b46..844a94d62 100644
--- a/src/net/dhcpopts.c
+++ b/src/net/dhcpopts.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/net/dhcppkt.c b/src/net/dhcppkt.c
index 4e64f85e4..a9b454695 100644
--- a/src/net/dhcppkt.c
+++ b/src/net/dhcppkt.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/net/eap.c b/src/net/eap.c
index 87327d723..040566b57 100644
--- a/src/net/eap.c
+++ b/src/net/eap.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <errno.h>
diff --git a/src/net/eap_md5.c b/src/net/eap_md5.c
index 0664174f9..1e263c8ec 100644
--- a/src/net/eap_md5.c
+++ b/src/net/eap_md5.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <string.h>
diff --git a/src/net/eapol.c b/src/net/eapol.c
index 8b09ca231..d83d63386 100644
--- a/src/net/eapol.c
+++ b/src/net/eapol.c
@@ -22,7 +22,9 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+#include <string.h>
#include <assert.h>
#include <errno.h>
#include <byteswap.h>
diff --git a/src/net/eth_slow.c b/src/net/eth_slow.c
index 1103a49f3..fb4e0d972 100644
--- a/src/net/eth_slow.c
+++ b/src/net/eth_slow.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <string.h>
diff --git a/src/net/ethernet.c b/src/net/ethernet.c
index 3fcdafe6d..60219b98f 100644
--- a/src/net/ethernet.c
+++ b/src/net/ethernet.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/net/fakedhcp.c b/src/net/fakedhcp.c
index 009b12c56..0020d8225 100644
--- a/src/net/fakedhcp.c
+++ b/src/net/fakedhcp.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/net/fcoe.c b/src/net/fcoe.c
index 9f3ddf88b..d54f1d431 100644
--- a/src/net/fcoe.c
+++ b/src/net/fcoe.c
@@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stddef.h>
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/if_ether.h>
diff --git a/src/net/fcp.c b/src/net/fcp.c
index f78f7bd9b..9701b5d54 100644
--- a/src/net/fcp.c
+++ b/src/net/fcp.c
@@ -413,7 +413,7 @@ static int fcpcmd_recv_rddata ( struct fcp_command *fcpcmd,
fcpdev, fcpcmd->xchg_id, offset, ( offset + len ) );
/* Copy to user buffer */
- copy_to_user ( command->data_in, offset, iobuf->data, len );
+ memcpy ( ( command->data_in + offset ), iobuf->data, len );
fcpcmd->offset += len;
assert ( fcpcmd->offset <= command->data_in_len );
@@ -464,8 +464,8 @@ static int fcpcmd_send_wrdata ( struct fcp_command *fcpcmd ) {
}
/* Construct data IU frame */
- copy_from_user ( iob_put ( iobuf, len ), command->data_out,
- fcpcmd->offset, len );
+ memcpy ( iob_put ( iobuf, len ),
+ ( command->data_out + fcpcmd->offset ), len );
memset ( &meta, 0, sizeof ( meta ) );
meta.flags = ( XFER_FL_RESPONSE | XFER_FL_ABS_OFFSET );
meta.offset = fcpcmd->offset;
diff --git a/src/net/fragment.c b/src/net/fragment.c
index 781b9bc60..4976167ed 100644
--- a/src/net/fragment.c
+++ b/src/net/fragment.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/net/icmp.c b/src/net/icmp.c
index 5371277e4..740b42440 100644
--- a/src/net/icmp.c
+++ b/src/net/icmp.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <byteswap.h>
diff --git a/src/net/icmpv4.c b/src/net/icmpv4.c
index 0858ff37f..ffcc4b375 100644
--- a/src/net/icmpv4.c
+++ b/src/net/icmpv4.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <errno.h>
diff --git a/src/net/icmpv6.c b/src/net/icmpv6.c
index 8555aaf0b..5331b81e8 100644
--- a/src/net/icmpv6.c
+++ b/src/net/icmpv6.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <errno.h>
diff --git a/src/net/iobpad.c b/src/net/iobpad.c
index 936b4bde4..6366efb5e 100644
--- a/src/net/iobpad.c
+++ b/src/net/iobpad.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
diff --git a/src/net/ipv4.c b/src/net/ipv4.c
index b91fa2ad0..f3dd44384 100644
--- a/src/net/ipv4.c
+++ b/src/net/ipv4.c
@@ -49,6 +49,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/* Unique IP datagram identification number (high byte) */
static uint8_t next_ident_high = 0;
@@ -77,17 +78,34 @@ static struct profiler ipv4_rx_profiler __profiler = { .name = "ipv4.rx" };
*
* @v netdev Network device
* @v address IPv4 address
+ * @v network Subnet address
* @v netmask Subnet mask
* @v gateway Gateway address (if any)
* @ret rc Return status code
*/
-static int add_ipv4_miniroute ( struct net_device *netdev,
- struct in_addr address, struct in_addr netmask,
+static int ipv4_add_miniroute ( struct net_device *netdev,
+ struct in_addr address,
+ struct in_addr network,
+ struct in_addr netmask,
struct in_addr gateway ) {
struct ipv4_miniroute *miniroute;
+ struct ipv4_miniroute *before;
+ struct in_addr hostmask;
+ struct in_addr broadcast;
+ /* Calculate host mask */
+ if ( gateway.s_addr || IN_IS_SMALL ( netmask.s_addr ) ) {
+ hostmask.s_addr = INADDR_NONE;
+ } else {
+ hostmask.s_addr = ~netmask.s_addr;
+ }
+ broadcast.s_addr = ( network.s_addr | hostmask.s_addr );
+
+ /* Print debugging information */
DBGC ( netdev, "IPv4 add %s", inet_ntoa ( address ) );
+ DBGC ( netdev, " for %s", inet_ntoa ( network ) );
DBGC ( netdev, "/%s ", inet_ntoa ( netmask ) );
+ DBGC ( netdev, "bc %s ", inet_ntoa ( broadcast ) );
if ( gateway.s_addr )
DBGC ( netdev, "gw %s ", inet_ntoa ( gateway ) );
DBGC ( netdev, "via %s\n", netdev->name );
@@ -102,30 +120,159 @@ static int add_ipv4_miniroute ( struct net_device *netdev,
/* Record routing information */
miniroute->netdev = netdev_get ( netdev );
miniroute->address = address;
+ miniroute->network = network;
miniroute->netmask = netmask;
+ miniroute->hostmask = hostmask;
miniroute->gateway = gateway;
-
- /* Add to end of list if we have a gateway, otherwise
- * to start of list.
- */
- if ( gateway.s_addr ) {
- list_add_tail ( &miniroute->list, &ipv4_miniroutes );
- } else {
- list_add ( &miniroute->list, &ipv4_miniroutes );
+
+ /* Add to routing table ahead of any less specific routes */
+ list_for_each_entry ( before, &ipv4_miniroutes, list ) {
+ if ( netmask.s_addr & ~before->netmask.s_addr )
+ break;
+ }
+ list_add_tail ( &miniroute->list, &before->list );
+
+ return 0;
+}
+
+/**
+ * Add static route minirouting table entries
+ *
+ * @v netdev Network device
+ * @v address IPv4 address
+ * @v routes Static routes
+ * @v len Length of static routes
+ * @ret rc Return status code
+ */
+static int ipv4_add_static ( struct net_device *netdev, struct in_addr address,
+ const void *routes, size_t len ) {
+ const struct {
+ struct in_addr address;
+ } __attribute__ (( packed )) *encoded;
+ struct in_addr netmask;
+ struct in_addr network;
+ struct in_addr gateway;
+ unsigned int width;
+ unsigned int masklen;
+ size_t remaining;
+ const void *data;
+ int rc;
+
+ /* Parse and add static routes */
+ for ( data = routes, remaining = len ; remaining ; ) {
+
+ /* Extract subnet mask width */
+ width = *( ( uint8_t * ) data );
+ data++;
+ remaining--;
+ masklen = ( ( width + 7 ) / 8 );
+
+ /* Check remaining length */
+ if ( ( masklen + sizeof ( gateway ) ) > remaining ) {
+ DBGC ( netdev, "IPv4 invalid static route:\n" );
+ DBGC_HDA ( netdev, 0, routes, len );
+ return -EINVAL;
+ }
+
+ /* Calculate subnet mask */
+ if ( width ) {
+ netmask.s_addr = htonl ( -1UL << ( 32 - width ) );
+ } else {
+ netmask.s_addr = 0;
+ }
+
+ /* Extract network address */
+ encoded = data;
+ network.s_addr = ( encoded->address.s_addr & netmask.s_addr );
+ data += masklen;
+ remaining -= masklen;
+
+ /* Extract gateway address */
+ encoded = data;
+ gateway.s_addr = encoded->address.s_addr;
+ data += sizeof ( gateway );
+ remaining -= sizeof ( gateway );
+
+ /* Add route */
+ if ( ( rc = ipv4_add_miniroute ( netdev, address, network,
+ netmask, gateway ) ) != 0 )
+ return rc;
}
return 0;
}
/**
+ * Add IPv4 minirouting table entries
+ *
+ * @v netdev Network device
+ * @v address IPv4 address
+ * @ret rc Return status code
+ */
+static int ipv4_add_miniroutes ( struct net_device *netdev,
+ struct in_addr address ) {
+ struct settings *settings = netdev_settings ( netdev );
+ struct in_addr none = { 0 };
+ struct in_addr netmask;
+ struct in_addr gateway;
+ struct in_addr network;
+ void *routes;
+ int len;
+ int rc;
+
+ /* Get subnet mask */
+ fetch_ipv4_setting ( settings, &netmask_setting, &netmask );
+
+ /* Calculate default netmask, if necessary */
+ if ( ! netmask.s_addr ) {
+ if ( IN_IS_CLASSA ( address.s_addr ) ) {
+ netmask.s_addr = INADDR_NET_CLASSA;
+ } else if ( IN_IS_CLASSB ( address.s_addr ) ) {
+ netmask.s_addr = INADDR_NET_CLASSB;
+ } else if ( IN_IS_CLASSC ( address.s_addr ) ) {
+ netmask.s_addr = INADDR_NET_CLASSC;
+ }
+ }
+
+ /* Get default gateway, if present */
+ fetch_ipv4_setting ( settings, &gateway_setting, &gateway );
+
+ /* Get static routes, if present */
+ len = fetch_raw_setting_copy ( settings, &static_route_setting,
+ &routes );
+
+ /* Add local address */
+ network.s_addr = ( address.s_addr & netmask.s_addr );
+ if ( ( rc = ipv4_add_miniroute ( netdev, address, network, netmask,
+ none ) ) != 0 )
+ goto done;
+
+ /* Add static routes or default gateway, as applicable */
+ if ( len >= 0 ) {
+ if ( ( rc = ipv4_add_static ( netdev, address, routes,
+ len ) ) != 0 )
+ goto done;
+ } else if ( gateway.s_addr ) {
+ if ( ( rc = ipv4_add_miniroute ( netdev, address, none, none,
+ gateway ) ) != 0 )
+ goto done;
+ }
+
+ done:
+ free ( routes );
+ return rc;
+}
+
+/**
* Delete IPv4 minirouting table entry
*
* @v miniroute Routing table entry
*/
-static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) {
+static void ipv4_del_miniroute ( struct ipv4_miniroute *miniroute ) {
struct net_device *netdev = miniroute->netdev;
DBGC ( netdev, "IPv4 del %s", inet_ntoa ( miniroute->address ) );
+ DBGC ( netdev, " for %s", inet_ntoa ( miniroute->network ) );
DBGC ( netdev, "/%s ", inet_ntoa ( miniroute->netmask ) );
if ( miniroute->gateway.s_addr )
DBGC ( netdev, "gw %s ", inet_ntoa ( miniroute->gateway ) );
@@ -137,6 +284,19 @@ static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) {
}
/**
+ * Delete IPv4 minirouting table entries
+ *
+ */
+static void ipv4_del_miniroutes ( void ) {
+ struct ipv4_miniroute *miniroute;
+ struct ipv4_miniroute *tmp;
+
+ /* Delete all existing routes */
+ list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list )
+ ipv4_del_miniroute ( miniroute );
+}
+
+/**
* Perform IPv4 routing
*
* @v scope_id Destination address scope ID
@@ -147,8 +307,8 @@ static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) {
* If the route requires use of a gateway, the next hop destination
* address will be overwritten with the gateway address.
*/
-static struct ipv4_miniroute * ipv4_route ( unsigned int scope_id,
- struct in_addr *dest ) {
+struct ipv4_miniroute * ipv4_route ( unsigned int scope_id,
+ struct in_addr *dest ) {
struct ipv4_miniroute *miniroute;
/* Find first usable route in routing table */
@@ -160,27 +320,23 @@ static struct ipv4_miniroute * ipv4_route ( unsigned int scope_id,
if ( IN_IS_MULTICAST ( dest->s_addr ) ) {
- /* If destination is non-global, and the scope ID
- * matches this network device, then use this route.
+ /* If destination is non-global, and the scope
+ * ID matches this network device, then use
+ * the first matching route.
*/
if ( miniroute->netdev->scope_id == scope_id )
return miniroute;
} else {
- /* If destination is an on-link global
- * address, then use this route.
- */
- if ( ( ( dest->s_addr ^ miniroute->address.s_addr )
- & miniroute->netmask.s_addr ) == 0 )
- return miniroute;
-
- /* If destination is an off-link global
- * address, and we have a default gateway,
- * then use this route.
+ /* If destination is global, then use the
+ * first matching route (via its gateway if
+ * specified).
*/
- if ( miniroute->gateway.s_addr ) {
- *dest = miniroute->gateway;
+ if ( ( ( dest->s_addr ^ miniroute->network.s_addr )
+ & miniroute->netmask.s_addr ) == 0 ) {
+ if ( miniroute->gateway.s_addr )
+ *dest = miniroute->gateway;
return miniroute;
}
}
@@ -310,7 +466,7 @@ static int ipv4_tx ( struct io_buffer *iobuf,
struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
struct ipv4_miniroute *miniroute;
struct in_addr next_hop;
- struct in_addr netmask = { .s_addr = 0 };
+ struct in_addr hostmask = { .s_addr = INADDR_NONE };
uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
const void *ll_dest;
int rc;
@@ -338,7 +494,7 @@ static int ipv4_tx ( struct io_buffer *iobuf,
( ( miniroute = ipv4_route ( sin_dest->sin_scope_id,
&next_hop ) ) != NULL ) ) {
iphdr->src = miniroute->address;
- netmask = miniroute->netmask;
+ hostmask = miniroute->hostmask;
netdev = miniroute->netdev;
}
if ( ! netdev ) {
@@ -373,7 +529,7 @@ static int ipv4_tx ( struct io_buffer *iobuf,
ntohs ( iphdr->chksum ) );
/* Calculate link-layer destination address, if possible */
- if ( ( ( next_hop.s_addr ^ INADDR_BROADCAST ) & ~netmask.s_addr ) == 0){
+ if ( ( ( ~next_hop.s_addr ) & hostmask.s_addr ) == 0 ) {
/* Broadcast address */
ipv4_stats.out_bcast_pkts++;
ll_dest = netdev->ll_broadcast;
@@ -408,7 +564,7 @@ static int ipv4_tx ( struct io_buffer *iobuf,
}
} else {
if ( ( rc = arp_tx ( iobuf, netdev, &ipv4_protocol, &next_hop,
- &iphdr->src, netdev->ll_addr ) ) != 0 ) {
+ &iphdr->src ) ) != 0 ) {
DBGC ( sin_dest->sin_addr, "IPv4 could not transmit "
"packet via %s: %s\n",
netdev->name, strerror ( rc ) );
@@ -812,19 +968,24 @@ const struct setting gateway_setting __setting ( SETTING_IP4, gateway ) = {
.type = &setting_type_ipv4,
};
+/** Classless static routes setting */
+const struct setting static_route_setting __setting ( SETTING_IP4,
+ static_routes ) = {
+ .name = "static-routes",
+ .description = "Static routes",
+ .tag = DHCP_STATIC_ROUTES,
+ .type = &setting_type_hex,
+};
+
/**
* Send gratuitous ARP, if applicable
*
* @v netdev Network device
* @v address IPv4 address
- * @v netmask Subnet mask
- * @v gateway Gateway address (if any)
* @ret rc Return status code
*/
static int ipv4_gratuitous_arp ( struct net_device *netdev,
- struct in_addr address,
- struct in_addr netmask __unused,
- struct in_addr gateway __unused ) {
+ struct in_addr address ) {
int rc;
/* Do nothing if network device already has this IPv4 address */
@@ -851,14 +1012,10 @@ static int ipv4_gratuitous_arp ( struct net_device *netdev,
* @ret rc Return status code
*/
static int ipv4_settings ( int ( * apply ) ( struct net_device *netdev,
- struct in_addr address,
- struct in_addr netmask,
- struct in_addr gateway ) ) {
+ struct in_addr address ) ) {
struct net_device *netdev;
struct settings *settings;
- struct in_addr address = { 0 };
- struct in_addr netmask = { 0 };
- struct in_addr gateway = { 0 };
+ struct in_addr address;
int rc;
/* Process settings for each network device */
@@ -868,30 +1025,12 @@ static int ipv4_settings ( int ( * apply ) ( struct net_device *netdev,
settings = netdev_settings ( netdev );
/* Get IPv4 address */
- address.s_addr = 0;
fetch_ipv4_setting ( settings, &ip_setting, &address );
if ( ! address.s_addr )
continue;
- /* Get subnet mask */
- fetch_ipv4_setting ( settings, &netmask_setting, &netmask );
-
- /* Calculate default netmask, if necessary */
- if ( ! netmask.s_addr ) {
- if ( IN_IS_CLASSA ( address.s_addr ) ) {
- netmask.s_addr = INADDR_NET_CLASSA;
- } else if ( IN_IS_CLASSB ( address.s_addr ) ) {
- netmask.s_addr = INADDR_NET_CLASSB;
- } else if ( IN_IS_CLASSC ( address.s_addr ) ) {
- netmask.s_addr = INADDR_NET_CLASSC;
- }
- }
-
- /* Get default gateway, if present */
- fetch_ipv4_setting ( settings, &gateway_setting, &gateway );
-
/* Apply settings */
- if ( ( rc = apply ( netdev, address, netmask, gateway ) ) != 0 )
+ if ( ( rc = apply ( netdev, address ) ) != 0 )
return rc;
}
@@ -903,20 +1042,17 @@ static int ipv4_settings ( int ( * apply ) ( struct net_device *netdev,
*
* @ret rc Return status code
*/
-static int ipv4_create_routes ( void ) {
- struct ipv4_miniroute *miniroute;
- struct ipv4_miniroute *tmp;
+static int ipv4_apply_routes ( void ) {
int rc;
/* Send gratuitous ARPs for any new IPv4 addresses */
ipv4_settings ( ipv4_gratuitous_arp );
/* Delete all existing routes */
- list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list )
- del_ipv4_miniroute ( miniroute );
+ ipv4_del_miniroutes();
- /* Create a route for each configured network device */
- if ( ( rc = ipv4_settings ( add_ipv4_miniroute ) ) != 0 )
+ /* Create routes for each configured network device */
+ if ( ( rc = ipv4_settings ( ipv4_add_miniroutes ) ) != 0 )
return rc;
return 0;
@@ -924,7 +1060,7 @@ static int ipv4_create_routes ( void ) {
/** IPv4 settings applicator */
struct settings_applicator ipv4_settings_applicator __settings_applicator = {
- .apply = ipv4_create_routes,
+ .apply = ipv4_apply_routes,
};
/* Drag in objects via ipv4_protocol */
diff --git a/src/net/ipv6.c b/src/net/ipv6.c
index 8ee0804d3..7e908dd2a 100644
--- a/src/net/ipv6.c
+++ b/src/net/ipv6.c
@@ -18,6 +18,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdio.h>
@@ -617,8 +618,8 @@ static int ipv6_tx ( struct io_buffer *iobuf,
return rc;
}
} else {
- if ( ( rc = ndp_tx ( iobuf, netdev, next_hop, &iphdr->src,
- netdev->ll_addr ) ) != 0 ) {
+ if ( ( rc = ndp_tx ( iobuf, netdev, next_hop,
+ &iphdr->src ) ) != 0 ) {
DBGC ( ipv6col ( &iphdr->dest ), "IPv6 could not "
"transmit packet via %s: %s\n",
netdev->name, strerror ( rc ) );
diff --git a/src/net/lldp.c b/src/net/lldp.c
index a854d0ace..d0e990f23 100644
--- a/src/net/lldp.c
+++ b/src/net/lldp.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
@@ -30,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/iobuf.h>
diff --git a/src/net/ndp.c b/src/net/ndp.c
index 373a9360b..6d96270c1 100644
--- a/src/net/ndp.c
+++ b/src/net/ndp.c
@@ -18,6 +18,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <stdio.h>
@@ -1221,7 +1222,7 @@ ipv6conf_rx_router_advertisement ( struct net_device *netdev,
/* Start DHCPv6 if required */
if ( radv->flags & ( NDP_ROUTER_MANAGED | NDP_ROUTER_OTHER ) ) {
stateful = ( radv->flags & NDP_ROUTER_MANAGED );
- if ( ( rc = start_dhcpv6 ( &ipv6conf->dhcp, netdev,
+ if ( ( rc = start_dhcpv6 ( &ipv6conf->dhcp, netdev, router,
stateful ) ) != 0 ) {
DBGC ( netdev, "NDP %s could not start state%s DHCPv6: "
"%s\n", netdev->name,
diff --git a/src/net/neighbour.c b/src/net/neighbour.c
index 13a8bc3ba..fa8fba5cd 100644
--- a/src/net/neighbour.c
+++ b/src/net/neighbour.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
@@ -31,7 +32,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/retry.h>
#include <ipxe/timer.h>
#include <ipxe/malloc.h>
+#include <ipxe/pending.h>
#include <ipxe/neighbour.h>
+#include <config/fault.h>
/** @file
*
@@ -48,9 +51,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Neighbour discovery maximum timeout */
#define NEIGHBOUR_MAX_TIMEOUT ( TICKS_PER_SEC * 3 )
+/** Neighbour discovery maximum burst count for delayed transmissions
+ *
+ * When using delay injection, timer quantisation can cause a large
+ * number of delayed packets to be scheduled at the same time. This
+ * can quickly exhaust available transmit descriptors, leading to
+ * packets that are dropped completely (not just delayed).
+ *
+ * Limit the number of delayed packets that we will attempt to
+ * transmit at once, to allow time for transmit completions to occur.
+ */
+#define NEIGHBOUR_DELAY_MAX_BURST 2
+
/** The neighbour cache */
struct list_head neighbours = LIST_HEAD_INIT ( neighbours );
+/** Pending operation for delayed transmissions */
+static struct pending_operation neighbour_delayed;
+
static void neighbour_expired ( struct retry_timer *timer, int over );
/**
@@ -164,25 +182,21 @@ static void neighbour_discover ( struct neighbour *neighbour,
}
/**
- * Complete neighbour discovery
+ * Transmit deferred packets
*
* @v neighbour Neighbour cache entry
- * @v ll_dest Destination link-layer address
*/
-static void neighbour_discovered ( struct neighbour *neighbour,
- const void *ll_dest ) {
+static void neighbour_tx_queue ( struct neighbour *neighbour ) {
struct net_device *netdev = neighbour->netdev;
- struct ll_protocol *ll_protocol = netdev->ll_protocol;
struct net_protocol *net_protocol = neighbour->net_protocol;
+ const void *ll_dest = neighbour->ll_dest;
+ struct neighbour_delay *delay;
struct io_buffer *iobuf;
+ unsigned long elapsed;
+ unsigned long threshold;
+ unsigned int count = 0;
int rc;
- /* Fill in link-layer address */
- memcpy ( neighbour->ll_dest, ll_dest, ll_protocol->ll_addr_len );
- DBGC ( neighbour, "NEIGHBOUR %s %s %s is %s %s\n", netdev->name,
- net_protocol->name, net_protocol->ntoa ( neighbour->net_dest ),
- ll_protocol->name, ll_protocol->ntoa ( neighbour->ll_dest ) );
-
/* Stop retransmission timer */
stop_timer ( &neighbour->timer );
@@ -193,6 +207,36 @@ static void neighbour_discovered ( struct neighbour *neighbour,
ref_get ( &neighbour->refcnt );
while ( ( iobuf = list_first_entry ( &neighbour->tx_queue,
struct io_buffer, list )) != NULL){
+
+ /* Handle delay injection */
+ if ( NEIGHBOUR_DELAY_MS ) {
+
+ /* Determine elapsed time since transmission attempt */
+ delay = iobuf->data;
+ elapsed = ( currticks() - delay->start );
+ threshold = ( NEIGHBOUR_DELAY_MS * TICKS_PER_MS );
+
+ /* Defer transmission if not yet scheduled */
+ if ( elapsed < threshold ) {
+ start_timer_fixed ( &neighbour->timer,
+ ( threshold - elapsed ) );
+ break;
+ }
+
+ /* Defer transmission if maximum burst count reached */
+ if ( ++count >= NEIGHBOUR_DELAY_MAX_BURST ) {
+ start_timer_nodelay ( &neighbour->timer );
+ break;
+ }
+
+ /* Strip pseudo-header */
+ iob_pull ( iobuf, sizeof ( *delay ) );
+
+ /* Remove pending operation */
+ pending_put ( &neighbour_delayed );
+ }
+
+ /* Transmit deferred packet */
DBGC2 ( neighbour, "NEIGHBOUR %s %s %s transmitting deferred "
"packet\n", netdev->name, net_protocol->name,
net_protocol->ntoa ( neighbour->net_dest ) );
@@ -211,6 +255,31 @@ static void neighbour_discovered ( struct neighbour *neighbour,
}
/**
+ * Complete neighbour discovery
+ *
+ * @v neighbour Neighbour cache entry
+ * @v ll_dest Destination link-layer address
+ */
+static void neighbour_discovered ( struct neighbour *neighbour,
+ const void *ll_dest ) {
+ struct net_device *netdev = neighbour->netdev;
+ struct ll_protocol *ll_protocol = netdev->ll_protocol;
+ struct net_protocol *net_protocol = neighbour->net_protocol;
+
+ /* Fill in link-layer address */
+ memcpy ( neighbour->ll_dest, ll_dest, ll_protocol->ll_addr_len );
+ DBGC ( neighbour, "NEIGHBOUR %s %s %s is %s %s\n", netdev->name,
+ net_protocol->name, net_protocol->ntoa ( neighbour->net_dest ),
+ ll_protocol->name, ll_protocol->ntoa ( neighbour->ll_dest ) );
+
+ /* Mark discovery as complete */
+ neighbour->discovery = NULL;
+
+ /* Transmit any deferred packets */
+ neighbour_tx_queue ( neighbour );
+}
+
+/**
* Destroy neighbour cache entry
*
* @v neighbour Neighbour cache entry
@@ -235,6 +304,8 @@ static void neighbour_destroy ( struct neighbour *neighbour, int rc ) {
net_protocol->ntoa ( neighbour->net_dest ),
strerror ( rc ) );
list_del ( &iobuf->list );
+ if ( NEIGHBOUR_DELAY_MS )
+ pending_put ( &neighbour_delayed );
netdev_tx_err ( neighbour->netdev, iobuf, rc );
}
@@ -263,6 +334,14 @@ static void neighbour_expired ( struct retry_timer *timer, int fail ) {
const void *net_source = neighbour->net_source;
int rc;
+ /* If the timer is being (ab)used for delay injection, then
+ * transmit the deferred packet queue.
+ */
+ if ( NEIGHBOUR_DELAY_MS && ( ! neighbour->discovery ) ) {
+ neighbour_tx_queue ( neighbour );
+ return;
+ }
+
/* If we have failed, destroy the cache entry */
if ( fail ) {
neighbour_destroy ( neighbour, -ETIMEDOUT );
@@ -293,14 +372,15 @@ static void neighbour_expired ( struct retry_timer *timer, int fail ) {
* @v net_protocol Network-layer protocol
* @v net_dest Destination network-layer address
* @v net_source Source network-layer address
- * @v ll_source Source link-layer address
* @ret rc Return status code
*/
int neighbour_tx ( struct io_buffer *iobuf, struct net_device *netdev,
struct net_protocol *net_protocol, const void *net_dest,
struct neighbour_discovery *discovery,
- const void *net_source, const void *ll_source ) {
+ const void *net_source ) {
struct neighbour *neighbour;
+ struct neighbour_delay *delay;
+ int rc;
/* Find or create neighbour cache entry */
neighbour = neighbour_find ( netdev, net_protocol, net_dest );
@@ -311,19 +391,42 @@ int neighbour_tx ( struct io_buffer *iobuf, struct net_device *netdev,
neighbour_discover ( neighbour, discovery, net_source );
}
- /* If a link-layer address is available then transmit
- * immediately, otherwise queue for later transmission.
+ /* If discovery is still in progress or if delay injection is
+ * in use, then queue for later transmission.
*/
- if ( neighbour_has_ll_dest ( neighbour ) ) {
- return net_tx ( iobuf, netdev, net_protocol, neighbour->ll_dest,
- ll_source );
- } else {
+ if ( NEIGHBOUR_DELAY_MS || neighbour->discovery ) {
+
+ /* Add to deferred packet queue */
DBGC2 ( neighbour, "NEIGHBOUR %s %s %s deferring packet\n",
netdev->name, net_protocol->name,
net_protocol->ntoa ( net_dest ) );
list_add_tail ( &iobuf->list, &neighbour->tx_queue );
+
+ /* Handle delay injection, if applicable */
+ if ( NEIGHBOUR_DELAY_MS ) {
+
+ /* Record original transmission time */
+ delay = iob_push ( iobuf, sizeof ( *delay ) );
+ delay->start = currticks();
+
+ /* Add pending operation */
+ pending_get ( &neighbour_delayed );
+
+ /* Process deferred packet queue, if possible */
+ if ( ! neighbour->discovery )
+ neighbour_tx_queue ( neighbour );
+ }
+
return 0;
}
+
+ /* Otherwise, transmit immediately */
+ if ( ( rc = net_tx ( iobuf, netdev, net_protocol, neighbour->ll_dest,
+ netdev->ll_addr ) ) != 0 ) {
+ return rc;
+ }
+
+ return 0;
}
/**
diff --git a/src/net/netdev_settings.c b/src/net/netdev_settings.c
index 080b6d2a5..8bc8ce57b 100644
--- a/src/net/netdev_settings.c
+++ b/src/net/netdev_settings.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <errno.h>
@@ -169,6 +170,7 @@ static int netdev_fetch_bustype ( struct net_device *netdev, void *data,
[BUS_TYPE_XEN] = "XEN",
[BUS_TYPE_HV] = "HV",
[BUS_TYPE_USB] = "USB",
+ [BUS_TYPE_DT] = "DT",
};
struct device_description *desc = &netdev->dev->desc;
const char *bustype;
@@ -428,6 +430,7 @@ static void netdev_redirect_settings_init ( void ) {
/** "netX" settings initialiser */
struct init_fn netdev_redirect_settings_init_fn __init_fn ( INIT_LATE ) = {
+ .name = "netX",
.initialise = netdev_redirect_settings_init,
};
diff --git a/src/net/netdevice.c b/src/net/netdevice.c
index a9ed18134..0af916ff5 100644
--- a/src/net/netdevice.c
+++ b/src/net/netdevice.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
@@ -611,8 +612,8 @@ void netdev_rx_err ( struct net_device *netdev,
*/
void netdev_poll ( struct net_device *netdev ) {
- /* Avoid calling poll() on unopened network devices */
- if ( ! netdev_is_open ( netdev ) )
+ /* Call poll() only on open (or insomniac) network devices */
+ if ( ! ( netdev->state & ( NETDEV_OPEN | NETDEV_INSOMNIAC ) ) )
return;
/* Guard against re-entry */
diff --git a/src/net/nullnet.c b/src/net/nullnet.c
index 2948b38c0..c665b203a 100644
--- a/src/net/nullnet.c
+++ b/src/net/nullnet.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <errno.h>
diff --git a/src/net/oncrpc/mount.c b/src/net/oncrpc/mount.c
index 8838a147c..32279cc25 100644
--- a/src/net/oncrpc/mount.c
+++ b/src/net/oncrpc/mount.c
@@ -17,6 +17,8 @@
* 02110-1301, USA.
*/
+FILE_SECBOOT ( FORBIDDEN );
+
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
diff --git a/src/net/oncrpc/nfs.c b/src/net/oncrpc/nfs.c
index b6118f91a..3a3a894f0 100644
--- a/src/net/oncrpc/nfs.c
+++ b/src/net/oncrpc/nfs.c
@@ -17,6 +17,8 @@
* 02110-1301, USA.
*/
+FILE_SECBOOT ( FORBIDDEN );
+
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
diff --git a/src/net/oncrpc/nfs_open.c b/src/net/oncrpc/nfs_open.c
index c0dceb82f..d83991255 100644
--- a/src/net/oncrpc/nfs_open.c
+++ b/src/net/oncrpc/nfs_open.c
@@ -17,6 +17,8 @@
* 02110-1301, USA.
*/
+FILE_SECBOOT ( FORBIDDEN );
+
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
diff --git a/src/net/oncrpc/nfs_uri.c b/src/net/oncrpc/nfs_uri.c
index c4c3f21e9..b97fb91f9 100644
--- a/src/net/oncrpc/nfs_uri.c
+++ b/src/net/oncrpc/nfs_uri.c
@@ -17,6 +17,8 @@
* 02110-1301, USA.
*/
+FILE_SECBOOT ( FORBIDDEN );
+
#include <stdlib.h>
#include <string.h>
#include <errno.h>
diff --git a/src/net/oncrpc/oncrpc_iob.c b/src/net/oncrpc/oncrpc_iob.c
index be51805e7..04bb20edd 100644
--- a/src/net/oncrpc/oncrpc_iob.c
+++ b/src/net/oncrpc/oncrpc_iob.c
@@ -17,6 +17,8 @@
* 02110-1301, USA.
*/
+FILE_SECBOOT ( FORBIDDEN );
+
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
diff --git a/src/net/oncrpc/portmap.c b/src/net/oncrpc/portmap.c
index df62221dc..be11c42da 100644
--- a/src/net/oncrpc/portmap.c
+++ b/src/net/oncrpc/portmap.c
@@ -17,6 +17,8 @@
* 02110-1301, USA.
*/
+FILE_SECBOOT ( FORBIDDEN );
+
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
diff --git a/src/net/pccrc.c b/src/net/pccrc.c
index a94bc0e11..4bf2f441e 100644
--- a/src/net/pccrc.c
+++ b/src/net/pccrc.c
@@ -22,10 +22,10 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <errno.h>
#include <assert.h>
-#include <ipxe/uaccess.h>
#include <ipxe/sha256.h>
#include <ipxe/sha512.h>
#include <ipxe/hmac.h>
@@ -88,7 +88,7 @@ static int peerdist_info_get ( const struct peerdist_info *info, void *data,
}
/* Copy data */
- copy_from_user ( data, info->raw.data, offset, len );
+ memcpy ( data, ( info->raw.data + offset ), len );
return 0;
}
@@ -667,7 +667,8 @@ static struct peerdist_info_operations peerdist_info_v2_operations = {
* @v info Content information to fill in
* @ret rc Return status code
*/
-int peerdist_info ( userptr_t data, size_t len, struct peerdist_info *info ) {
+int peerdist_info ( const void *data, size_t len,
+ struct peerdist_info *info ) {
union peerdist_info_version version;
int rc;
diff --git a/src/net/pccrd.c b/src/net/pccrd.c
index 04b5dd86c..a7182c8ee 100644
--- a/src/net/pccrd.c
+++ b/src/net/pccrd.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stddef.h>
#include <stdlib.h>
diff --git a/src/net/peerblk.c b/src/net/peerblk.c
index bbd5f16ed..6efd4ebf6 100644
--- a/src/net/peerblk.c
+++ b/src/net/peerblk.c
@@ -22,9 +22,11 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include <errno.h>
#include <ipxe/http.h>
#include <ipxe/iobuf.h>
diff --git a/src/net/peerdisc.c b/src/net/peerdisc.c
index 86ff94a87..2ba733697 100644
--- a/src/net/peerdisc.c
+++ b/src/net/peerdisc.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <string.h>
diff --git a/src/net/peerdist.c b/src/net/peerdist.c
index 3210ac0ec..8e0f5dc13 100644
--- a/src/net/peerdist.c
+++ b/src/net/peerdist.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdio.h>
#include <ipxe/http.h>
diff --git a/src/net/peermux.c b/src/net/peermux.c
index a391ed373..7160d1c43 100644
--- a/src/net/peermux.c
+++ b/src/net/peermux.c
@@ -22,9 +22,11 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include <errno.h>
#include <ipxe/uri.h>
#include <ipxe/xferbuf.h>
@@ -129,6 +131,7 @@ static int peermux_info_deliver ( struct peerdist_multiplexer *peermux,
* @v rc Reason for close
*/
static void peermux_info_close ( struct peerdist_multiplexer *peermux, int rc ){
+ struct xfer_buffer *buffer = &peermux->buffer;
struct peerdist_info *info = &peermux->cache.info;
size_t len;
@@ -145,8 +148,7 @@ static void peermux_info_close ( struct peerdist_multiplexer *peermux, int rc ){
intf_shutdown ( &peermux->info, rc );
/* Parse content information */
- if ( ( rc = peerdist_info ( info->raw.data, peermux->buffer.len,
- info ) ) != 0 ) {
+ if ( ( rc = peerdist_info ( buffer->data, buffer->len, info ) ) != 0 ) {
DBGC ( peermux, "PEERMUX %p could not parse content info: %s\n",
peermux, strerror ( rc ) );
goto err;
@@ -422,8 +424,7 @@ int peermux_filter ( struct interface *xfer, struct interface *info,
intf_init ( &peermux->xfer, &peermux_xfer_desc, &peermux->refcnt );
intf_init ( &peermux->info, &peermux_info_desc, &peermux->refcnt );
peermux->uri = uri_get ( uri );
- xferbuf_umalloc_init ( &peermux->buffer,
- &peermux->cache.info.raw.data );
+ xferbuf_umalloc_init ( &peermux->buffer );
process_init_stopped ( &peermux->process, &peermux_process_desc,
&peermux->refcnt );
INIT_LIST_HEAD ( &peermux->busy );
diff --git a/src/net/ping.c b/src/net/ping.c
index f0729e159..5782813e1 100644
--- a/src/net/ping.c
+++ b/src/net/ping.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <string.h>
diff --git a/src/net/retry.c b/src/net/retry.c
index 734567be5..c13ecb6b1 100644
--- a/src/net/retry.c
+++ b/src/net/retry.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stddef.h>
#include <ipxe/timer.h>
diff --git a/src/net/rndis.c b/src/net/rndis.c
index a3b562bc2..f04bc775f 100644
--- a/src/net/rndis.c
+++ b/src/net/rndis.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/net/socket.c b/src/net/socket.c
index 2009ab237..6fed73128 100644
--- a/src/net/socket.c
+++ b/src/net/socket.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stddef.h>
#include <errno.h>
diff --git a/src/net/stp.c b/src/net/stp.c
index 3d78400af..d1b0d4862 100644
--- a/src/net/stp.c
+++ b/src/net/stp.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <errno.h>
#include <byteswap.h>
diff --git a/src/net/tcp.c b/src/net/tcp.c
index 2a98221f6..f08db5250 100644
--- a/src/net/tcp.c
+++ b/src/net/tcp.c
@@ -28,6 +28,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** A TCP connection */
struct tcp_connection {
@@ -167,6 +168,9 @@ struct tcp_rx_queued_header {
*/
static LIST_HEAD ( tcp_conns );
+/** TCP statistics */
+struct tcp_statistics tcp_stats;
+
/** Transmit profiler */
static struct profiler tcp_tx_profiler __profiler = { .name = "tcp.tx" };
@@ -1216,6 +1220,9 @@ static int tcp_rx_data ( struct tcp_connection *tcp, uint32_t seq,
/* Acknowledge new data */
tcp_rx_seq ( tcp, len );
+ /* Update statistics */
+ tcp_stats.in_octets_good += len;
+
/* Deliver data to application */
profile_start ( &tcp_xfer_profiler );
if ( ( rc = xfer_deliver_iob ( &tcp->xfer, iobuf ) ) != 0 ) {
@@ -1299,6 +1306,7 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq,
size_t len;
uint32_t seq_len;
uint32_t nxt;
+ uint32_t gap;
/* Calculate remaining flags and sequence length. Note that
* SYN, if present, has already been processed by this point.
@@ -1317,7 +1325,7 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq,
*/
if ( ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) ||
( tcp_cmp ( seq, tcp->rcv_ack + tcp->rcv_win ) >= 0 ) ||
- ( tcp_cmp ( nxt, tcp->rcv_ack ) < 0 ) ||
+ ( tcp_cmp ( nxt, tcp->rcv_ack ) <= 0 ) ||
( seq_len == 0 ) ) {
free_iob ( iobuf );
return;
@@ -1330,12 +1338,18 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq,
tcpqhdr->flags = flags;
/* Add to RX queue */
+ gap = tcp->rcv_ack;
list_for_each_entry ( queued, &tcp->rx_queue, list ) {
tcpqhdr = queued->data;
if ( tcp_cmp ( seq, tcpqhdr->seq ) < 0 )
break;
+ gap = tcpqhdr->nxt;
}
list_add_tail ( &iobuf->list, &queued->list );
+
+ /* Update statistics */
+ if ( seq != gap )
+ tcp_stats.in_out_of_order++;
}
/**
@@ -1459,6 +1473,10 @@ static int tcp_rx ( struct io_buffer *iobuf,
seq_len = ( len + ( ( flags & TCP_SYN ) ? 1 : 0 ) +
( ( flags & TCP_FIN ) ? 1 : 0 ) );
+ /* Update statistics */
+ tcp_stats.in_segs++;
+ tcp_stats.in_octets += len;
+
/* Dump header */
DBGC2 ( tcp, "TCP %p RX %d<-%d %08x %08x..%08x %4zd",
tcp, ntohs ( tcphdr->dest ), ntohs ( tcphdr->src ),
@@ -1569,6 +1587,9 @@ static unsigned int tcp_discard ( void ) {
list_del ( &iobuf->list );
free_iob ( iobuf );
+ /* Update statistics */
+ tcp_stats.in_discards++;
+
/* Report discard */
discarded++;
break;
diff --git a/src/net/tcp/http.c b/src/net/tcp/http.c
index b000ed80f..16cfd035e 100644
--- a/src/net/tcp/http.c
+++ b/src/net/tcp/http.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
diff --git a/src/net/tcp/httpauth.c b/src/net/tcp/httpauth.c
index 2c57e3d48..d682c5f8f 100644
--- a/src/net/tcp/httpauth.c
+++ b/src/net/tcp/httpauth.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
diff --git a/src/net/tcp/httpbasic.c b/src/net/tcp/httpbasic.c
index 52a67063d..4dffc7e0f 100644
--- a/src/net/tcp/httpbasic.c
+++ b/src/net/tcp/httpbasic.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
diff --git a/src/net/tcp/httpblock.c b/src/net/tcp/httpblock.c
index 1abd6b34d..14398869e 100644
--- a/src/net/tcp/httpblock.c
+++ b/src/net/tcp/httpblock.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
@@ -31,7 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
#include <stdint.h>
-#include <ipxe/uaccess.h>
+#include <string.h>
#include <ipxe/blocktrans.h>
#include <ipxe/blockdev.h>
#include <ipxe/acpi.h>
@@ -52,7 +53,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* @ret rc Return status code
*/
int http_block_read ( struct http_transaction *http, struct interface *data,
- uint64_t lba, unsigned int count, userptr_t buffer,
+ uint64_t lba, unsigned int count, void *buffer,
size_t len ) {
struct http_request_range range;
int rc;
@@ -101,7 +102,7 @@ int http_block_read_capacity ( struct http_transaction *http,
goto err_open;
/* Insert block device translator */
- if ( ( rc = block_translate ( data, UNULL, HTTP_BLKSIZE ) ) != 0 ) {
+ if ( ( rc = block_translate ( data, NULL, HTTP_BLKSIZE ) ) != 0 ) {
DBGC ( http, "HTTP %p could not insert block translator: %s\n",
http, strerror ( rc ) );
goto err_translate;
diff --git a/src/net/tcp/httpconn.c b/src/net/tcp/httpconn.c
index 538c4dcf6..4b99209f0 100644
--- a/src/net/tcp/httpconn.c
+++ b/src/net/tcp/httpconn.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c
index af2a237cf..912bea407 100644
--- a/src/net/tcp/httpcore.c
+++ b/src/net/tcp/httpcore.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
@@ -106,6 +107,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Retry delay used when we cannot understand the Retry-After header */
#define HTTP_RETRY_SECONDS 5
+/** Idle connection watchdog timeout */
+#define HTTP_WATCHDOG_SECONDS 120
+
/** Receive profiler */
static struct profiler http_rx_profiler __profiler = { .name = "http.rx" };
@@ -281,8 +285,9 @@ static void http_close ( struct http_transaction *http, int rc ) {
/* Stop process */
process_del ( &http->process );
- /* Stop timer */
- stop_timer ( &http->timer );
+ /* Stop timers */
+ stop_timer ( &http->retry );
+ stop_timer ( &http->watchdog );
/* Close all interfaces */
intfs_shutdown ( rc, &http->conn, &http->transfer, &http->content,
@@ -302,6 +307,18 @@ static void http_close_error ( struct http_transaction *http, int rc ) {
}
/**
+ * Hold off HTTP idle connection watchdog timer
+ *
+ * @v http HTTP transaction
+ */
+static inline void http_watchdog ( struct http_transaction *http ) {
+
+ /* (Re)start watchdog timer */
+ start_timer_fixed ( &http->watchdog,
+ ( HTTP_WATCHDOG_SECONDS * TICKS_PER_SEC ) );
+}
+
+/**
* Reopen stale HTTP connection
*
* @v http HTTP transaction
@@ -322,6 +339,9 @@ static void http_reopen ( struct http_transaction *http ) {
/* Reset state */
http->state = &http_request;
+ /* Restart idle connection watchdog timer */
+ http_watchdog ( http );
+
/* Reschedule transmission process */
process_add ( &http->process );
@@ -332,20 +352,37 @@ static void http_reopen ( struct http_transaction *http ) {
}
/**
- * Handle retry timer expiry
+ * Handle connection retry timer expiry
*
- * @v timer Retry timer
+ * @v retry Retry timer
* @v over Failure indicator
*/
-static void http_expired ( struct retry_timer *timer, int over __unused ) {
+static void http_retry_expired ( struct retry_timer *retry,
+ int over __unused ) {
struct http_transaction *http =
- container_of ( timer, struct http_transaction, timer );
+ container_of ( retry, struct http_transaction, retry );
/* Reopen connection */
http_reopen ( http );
}
/**
+ * Handle idle connection watchdog timer expiry
+ *
+ * @v watchdog Idle connection watchdog timer
+ * @v over Failure indicator
+ */
+static void http_watchdog_expired ( struct retry_timer *watchdog,
+ int over __unused ) {
+ struct http_transaction *http =
+ container_of ( watchdog, struct http_transaction, watchdog );
+
+ /* Abort connection */
+ DBGC ( http, "HTTP %p aborting idle connection\n", http );
+ http_close ( http, -ETIMEDOUT );
+}
+
+/**
* HTTP transmit process
*
* @v http HTTP transaction
@@ -460,6 +497,9 @@ static int http_content_deliver ( struct http_transaction *http,
return 0;
}
+ /* Hold off idle connection watchdog timer */
+ http_watchdog ( http );
+
/* Deliver to data transfer interface */
profile_start ( &http_xfer_profiler );
if ( ( rc = xfer_deliver ( &http->xfer, iob_disown ( iobuf ),
@@ -503,7 +543,7 @@ http_content_buffer ( struct http_transaction *http ) {
__weak int http_block_read ( struct http_transaction *http __unused,
struct interface *data __unused,
uint64_t lba __unused, unsigned int count __unused,
- userptr_t buffer __unused, size_t len __unused ) {
+ void *buffer __unused, size_t len __unused ) {
return -ENOTSUP;
}
@@ -649,7 +689,8 @@ int http_open ( struct interface *xfer, struct http_method *method,
intf_init ( &http->conn, &http_conn_desc, &http->refcnt );
intf_plug_plug ( &http->transfer, &http->content );
process_init ( &http->process, &http_process_desc, &http->refcnt );
- timer_init ( &http->timer, http_expired, &http->refcnt );
+ timer_init ( &http->retry, http_retry_expired, &http->refcnt );
+ timer_init ( &http->watchdog, http_watchdog_expired, &http->refcnt );
http->uri = uri_get ( uri );
http->request.method = method;
http->request.uri = request_uri_string;
@@ -675,6 +716,9 @@ int http_open ( struct interface *xfer, struct http_method *method,
goto err_connect;
}
+ /* Start watchdog timer */
+ http_watchdog ( http );
+
/* Attach to parent interface, mortalise self, and return */
intf_plug_plug ( &http->xfer, xfer );
ref_put ( &http->refcnt );
@@ -809,8 +853,9 @@ static int http_transfer_complete ( struct http_transaction *http ) {
/* Start timer to initiate retry */
DBGC2 ( http, "HTTP %p retrying after %d seconds\n",
http, http->response.retry_after );
- start_timer_fixed ( &http->timer,
+ start_timer_fixed ( &http->retry,
( http->response.retry_after * TICKS_PER_SEC ) );
+ stop_timer ( &http->watchdog );
return 0;
}
@@ -1090,7 +1135,8 @@ static int http_tx_request ( struct http_transaction *http ) {
}
/* Allocate I/O buffer */
- iobuf = alloc_iob ( len + 1 /* NUL */ + http->request.content.len );
+ iobuf = xfer_alloc_iob ( &http->conn, ( len + 1 /* NUL */ +
+ http->request.content.len ) );
if ( ! iobuf ) {
rc = -ENOMEM;
goto err_alloc;
diff --git a/src/net/tcp/httpdigest.c b/src/net/tcp/httpdigest.c
index 4074078c7..8ff6dbfa5 100644
--- a/src/net/tcp/httpdigest.c
+++ b/src/net/tcp/httpdigest.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
diff --git a/src/net/tcp/httpntlm.c b/src/net/tcp/httpntlm.c
index 25187bd19..a7e44d5f6 100644
--- a/src/net/tcp/httpntlm.c
+++ b/src/net/tcp/httpntlm.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
diff --git a/src/net/tcp/https.c b/src/net/tcp/https.c
index 85f1f124f..bccfafe15 100644
--- a/src/net/tcp/https.c
+++ b/src/net/tcp/https.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c
index dd20849ce..0d1f0f645 100644
--- a/src/net/tcp/iscsi.c
+++ b/src/net/tcp/iscsi.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stddef.h>
#include <string.h>
@@ -39,7 +40,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/open.h>
#include <ipxe/scsi.h>
#include <ipxe/process.h>
-#include <ipxe/uaccess.h>
#include <ipxe/tcpip.h>
#include <ipxe/settings.h>
#include <ipxe/features.h>
@@ -478,7 +478,7 @@ static int iscsi_rx_data_in ( struct iscsi_session *iscsi,
assert ( iscsi->command != NULL );
assert ( iscsi->command->data_in );
assert ( ( offset + len ) <= iscsi->command->data_in_len );
- copy_to_user ( iscsi->command->data_in, offset, data, len );
+ memcpy ( ( iscsi->command->data_in + offset ), data, len );
/* Wait for whole SCSI response to arrive */
if ( remaining )
@@ -598,8 +598,8 @@ static int iscsi_tx_data_out ( struct iscsi_session *iscsi ) {
if ( ! iobuf )
return -ENOMEM;
- copy_from_user ( iob_put ( iobuf, len ),
- iscsi->command->data_out, offset, len );
+ memcpy ( iob_put ( iobuf, len ),
+ ( iscsi->command->data_out + offset ), len );
memset ( iob_put ( iobuf, pad_len ), 0, pad_len );
return xfer_deliver_iob ( &iscsi->socket, iobuf );
diff --git a/src/net/tcp/oncrpc.c b/src/net/tcp/oncrpc.c
index cb66aeb85..64734a808 100644
--- a/src/net/tcp/oncrpc.c
+++ b/src/net/tcp/oncrpc.c
@@ -150,7 +150,7 @@ int oncrpc_call ( struct interface *intf, struct oncrpc_session *session,
frame_size = oncrpc_compute_size ( header );
frame_size += oncrpc_compute_size ( fields );
- io_buf = alloc_iob ( frame_size );
+ io_buf = xfer_alloc_iob ( intf, frame_size );
if ( ! io_buf )
return -ENOBUFS;
diff --git a/src/net/tcp/syslogs.c b/src/net/tcp/syslogs.c
index f1f70d59e..eff53ea94 100644
--- a/src/net/tcp/syslogs.c
+++ b/src/net/tcp/syslogs.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
@@ -31,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <byteswap.h>
#include <ipxe/xfer.h>
#include <ipxe/open.h>
diff --git a/src/net/tcpip.c b/src/net/tcpip.c
index cc7d02005..5ed3d68a9 100644
--- a/src/net/tcpip.c
+++ b/src/net/tcpip.c
@@ -18,6 +18,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* Process a received TCP/IP packet
diff --git a/src/net/tls.c b/src/net/tls.c
index 5f89be452..4f8ea2692 100644
--- a/src/net/tls.c
+++ b/src/net/tls.c
@@ -18,6 +18,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
@@ -50,6 +51,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/validator.h>
#include <ipxe/job.h>
#include <ipxe/dhe.h>
+#include <ipxe/ecdhe.h>
#include <ipxe/tls.h>
#include <config/crypto.h>
@@ -170,10 +172,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define EINFO_EPERM_VERIFY \
__einfo_uniqify ( EINFO_EPERM, 0x02, \
"Handshake verification failed" )
-#define EPERM_CLIENT_CERT __einfo_error ( EINFO_EPERM_CLIENT_CERT )
-#define EINFO_EPERM_CLIENT_CERT \
- __einfo_uniqify ( EINFO_EPERM, 0x03, \
- "No suitable client certificate available" )
#define EPERM_RENEG_INSECURE __einfo_error ( EINFO_EPERM_RENEG_INSECURE )
#define EINFO_EPERM_RENEG_INSECURE \
__einfo_uniqify ( EINFO_EPERM, 0x04, \
@@ -186,6 +184,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define EINFO_EPERM_KEY_EXCHANGE \
__einfo_uniqify ( EINFO_EPERM, 0x06, \
"ServerKeyExchange verification failed" )
+#define EPERM_EMS __einfo_error ( EINFO_EPERM_EMS )
+#define EINFO_EPERM_EMS \
+ __einfo_uniqify ( EINFO_EPERM, 0x07, \
+ "Extended master secret extension mismatch" )
#define EPROTO_VERSION __einfo_error ( EINFO_EPROTO_VERSION )
#define EINFO_EPROTO_VERSION \
__einfo_uniqify ( EINFO_EPROTO, 0x01, \
@@ -195,10 +197,15 @@ FILE_LICENCE ( GPL2_OR_LATER );
static LIST_HEAD ( tls_sessions );
static void tls_tx_resume_all ( struct tls_session *session );
+static struct io_buffer * tls_alloc_iob ( struct tls_connection *tls,
+ size_t len );
+static int tls_send_record ( struct tls_connection *tls, unsigned int type,
+ struct io_buffer *iobuf );
static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
const void *data, size_t len );
static void tls_clear_cipher ( struct tls_connection *tls,
struct tls_cipherspec *cipherspec );
+static void tls_verify_handshake ( struct tls_connection *tls, void *out );
/******************************************************************************
*
@@ -251,8 +258,8 @@ static void tls_set_uint24 ( tls24_t *field24, unsigned long value ) {
* @ret is_ready TLS connection is ready
*/
static int tls_ready ( struct tls_connection *tls ) {
- return ( ( ! is_pending ( &tls->client_negotiation ) ) &&
- ( ! is_pending ( &tls->server_negotiation ) ) );
+ return ( ( ! is_pending ( &tls->client.negotiation ) ) &&
+ ( ! is_pending ( &tls->server.negotiation ) ) );
}
/**
@@ -382,21 +389,21 @@ static void free_tls ( struct refcnt *refcnt ) {
/* Free dynamically-allocated resources */
free ( tls->new_session_ticket );
- tls_clear_cipher ( tls, &tls->tx_cipherspec );
- tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
- tls_clear_cipher ( tls, &tls->rx_cipherspec );
- tls_clear_cipher ( tls, &tls->rx_cipherspec_pending );
- free ( tls->server_key );
+ tls_clear_cipher ( tls, &tls->tx.cipherspec.active );
+ tls_clear_cipher ( tls, &tls->tx.cipherspec.pending );
+ tls_clear_cipher ( tls, &tls->rx.cipherspec.active );
+ tls_clear_cipher ( tls, &tls->rx.cipherspec.pending );
+ free ( tls->server.exchange );
free ( tls->handshake_ctx );
- list_for_each_entry_safe ( iobuf, tmp, &tls->rx_data, list ) {
+ list_for_each_entry_safe ( iobuf, tmp, &tls->rx.data, list ) {
list_del ( &iobuf->list );
free_iob ( iobuf );
}
- free_iob ( tls->rx_handshake );
- x509_chain_put ( tls->certs );
- x509_chain_put ( tls->chain );
- x509_root_put ( tls->root );
- privkey_put ( tls->key );
+ free_iob ( tls->rx.handshake );
+ privkey_put ( tls->client.key );
+ x509_chain_put ( tls->client.chain );
+ x509_chain_put ( tls->server.chain );
+ x509_root_put ( tls->server.root );
/* Drop reference to session */
assert ( list_empty ( &tls->list ) );
@@ -415,17 +422,17 @@ static void free_tls ( struct refcnt *refcnt ) {
static void tls_close ( struct tls_connection *tls, int rc ) {
/* Remove pending operations, if applicable */
- pending_put ( &tls->client_negotiation );
- pending_put ( &tls->server_negotiation );
- pending_put ( &tls->validation );
+ pending_put ( &tls->client.negotiation );
+ pending_put ( &tls->server.negotiation );
+ pending_put ( &tls->server.validation );
/* Remove process */
- process_del ( &tls->process );
+ process_del ( &tls->tx.process );
/* Close all interfaces */
intf_shutdown ( &tls->cipherstream, rc );
intf_shutdown ( &tls->plainstream, rc );
- intf_shutdown ( &tls->validator, rc );
+ intf_shutdown ( &tls->server.validator, rc );
/* Remove from session */
list_del ( &tls->list );
@@ -636,21 +643,43 @@ static void tls_prf ( struct tls_connection *tls, const void *secret,
static void tls_generate_master_secret ( struct tls_connection *tls,
const void *pre_master_secret,
size_t pre_master_secret_len ) {
+ struct digest_algorithm *digest = tls->handshake_digest;
+ uint8_t digest_out[ digest->digestsize ];
+
+ /* Generate handshake digest */
+ tls_verify_handshake ( tls, digest_out );
- DBGC ( tls, "TLS %p pre-master-secret:\n", tls );
+ /* Show inputs */
+ DBGC ( tls, "TLS %p pre-master secret:\n", tls );
DBGC_HD ( tls, pre_master_secret, pre_master_secret_len );
DBGC ( tls, "TLS %p client random bytes:\n", tls );
- DBGC_HD ( tls, &tls->client_random, sizeof ( tls->client_random ) );
+ DBGC_HD ( tls, &tls->client.random, sizeof ( tls->client.random ) );
DBGC ( tls, "TLS %p server random bytes:\n", tls );
- DBGC_HD ( tls, &tls->server_random, sizeof ( tls->server_random ) );
-
- tls_prf_label ( tls, pre_master_secret, pre_master_secret_len,
- &tls->master_secret, sizeof ( tls->master_secret ),
- "master secret",
- &tls->client_random, sizeof ( tls->client_random ),
- &tls->server_random, sizeof ( tls->server_random ) );
+ DBGC_HD ( tls, &tls->server.random, sizeof ( tls->server.random ) );
+ DBGC ( tls, "TLS %p session hash:\n", tls );
+ DBGC_HD ( tls, digest_out, sizeof ( digest_out ) );
- DBGC ( tls, "TLS %p generated master secret:\n", tls );
+ /* Generate master secret */
+ if ( tls->extended_master_secret ) {
+ tls_prf_label ( tls, pre_master_secret, pre_master_secret_len,
+ &tls->master_secret,
+ sizeof ( tls->master_secret ),
+ "extended master secret",
+ digest_out, sizeof ( digest_out ) );
+ } else {
+ tls_prf_label ( tls, pre_master_secret, pre_master_secret_len,
+ &tls->master_secret,
+ sizeof ( tls->master_secret ),
+ "master secret",
+ &tls->client.random,
+ sizeof ( tls->client.random ),
+ &tls->server.random,
+ sizeof ( tls->server.random ) );
+ }
+
+ /* Show output */
+ DBGC ( tls, "TLS %p generated %smaster secret:\n", tls,
+ ( tls->extended_master_secret ? "extended ": "" ) );
DBGC_HD ( tls, &tls->master_secret, sizeof ( tls->master_secret ) );
}
@@ -662,8 +691,8 @@ static void tls_generate_master_secret ( struct tls_connection *tls,
* The master secret must already be known.
*/
static int tls_generate_keys ( struct tls_connection *tls ) {
- struct tls_cipherspec *tx_cipherspec = &tls->tx_cipherspec_pending;
- struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending;
+ struct tls_cipherspec *tx_cipherspec = &tls->tx.cipherspec.pending;
+ struct tls_cipherspec *rx_cipherspec = &tls->rx.cipherspec.pending;
size_t hash_size = tx_cipherspec->suite->mac_len;
size_t key_size = tx_cipherspec->suite->key_len;
size_t iv_size = tx_cipherspec->suite->fixed_iv_len;
@@ -675,8 +704,8 @@ static int tls_generate_keys ( struct tls_connection *tls ) {
/* Generate key block */
tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ),
key_block, sizeof ( key_block ), "key expansion",
- &tls->server_random, sizeof ( tls->server_random ),
- &tls->client_random, sizeof ( tls->client_random ) );
+ &tls->server.random, sizeof ( tls->server.random ),
+ &tls->client.random, sizeof ( tls->client.random ) );
/* Split key block into portions */
key = key_block;
@@ -856,10 +885,6 @@ tls_find_cipher_suite ( unsigned int cipher_suite ) {
static void tls_clear_cipher ( struct tls_connection *tls __unused,
struct tls_cipherspec *cipherspec ) {
- if ( cipherspec->suite ) {
- pubkey_final ( cipherspec->suite->pubkey,
- cipherspec->pubkey_ctx );
- }
free ( cipherspec->dynamic );
memset ( cipherspec, 0, sizeof ( *cipherspec ) );
cipherspec->suite = &tls_cipher_suite_null;
@@ -876,7 +901,6 @@ static void tls_clear_cipher ( struct tls_connection *tls __unused,
static int tls_set_cipher ( struct tls_connection *tls,
struct tls_cipherspec *cipherspec,
struct tls_cipher_suite *suite ) {
- struct pubkey_algorithm *pubkey = suite->pubkey;
struct cipher_algorithm *cipher = suite->cipher;
size_t total;
void *dynamic;
@@ -885,8 +909,7 @@ static int tls_set_cipher ( struct tls_connection *tls,
tls_clear_cipher ( tls, cipherspec );
/* Allocate dynamic storage */
- total = ( pubkey->ctxsize + cipher->ctxsize + suite->mac_len +
- suite->fixed_iv_len );
+ total = ( cipher->ctxsize + suite->mac_len + suite->fixed_iv_len );
dynamic = zalloc ( total );
if ( ! dynamic ) {
DBGC ( tls, "TLS %p could not allocate %zd bytes for crypto "
@@ -896,7 +919,6 @@ static int tls_set_cipher ( struct tls_connection *tls,
/* Assign storage */
cipherspec->dynamic = dynamic;
- cipherspec->pubkey_ctx = dynamic; dynamic += pubkey->ctxsize;
cipherspec->cipher_ctx = dynamic; dynamic += cipher->ctxsize;
cipherspec->mac_secret = dynamic; dynamic += suite->mac_len;
cipherspec->fixed_iv = dynamic; dynamic += suite->fixed_iv_len;
@@ -936,10 +958,10 @@ static int tls_select_cipher ( struct tls_connection *tls,
return rc;
/* Set ciphers */
- if ( ( rc = tls_set_cipher ( tls, &tls->tx_cipherspec_pending,
+ if ( ( rc = tls_set_cipher ( tls, &tls->tx.cipherspec.pending,
suite ) ) != 0 )
return rc;
- if ( ( rc = tls_set_cipher ( tls, &tls->rx_cipherspec_pending,
+ if ( ( rc = tls_set_cipher ( tls, &tls->rx.cipherspec.pending,
suite ) ) != 0 )
return rc;
@@ -955,22 +977,20 @@ static int tls_select_cipher ( struct tls_connection *tls,
* Activate next cipher suite
*
* @v tls TLS connection
- * @v pending Pending cipher specification
- * @v active Active cipher specification to replace
+ * @v pair Cipher specification pair
* @ret rc Return status code
*/
static int tls_change_cipher ( struct tls_connection *tls,
- struct tls_cipherspec *pending,
- struct tls_cipherspec *active ) {
+ struct tls_cipherspec_pair *pair ) {
/* Sanity check */
- if ( pending->suite == &tls_cipher_suite_null ) {
+ if ( pair->pending.suite == &tls_cipher_suite_null ) {
DBGC ( tls, "TLS %p refusing to use null cipher\n", tls );
return -ENOTSUP_NULL;
}
- tls_clear_cipher ( tls, active );
- memswap ( active, pending, sizeof ( *active ) );
+ tls_clear_cipher ( tls, &pair->active );
+ memswap ( &pair->active, &pair->pending, sizeof ( pair->active ) );
return 0;
}
@@ -1088,7 +1108,7 @@ tls_find_named_curve ( unsigned int named_curve ) {
* @v tls TLS connection
*/
static void tls_tx_resume ( struct tls_connection *tls ) {
- process_add ( &tls->process );
+ process_add ( &tls->tx.process );
}
/**
@@ -1111,16 +1131,16 @@ static void tls_tx_resume_all ( struct tls_session *session ) {
static void tls_restart ( struct tls_connection *tls ) {
/* Sanity check */
- assert ( ! tls->tx_pending );
- assert ( ! is_pending ( &tls->client_negotiation ) );
- assert ( ! is_pending ( &tls->server_negotiation ) );
- assert ( ! is_pending ( &tls->validation ) );
+ assert ( ! tls->tx.pending );
+ assert ( ! is_pending ( &tls->client.negotiation ) );
+ assert ( ! is_pending ( &tls->server.negotiation ) );
+ assert ( ! is_pending ( &tls->server.validation ) );
/* (Re)start negotiation */
- tls->tx_pending = TLS_TX_CLIENT_HELLO;
+ tls->tx.pending = TLS_TX_CLIENT_HELLO;
tls_tx_resume ( tls );
- pending_get ( &tls->client_negotiation );
- pending_get ( &tls->server_negotiation );
+ pending_get ( &tls->client.negotiation );
+ pending_get ( &tls->server.negotiation );
}
/**
@@ -1134,9 +1154,6 @@ static void tls_restart ( struct tls_connection *tls ) {
static int tls_send_handshake ( struct tls_connection *tls,
const void *data, size_t len ) {
- /* Add to handshake digest */
- tls_add_handshake ( tls, data, len );
-
/* Send record */
return tls_send_plaintext ( tls, TLS_TYPE_HANDSHAKE, data, len );
}
@@ -1207,11 +1224,16 @@ static int tls_client_hello ( struct tls_connection *tls,
} __attribute__ (( packed )) data;
} __attribute__ (( packed )) *named_curve_ext;
struct {
+ uint16_t type;
+ uint16_t len;
+ } __attribute__ (( packed )) *extended_master_secret_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 ( *extended_master_secret_ext ) extended_master_secret;
typeof ( *named_curve_ext )
named_curve[TLS_NUM_NAMED_CURVES ? 1 : 0];
} __attribute__ (( packed )) *extensions;
@@ -1239,7 +1261,7 @@ static int tls_client_hello ( struct tls_connection *tls,
htonl ( sizeof ( hello ) -
sizeof ( hello.type_length ) ) );
hello.version = htons ( TLS_VERSION_MAX );
- memcpy ( &hello.random, &tls->client_random, sizeof ( hello.random ) );
+ memcpy ( &hello.random, &tls->client.random, sizeof ( hello.random ) );
hello.session_id_len = tls->session_id_len;
memcpy ( hello.session_id, tls->session_id,
sizeof ( hello.session_id ) );
@@ -1267,7 +1289,7 @@ static int tls_client_hello ( struct tls_connection *tls,
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;
+ max_fragment_length_ext->data.max = TLS_MAX_FRAGMENT_LENGTH_VALUE;
/* Construct supported signature algorithms extension */
signature_algorithms_ext = &extensions->signature_algorithms;
@@ -1297,6 +1319,12 @@ static int tls_client_hello ( struct tls_connection *tls,
memcpy ( session_ticket_ext->data.data, session->ticket,
sizeof ( session_ticket_ext->data.data ) );
+ /* Construct extended master secret extension */
+ extended_master_secret_ext = &extensions->extended_master_secret;
+ extended_master_secret_ext->type
+ = htons ( TLS_EXTENDED_MASTER_SECRET );
+ extended_master_secret_ext->len = 0;
+
/* Construct named curves extension, if applicable */
if ( sizeof ( extensions->named_curve ) ) {
named_curve_ext = &extensions->named_curve[0];
@@ -1341,12 +1369,12 @@ static int tls_send_certificate ( struct tls_connection *tls ) {
} __attribute__ (( packed )) *certificates;
struct x509_link *link;
struct x509_certificate *cert;
+ struct io_buffer *iobuf;
size_t len;
- int rc;
/* Calculate length of client certificates */
len = 0;
- list_for_each_entry ( link, &tls->certs->links, list ) {
+ list_for_each_entry ( link, &tls->client.chain->links, list ) {
cert = link->cert;
len += ( sizeof ( *certificate ) + cert->raw.len );
DBGC ( tls, "TLS %p sending client certificate %s\n",
@@ -1356,33 +1384,28 @@ static int tls_send_certificate ( struct tls_connection *tls ) {
/* Allocate storage for Certificate record (which may be too
* large for the stack).
*/
- certificates = zalloc ( sizeof ( *certificates ) + len );
- if ( ! certificates )
+ iobuf = tls_alloc_iob ( tls, ( sizeof ( *certificates ) + len ) );
+ if ( ! iobuf )
return -ENOMEM_CERTIFICATE;
/* Populate record */
+ certificates = iob_put ( iobuf, sizeof ( *certificates ) );
certificates->type_length =
( cpu_to_le32 ( TLS_CERTIFICATE ) |
htonl ( sizeof ( *certificates ) + len -
sizeof ( certificates->type_length ) ) );
tls_set_uint24 ( &certificates->length, len );
- certificate = &certificates->certificates[0];
- list_for_each_entry ( link, &tls->certs->links, list ) {
+ list_for_each_entry ( link, &tls->client.chain->links, list ) {
cert = link->cert;
+ certificate = iob_put ( iobuf, sizeof ( *certificate ) );
tls_set_uint24 ( &certificate->length, cert->raw.len );
- memcpy ( certificate->data, cert->raw.data, cert->raw.len );
- certificate = ( ( ( void * ) certificate->data ) +
- cert->raw.len );
+ memcpy ( iob_put ( iobuf, cert->raw.len ), cert->raw.data,
+ cert->raw.len );
}
/* Transmit record */
- rc = tls_send_handshake ( tls, certificates,
- ( sizeof ( *certificates ) + len ) );
-
- /* Free record */
- free ( certificates );
-
- return rc;
+ return tls_send_record ( tls, TLS_TYPE_HANDSHAKE,
+ iob_disown ( iobuf ) );
}
/**
@@ -1392,55 +1415,71 @@ static int tls_send_certificate ( struct tls_connection *tls ) {
* @ret rc Return status code
*/
static int tls_send_client_key_exchange_pubkey ( struct tls_connection *tls ) {
- struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
+ struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.pending;
struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey;
- size_t max_len = pubkey_max_len ( pubkey, cipherspec->pubkey_ctx );
struct {
uint16_t version;
uint8_t random[46];
} __attribute__ (( packed )) pre_master_secret;
- struct {
- uint32_t type_length;
- uint16_t encrypted_pre_master_secret_len;
- uint8_t encrypted_pre_master_secret[max_len];
- } __attribute__ (( packed )) key_xchg;
- size_t unused;
- int len;
+ struct asn1_cursor cursor = {
+ .data = &pre_master_secret,
+ .len = sizeof ( pre_master_secret ),
+ };
+ struct asn1_builder builder = { NULL, 0 };
int rc;
/* Generate pre-master secret */
pre_master_secret.version = htons ( TLS_VERSION_MAX );
if ( ( rc = tls_generate_random ( tls, &pre_master_secret.random,
( sizeof ( pre_master_secret.random ) ) ) ) != 0 ) {
- return rc;
+ goto err_random;
}
- /* Generate master secret */
- tls_generate_master_secret ( tls, &pre_master_secret,
- sizeof ( pre_master_secret ) );
-
/* Encrypt pre-master secret using server's public key */
- memset ( &key_xchg, 0, sizeof ( key_xchg ) );
- len = pubkey_encrypt ( pubkey, cipherspec->pubkey_ctx,
- &pre_master_secret, sizeof ( pre_master_secret ),
- key_xchg.encrypted_pre_master_secret );
- if ( len < 0 ) {
- rc = len;
+ if ( ( rc = pubkey_encrypt ( pubkey, &tls->server.key, &cursor,
+ &builder ) ) != 0 ) {
DBGC ( tls, "TLS %p could not encrypt pre-master secret: %s\n",
tls, strerror ( rc ) );
- return rc;
+ goto err_encrypt;
+ }
+
+ /* Construct Client Key Exchange record */
+ {
+ struct {
+ uint32_t type_length;
+ uint16_t encrypted_pre_master_secret_len;
+ } __attribute__ (( packed )) header;
+
+ header.type_length =
+ ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) |
+ htonl ( builder.len + sizeof ( header ) -
+ sizeof ( header.type_length ) ) );
+ header.encrypted_pre_master_secret_len = htons ( builder.len );
+
+ if ( ( rc = asn1_prepend_raw ( &builder, &header,
+ sizeof ( header ) ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not construct Client Key "
+ "Exchange: %s\n", tls, strerror ( rc ) );
+ goto err_prepend;
+ }
}
- unused = ( max_len - len );
- key_xchg.type_length =
- ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) |
- htonl ( sizeof ( key_xchg ) -
- sizeof ( key_xchg.type_length ) - unused ) );
- key_xchg.encrypted_pre_master_secret_len =
- htons ( sizeof ( key_xchg.encrypted_pre_master_secret ) -
- unused );
- return tls_send_handshake ( tls, &key_xchg,
- ( sizeof ( key_xchg ) - unused ) );
+ /* Transmit Client Key Exchange record */
+ if ( ( rc = tls_send_handshake ( tls, builder.data,
+ builder.len ) ) != 0 ) {
+ goto err_send;
+ }
+
+ /* Generate master secret */
+ tls_generate_master_secret ( tls, &pre_master_secret,
+ sizeof ( pre_master_secret ) );
+
+ err_random:
+ err_encrypt:
+ err_prepend:
+ err_send:
+ free ( builder.data );
+ return rc;
}
/** Public key exchange algorithm */
@@ -1458,7 +1497,7 @@ struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm = {
*/
static int tls_verify_dh_params ( struct tls_connection *tls,
size_t param_len ) {
- struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
+ 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 );
@@ -1467,14 +1506,15 @@ static int tls_verify_dh_params ( struct tls_connection *tls,
uint16_t signature_len;
uint8_t signature[0];
} __attribute__ (( packed )) *sig;
+ struct asn1_cursor signature;
const void *data;
size_t remaining;
int rc;
/* Signature follows parameters */
- assert ( param_len <= tls->server_key_len );
- data = ( tls->server_key + param_len );
- remaining = ( tls->server_key_len - param_len );
+ assert ( param_len <= tls->server.exchange_len );
+ data = ( tls->server.exchange + param_len );
+ remaining = ( tls->server.exchange_len - param_len );
/* Parse signature from ServerKeyExchange */
sig = data;
@@ -1483,9 +1523,12 @@ static int tls_verify_dh_params ( struct tls_connection *tls,
sizeof ( *sig ) ) ) ) {
DBGC ( tls, "TLS %p received underlength ServerKeyExchange\n",
tls );
- DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_len );
return -EINVAL_KEY_EXCHANGE;
}
+ signature.data = sig->signature;
+ signature.len = ntohs ( sig->signature_len );
/* Identify signature and hash algorithm */
if ( use_sig_hash ) {
@@ -1509,28 +1552,25 @@ static int tls_verify_dh_params ( struct tls_connection *tls,
/* Verify signature */
{
- const void *signature = sig->signature;
- size_t signature_len = ntohs ( sig->signature_len );
uint8_t ctx[digest->ctxsize];
uint8_t hash[digest->digestsize];
/* Calculate digest */
digest_init ( digest, ctx );
- digest_update ( digest, ctx, &tls->client_random,
- sizeof ( tls->client_random ) );
- digest_update ( digest, ctx, tls->server_random,
- sizeof ( tls->server_random ) );
- digest_update ( digest, ctx, tls->server_key, param_len );
+ digest_update ( digest, ctx, &tls->client.random,
+ sizeof ( tls->client.random ) );
+ digest_update ( digest, ctx, tls->server.random,
+ sizeof ( tls->server.random ) );
+ digest_update ( digest, ctx, tls->server.exchange, param_len );
digest_final ( digest, ctx, hash );
/* Verify signature */
- if ( ( rc = pubkey_verify ( pubkey, cipherspec->pubkey_ctx,
- digest, hash, signature,
- signature_len ) ) != 0 ) {
+ if ( ( rc = pubkey_verify ( pubkey, &tls->server.key, digest,
+ hash, &signature ) ) != 0 ) {
DBGC ( tls, "TLS %p ServerKeyExchange failed "
"verification\n", tls );
- DBGC_HDA ( tls, 0, tls->server_key,
- tls->server_key_len );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_len );
return -EPERM_KEY_EXCHANGE;
}
}
@@ -1545,7 +1585,7 @@ static int tls_verify_dh_params ( struct tls_connection *tls,
* @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 ) ];
+ uint8_t private[ sizeof ( tls->client.random.random ) ];
const struct {
uint16_t len;
uint8_t data[0];
@@ -1558,8 +1598,8 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) {
int rc;
/* Parse ServerKeyExchange */
- data = tls->server_key;
- remaining = tls->server_key_len;
+ data = tls->server.exchange;
+ remaining = tls->server.exchange_len;
for ( i = 0 ; i < ( sizeof ( dh_val ) / sizeof ( dh_val[0] ) ) ; i++ ){
dh_val[i] = data;
if ( ( sizeof ( *dh_val[i] ) > remaining ) ||
@@ -1567,8 +1607,8 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) {
sizeof ( *dh_val[i] ) ) )){
DBGC ( tls, "TLS %p received underlength "
"ServerKeyExchange\n", tls );
- DBGC_HDA ( tls, 0, tls->server_key,
- tls->server_key_len );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_len );
rc = -EINVAL_KEY_EXCHANGE;
goto err_header;
}
@@ -1576,7 +1616,7 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) {
data += frag_len;
remaining -= frag_len;
}
- param_len = ( tls->server_key_len - remaining );
+ param_len = ( tls->server.exchange_len - remaining );
/* Verify parameter signature */
if ( ( rc = tls_verify_dh_params ( tls, param_len ) ) != 0 )
@@ -1637,15 +1677,15 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) {
len--;
}
- /* Generate master secret */
- tls_generate_master_secret ( tls, pre_master_secret, len );
-
/* Transmit Client Key Exchange record */
if ( ( rc = tls_send_handshake ( tls, key_xchg,
sizeof ( *key_xchg ) ) ) !=0){
goto err_send_handshake;
}
+ /* Generate master secret */
+ tls_generate_master_secret ( tls, pre_master_secret, len );
+
err_send_handshake:
err_dhe_key:
free ( dynamic );
@@ -1678,15 +1718,20 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) {
uint8_t public[0];
} __attribute__ (( packed )) *ecdh;
size_t param_len;
+ size_t pointsize;
+ size_t keysize;
+ size_t offset;
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 ) ))){
+ ecdh = tls->server.exchange;
+ if ( ( sizeof ( *ecdh ) > tls->server.exchange_len ) ||
+ ( ecdh->public_len > ( tls->server.exchange_len -
+ sizeof ( *ecdh ) ) ) ) {
DBGC ( tls, "TLS %p received underlength ServerKeyExchange\n",
tls );
- DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_len );
return -EINVAL_KEY_EXCHANGE;
}
param_len = ( sizeof ( *ecdh ) + ecdh->public_len );
@@ -1699,34 +1744,49 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) {
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 );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_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 );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_len );
return -ENOTSUP_CURVE;
}
+ DBGC ( tls, "TLS %p using named curve %s\n", tls, curve->curve->name );
+ pointsize = curve->curve->pointsize;
+ keysize = curve->curve->keysize;
+ offset = ( curve->format ? 1 : 0 );
/* Check key length */
- if ( ecdh->public_len != curve->curve->keysize ) {
+ if ( ecdh->public_len != ( offset + pointsize ) ) {
DBGC ( tls, "TLS %p invalid %s key\n",
tls, curve->curve->name );
- DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_len );
+ return -EINVAL_KEY_EXCHANGE;
+ }
+
+ /* Check curve point format byte (if present) */
+ if ( curve->format && ( ecdh->public[0] != curve->format ) ) {
+ DBGC ( tls, "TLS %p invalid %s curve point format\n",
+ tls, curve->curve->name );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_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];
+ uint8_t private[keysize];
+ uint8_t pre_master_secret[pointsize];
struct {
uint32_t type_length;
uint8_t public_len;
- uint8_t public[len];
+ uint8_t public[ecdh->public_len];
} __attribute__ (( packed )) key_xchg;
/* Generate ephemeral private key */
@@ -1735,36 +1795,33 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) {
return rc;
}
- /* Calculate pre-master secret */
- if ( ( rc = elliptic_multiply ( curve->curve,
- ecdh->public, private,
- pre_master_secret ) ) != 0 ) {
+ /* Exchange keys */
+ if ( ( rc = ecdhe_key ( curve->curve, ( ecdh->public + offset ),
+ private, ( key_xchg.public + offset ),
+ 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;
- }
+ key_xchg.public_len = sizeof ( key_xchg.public );
+ if ( curve->format )
+ key_xchg.public[0] = curve->format;
/* Transmit Client Key Exchange record */
if ( ( rc = tls_send_handshake ( tls, &key_xchg,
sizeof ( key_xchg ) ) ) !=0){
return rc;
}
+
+ /* Generate master secret */
+ tls_generate_master_secret ( tls, pre_master_secret,
+ curve->pre_master_secret_len );
}
return 0;
@@ -1783,7 +1840,7 @@ struct tls_key_exchange_algorithm tls_ecdhe_exchange_algorithm = {
* @ret rc Return status code
*/
static int tls_send_client_key_exchange ( struct tls_connection *tls ) {
- struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
+ struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.pending;
struct tls_cipher_suite *suite = cipherspec->suite;
int rc;
@@ -1812,24 +1869,17 @@ static int tls_send_client_key_exchange ( struct tls_connection *tls ) {
*/
static int tls_send_certificate_verify ( struct tls_connection *tls ) {
struct digest_algorithm *digest = tls->handshake_digest;
- struct x509_certificate *cert = x509_first ( tls->certs );
+ struct x509_certificate *cert = x509_first ( tls->client.chain );
struct pubkey_algorithm *pubkey = cert->signature_algorithm->pubkey;
- struct asn1_cursor *key = privkey_cursor ( tls->key );
+ struct asn1_cursor *key = privkey_cursor ( tls->client.key );
uint8_t digest_out[ digest->digestsize ];
- uint8_t ctx[ pubkey->ctxsize ];
struct tls_signature_hash_algorithm *sig_hash = NULL;
+ struct asn1_builder builder = { NULL, 0 };
int rc;
/* Generate digest to be signed */
tls_verify_handshake ( tls, digest_out );
- /* Initialise public-key algorithm */
- if ( ( rc = pubkey_init ( pubkey, ctx, key->data, key->len ) ) != 0 ) {
- DBGC ( tls, "TLS %p could not initialise %s client private "
- "key: %s\n", tls, pubkey->name, strerror ( rc ) );
- goto err_pubkey_init;
- }
-
/* TLSv1.2 and later use explicit algorithm identifiers */
if ( tls_version ( tls, TLS_VERSION_TLS_1_2 ) ) {
sig_hash = tls_signature_hash_algorithm ( pubkey, digest );
@@ -1842,55 +1892,53 @@ static int tls_send_certificate_verify ( struct tls_connection *tls ) {
}
}
- /* Generate and transmit record */
+ /* Sign digest */
+ if ( ( rc = pubkey_sign ( pubkey, key, digest, digest_out,
+ &builder ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not sign %s digest using %s client "
+ "private key: %s\n", tls, digest->name, pubkey->name,
+ strerror ( rc ) );
+ goto err_pubkey_sign;
+ }
+
+ /* Construct Certificate Verify record */
{
- size_t max_len = pubkey_max_len ( pubkey, ctx );
int use_sig_hash = ( ( sig_hash == NULL ) ? 0 : 1 );
struct {
uint32_t type_length;
struct tls_signature_hash_id sig_hash[use_sig_hash];
uint16_t signature_len;
- uint8_t signature[max_len];
- } __attribute__ (( packed )) certificate_verify;
- size_t unused;
- int len;
-
- /* Sign digest */
- len = pubkey_sign ( pubkey, ctx, digest, digest_out,
- certificate_verify.signature );
- if ( len < 0 ) {
- rc = len;
- DBGC ( tls, "TLS %p could not sign %s digest using %s "
- "client private key: %s\n", tls, digest->name,
- pubkey->name, strerror ( rc ) );
- goto err_pubkey_sign;
- }
- unused = ( max_len - len );
-
- /* Construct Certificate Verify record */
- certificate_verify.type_length =
- ( cpu_to_le32 ( TLS_CERTIFICATE_VERIFY ) |
- htonl ( sizeof ( certificate_verify ) -
- sizeof ( certificate_verify.type_length ) -
- unused ) );
+ } __attribute__ (( packed )) header;
+
+ header.type_length = ( cpu_to_le32 ( TLS_CERTIFICATE_VERIFY ) |
+ htonl ( builder.len +
+ sizeof ( header ) -
+ sizeof ( header.type_length )));
if ( use_sig_hash ) {
- memcpy ( &certificate_verify.sig_hash[0],
- &sig_hash->code,
- sizeof ( certificate_verify.sig_hash[0] ) );
+ memcpy ( &header.sig_hash[0], &sig_hash->code,
+ sizeof ( header.sig_hash[0] ) );
}
- certificate_verify.signature_len =
- htons ( sizeof ( certificate_verify.signature ) -
- unused );
+ header.signature_len = htons ( builder.len );
- /* Transmit record */
- rc = tls_send_handshake ( tls, &certificate_verify,
- ( sizeof ( certificate_verify ) - unused ) );
+ if ( ( rc = asn1_prepend_raw ( &builder, &header,
+ sizeof ( header ) ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not construct Certificate "
+ "Verify: %s\n", tls, strerror ( rc ) );
+ goto err_prepend;
+ }
}
+ /* Transmit record */
+ if ( ( rc = tls_send_handshake ( tls, builder.data,
+ builder.len ) ) != 0 ) {
+ goto err_send;
+ }
+
+ err_send:
+ err_prepend:
err_pubkey_sign:
err_sig_hash:
- pubkey_final ( pubkey, ctx );
- err_pubkey_init:
+ free ( builder.data );
return rc;
}
@@ -1946,7 +1994,7 @@ static int tls_send_finished ( struct tls_connection *tls ) {
return rc;
/* Mark client as finished */
- pending_put ( &tls->client_negotiation );
+ pending_put ( &tls->client.negotiation );
return 0;
}
@@ -1976,13 +2024,12 @@ static int tls_new_change_cipher ( struct tls_connection *tls,
iob_pull ( iobuf, sizeof ( *change_cipher ) );
/* Change receive cipher spec */
- if ( ( rc = tls_change_cipher ( tls, &tls->rx_cipherspec_pending,
- &tls->rx_cipherspec ) ) != 0 ) {
+ if ( ( rc = tls_change_cipher ( tls, &tls->rx.cipherspec ) ) != 0 ) {
DBGC ( tls, "TLS %p could not activate RX cipher: %s\n",
tls, strerror ( rc ) );
return rc;
}
- tls->rx_seq = ~( ( uint64_t ) 0 );
+ tls->rx.seq = ~( ( uint64_t ) 0 );
return 0;
}
@@ -2047,7 +2094,7 @@ static int tls_new_hello_request ( struct tls_connection *tls,
}
/* Fail unless server supports secure renegotiation */
- if ( ! tls->secure_renegotiation ) {
+ if ( ! ( tls->secure_renegotiation && tls->extended_master_secret ) ) {
DBGC ( tls, "TLS %p refusing to renegotiate insecurely\n",
tls );
return -EPERM_RENEG_INSECURE;
@@ -2094,6 +2141,9 @@ static int tls_new_server_hello ( struct tls_connection *tls,
uint8_t len;
uint8_t data[0];
} __attribute__ (( packed )) *reneg = NULL;
+ const struct {
+ uint8_t data[0];
+ } __attribute__ (( packed )) *ems = NULL;
uint16_t version;
size_t exts_len;
size_t ext_len;
@@ -2158,6 +2208,9 @@ static int tls_new_server_hello ( struct tls_connection *tls,
return -EINVAL_HELLO;
}
break;
+ case htons ( TLS_EXTENDED_MASTER_SECRET ) :
+ ems = ( ( void * ) ext->data );
+ break;
}
}
}
@@ -2188,8 +2241,11 @@ static int tls_new_server_hello ( struct tls_connection *tls,
return rc;
/* Copy out server random bytes */
- memcpy ( &tls->server_random, &hello_a->random,
- sizeof ( tls->server_random ) );
+ memcpy ( &tls->server.random, &hello_a->random,
+ sizeof ( tls->server.random ) );
+
+ /* Handle extended master secret */
+ tls->extended_master_secret = ( !! ems );
/* Check session ID */
if ( hello_a->session_id_len &&
@@ -2203,6 +2259,14 @@ static int tls_new_server_hello ( struct tls_connection *tls,
if ( ( rc = tls_generate_keys ( tls ) ) != 0 )
return rc;
+ /* Ensure master secret generation method matches */
+ if ( tls->extended_master_secret !=
+ tls->session->extended_master_secret ) {
+ DBGC ( tls, "TLS %p mismatched extended master secret "
+ "extension\n", tls );
+ return -EPERM_EMS;
+ }
+
} else {
/* Record new session ID, if present */
@@ -2309,12 +2373,13 @@ static int tls_parse_chain ( struct tls_connection *tls,
int rc;
/* Free any existing certificate chain */
- x509_chain_put ( tls->chain );
- tls->chain = NULL;
+ memset ( &tls->server.key, 0, sizeof ( tls->server.key ) );
+ x509_chain_put ( tls->server.chain );
+ tls->server.chain = NULL;
/* Create certificate chain */
- tls->chain = x509_alloc_chain();
- if ( ! tls->chain ) {
+ tls->server.chain = x509_alloc_chain();
+ if ( ! tls->server.chain ) {
rc = -ENOMEM_CHAIN;
goto err_alloc_chain;
}
@@ -2346,14 +2411,15 @@ static int tls_parse_chain ( struct tls_connection *tls,
record_len = ( sizeof ( *certificate ) + certificate_len );
/* Add certificate to chain */
- if ( ( rc = x509_append_raw ( tls->chain, certificate->data,
+ if ( ( rc = x509_append_raw ( tls->server.chain,
+ certificate->data,
certificate_len ) ) != 0 ) {
DBGC ( tls, "TLS %p could not append certificate: %s\n",
tls, strerror ( rc ) );
DBGC_HDA ( tls, 0, data, remaining );
goto err_parse;
}
- cert = x509_last ( tls->chain );
+ cert = x509_last ( tls->server.chain );
DBGC ( tls, "TLS %p found certificate %s\n",
tls, x509_name ( cert ) );
@@ -2367,8 +2433,9 @@ static int tls_parse_chain ( struct tls_connection *tls,
err_parse:
err_overlength:
err_underlength:
- x509_chain_put ( tls->chain );
- tls->chain = NULL;
+ memset ( &tls->server.key, 0, sizeof ( tls->server.key ) );
+ x509_chain_put ( tls->server.chain );
+ tls->server.chain = NULL;
err_alloc_chain:
return rc;
}
@@ -2425,12 +2492,12 @@ static int tls_new_server_key_exchange ( struct tls_connection *tls,
const void *data, size_t len ) {
/* Free any existing server key exchange record */
- free ( tls->server_key );
- tls->server_key_len = 0;
+ free ( tls->server.exchange );
+ tls->server.exchange_len = 0;
/* Allocate copy of server key exchange record */
- tls->server_key = malloc ( len );
- if ( ! tls->server_key )
+ tls->server.exchange = malloc ( len );
+ if ( ! tls->server.exchange )
return -ENOMEM;
/* Store copy of server key exchange record for later
@@ -2438,8 +2505,8 @@ static int tls_new_server_key_exchange ( struct tls_connection *tls,
* since the certificate validation will not yet have
* completed.
*/
- memcpy ( tls->server_key, data, len );
- tls->server_key_len = len;
+ memcpy ( tls->server.exchange, data, len );
+ tls->server.exchange_len = len;
return 0;
}
@@ -2463,48 +2530,44 @@ static int tls_new_certificate_request ( struct tls_connection *tls,
*/
/* Free any existing client certificate chain */
- x509_chain_put ( tls->certs );
- tls->certs = NULL;
-
- /* Determine client certificate to be sent */
- cert = certstore_find_key ( tls->key );
- if ( ! cert ) {
- DBGC ( tls, "TLS %p could not find certificate corresponding "
- "to private key\n", tls );
- rc = -EPERM_CLIENT_CERT;
- goto err_find;
- }
- x509_get ( cert );
- DBGC ( tls, "TLS %p selected client certificate %s\n",
- tls, x509_name ( cert ) );
+ x509_chain_put ( tls->client.chain );
+ tls->client.chain = NULL;
/* Create client certificate chain */
- tls->certs = x509_alloc_chain();
- if ( ! tls->certs ) {
+ tls->client.chain = x509_alloc_chain();
+ if ( ! tls->client.chain ) {
rc = -ENOMEM;
goto err_alloc;
}
- /* Append client certificate to chain */
- if ( ( rc = x509_append ( tls->certs, cert ) ) != 0 )
- goto err_append;
+ /* Determine client certificate to be sent, if any */
+ cert = x509_find_key ( NULL, tls->client.key );
+ if ( cert ) {
+ DBGC ( tls, "TLS %p selected client certificate %s\n",
+ tls, x509_name ( cert ) );
- /* Append any relevant issuer certificates */
- if ( ( rc = x509_auto_append ( tls->certs, &certstore ) ) != 0 )
- goto err_auto_append;
+ /* Append client certificate to chain */
+ if ( ( rc = x509_append ( tls->client.chain, cert ) ) != 0 )
+ goto err_append;
- /* Drop local reference to client certificate */
- x509_put ( cert );
+ /* Append any relevant issuer certificates */
+ if ( ( rc = x509_auto_append ( tls->client.chain,
+ &certstore ) ) != 0 )
+ goto err_auto_append;
+ } else {
+
+ /* Send an empty certificate chain */
+ DBGC ( tls, "TLS %p could not find certificate corresponding "
+ "to private key\n", tls );
+ }
return 0;
err_auto_append:
err_append:
- x509_chain_put ( tls->certs );
- tls->certs = NULL;
+ x509_chain_put ( tls->client.chain );
+ tls->client.chain = NULL;
err_alloc:
- x509_put ( cert );
- err_find:
return rc;
}
@@ -2532,13 +2595,14 @@ static int tls_new_server_hello_done ( struct tls_connection *tls,
}
/* Begin certificate validation */
- if ( ( rc = create_validator ( &tls->validator, tls->chain,
- tls->root ) ) != 0 ) {
+ if ( ( rc = create_validator ( &tls->server.validator,
+ tls->server.chain,
+ tls->server.root ) ) != 0 ) {
DBGC ( tls, "TLS %p could not start certificate validation: "
"%s\n", tls, strerror ( rc ) );
return rc;
}
- pending_get ( &tls->validation );
+ pending_get ( &tls->server.validation );
return 0;
}
@@ -2580,14 +2644,14 @@ static int tls_new_finished ( struct tls_connection *tls,
}
/* Mark server as finished */
- pending_put ( &tls->server_negotiation );
+ pending_put ( &tls->server.negotiation );
/* If we are resuming a session (i.e. if the server Finished
* arrives before the client Finished is sent), then schedule
* transmission of Change Cipher and Finished.
*/
- if ( is_pending ( &tls->client_negotiation ) ) {
- tls->tx_pending |= ( TLS_TX_CHANGE_CIPHER | TLS_TX_FINISHED );
+ if ( is_pending ( &tls->client.negotiation ) ) {
+ tls->tx.pending |= ( TLS_TX_CHANGE_CIPHER | TLS_TX_FINISHED );
tls_tx_resume ( tls );
}
@@ -2595,6 +2659,7 @@ static int tls_new_finished ( struct tls_connection *tls,
if ( tls->session_id_len || tls->new_session_ticket_len ) {
memcpy ( session->master_secret, tls->master_secret,
sizeof ( session->master_secret ) );
+ session->extended_master_secret = tls->extended_master_secret;
}
if ( tls->session_id_len ) {
session->id_len = tls->session_id_len;
@@ -2788,7 +2853,7 @@ static int tls_new_record ( struct tls_connection *tls, unsigned int type,
break;
case TLS_TYPE_HANDSHAKE:
handler = tls_new_handshake;
- iobuf = &tls->rx_handshake;
+ iobuf = &tls->rx.handshake;
break;
default:
DBGC ( tls, "TLS %p unknown record type %d\n", tls, type );
@@ -2925,143 +2990,228 @@ static void tls_hmac_list ( struct tls_cipherspec *cipherspec,
}
/**
- * Send plaintext record
+ * Calculate maximum additional length required for transmitted record(s)
+ *
+ * @v tls TLS connection
+ * @v len I/O buffer payload length
+ * @ret reserve Maximum additional length to reserve
+ */
+static size_t tls_iob_reserved ( struct tls_connection *tls, size_t len ) {
+ struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.active;
+ struct tls_cipher_suite *suite = cipherspec->suite;
+ struct cipher_algorithm *cipher = suite->cipher;
+ struct tls_header *tlshdr;
+ unsigned int count;
+ size_t each;
+
+ /* Calculate number of records (allowing for zero-length records) */
+ count = ( len ? ( ( len + TLS_TX_BUFSIZE - 1 ) / TLS_TX_BUFSIZE ) : 1 );
+
+ /* Calculate maximum additional length per record */
+ each = ( sizeof ( *tlshdr ) + suite->record_iv_len + suite->mac_len +
+ ( is_block_cipher ( cipher ) ? cipher->blocksize : 0 ) +
+ cipher->authsize );
+
+ /* Calculate maximum total additional length */
+ return ( count * each );
+}
+
+/**
+ * Allocate I/O buffer for transmitted record(s)
+ *
+ * @v tls TLS connection
+ * @v len I/O buffer payload length
+ * @ret iobuf I/O buffer
+ */
+static struct io_buffer * tls_alloc_iob ( struct tls_connection *tls,
+ size_t len ) {
+ struct io_buffer *iobuf;
+ size_t reserve;
+
+ /* Calculate maximum additional length to reserve */
+ reserve = tls_iob_reserved ( tls, len );
+
+ /* Allocate I/O buffer */
+ iobuf = xfer_alloc_iob ( &tls->cipherstream, ( reserve + len ) );
+ if ( ! iobuf )
+ return NULL;
+
+ /* Reserve space */
+ iob_reserve ( iobuf, reserve );
+
+ return iobuf;
+}
+
+/**
+ * Send plaintext record(s)
*
* @v tls TLS connection
* @v type Record type
- * @v data Plaintext record
- * @v len Length of plaintext record
+ * @v iobuf I/O buffer
* @ret rc Return status code
*/
-static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
- const void *data, size_t len ) {
- struct tls_cipherspec *cipherspec = &tls->tx_cipherspec;
+static int tls_send_record ( struct tls_connection *tls, unsigned int type,
+ struct io_buffer *iobuf ) {
+ struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.active;
struct tls_cipher_suite *suite = cipherspec->suite;
struct cipher_algorithm *cipher = suite->cipher;
struct digest_algorithm *digest = suite->digest;
struct {
uint8_t fixed[suite->fixed_iv_len];
- uint8_t record[suite->record_iv_len];
+ uint8_t rec[suite->record_iv_len];
} __attribute__ (( packed )) iv;
struct tls_auth_header authhdr;
struct tls_header *tlshdr;
- void *plaintext;
- size_t plaintext_len;
- struct io_buffer *ciphertext;
- size_t ciphertext_len;
- size_t padding_len;
uint8_t mac[digest->digestsize];
- void *tmp;
+ const void *plaintext;
+ const void *encrypt;
+ void *ciphertext;
+ size_t record_len;
+ size_t encrypt_len;
+ size_t pad_len;
+ size_t len;
int rc;
- /* Construct initialisation vector */
- memcpy ( iv.fixed, cipherspec->fixed_iv, sizeof ( iv.fixed ) );
- 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 );
- authhdr.header.type = type;
- authhdr.header.version = htons ( tls->version );
- authhdr.header.length = htons ( len );
+ /* Record plaintext pointer and length */
+ plaintext = iobuf->data;
+ len = iob_len ( iobuf );
+
+ /* Add to handshake digest if applicable */
+ if ( type == TLS_TYPE_HANDSHAKE )
+ tls_add_handshake ( tls, plaintext, len );
+
+ /* Start constructing ciphertext at start of reserved space */
+ iob_push ( iobuf, tls_iob_reserved ( tls, len ) );
+ iob_unput ( iobuf, iob_len ( iobuf ) );
+
+ /* Construct records */
+ do {
+ /* Limit length of this record (may be zero) */
+ record_len = len;
+ if ( record_len > TLS_TX_BUFSIZE )
+ record_len = TLS_TX_BUFSIZE;
+
+ /* Construct and set initialisation vector */
+ memcpy ( iv.fixed, cipherspec->fixed_iv, sizeof ( iv.fixed ) );
+ if ( ( rc = tls_generate_random ( tls, iv.rec,
+ sizeof ( iv.rec ) ) ) != 0 ) {
+ goto err_random;
+ }
+ cipher_setiv ( cipher, cipherspec->cipher_ctx, &iv,
+ sizeof ( iv ) );
+
+ /* Construct and process authentication data */
+ authhdr.seq = cpu_to_be64 ( tls->tx.seq );
+ authhdr.header.type = type;
+ authhdr.header.version = htons ( tls->version );
+ authhdr.header.length = htons ( record_len );
+ if ( suite->mac_len ) {
+ tls_hmac ( cipherspec, &authhdr, plaintext, record_len,
+ mac );
+ }
+ if ( is_auth_cipher ( cipher ) ) {
+ cipher_encrypt ( cipher, cipherspec->cipher_ctx,
+ &authhdr, NULL, sizeof ( authhdr ) );
+ }
- /* Calculate padding length */
- plaintext_len = ( len + suite->mac_len );
- if ( is_block_cipher ( cipher ) ) {
- padding_len = ( ( ( cipher->blocksize - 1 ) &
- -( plaintext_len + 1 ) ) + 1 );
- } else {
- padding_len = 0;
- }
- plaintext_len += padding_len;
+ /* Calculate encryption length */
+ encrypt_len = ( record_len + suite->mac_len );
+ if ( is_block_cipher ( cipher ) ) {
+ pad_len = ( ( ( cipher->blocksize - 1 ) &
+ -( encrypt_len + 1 ) ) + 1 );
+ } else {
+ pad_len = 0;
+ }
+ encrypt_len += pad_len;
+
+ /* Add record header */
+ tlshdr = iob_put ( iobuf, sizeof ( *tlshdr ) );
+ tlshdr->type = type;
+ tlshdr->version = htons ( tls->version );
+ tlshdr->length = htons ( sizeof ( iv.rec ) + encrypt_len +
+ cipher->authsize );
+
+ /* Add record initialisation vector, if applicable */
+ memcpy ( iob_put ( iobuf, sizeof ( iv.rec ) ), iv.rec,
+ sizeof ( iv.rec ) );
+
+ /* Copy plaintext data if necessary */
+ ciphertext = iob_put ( iobuf, record_len );
+ assert ( ciphertext <= plaintext );
+ if ( encrypt_len > record_len ) {
+ memmove ( ciphertext, plaintext, record_len );
+ encrypt = ciphertext;
+ } else {
+ encrypt = plaintext;
+ }
- /* Allocate plaintext */
- plaintext = malloc ( plaintext_len );
- if ( ! plaintext ) {
- DBGC ( tls, "TLS %p could not allocate %zd bytes for "
- "plaintext\n", tls, plaintext_len );
- rc = -ENOMEM_TX_PLAINTEXT;
- goto err_plaintext;
- }
+ /* Add MAC, if applicable */
+ memcpy ( iob_put ( iobuf, suite->mac_len ), mac,
+ suite->mac_len );
- /* Assemble plaintext */
- tmp = plaintext;
- memcpy ( tmp, data, len );
- tmp += len;
- if ( suite->mac_len )
- tls_hmac ( cipherspec, &authhdr, data, len, mac );
- memcpy ( tmp, mac, suite->mac_len );
- tmp += suite->mac_len;
- memset ( tmp, ( padding_len - 1 ), padding_len );
- tmp += padding_len;
- assert ( tmp == ( plaintext + plaintext_len ) );
- DBGC2 ( tls, "Sending plaintext data:\n" );
- DBGC2_HD ( tls, plaintext, plaintext_len );
+ /* Add padding, if applicable */
+ memset ( iob_put ( iobuf, pad_len ), ( pad_len - 1 ), pad_len );
- /* Set initialisation vector */
- cipher_setiv ( cipher, cipherspec->cipher_ctx, &iv, sizeof ( iv ) );
+ /* Encrypt data and append authentication tag */
+ DBGC2 ( tls, "Sending plaintext data:\n" );
+ DBGC2_HDA ( tls, 0, encrypt, encrypt_len );
+ cipher_encrypt ( cipher, cipherspec->cipher_ctx, encrypt,
+ ciphertext, encrypt_len );
+ cipher_auth ( cipher, cipherspec->cipher_ctx,
+ iob_put ( iobuf, cipher->authsize ) );
- /* Process authentication data, if applicable */
- if ( is_auth_cipher ( cipher ) ) {
- cipher_encrypt ( cipher, cipherspec->cipher_ctx, &authhdr,
- NULL, sizeof ( authhdr ) );
- }
+ /* Move to next record */
+ tls->tx.seq += 1;
+ plaintext += record_len;
+ len -= record_len;
- /* Allocate ciphertext */
- ciphertext_len = ( sizeof ( *tlshdr ) + sizeof ( iv.record ) +
- plaintext_len + cipher->authsize );
- ciphertext = xfer_alloc_iob ( &tls->cipherstream, ciphertext_len );
- if ( ! ciphertext ) {
- DBGC ( tls, "TLS %p could not allocate %zd bytes for "
- "ciphertext\n", tls, ciphertext_len );
- rc = -ENOMEM_TX_CIPHERTEXT;
- goto err_ciphertext;
- }
-
- /* Assemble ciphertext */
- tlshdr = iob_put ( ciphertext, sizeof ( *tlshdr ) );
- tlshdr->type = type;
- tlshdr->version = htons ( tls->version );
- tlshdr->length = htons ( ciphertext_len - sizeof ( *tlshdr ) );
- memcpy ( iob_put ( ciphertext, sizeof ( iv.record ) ), iv.record,
- sizeof ( iv.record ) );
- cipher_encrypt ( cipher, cipherspec->cipher_ctx, plaintext,
- iob_put ( ciphertext, plaintext_len ), plaintext_len );
- cipher_auth ( cipher, cipherspec->cipher_ctx,
- iob_put ( ciphertext, cipher->authsize ) );
- assert ( iob_len ( ciphertext ) == ciphertext_len );
-
- /* Free plaintext as soon as possible to conserve memory */
- free ( plaintext );
- plaintext = NULL;
+ } while ( len );
/* Send ciphertext */
if ( ( rc = xfer_deliver_iob ( &tls->cipherstream,
- iob_disown ( ciphertext ) ) ) != 0 ) {
+ iob_disown ( iobuf ) ) ) != 0 ) {
DBGC ( tls, "TLS %p could not deliver ciphertext: %s\n",
tls, strerror ( rc ) );
goto err_deliver;
}
- /* Update TX state machine to next record */
- tls->tx_seq += 1;
-
- assert ( plaintext == NULL );
- assert ( ciphertext == NULL );
+ assert ( iobuf == NULL );
return 0;
err_deliver:
- free_iob ( ciphertext );
- err_ciphertext:
- free ( plaintext );
- err_plaintext:
err_random:
+ free_iob ( iobuf );
return rc;
}
/**
+ * Send plaintext record
+ *
+ * @v tls TLS connection
+ * @v type Record type
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
+ const void *data, size_t len ) {
+ struct io_buffer *iobuf;
+ int rc;
+
+ /* Allocate I/O buffer */
+ iobuf = tls_alloc_iob ( tls, len );
+ if ( ! iobuf )
+ return -ENOMEM_TX_PLAINTEXT;
+ memcpy ( iob_put ( iobuf, len ), data, len );
+
+ /* Transmit I/O buffer */
+ if ( ( rc = tls_send_record ( tls, type, iob_disown ( iobuf ) ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
* Verify block padding
*
* @v tls TLS connection
@@ -3107,7 +3257,7 @@ static int tls_verify_padding ( struct tls_connection *tls,
static int tls_new_ciphertext ( struct tls_connection *tls,
struct tls_header *tlshdr,
struct list_head *rx_data ) {
- struct tls_cipherspec *cipherspec = &tls->rx_cipherspec;
+ struct tls_cipherspec *cipherspec = &tls->rx.cipherspec.active;
struct tls_cipher_suite *suite = cipherspec->suite;
struct cipher_algorithm *cipher = suite->cipher;
struct digest_algorithm *digest = suite->digest;
@@ -3156,7 +3306,7 @@ static int tls_new_ciphertext ( struct tls_connection *tls,
auth = last->tail;
/* Construct authentication data */
- authhdr.seq = cpu_to_be64 ( tls->rx_seq );
+ authhdr.seq = cpu_to_be64 ( tls->rx.seq );
authhdr.header.type = tlshdr->type;
authhdr.header.version = tlshdr->version;
authhdr.header.length = htons ( len );
@@ -3172,7 +3322,7 @@ static int tls_new_ciphertext ( struct tls_connection *tls,
/* Decrypt the received data */
check_len = 0;
- list_for_each_entry ( iobuf, &tls->rx_data, list ) {
+ list_for_each_entry ( iobuf, &tls->rx.data, list ) {
cipher_decrypt ( cipher, cipherspec->cipher_ctx,
iobuf->data, iobuf->data, iob_len ( iobuf ) );
check_len += iob_len ( iobuf );
@@ -3278,8 +3428,9 @@ static int tls_plainstream_deliver ( struct tls_connection *tls,
goto done;
}
- if ( ( rc = tls_send_plaintext ( tls, TLS_TYPE_DATA, iobuf->data,
- iob_len ( iobuf ) ) ) != 0 )
+ /* Send data record */
+ if ( ( rc = tls_send_record ( tls, TLS_TYPE_DATA,
+ iob_disown ( iobuf ) ) ) != 0 )
goto done;
done:
@@ -3298,8 +3449,8 @@ static int tls_progress ( struct tls_connection *tls,
struct job_progress *progress ) {
/* Return cipherstream or validator progress as applicable */
- if ( is_pending ( &tls->validation ) ) {
- return job_progress ( &tls->validator, progress );
+ if ( is_pending ( &tls->server.validation ) ) {
+ return job_progress ( &tls->server.validator, progress );
} else {
return job_progress ( &tls->cipherstream, progress );
}
@@ -3307,6 +3458,7 @@ static int tls_progress ( struct tls_connection *tls,
/** TLS plaintext stream interface operations */
static struct interface_operation tls_plainstream_ops[] = {
+ INTF_OP ( xfer_alloc_iob, struct tls_connection *, tls_alloc_iob ),
INTF_OP ( xfer_deliver, struct tls_connection *,
tls_plainstream_deliver ),
INTF_OP ( xfer_window, struct tls_connection *,
@@ -3334,10 +3486,10 @@ static struct interface_descriptor tls_plainstream_desc =
* @ret rc Returned status code
*/
static int tls_newdata_process_header ( struct tls_connection *tls ) {
- struct tls_cipherspec *cipherspec = &tls->rx_cipherspec;
+ struct tls_cipherspec *cipherspec = &tls->rx.cipherspec.active;
struct cipher_algorithm *cipher = cipherspec->suite->cipher;
size_t iv_len = cipherspec->suite->record_iv_len;
- size_t data_len = ntohs ( tls->rx_header.length );
+ size_t data_len = ntohs ( tls->rx.header.length );
size_t remaining = data_len;
size_t frag_len;
size_t reserve;
@@ -3353,7 +3505,7 @@ static int tls_newdata_process_header ( struct tls_connection *tls ) {
remaining += reserve;
/* Allocate data buffers now that we know the length */
- assert ( list_empty ( &tls->rx_data ) );
+ assert ( list_empty ( &tls->rx.data ) );
while ( remaining ) {
/* Calculate fragment length. Ensure that no block is
@@ -3394,16 +3546,16 @@ static int tls_newdata_process_header ( struct tls_connection *tls ) {
reserve = 0;
/* Add I/O buffer to list */
- list_add_tail ( &iobuf->list, &tls->rx_data );
+ list_add_tail ( &iobuf->list, &tls->rx.data );
}
/* Move to data state */
- tls->rx_state = TLS_RX_DATA;
+ tls->rx.state = TLS_RX_DATA;
return 0;
err:
- list_for_each_entry_safe ( iobuf, tmp, &tls->rx_data, list ) {
+ list_for_each_entry_safe ( iobuf, tmp, &tls->rx.data, list ) {
list_del ( &iobuf->list );
free_iob ( iobuf );
}
@@ -3421,27 +3573,27 @@ static int tls_newdata_process_data ( struct tls_connection *tls ) {
int rc;
/* Move current buffer to end of list */
- iobuf = list_first_entry ( &tls->rx_data, struct io_buffer, list );
+ iobuf = list_first_entry ( &tls->rx.data, struct io_buffer, list );
list_del ( &iobuf->list );
- list_add_tail ( &iobuf->list, &tls->rx_data );
+ list_add_tail ( &iobuf->list, &tls->rx.data );
/* Continue receiving data if any space remains */
- iobuf = list_first_entry ( &tls->rx_data, struct io_buffer, list );
+ iobuf = list_first_entry ( &tls->rx.data, struct io_buffer, list );
if ( iob_tailroom ( iobuf ) )
return 0;
/* Process record */
- if ( ( rc = tls_new_ciphertext ( tls, &tls->rx_header,
- &tls->rx_data ) ) != 0 )
+ if ( ( rc = tls_new_ciphertext ( tls, &tls->rx.header,
+ &tls->rx.data ) ) != 0 )
return rc;
/* Increment RX sequence number */
- tls->rx_seq += 1;
+ tls->rx.seq += 1;
/* Return to header state */
- assert ( list_empty ( &tls->rx_data ) );
- tls->rx_state = TLS_RX_HEADER;
- iob_unput ( &tls->rx_header_iobuf, sizeof ( tls->rx_header ) );
+ assert ( list_empty ( &tls->rx.data ) );
+ tls->rx.state = TLS_RX_HEADER;
+ iob_unput ( &tls->rx.iobuf, sizeof ( tls->rx.header ) );
return 0;
}
@@ -3480,13 +3632,13 @@ static int tls_cipherstream_deliver ( struct tls_connection *tls,
while ( iob_len ( iobuf ) ) {
/* Select buffer according to current state */
- switch ( tls->rx_state ) {
+ switch ( tls->rx.state ) {
case TLS_RX_HEADER:
- dest = &tls->rx_header_iobuf;
+ dest = &tls->rx.iobuf;
process = tls_newdata_process_header;
break;
case TLS_RX_DATA:
- dest = list_first_entry ( &tls->rx_data,
+ dest = list_first_entry ( &tls->rx.data,
struct io_buffer, list );
assert ( dest != NULL );
process = tls_newdata_process_data;
@@ -3550,15 +3702,13 @@ static struct interface_descriptor tls_cipherstream_desc =
*/
static void tls_validator_done ( struct tls_connection *tls, int rc ) {
struct tls_session *session = tls->session;
- struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
- struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey;
struct x509_certificate *cert;
/* Mark validation as complete */
- pending_put ( &tls->validation );
+ pending_put ( &tls->server.validation );
/* Close validator interface */
- intf_restart ( &tls->validator, rc );
+ intf_restart ( &tls->server.validator, rc );
/* Check for validation failure */
if ( rc != 0 ) {
@@ -3569,7 +3719,7 @@ static void tls_validator_done ( struct tls_connection *tls, int rc ) {
DBGC ( tls, "TLS %p certificate validation succeeded\n", tls );
/* Extract first certificate */
- cert = x509_first ( tls->chain );
+ cert = x509_first ( tls->server.chain );
assert ( cert != NULL );
/* Verify server name */
@@ -3579,22 +3729,18 @@ static void tls_validator_done ( struct tls_connection *tls, int rc ) {
goto err;
}
- /* Initialise public key algorithm */
- if ( ( rc = pubkey_init ( pubkey, cipherspec->pubkey_ctx,
- cert->subject.public_key.raw.data,
- cert->subject.public_key.raw.len ) ) != 0 ) {
- DBGC ( tls, "TLS %p cannot initialise public key: %s\n",
- tls, strerror ( rc ) );
- goto err;
- }
+ /* Extract the now trusted server public key */
+ memcpy ( &tls->server.key, &cert->subject.public_key.raw,
+ sizeof ( tls->server.key ) );
- /* Schedule Client Key Exchange, Change Cipher, and Finished */
- tls->tx_pending |= ( TLS_TX_CLIENT_KEY_EXCHANGE |
+ /* Schedule transmission of applicable handshake messages */
+ tls->tx.pending |= ( TLS_TX_CLIENT_KEY_EXCHANGE |
TLS_TX_CHANGE_CIPHER |
TLS_TX_FINISHED );
- if ( tls->certs ) {
- tls->tx_pending |= ( TLS_TX_CERTIFICATE |
- TLS_TX_CERTIFICATE_VERIFY );
+ if ( tls->client.chain ) {
+ tls->tx.pending |= TLS_TX_CERTIFICATE;
+ if ( ! list_empty ( &tls->client.chain->links ) )
+ tls->tx.pending |= TLS_TX_CERTIFICATE_VERIFY;
}
tls_tx_resume ( tls );
@@ -3612,7 +3758,8 @@ static struct interface_operation tls_validator_ops[] = {
/** TLS certificate validator interface descriptor */
static struct interface_descriptor tls_validator_desc =
- INTF_DESC ( struct tls_connection, validator, tls_validator_ops );
+ INTF_DESC ( struct tls_connection, server.validator,
+ tls_validator_ops );
/******************************************************************************
*
@@ -3636,7 +3783,7 @@ static void tls_tx_step ( struct tls_connection *tls ) {
return;
/* Send first pending transmission */
- if ( tls->tx_pending & TLS_TX_CLIENT_HELLO ) {
+ if ( tls->tx.pending & TLS_TX_CLIENT_HELLO ) {
/* Serialise server negotiations within a session, to
* provide a consistent view of session IDs and
* session tickets.
@@ -3644,7 +3791,7 @@ static void tls_tx_step ( struct tls_connection *tls ) {
list_for_each_entry ( conn, &session->conn, list ) {
if ( conn == tls )
break;
- if ( is_pending ( &conn->server_negotiation ) )
+ if ( is_pending ( &conn->server.negotiation ) )
return;
}
/* Record or generate session ID and associated master secret */
@@ -3658,8 +3805,8 @@ static void tls_tx_step ( struct tls_connection *tls ) {
} else {
/* No existing session: use a random session ID */
assert ( sizeof ( tls->session_id ) ==
- sizeof ( tls->client_random ) );
- memcpy ( tls->session_id, &tls->client_random,
+ sizeof ( tls->client.random ) );
+ memcpy ( tls->session_id, &tls->client.random,
sizeof ( tls->session_id ) );
tls->session_id_len = sizeof ( tls->session_id );
}
@@ -3669,32 +3816,32 @@ static void tls_tx_step ( struct tls_connection *tls ) {
tls, strerror ( rc ) );
goto err;
}
- tls->tx_pending &= ~TLS_TX_CLIENT_HELLO;
- } else if ( tls->tx_pending & TLS_TX_CERTIFICATE ) {
+ tls->tx.pending &= ~TLS_TX_CLIENT_HELLO;
+ } else if ( tls->tx.pending & TLS_TX_CERTIFICATE ) {
/* Send Certificate */
if ( ( rc = tls_send_certificate ( tls ) ) != 0 ) {
DBGC ( tls, "TLS %p cold not send Certificate: %s\n",
tls, strerror ( rc ) );
goto err;
}
- tls->tx_pending &= ~TLS_TX_CERTIFICATE;
- } else if ( tls->tx_pending & TLS_TX_CLIENT_KEY_EXCHANGE ) {
+ tls->tx.pending &= ~TLS_TX_CERTIFICATE;
+ } else if ( tls->tx.pending & TLS_TX_CLIENT_KEY_EXCHANGE ) {
/* Send Client Key Exchange */
if ( ( rc = tls_send_client_key_exchange ( tls ) ) != 0 ) {
DBGC ( tls, "TLS %p could not send Client Key "
"Exchange: %s\n", tls, strerror ( rc ) );
goto err;
}
- tls->tx_pending &= ~TLS_TX_CLIENT_KEY_EXCHANGE;
- } else if ( tls->tx_pending & TLS_TX_CERTIFICATE_VERIFY ) {
+ tls->tx.pending &= ~TLS_TX_CLIENT_KEY_EXCHANGE;
+ } else if ( tls->tx.pending & TLS_TX_CERTIFICATE_VERIFY ) {
/* Send Certificate Verify */
if ( ( rc = tls_send_certificate_verify ( tls ) ) != 0 ) {
DBGC ( tls, "TLS %p could not send Certificate "
"Verify: %s\n", tls, strerror ( rc ) );
goto err;
}
- tls->tx_pending &= ~TLS_TX_CERTIFICATE_VERIFY;
- } else if ( tls->tx_pending & TLS_TX_CHANGE_CIPHER ) {
+ tls->tx.pending &= ~TLS_TX_CERTIFICATE_VERIFY;
+ } else if ( tls->tx.pending & TLS_TX_CHANGE_CIPHER ) {
/* Send Change Cipher, and then change the cipher in use */
if ( ( rc = tls_send_change_cipher ( tls ) ) != 0 ) {
DBGC ( tls, "TLS %p could not send Change Cipher: "
@@ -3702,28 +3849,27 @@ static void tls_tx_step ( struct tls_connection *tls ) {
goto err;
}
if ( ( rc = tls_change_cipher ( tls,
- &tls->tx_cipherspec_pending,
- &tls->tx_cipherspec )) != 0 ){
+ &tls->tx.cipherspec ) ) != 0 ){
DBGC ( tls, "TLS %p could not activate TX cipher: "
"%s\n", tls, strerror ( rc ) );
goto err;
}
- tls->tx_seq = 0;
- tls->tx_pending &= ~TLS_TX_CHANGE_CIPHER;
- } else if ( tls->tx_pending & TLS_TX_FINISHED ) {
+ tls->tx.seq = 0;
+ tls->tx.pending &= ~TLS_TX_CHANGE_CIPHER;
+ } else if ( tls->tx.pending & TLS_TX_FINISHED ) {
/* Send Finished */
if ( ( rc = tls_send_finished ( tls ) ) != 0 ) {
DBGC ( tls, "TLS %p could not send Finished: %s\n",
tls, strerror ( rc ) );
goto err;
}
- tls->tx_pending &= ~TLS_TX_FINISHED;
+ tls->tx.pending &= ~TLS_TX_FINISHED;
}
/* Reschedule process if pending transmissions remain,
* otherwise send notification of a window change.
*/
- if ( tls->tx_pending ) {
+ if ( tls->tx.pending ) {
tls_tx_resume ( tls );
} else {
xfer_window_changed ( &tls->plainstream );
@@ -3737,7 +3883,7 @@ static void tls_tx_step ( struct tls_connection *tls ) {
/** TLS TX process descriptor */
static struct process_descriptor tls_process_desc =
- PROC_DESC_ONCE ( struct tls_connection, process, tls_tx_step );
+ PROC_DESC_ONCE ( struct tls_connection, tx.process, tls_tx_step );
/******************************************************************************
*
@@ -3761,8 +3907,8 @@ static int tls_session ( struct tls_connection *tls, const char *name ) {
/* Find existing matching session, if any */
list_for_each_entry ( session, &tls_sessions, list ) {
if ( ( strcmp ( name, session->name ) == 0 ) &&
- ( tls->root == session->root ) &&
- ( tls->key == session->key ) ) {
+ ( tls->server.root == session->root ) &&
+ ( tls->client.key == session->key ) ) {
ref_get ( &session->refcnt );
tls->session = session;
DBGC ( tls, "TLS %p joining session %s\n", tls, name );
@@ -3781,8 +3927,8 @@ static int tls_session ( struct tls_connection *tls, const char *name ) {
name_copy = ( ( ( void * ) session ) + sizeof ( *session ) );
strcpy ( name_copy, name );
session->name = name_copy;
- session->root = x509_root_get ( tls->root );
- session->key = privkey_get ( tls->key );
+ session->root = x509_root_get ( tls->server.root );
+ session->key = privkey_get ( tls->client.key );
INIT_LIST_HEAD ( &session->conn );
list_add ( &session->list, &tls_sessions );
@@ -3829,23 +3975,23 @@ int add_tls ( struct interface *xfer, const char *name,
INIT_LIST_HEAD ( &tls->list );
intf_init ( &tls->plainstream, &tls_plainstream_desc, &tls->refcnt );
intf_init ( &tls->cipherstream, &tls_cipherstream_desc, &tls->refcnt );
- intf_init ( &tls->validator, &tls_validator_desc, &tls->refcnt );
- process_init_stopped ( &tls->process, &tls_process_desc,
+ intf_init ( &tls->server.validator, &tls_validator_desc, &tls->refcnt );
+ process_init_stopped ( &tls->tx.process, &tls_process_desc,
&tls->refcnt );
- tls->key = privkey_get ( key ? key : &private_key );
- tls->root = x509_root_get ( root ? root : &root_certificates );
+ tls->client.key = privkey_get ( key ? key : &private_key );
+ tls->server.root = x509_root_get ( root ? root : &root_certificates );
tls->version = TLS_VERSION_MAX;
- tls_clear_cipher ( tls, &tls->tx_cipherspec );
- tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
- tls_clear_cipher ( tls, &tls->rx_cipherspec );
- tls_clear_cipher ( tls, &tls->rx_cipherspec_pending );
+ tls_clear_cipher ( tls, &tls->tx.cipherspec.active );
+ tls_clear_cipher ( tls, &tls->tx.cipherspec.pending );
+ tls_clear_cipher ( tls, &tls->rx.cipherspec.active );
+ tls_clear_cipher ( tls, &tls->rx.cipherspec.pending );
tls_clear_handshake ( tls );
- tls->client_random.gmt_unix_time = time ( NULL );
- iob_populate ( &tls->rx_header_iobuf, &tls->rx_header, 0,
- sizeof ( tls->rx_header ) );
- INIT_LIST_HEAD ( &tls->rx_data );
- if ( ( rc = tls_generate_random ( tls, &tls->client_random.random,
- ( sizeof ( tls->client_random.random ) ) ) ) != 0 ) {
+ tls->client.random.gmt_unix_time = time ( NULL );
+ iob_populate ( &tls->rx.iobuf, &tls->rx.header, 0,
+ sizeof ( tls->rx.header ) );
+ INIT_LIST_HEAD ( &tls->rx.data );
+ if ( ( rc = tls_generate_random ( tls, &tls->client.random.random,
+ ( sizeof ( tls->client.random.random ) ) ) ) != 0 ) {
goto err_random;
}
if ( ( rc = tls_session ( tls, name ) ) != 0 )
diff --git a/src/net/udp.c b/src/net/udp.c
index 2c0b343dc..41aba2fca 100644
--- a/src/net/udp.c
+++ b/src/net/udp.c
@@ -18,6 +18,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* A UDP connection
diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c
index 8e2e97f10..59b7c663d 100644
--- a/src/net/udp/dhcp.c
+++ b/src/net/udp/dhcp.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <stdlib.h>
@@ -94,7 +95,7 @@ static uint8_t dhcp_request_options_data[] = {
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,
+ DHCP_DOMAIN_SEARCH, DHCP_STATIC_ROUTES,
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/dhcpv6.c b/src/net/udp/dhcpv6.c
index 9e27dec6f..43a569d6e 100644
--- a/src/net/udp/dhcpv6.c
+++ b/src/net/udp/dhcpv6.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <stdio.h>
@@ -268,6 +269,8 @@ struct dhcpv6_settings {
struct settings settings;
/** Leased address */
struct in6_addr lease;
+ /** Router address */
+ struct in6_addr router;
/** Option list */
struct dhcpv6_option_list options;
};
@@ -283,25 +286,21 @@ static int dhcpv6_applies ( struct settings *settings __unused,
const struct setting *setting ) {
return ( ( setting->scope == &dhcpv6_scope ) ||
- ( setting_cmp ( setting, &ip6_setting ) == 0 ) );
+ ( setting->scope == &ipv6_settings_scope ) );
}
/**
* Fetch value of DHCPv6 leased address
*
- * @v dhcpset DHCPv6 settings
+ * @v dhcpv6set DHCPv6 settings
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
-static int dhcpv6_fetch_lease ( struct dhcpv6_settings *dhcpv6set,
- void *data, size_t len ) {
+static int dhcpv6_fetch_ip6 ( struct dhcpv6_settings *dhcpv6set,
+ void *data, size_t len ) {
struct in6_addr *lease = &dhcpv6set->lease;
- /* Do nothing unless a leased address exists */
- if ( IN6_IS_ADDR_UNSPECIFIED ( lease ) )
- return -ENOENT;
-
/* Copy leased address */
if ( len > sizeof ( *lease ) )
len = sizeof ( *lease );
@@ -311,6 +310,72 @@ static int dhcpv6_fetch_lease ( struct dhcpv6_settings *dhcpv6set,
}
/**
+ * Fetch value of DHCPv6 implicit address prefix length
+ *
+ * @v dhcpv6set DHCPv6 settings
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+static int dhcpv6_fetch_len6 ( struct dhcpv6_settings *dhcpv6set __unused,
+ void *data, size_t len ) {
+ uint8_t *len6 = data;
+
+ /* Default to assuming this is the only address on the link.
+ * If the address falls within a known prefix, then the IPv6
+ * routing table construction logic will match it against that
+ * prefix.
+ */
+ if ( len )
+ *len6 = IPV6_MAX_PREFIX_LEN;
+
+ return sizeof ( *len6 );
+}
+
+/**
+ * Fetch value of DHCPv6 router address
+ *
+ * @v dhcpv6set DHCPv6 settings
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+static int dhcpv6_fetch_gateway6 ( struct dhcpv6_settings *dhcpv6set,
+ void *data, size_t len ) {
+ struct in6_addr *router = &dhcpv6set->router;
+
+ /* Copy router address */
+ if ( len > sizeof ( *router ) )
+ len = sizeof ( *router );
+ memcpy ( data, router, len );
+
+ return sizeof ( *router );
+}
+
+/** A DHCPv6 address setting operation */
+struct dhcpv6_address_operation {
+ /** Generic setting */
+ const struct setting *setting;
+ /**
+ * Fetch value of setting
+ *
+ * @v dhcpv6set DHCPv6 settings
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+ int ( * fetch ) ( struct dhcpv6_settings *dhcpv6set,
+ void *data, size_t len );
+};
+
+/** DHCPv6 address settings operations */
+static struct dhcpv6_address_operation dhcpv6_address_operations[] = {
+ { &ip6_setting, dhcpv6_fetch_ip6 },
+ { &len6_setting, dhcpv6_fetch_len6 },
+ { &gateway6_setting, dhcpv6_fetch_gateway6 },
+};
+
+/**
* Fetch value of DHCPv6 setting
*
* @v settings Settings block
@@ -325,11 +390,20 @@ static int dhcpv6_fetch ( struct settings *settings,
struct dhcpv6_settings *dhcpv6set =
container_of ( settings, struct dhcpv6_settings, settings );
const union dhcpv6_any_option *option;
+ struct dhcpv6_address_operation *op;
size_t option_len;
-
- /* Handle leased address */
- if ( setting_cmp ( setting, &ip6_setting ) == 0 )
- return dhcpv6_fetch_lease ( dhcpv6set, data, len );
+ unsigned int i;
+
+ /* Handle address settings */
+ for ( i = 0 ; i < ( sizeof ( dhcpv6_address_operations ) /
+ sizeof ( dhcpv6_address_operations[0] ) ) ; i++ ) {
+ op = &dhcpv6_address_operations[i];
+ if ( setting_cmp ( setting, op->setting ) != 0 )
+ continue;
+ if ( IN6_IS_ADDR_UNSPECIFIED ( &dhcpv6set->lease ) )
+ return -ENOENT;
+ return op->fetch ( dhcpv6set, data, len );
+ }
/* Find option */
option = dhcpv6_option ( &dhcpv6set->options, setting->tag );
@@ -354,11 +428,12 @@ static struct settings_operations dhcpv6_settings_operations = {
* Register DHCPv6 options as network device settings
*
* @v lease DHCPv6 leased address
+ * @v router DHCPv6 router address
* @v options DHCPv6 option list
* @v parent Parent settings block
* @ret rc Return status code
*/
-static int dhcpv6_register ( struct in6_addr *lease,
+static int dhcpv6_register ( struct in6_addr *lease, struct in6_addr *router,
struct dhcpv6_option_list *options,
struct settings *parent ) {
struct dhcpv6_settings *dhcpv6set;
@@ -382,6 +457,7 @@ static int dhcpv6_register ( struct in6_addr *lease,
dhcpv6set->options.data = data;
dhcpv6set->options.len = len;
memcpy ( &dhcpv6set->lease, lease, sizeof ( dhcpv6set->lease ) );
+ memcpy ( &dhcpv6set->router, router, sizeof ( dhcpv6set->router ) );
/* Register settings */
if ( ( rc = register_settings ( &dhcpv6set->settings, parent,
@@ -501,6 +577,8 @@ struct dhcpv6_session {
/** Network device being configured */
struct net_device *netdev;
+ /** Router address */
+ struct in6_addr router;
/** Transaction ID */
uint8_t xid[3];
/** Identity association ID */
@@ -876,8 +954,8 @@ static int dhcpv6_rx ( struct dhcpv6_session *dhcpv6,
}
/* Register settings */
- if ( ( rc = dhcpv6_register ( &dhcpv6->lease, &options,
- parent ) ) != 0 ) {
+ if ( ( rc = dhcpv6_register ( &dhcpv6->lease, &dhcpv6->router,
+ &options, parent ) ) != 0 ) {
DBGC ( dhcpv6, "DHCPv6 %s could not register settings: %s\n",
dhcpv6->netdev->name, strerror ( rc ) );
goto done;
@@ -915,11 +993,12 @@ static struct interface_descriptor dhcpv6_xfer_desc =
*
* @v job Job control interface
* @v netdev Network device
+ * @v router Router address
* @v stateful Perform stateful address autoconfiguration
* @ret rc Return status code
*/
int start_dhcpv6 ( struct interface *job, struct net_device *netdev,
- int stateful ) {
+ struct in6_addr *router, int stateful ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
struct dhcpv6_session *dhcpv6;
struct {
@@ -944,6 +1023,7 @@ int start_dhcpv6 ( struct interface *job, struct net_device *netdev,
intf_init ( &dhcpv6->job, &dhcpv6_job_desc, &dhcpv6->refcnt );
intf_init ( &dhcpv6->xfer, &dhcpv6_xfer_desc, &dhcpv6->refcnt );
dhcpv6->netdev = netdev_get ( netdev );
+ memcpy ( &dhcpv6->router, router, sizeof ( dhcpv6->router ) );
xid = random();
memcpy ( dhcpv6->xid, &xid, sizeof ( dhcpv6->xid ) );
dhcpv6->start = currticks();
diff --git a/src/net/udp/dns.c b/src/net/udp/dns.c
index f46eeb5c8..3f534b99f 100644
--- a/src/net/udp/dns.c
+++ b/src/net/udp/dns.c
@@ -25,6 +25,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/net/udp/ntp.c b/src/net/udp/ntp.c
index 559233575..b3056184d 100644
--- a/src/net/udp/ntp.c
+++ b/src/net/udp/ntp.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <string.h>
diff --git a/src/net/udp/syslog.c b/src/net/udp/syslog.c
index a45fc459d..07ab3ed0c 100644
--- a/src/net/udp/syslog.c
+++ b/src/net/udp/syslog.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
@@ -31,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <ctype.h>
#include <byteswap.h>
#include <ipxe/xfer.h>
diff --git a/src/net/udp/tftp.c b/src/net/udp/tftp.c
index 2ee01862a..760af10e9 100644
--- a/src/net/udp/tftp.c
+++ b/src/net/udp/tftp.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/net/validator.c b/src/net/validator.c
index 69c0df333..c1f353b2a 100644
--- a/src/net/validator.c
+++ b/src/net/validator.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <stdio.h>
@@ -135,9 +136,11 @@ struct validator {
* @ret name Validator name
*/
static const char * validator_name ( struct validator *validator ) {
+ struct x509_certificate *cert;
- /* Use name of first certificate in chain */
- return x509_name ( x509_first ( validator->chain ) );
+ /* Use name of first certificate in chain, if present */
+ cert = x509_first ( validator->chain );
+ return ( cert ? x509_name ( cert ) : "<empty>" );
}
/**
diff --git a/src/net/vlan.c b/src/net/vlan.c
index c61bb850e..f7697a9be 100644
--- a/src/net/vlan.c
+++ b/src/net/vlan.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <string.h>