diff options
| author | Simon Rettberg | 2026-01-28 12:53:53 +0100 |
|---|---|---|
| committer | Simon Rettberg | 2026-01-28 12:53:53 +0100 |
| commit | 8e82785c584dc13e20f9229decb95bd17bbe9cd1 (patch) | |
| tree | a8b359e59196be5b2e3862bed189107f4bc9975f /src/net | |
| parent | Merge branch 'master' into openslx (diff) | |
| parent | [prefix] Make unlzma.S compatible with 386 class CPUs (diff) | |
| download | ipxe-openslx.tar.gz ipxe-openslx.tar.xz ipxe-openslx.zip | |
Merge branch 'master' into openslxopenslx
Diffstat (limited to 'src/net')
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> |
