summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile1
-rw-r--r--src/arch/i386/Config12
-rw-r--r--src/arch/i386/core/relocate.c2
-rw-r--r--src/arch/i386/core/udivmod64.c336
-rw-r--r--src/arch/i386/drivers/net/undinet.c8
-rw-r--r--src/arch/i386/image/bzimage.c112
-rw-r--r--src/arch/i386/image/eltorito.c2
-rw-r--r--src/arch/i386/image/multiboot.c2
-rw-r--r--src/arch/i386/image/nbi.c13
-rw-r--r--src/arch/i386/interface/pcbios/int13.c2
-rw-r--r--src/arch/i386/interface/pxe/pxe_call.c4
-rw-r--r--src/config.h1
-rw-r--r--src/core/abft.c60
-rw-r--r--src/core/config.c3
-rw-r--r--src/core/filter.c78
-rw-r--r--src/core/main.c2
-rw-r--r--src/crypto/axtls/axtls_asn1.c867
-rw-r--r--src/crypto/axtls/bigint.c37
-rw-r--r--src/crypto/axtls/bigint.h10
-rw-r--r--src/crypto/axtls/crypto.h15
-rw-r--r--src/crypto/axtls/os_port.h3
-rw-r--r--src/crypto/axtls/rsa.c30
-rw-r--r--src/crypto/hmac.c120
-rw-r--r--src/crypto/md5.c2
-rw-r--r--src/drivers/bitbash/spi_bit.c4
-rw-r--r--src/drivers/net/bnx2.c2
-rwxr-xr-xsrc/drivers/net/ns83820.c2
-rw-r--r--src/drivers/net/pcnet32.c2
-rw-r--r--src/drivers/net/r8169.c4
-rw-r--r--src/drivers/net/via-velocity.h4
-rw-r--r--src/hci/commands/image_cmd.c3
-rw-r--r--src/hci/mucurses/kb.c13
-rw-r--r--src/hci/mucurses/slk.c2
-rw-r--r--src/include/compiler.h3
-rw-r--r--src/include/curses.h4
-rw-r--r--src/include/gpxe/abft.h35
-rw-r--r--src/include/gpxe/aoe.h25
-rw-r--r--src/include/gpxe/ata.h3
-rw-r--r--src/include/gpxe/dhcp.h29
-rw-r--r--src/include/gpxe/errfile.h2
-rw-r--r--src/include/gpxe/filter.h75
-rw-r--r--src/include/gpxe/hmac.h30
-rw-r--r--src/include/gpxe/http.h5
-rw-r--r--src/include/gpxe/iscsi.h9
-rw-r--r--src/include/gpxe/open.h1
-rw-r--r--src/include/gpxe/tls.h169
-rw-r--r--src/include/usr/aoeboot.h6
-rw-r--r--src/interface/pxe/pxe_preboot.c18
-rw-r--r--src/libgcc/__divdi3.c26
-rw-r--r--src/libgcc/__moddi3.c26
-rw-r--r--src/libgcc/__udivdi3.c10
-rw-r--r--src/libgcc/__udivmoddi4.c32
-rw-r--r--src/libgcc/__umoddi3.c13
-rw-r--r--src/libgcc/libgcc.h26
-rw-r--r--src/libgcc/memcpy.c (renamed from src/core/gcc_implicit.c)2
-rw-r--r--src/net/aoe.c130
-rw-r--r--src/net/ndp.c5
-rw-r--r--src/net/tcp/http.c45
-rw-r--r--src/net/tcp/https.c (renamed from src/drivers/ata/aoedev.c)47
-rw-r--r--src/net/tcp/iscsi.c116
-rw-r--r--src/net/tls.c1733
-rw-r--r--src/net/udp/dhcp.c166
-rw-r--r--src/tests/aoeboot.c71
-rw-r--r--src/usr/aoeboot.c104
-rw-r--r--src/usr/autoboot.c13
-rw-r--r--src/usr/iscsiboot.c24
66 files changed, 4027 insertions, 734 deletions
diff --git a/src/Makefile b/src/Makefile
index a567b922a..8e0c8bea7 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -145,6 +145,7 @@ DEBUG_TARGETS += dbg%.o c s
# SRCDIRS lists all directories containing source files.
#
+SRCDIRS += libgcc
SRCDIRS += core
SRCDIRS += proto
SRCDIRS += net net/tcp net/udp
diff --git a/src/arch/i386/Config b/src/arch/i386/Config
index 877913210..16de411b1 100644
--- a/src/arch/i386/Config
+++ b/src/arch/i386/Config
@@ -130,6 +130,18 @@ endif
# this is almost always a win. the kernel uses it, too.
CFLAGS+= -mpreferred-stack-boundary=2
+# use regparm for all functions - C functions called from assembly (or
+# vice versa) need __cdecl now
+CFLAGS+= -mregparm=3
+
+# use -mrtd (same __cdecl requirements as above)
+CFLAGS+= -mrtd
+
+# this is the logical complement to -mregparm=3.
+# it doesn't currently buy us anything, but if anything ever tries
+# to return small structures, let's be prepared
+CFLAGS+= -freg-struct-return
+
LDFLAGS+= -N --no-check-sections
ifeq "$(shell uname -s)" "FreeBSD"
diff --git a/src/arch/i386/core/relocate.c b/src/arch/i386/core/relocate.c
index d3b42d0da..39d00b098 100644
--- a/src/arch/i386/core/relocate.c
+++ b/src/arch/i386/core/relocate.c
@@ -39,7 +39,7 @@ extern char _end[];
* address space, and returns the physical address of the new location
* to the prefix in %edi.
*/
-void relocate ( struct i386_all_regs *ix86 ) {
+__cdecl void relocate ( struct i386_all_regs *ix86 ) {
struct memory_map memmap;
unsigned long start, end, size, padded_size;
unsigned long new_start, new_end;
diff --git a/src/arch/i386/core/udivmod64.c b/src/arch/i386/core/udivmod64.c
deleted file mode 100644
index faf6fd8c8..000000000
--- a/src/arch/i386/core/udivmod64.c
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/** @file
- *
- * 64-bit division
- *
- * The x86 CPU (386 upwards) has a divl instruction which will perform
- * unsigned division of a 64-bit dividend by a 32-bit divisor. If the
- * resulting quotient does not fit in 32 bits, then a CPU exception
- * will occur.
- *
- * Unsigned integer division is expressed as solving
- *
- * x = d.q + r 0 <= q, 0 <= r < d
- *
- * given the dividend (x) and divisor (d), to find the quotient (q)
- * and remainder (r).
- *
- * The x86 divl instruction will solve
- *
- * x = d.q + r 0 <= q, 0 <= r < d
- *
- * given x in the range 0 <= x < 2^64 and 1 <= d < 2^32, and causing a
- * hardware exception if the resulting q >= 2^32.
- *
- * We can therefore use divl only if we can prove that the conditions
- *
- * 0 <= x < 2^64
- * 1 <= d < 2^32
- * q < 2^32
- *
- * are satisfied.
- *
- *
- * Case 1 : 1 <= d < 2^32
- * ======================
- *
- * We express x as
- *
- * x = xh.2^32 + xl 0 <= xh < 2^32, 0 <= xl < 2^32 (1)
- *
- * i.e. split x into low and high dwords. We then solve
- *
- * xh = d.qh + r' 0 <= qh, 0 <= r' < d (2)
- *
- * which we can do using a divl instruction since
- *
- * 0 <= xh < 2^64 since 0 <= xh < 2^32 from (1) (3)
- *
- * and
- *
- * 1 <= d < 2^32 by definition of this Case (4)
- *
- * and
- *
- * d.qh = xh - r' from (2)
- * d.qh <= xh since r' >= 0 from (2)
- * qh <= xh since d >= 1 from (2)
- * qh < 2^32 since xh < 2^32 from (1) (5)
- *
- * Having obtained qh and r', we then solve
- *
- * ( r'.2^32 + xl ) = d.ql + r 0 <= ql, 0 <= r < d (6)
- *
- * which we can do using another divl instruction since
- *
- * xl <= 2^32 - 1 from (1), so
- * r'.2^32 + xl <= ( r' + 1 ).2^32 - 1
- * r'.2^32 + xl <= d.2^32 - 1 since r' < d from (2)
- * r'.2^32 + xl < d.2^32 (7)
- * r'.2^32 + xl < 2^64 since d < 2^32 from (4) (8)
- *
- * and
- *
- * 1 <= d < 2^32 by definition of this Case (9)
- *
- * and
- *
- * d.ql = ( r'.2^32 + xl ) - r from (6)
- * d.ql <= r'.2^32 + xl since r >= 0 from (6)
- * d.ql < d.2^32 from (7)
- * ql < 2^32 since d >= 1 from (2) (10)
- *
- * This then gives us
- *
- * x = xh.2^32 + xl from (1)
- * x = ( d.qh + r' ).2^32 + xl from (2)
- * x = d.qh.2^32 + ( r'.2^32 + xl )
- * x = d.qh.2^32 + d.ql + r from (3)
- * x = d.( qh.2^32 + ql ) + r (11)
- *
- * Letting
- *
- * q = qh.2^32 + ql (12)
- *
- * gives
- *
- * x = d.q + r from (11) and (12)
- *
- * which is the solution.
- *
- *
- * This therefore gives us a two-step algorithm:
- *
- * xh = d.qh + r' 0 <= qh, 0 <= r' < d (2)
- * ( r'.2^32 + xl ) = d.ql + r 0 <= ql, 0 <= r < d (6)
- *
- * which translates to
- *
- * %edx:%eax = 0:xh
- * divl d
- * qh = %eax
- * r' = %edx
- *
- * %edx:%eax = r':xl
- * divl d
- * ql = %eax
- * r = %edx
- *
- * Note that if
- *
- * xh < d
- *
- * (which is a fast dword comparison) then the first divl instruction
- * can be omitted, since the answer will be
- *
- * qh = 0
- * r = xh
- *
- *
- * Case 2 : 2^32 <= d < 2^64
- * =========================
- *
- * We first express d as
- *
- * d = dh.2^k + dl 2^31 <= dh < 2^32,
- * 0 <= dl < 2^k, 1 <= k <= 32 (1)
- *
- * i.e. find the highest bit set in d, subtract 32, and split d into
- * dh and dl at that point.
- *
- * We then express x as
- *
- * x = xh.2^k + xl 0 <= xl < 2^k (2)
- *
- * giving
- *
- * xh.2^k = x - xl from (2)
- * xh.2^k <= x since xl >= 0 from (1)
- * xh.2^k < 2^64 since xh < 2^64 from (1)
- * xh < 2^(64-k) (3)
- *
- * We then solve the division
- *
- * xh = dh.q' + r' 0 <= r' < dh (4)
- *
- * which we can do using a divl instruction since
- *
- * 0 <= xh < 2^64 since x < 2^64 and xh < x
- *
- * and
- *
- * 1 <= dh < 2^32 from (1)
- *
- * and
- *
- * dh.q' = xh - r' from (4)
- * dh.q' <= xh since r' >= 0 from (4)
- * dh.q' < 2^(64-k) from (3) (5)
- * q'.2^31 <= dh.q' since dh >= 2^31 from (1) (6)
- * q'.2^31 < 2^(64-k) from (5) and (6)
- * q' < 2^(33-k)
- * q' < 2^32 since k >= 1 from (1) (7)
- *
- * This gives us
- *
- * xh.2^k = dh.q'.2^k + r'.2^k from (4)
- * x - xl = ( d - dl ).q' + r'.2^k from (1) and (2)
- * x = d.q' + ( r'.2^k + xl ) - dl.q' (8)
- *
- * Now
- *
- * r'.2^k + xl < r'.2^k + 2^k since xl < 2^k from (2)
- * r'.2^k + xl < ( r' + 1 ).2^k
- * r'.2^k + xl < dh.2^k since r' < dh from (4)
- * r'.2^k + xl < ( d - dl ) from (1) (9)
- *
- *
- * (missing)
- *
- *
- * This gives us two cases to consider:
- *
- * case (a):
- *
- * dl.q' <= ( r'.2^k + xl ) (15a)
- *
- * in which case
- *
- * x = d.q' + ( r'.2^k + xl - dl.q' )
- *
- * is a direct solution to the division, since
- *
- * r'.2^k + xl < d from (9)
- * ( r'.2^k + xl - dl.q' ) < d since dl >= 0 and q' >= 0
- *
- * and
- *
- * 0 <= ( r'.2^k + xl - dl.q' ) from (15a)
- *
- * case (b):
- *
- * dl.q' > ( r'.2^k + xl ) (15b)
- *
- * Express
- *
- * x = d.(q'-1) + ( r'.2^k + xl ) + ( d - dl.q' )
- *
- *
- * (missing)
- *
- *
- * special case: k = 32 cannot be handled with shifts
- *
- * (missing)
- *
- */
-
-#include <stdint.h>
-#include <assert.h>
-
-typedef uint64_t UDItype;
-
-struct uint64 {
- uint32_t l;
- uint32_t h;
-};
-
-static inline void udivmod64_lo ( const struct uint64 *x,
- const struct uint64 *d,
- struct uint64 *q,
- struct uint64 *r ) {
- uint32_t r_dash;
-
- q->h = 0;
- r->h = 0;
- r_dash = x->h;
-
- if ( x->h >= d->l ) {
- __asm__ ( "divl %2"
- : "=&a" ( q->h ), "=&d" ( r_dash )
- : "g" ( d->l ), "0" ( x->h ), "1" ( 0 ) );
- }
-
- __asm__ ( "divl %2"
- : "=&a" ( q->l ), "=&d" ( r->l )
- : "g" ( d->l ), "0" ( x->l ), "1" ( r_dash ) );
-}
-
-static void udivmod64 ( const struct uint64 *x,
- const struct uint64 *d,
- struct uint64 *q,
- struct uint64 *r ) {
-
- if ( d->h == 0 ) {
- udivmod64_lo ( x, d, q, r );
- } else {
- assert ( 0 );
- while ( 1 ) {};
- }
-}
-
-/**
- * 64-bit division with remainder
- *
- * @v x Dividend
- * @v d Divisor
- * @ret r Remainder
- * @ret q Quotient
- */
-UDItype __udivmoddi4 ( UDItype x, UDItype d, UDItype *r ) {
- UDItype q;
- UDItype *_x = &x;
- UDItype *_d = &d;
- UDItype *_q = &q;
- UDItype *_r = r;
-
- udivmod64 ( ( struct uint64 * ) _x, ( struct uint64 * ) _d,
- ( struct uint64 * ) _q, ( struct uint64 * ) _r );
-
- assert ( ( x == ( ( d * q ) + (*r) ) ) );
- assert ( (*r) < d );
-
- return q;
-}
-
-/**
- * 64-bit division
- *
- * @v x Dividend
- * @v d Divisor
- * @ret q Quotient
- */
-UDItype __udivdi3 ( UDItype x, UDItype d ) {
- UDItype r;
- return __udivmoddi4 ( x, d, &r );
-}
-
-/**
- * 64-bit modulus
- *
- * @v x Dividend
- * @v d Divisor
- * @ret q Quotient
- */
-UDItype __umoddi3 ( UDItype x, UDItype d ) {
- UDItype r;
- __udivmoddi4 ( x, d, &r );
- return r;
-}
diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/i386/drivers/net/undinet.c
index 38032ab10..a5746ce8d 100644
--- a/src/arch/i386/drivers/net/undinet.c
+++ b/src/arch/i386/drivers/net/undinet.c
@@ -423,8 +423,14 @@ static void undinet_poll ( struct net_device *netdev ) {
if ( ! undinic->isr_processing ) {
/* Do nothing unless ISR has been triggered */
- if ( ! undinet_isr_triggered() )
+ if ( ! undinet_isr_triggered() ) {
+ /* Allow interrupt to occur */
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "cli\n\t" ) : : );
return;
+ }
/* Start ISR processing */
undinic->isr_processing = 1;
diff --git a/src/arch/i386/image/bzimage.c b/src/arch/i386/image/bzimage.c
index 3a3e82efc..0d01f6a09 100644
--- a/src/arch/i386/image/bzimage.c
+++ b/src/arch/i386/image/bzimage.c
@@ -33,7 +33,6 @@
#include <gpxe/uaccess.h>
#include <gpxe/image.h>
#include <gpxe/segment.h>
-#include <gpxe/memmap.h>
#include <gpxe/init.h>
#include <gpxe/initrd.h>
@@ -168,58 +167,78 @@ static int bzimage_set_cmdline ( struct image *image,
}
/**
- * Load initrd, if any
+ * Load initrds, if any
*
* @v image bzImage image
* @v exec_ctx Execution context
* @ret rc Return status code
*/
static int bzimage_load_initrd ( struct image *image,
- struct bzimage_exec_context *exec_ctx,
- struct image *initrd ) {
- physaddr_t start = user_to_phys ( initrd->data, 0 );
+ struct bzimage_exec_context *exec_ctx ) {
+ struct image *initrd;
+ size_t initrd_len;
+ size_t total_len = 0;
+ size_t offset = 0;
+ physaddr_t start;
int rc;
- DBGC ( image, "bzImage %p loading initrd %p (%s)\n",
- image, initrd, initrd->name );
-
- /* Find a suitable start address */
- if ( ( start + initrd->len ) <= exec_ctx->mem_limit ) {
- /* Just use initrd in situ */
- DBGC ( image, "bzImage %p using initrd as [%lx,%lx)\n",
- image, start, ( start + initrd->len ) );
- } else {
- for ( ; ; start -= 0x100000 ) {
- /* Check that we're not going to overwrite the
- * kernel itself. This check isn't totally
- * accurate, but errs on the side of caution.
- */
- if ( start <= ( BZI_LOAD_HIGH_ADDR + image->len ) ) {
- DBGC ( image, "bzImage %p could not find a "
- "location for initrd\n", image );
- return -ENOBUFS;
- }
- /* Check that we are within the kernel's range */
- if ( ( start + initrd->len ) > exec_ctx->mem_limit )
- continue;
- /* Prepare and verify segment */
- if ( ( rc = prep_segment ( phys_to_user ( start ),
- initrd->len,
- initrd->len ) ) != 0 )
- continue;
- /* Copy to segment */
- DBGC ( image, "bzImage %p relocating initrd to "
- "[%lx,%lx)\n", image, start,
- ( start + initrd->len ) );
- memcpy_user ( phys_to_user ( start ), 0,
- initrd->data, 0, initrd->len );
- break;
+ /* Add up length of all initrd images */
+ for_each_image ( initrd ) {
+ if ( initrd->type != &initrd_image_type )
+ continue;
+ initrd_len = ( ( initrd->len + 0x0f ) & ~0x0f );
+ total_len += initrd_len;
+ }
+
+ /* Give up if no initrd images found */
+ if ( ! total_len )
+ return 0;
+
+ /* Find a suitable start address. Try 1MB boundaries,
+ * starting from the downloaded kernel image itself and
+ * working downwards until we hit an available region.
+ */
+ for ( start = ( user_to_phys ( image->data, 0 ) & ~0xfffff ) ; ;
+ start -= 0x100000 ) {
+ /* Check that we're not going to overwrite the
+ * kernel itself. This check isn't totally
+ * accurate, but errs on the side of caution.
+ */
+ if ( start <= ( BZI_LOAD_HIGH_ADDR + image->len ) ) {
+ DBGC ( image, "bzImage %p could not find a location "
+ "for initrd\n", image );
+ return -ENOBUFS;
}
+ /* Check that we are within the kernel's range */
+ if ( ( start + total_len ) > exec_ctx->mem_limit )
+ continue;
+ /* Prepare and verify segment */
+ if ( ( rc = prep_segment ( phys_to_user ( start ), 0,
+ total_len ) ) != 0 )
+ continue;
+ /* Use this address */
+ break;
}
+ /* Construct initrd */
+ DBGC ( image, "bzImage %p constructing initrd at [%lx,%lx)\n",
+ image, start, ( start + total_len ) );
+ for_each_image ( initrd ) {
+ if ( initrd->type != &initrd_image_type )
+ continue;
+ initrd_len = ( ( initrd->len + 0x0f ) & ~0x0f );
+ DBGC ( image, "bzImage %p has initrd %p at [%lx,%lx)\n",
+ image, initrd, ( start + offset ),
+ ( start + offset + initrd->len ) );
+ memcpy_user ( phys_to_user ( start ), offset,
+ initrd->data, 0, initrd->len );
+ offset += initrd_len;
+ }
+ assert ( offset == total_len );
+
/* Record initrd location */
exec_ctx->ramdisk_image = start;
- exec_ctx->ramdisk_size = initrd->len;
+ exec_ctx->ramdisk_size = total_len;
return 0;
}
@@ -234,7 +253,6 @@ static int bzimage_exec ( struct image *image ) {
struct bzimage_exec_context exec_ctx;
struct bzimage_header bzhdr;
const char *cmdline = ( image->cmdline ? image->cmdline : "" );
- struct image *initrd;
int rc;
/* Initialise context */
@@ -262,15 +280,9 @@ static int bzimage_exec ( struct image *image ) {
if ( ( rc = bzimage_set_cmdline ( image, &exec_ctx, cmdline ) ) != 0 )
return rc;
- /* Load an initrd, if one exists */
- for_each_image ( initrd ) {
- if ( initrd->type == &initrd_image_type ) {
- if ( ( rc = bzimage_load_initrd ( image, &exec_ctx,
- initrd ) ) != 0 )
- return rc;
- break;
- }
- }
+ /* Load any initrds */
+ if ( ( rc = bzimage_load_initrd ( image, &exec_ctx ) ) != 0 )
+ return rc;
/* Update and store kernel header */
bzhdr.vid_mode = exec_ctx.vid_mode;
diff --git a/src/arch/i386/image/eltorito.c b/src/arch/i386/image/eltorito.c
index 53e25ca56..9d573106d 100644
--- a/src/arch/i386/image/eltorito.c
+++ b/src/arch/i386/image/eltorito.c
@@ -298,7 +298,7 @@ static int eltorito_load_disk ( struct image *image,
* @v image El Torito file
* @ret rc Return status code
*/
-int eltorito_load ( struct image *image ) {
+static int eltorito_load ( struct image *image ) {
struct eltorito_boot_entry boot_entry;
unsigned long bootcat_offset;
int rc;
diff --git a/src/arch/i386/image/multiboot.c b/src/arch/i386/image/multiboot.c
index ab0b55ace..546de365d 100644
--- a/src/arch/i386/image/multiboot.c
+++ b/src/arch/i386/image/multiboot.c
@@ -360,7 +360,7 @@ static int multiboot_load_elf ( struct image *image ) {
* @v image Multiboot file
* @ret rc Return status code
*/
-int multiboot_load ( struct image *image ) {
+static int multiboot_load ( struct image *image ) {
struct multiboot_header_info hdr;
int rc;
diff --git a/src/arch/i386/image/nbi.c b/src/arch/i386/image/nbi.c
index 3a66e9cb0..2de381d07 100644
--- a/src/arch/i386/image/nbi.c
+++ b/src/arch/i386/image/nbi.c
@@ -244,7 +244,7 @@ static int nbi_process_segments ( struct image *image,
* @v image NBI image
* @ret rc Return status code
*/
-int nbi_load ( struct image *image ) {
+static int nbi_load ( struct image *image ) {
struct imgheader imgheader;
int rc;
@@ -397,16 +397,13 @@ static int nbi_prepare_dhcp ( struct image *image ) {
return -ENODEV;
}
- if ( ( rc = create_dhcp_packet ( boot_netdev, DHCPACK, basemem_packet,
- sizeof ( basemem_packet ),
- &dhcppkt ) ) != 0 ) {
+ if ( ( rc = create_dhcp_response ( boot_netdev, DHCPACK, NULL,
+ basemem_packet,
+ sizeof ( basemem_packet ),
+ &dhcppkt ) ) != 0 ) {
DBGC ( image, "NBI %p failed to build DHCP packet\n", image );
return rc;
}
- if ( ( rc = copy_dhcp_packet_options ( &dhcppkt, NULL ) ) != 0 ) {
- DBGC ( image, "NBI %p failed to copy DHCP options\n", image );
- return rc;
- }
return 0;
}
diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c
index f7cb67958..53817c7a8 100644
--- a/src/arch/i386/interface/pcbios/int13.c
+++ b/src/arch/i386/interface/pcbios/int13.c
@@ -321,7 +321,7 @@ static int int13_get_extended_parameters ( struct int13_drive *drive,
* INT 13 handler
*
*/
-static void int13 ( struct i386_all_regs *ix86 ) {
+static __cdecl void int13 ( struct i386_all_regs *ix86 ) {
int command = ix86->regs.ah;
unsigned int bios_drive = ix86->regs.dl;
unsigned int original_bios_drive = bios_drive;
diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/i386/interface/pxe/pxe_call.c
index 834ca7380..1c1b5066c 100644
--- a/src/arch/i386/interface/pxe/pxe_call.c
+++ b/src/arch/i386/interface/pxe/pxe_call.c
@@ -112,7 +112,7 @@ static PXENV_EXIT_t pxenv_unknown ( struct s_PXENV_UNKNOWN *pxenv_unknown ) {
* @v es:di Address of PXE parameter block
* @ret ax PXE exit code
*/
-void pxe_api_call ( struct i386_all_regs *ix86 ) {
+__cdecl void pxe_api_call ( struct i386_all_regs *ix86 ) {
int opcode = ix86->regs.bx;
userptr_t parameters = real_to_user ( ix86->segs.es, ix86->regs.di );
size_t param_len;
@@ -304,7 +304,7 @@ void pxe_api_call ( struct i386_all_regs *ix86 ) {
* @v es:di Address of PXE parameter block
* @ret ax PXE exit code
*/
-void pxe_loader_call ( struct i386_all_regs *ix86 ) {
+__cdecl void pxe_loader_call ( struct i386_all_regs *ix86 ) {
userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di );
struct s_UNDI_LOADER params;
PXENV_EXIT_t ret;
diff --git a/src/config.h b/src/config.h
index 55409b0eb..c436d100c 100644
--- a/src/config.h
+++ b/src/config.h
@@ -73,6 +73,7 @@
#define DOWNLOAD_PROTO_TFTP /* Trivial File Transfer Protocol */
#undef DOWNLOAD_PROTO_NFS /* Network File System */
#define DOWNLOAD_PROTO_HTTP /* Hypertext Transfer Protocol */
+#undef DOWNLOAD_PROTO_HTTPS /* Secure Hypertext Transfer Protocol */
#undef DOWNLOAD_PROTO_FTP /* File Transfer Protocol */
#undef DOWNLOAD_PROTO_TFTM /* Multicast Trivial File Transfer Protocol */
#undef DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */
diff --git a/src/core/abft.c b/src/core/abft.c
new file mode 100644
index 000000000..af28bbcfb
--- /dev/null
+++ b/src/core/abft.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <realmode.h>
+#include <gpxe/aoe.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/abft.h>
+
+/** @file
+ *
+ * AoE Boot Firmware Table
+ *
+ */
+
+#define abftab __use_data16 ( abftab )
+/** The aBFT used by gPXE */
+struct abft_table __data16 ( abftab ) __attribute__ (( aligned ( 16 ) )) = {
+ /* ACPI header */
+ .acpi = {
+ .signature = ABFT_SIG,
+ .length = sizeof ( abftab ),
+ .revision = 1,
+ .oem_id = "FENSYS",
+ .oem_table_id = "gPXE",
+ },
+};
+
+/**
+ * Fill in all variable portions of aBFT
+ *
+ * @v aoe AoE session
+ */
+void abft_fill_data ( struct aoe_session *aoe ) {
+
+ /* Fill in boot parameters */
+ abftab.shelf = aoe->major;
+ abftab.slot = aoe->minor;
+ memcpy ( abftab.mac, aoe->netdev->ll_addr, sizeof ( abftab.mac ) );
+
+ /* Update checksum */
+ acpi_fix_checksum ( &abftab.acpi );
+
+ DBG ( "AoE boot firmware table:\n" );
+ DBG_HD ( &abftab, sizeof ( abftab ) );
+}
diff --git a/src/core/config.c b/src/core/config.c
index a2194e8dc..7e70c12a1 100644
--- a/src/core/config.c
+++ b/src/core/config.c
@@ -87,6 +87,9 @@ REQUIRE_OBJECT ( nfs );
#ifdef DOWNLOAD_PROTO_HTTP
REQUIRE_OBJECT ( http );
#endif
+#ifdef DOWNLOAD_PROTO_HTTPS
+REQUIRE_OBJECT ( https );
+#endif
#ifdef DOWNLOAD_PROTO_FTP
REQUIRE_OBJECT ( ftp );
#endif
diff --git a/src/core/filter.c b/src/core/filter.c
new file mode 100644
index 000000000..51ec5c4bc
--- /dev/null
+++ b/src/core/filter.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/xfer.h>
+#include <gpxe/filter.h>
+
+/** @file
+ *
+ * Data transfer filters
+ *
+ */
+
+/*
+ * Pass-through methods to be used by filters which don't want to
+ * intercept all events.
+ *
+ */
+
+void filter_close ( struct xfer_interface *xfer, int rc ) {
+ struct xfer_interface *other = filter_other_half ( xfer );
+
+ xfer_close ( other, rc );
+}
+
+int filter_vredirect ( struct xfer_interface *xfer, int type,
+ va_list args ) {
+ struct xfer_interface *other = filter_other_half ( xfer );
+
+ return xfer_vredirect ( other, type, args );
+}
+
+int filter_seek ( struct xfer_interface *xfer, off_t offset, int whence ) {
+ struct xfer_interface *other = filter_other_half ( xfer );
+
+ return xfer_seek ( other, offset, whence );
+}
+
+size_t filter_window ( struct xfer_interface *xfer ) {
+ struct xfer_interface *other = filter_other_half ( xfer );
+
+ return xfer_window ( other );
+}
+
+struct io_buffer * filter_alloc_iob ( struct xfer_interface *xfer,
+ size_t len ) {
+ struct xfer_interface *other = filter_other_half ( xfer );
+
+ return xfer_alloc_iob ( other, len );
+}
+
+int filter_deliver_iob ( struct xfer_interface *xfer, struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ struct xfer_interface *other = filter_other_half ( xfer );
+
+ return xfer_deliver_iob_meta ( other, iobuf, meta );
+}
+
+int filter_deliver_raw ( struct xfer_interface *xfer, const void *data,
+ size_t len ) {
+ struct xfer_interface *other = filter_other_half ( xfer );
+
+ return xfer_deliver_raw ( other, data, len );
+}
diff --git a/src/core/main.c b/src/core/main.c
index 09dccc761..88fbb5794 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -24,7 +24,7 @@ Literature dealing with the network protocols:
*
* @ret rc Return status code
*/
-int main ( void ) {
+__cdecl int main ( void ) {
initialise();
startup();
diff --git a/src/crypto/axtls/axtls_asn1.c b/src/crypto/axtls/axtls_asn1.c
new file mode 100644
index 000000000..74411c704
--- /dev/null
+++ b/src/crypto/axtls/axtls_asn1.c
@@ -0,0 +1,867 @@
+/*
+ * Copyright(C) 2006 Cameron Rich
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * @file asn1.c
+ *
+ * Some primitive asn methods for extraction rsa modulus information. It also
+ * is used for retrieving information from X.509 certificates.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "crypto.h"
+
+#define SIG_OID_PREFIX_SIZE 8
+
+#define SIG_TYPE_MD2 0x02
+#define SIG_TYPE_MD5 0x04
+#define SIG_TYPE_SHA1 0x05
+
+/* Must be an RSA algorithm with either SHA1 or MD5 for verifying to work */
+static const uint8_t sig_oid_prefix[SIG_OID_PREFIX_SIZE] =
+{
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01
+};
+
+/* CN, O, OU */
+static const uint8_t g_dn_types[] = { 3, 10, 11 };
+
+static int get_asn1_length(const uint8_t *buf, int *offset)
+{
+ int len, i;
+
+ if (!(buf[*offset] & 0x80)) /* short form */
+ {
+ len = buf[(*offset)++];
+ }
+ else /* long form */
+ {
+ int length_bytes = buf[(*offset)++]&0x7f;
+ len = 0;
+ for (i = 0; i < length_bytes; i++)
+ {
+ len <<= 8;
+ len += buf[(*offset)++];
+ }
+ }
+
+ return len;
+}
+
+/**
+ * Skip the ASN1.1 object type and its length. Get ready to read the object's
+ * data.
+ */
+int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type)
+{
+ if (buf[*offset] != obj_type)
+ return X509_NOT_OK;
+ (*offset)++;
+ return get_asn1_length(buf, offset);
+}
+
+/**
+ * Skip over an ASN.1 object type completely. Get ready to read the next
+ * object.
+ */
+int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type)
+{
+ int len;
+
+ if (buf[*offset] != obj_type)
+ return X509_NOT_OK;
+ (*offset)++;
+ len = get_asn1_length(buf, offset);
+ *offset += len;
+ return 0;
+}
+
+/**
+ * Read an integer value for ASN.1 data
+ * Note: This function allocates memory which must be freed by the user.
+ */
+int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object)
+{
+ int len;
+
+ if ((len = asn1_next_obj(buf, offset, ASN1_INTEGER)) < 0)
+ goto end_int_array;
+
+ *object = (uint8_t *)malloc(len);
+ memcpy(*object, &buf[*offset], len);
+ *offset += len;
+
+end_int_array:
+ return len;
+}
+
+#if 0
+
+/**
+ * Get all the RSA private key specifics from an ASN.1 encoded file
+ */
+int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx)
+{
+ int offset = 7;
+ uint8_t *modulus, *priv_exp, *pub_exp;
+ int mod_len, priv_len, pub_len;
+#ifdef CONFIG_BIGINT_CRT
+ uint8_t *p, *q, *dP, *dQ, *qInv;
+ int p_len, q_len, dP_len, dQ_len, qInv_len;
+#endif
+
+ /* not in der format */
+ if (buf[0] != ASN1_SEQUENCE) /* basic sanity check */
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("Error: This is not a valid ASN.1 file\n");
+#endif
+ return X509_INVALID_PRIV_KEY;
+ }
+
+ /* initialise the RNG */
+ RNG_initialize(buf, len);
+
+ mod_len = asn1_get_int(buf, &offset, &modulus);
+ pub_len = asn1_get_int(buf, &offset, &pub_exp);
+ priv_len = asn1_get_int(buf, &offset, &priv_exp);
+
+ if (mod_len <= 0 || pub_len <= 0 || priv_len <= 0)
+ return X509_INVALID_PRIV_KEY;
+
+#ifdef CONFIG_BIGINT_CRT
+ p_len = asn1_get_int(buf, &offset, &p);
+ q_len = asn1_get_int(buf, &offset, &q);
+ dP_len = asn1_get_int(buf, &offset, &dP);
+ dQ_len = asn1_get_int(buf, &offset, &dQ);
+ qInv_len = asn1_get_int(buf, &offset, &qInv);
+
+ if (p_len <= 0 || q_len <= 0 || dP_len <= 0 || dQ_len <= 0 || qInv_len <= 0)
+ return X509_INVALID_PRIV_KEY;
+
+ RSA_priv_key_new(rsa_ctx,
+ modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len,
+ p, p_len, q, p_len, dP, dP_len, dQ, dQ_len, qInv, qInv_len);
+
+ free(p);
+ free(q);
+ free(dP);
+ free(dQ);
+ free(qInv);
+#else
+ RSA_priv_key_new(rsa_ctx,
+ modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len);
+#endif
+
+ free(modulus);
+ free(priv_exp);
+ free(pub_exp);
+ return X509_OK;
+}
+
+/**
+ * Get the time of a certificate. Ignore hours/minutes/seconds.
+ */
+static int asn1_get_utc_time(const uint8_t *buf, int *offset, time_t *t)
+{
+ int ret = X509_NOT_OK, len, t_offset;
+ struct tm tm;
+
+ if (buf[(*offset)++] != ASN1_UTC_TIME)
+ goto end_utc_time;
+ len = get_asn1_length(buf, offset);
+ t_offset = *offset;
+
+ memset(&tm, 0, sizeof(struct tm));
+ tm.tm_year = (buf[t_offset] - '0')*10 + (buf[t_offset+1] - '0');
+
+ if (tm.tm_year <= 50) /* 1951-2050 thing */
+ {
+ tm.tm_year += 100;
+ }
+
+ tm.tm_mon = (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0') - 1;
+ tm.tm_mday = (buf[t_offset+4] - '0')*10 + (buf[t_offset+5] - '0');
+ *t = mktime(&tm);
+ *offset += len;
+ ret = X509_OK;
+
+end_utc_time:
+ return ret;
+}
+
+/**
+ * Get the version type of a certificate (which we don't actually care about)
+ */
+static int asn1_version(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
+{
+ int ret = X509_NOT_OK;
+
+ (*offset) += 2; /* get past explicit tag */
+ if (asn1_skip_obj(cert, offset, ASN1_INTEGER))
+ goto end_version;
+
+ ret = X509_OK;
+end_version:
+ return ret;
+}
+
+/**
+ * Retrieve the notbefore and notafter certificate times.
+ */
+static int asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
+{
+ return (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
+ asn1_get_utc_time(cert, offset, &x509_ctx->not_before) ||
+ asn1_get_utc_time(cert, offset, &x509_ctx->not_after));
+}
+
+/**
+ * Get the components of a distinguished name
+ */
+static int asn1_get_oid_x520(const uint8_t *buf, int *offset)
+{
+ int dn_type = 0;
+ int len;
+
+ if ((len = asn1_next_obj(buf, offset, ASN1_OID)) < 0)
+ goto end_oid;
+
+ /* expect a sequence of 2.5.4.[x] where x is a one of distinguished name
+ components we are interested in. */
+ if (len == 3 && buf[(*offset)++] == 0x55 && buf[(*offset)++] == 0x04)
+ dn_type = buf[(*offset)++];
+ else
+ {
+ *offset += len; /* skip over it */
+ }
+
+end_oid:
+ return dn_type;
+}
+
+/**
+ * Obtain an ASN.1 printable string type.
+ */
+static int asn1_get_printable_str(const uint8_t *buf, int *offset, char **str)
+{
+ int len = X509_NOT_OK;
+
+ /* some certs have this awful crud in them for some reason */
+ if (buf[*offset] != ASN1_PRINTABLE_STR &&
+ buf[*offset] != ASN1_TELETEX_STR && buf[*offset] != ASN1_IA5_STR)
+ goto end_pnt_str;
+
+ (*offset)++;
+ len = get_asn1_length(buf, offset);
+ *str = (char *)malloc(len+1); /* allow for null */
+ memcpy(*str, &buf[*offset], len);
+ (*str)[len] = 0; /* null terminate */
+ *offset += len;
+end_pnt_str:
+ return len;
+}
+
+/**
+ * Get the subject name (or the issuer) of a certificate.
+ */
+static int asn1_name(const uint8_t *cert, int *offset, char *dn[])
+{
+ int ret = X509_NOT_OK;
+ int dn_type;
+ char *tmp = NULL;
+
+ if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0)
+ goto end_name;
+
+ while (asn1_next_obj(cert, offset, ASN1_SET) >= 0)
+ {
+ int i, found = 0;
+
+ if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
+ (dn_type = asn1_get_oid_x520(cert, offset)) < 0)
+ goto end_name;
+
+ if (asn1_get_printable_str(cert, offset, &tmp) < 0)
+ {
+ free(tmp);
+ goto end_name;
+ }
+
+ /* find the distinguished named type */
+ for (i = 0; i < X509_NUM_DN_TYPES; i++)
+ {
+ if (dn_type == g_dn_types[i])
+ {
+ if (dn[i] == NULL)
+ {
+ dn[i] = tmp;
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ if (found == 0) /* not found so get rid of it */
+ {
+ free(tmp);
+ }
+ }
+
+ ret = X509_OK;
+end_name:
+ return ret;
+}
+
+/**
+ * Read the modulus and public exponent of a certificate.
+ */
+static int asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
+{
+ int ret = X509_NOT_OK, mod_len, pub_len;
+ uint8_t *modulus, *pub_exp;
+
+ if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
+ asn1_skip_obj(cert, offset, ASN1_SEQUENCE) ||
+ asn1_next_obj(cert, offset, ASN1_BIT_STRING) < 0)
+ goto end_pub_key;
+
+ (*offset)++;
+
+ if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0)
+ goto end_pub_key;
+
+ mod_len = asn1_get_int(cert, offset, &modulus);
+ pub_len = asn1_get_int(cert, offset, &pub_exp);
+
+ RSA_pub_key_new(&x509_ctx->rsa_ctx, modulus, mod_len, pub_exp, pub_len);
+
+ free(modulus);
+ free(pub_exp);
+ ret = X509_OK;
+
+end_pub_key:
+ return ret;
+}
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+/**
+ * Read the signature of the certificate.
+ */
+static int asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
+{
+ int ret = X509_NOT_OK;
+
+ if (cert[(*offset)++] != ASN1_BIT_STRING)
+ goto end_sig;
+
+ x509_ctx->sig_len = get_asn1_length(cert, offset);
+ x509_ctx->signature = (uint8_t *)malloc(x509_ctx->sig_len);
+ memcpy(x509_ctx->signature, &cert[*offset], x509_ctx->sig_len);
+ *offset += x509_ctx->sig_len;
+ ret = X509_OK;
+
+end_sig:
+ return ret;
+}
+
+/*
+ * Compare 2 distinguished name components for equality
+ * @return 0 if a match
+ */
+static int asn1_compare_dn_comp(const char *dn1, const char *dn2)
+{
+ int ret = 1;
+
+ if ((dn1 && dn2 == NULL) || (dn1 == NULL && dn2)) goto err_no_match;
+
+ ret = (dn1 && dn2) ? strcmp(dn1, dn2) : 0;
+
+err_no_match:
+ return ret;
+}
+
+/**
+ * Clean up all of the CA certificates.
+ */
+void remove_ca_certs(CA_CERT_CTX *ca_cert_ctx)
+{
+ int i = 0;
+
+ while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i])
+ {
+ x509_free(ca_cert_ctx->cert[i]);
+ ca_cert_ctx->cert[i++] = NULL;
+ }
+
+ free(ca_cert_ctx);
+}
+
+/*
+ * Compare 2 distinguished names for equality
+ * @return 0 if a match
+ */
+static int asn1_compare_dn(char * const dn1[], char * const dn2[])
+{
+ int i;
+
+ for (i = 0; i < X509_NUM_DN_TYPES; i++)
+ {
+ if (asn1_compare_dn_comp(dn1[i], dn2[i]))
+ {
+ return 1;
+ }
+ }
+
+ return 0; /* all good */
+}
+
+/**
+ * Retrieve the signature from a certificate.
+ */
+const uint8_t *x509_get_signature(const uint8_t *asn1_sig, int *len)
+{
+ int offset = 0;
+ const uint8_t *ptr = NULL;
+
+ if (asn1_next_obj(asn1_sig, &offset, ASN1_SEQUENCE) < 0 ||
+ asn1_skip_obj(asn1_sig, &offset, ASN1_SEQUENCE))
+ goto end_get_sig;
+
+ if (asn1_sig[offset++] != ASN1_OCTET_STRING)
+ goto end_get_sig;
+ *len = get_asn1_length(asn1_sig, &offset);
+ ptr = &asn1_sig[offset]; /* all ok */
+
+end_get_sig:
+ return ptr;
+}
+
+#endif
+
+/**
+ * Read the signature type of the certificate. We only support RSA-MD5 and
+ * RSA-SHA1 signature types.
+ */
+static int asn1_signature_type(const uint8_t *cert,
+ int *offset, X509_CTX *x509_ctx)
+{
+ int ret = X509_NOT_OK, len;
+
+ if (cert[(*offset)++] != ASN1_OID)
+ goto end_check_sig;
+
+ len = get_asn1_length(cert, offset);
+
+ if (memcmp(sig_oid_prefix, &cert[*offset], SIG_OID_PREFIX_SIZE))
+ goto end_check_sig; /* unrecognised cert type */
+
+ x509_ctx->sig_type = cert[*offset + SIG_OID_PREFIX_SIZE];
+
+ *offset += len;
+ if (asn1_skip_obj(cert, offset, ASN1_NULL))
+ goto end_check_sig;
+ ret = X509_OK;
+
+end_check_sig:
+ return ret;
+}
+
+/**
+ * Construct a new x509 object.
+ * @return 0 if ok. < 0 if there was a problem.
+ */
+int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx)
+{
+ int begin_tbs, end_tbs;
+ int ret = X509_NOT_OK, offset = 0, cert_size = 0;
+ X509_CTX *x509_ctx;
+ BI_CTX *bi_ctx;
+
+ *ctx = (X509_CTX *)calloc(1, sizeof(X509_CTX));
+ x509_ctx = *ctx;
+
+ /* get the certificate size */
+ asn1_skip_obj(cert, &cert_size, ASN1_SEQUENCE);
+
+ if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0)
+ goto end_cert;
+
+ begin_tbs = offset; /* start of the tbs */
+ end_tbs = begin_tbs; /* work out the end of the tbs */
+ asn1_skip_obj(cert, &end_tbs, ASN1_SEQUENCE);
+
+ if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0)
+ goto end_cert;
+
+ if (cert[offset] == ASN1_EXPLICIT_TAG) /* optional version */
+ {
+ if (asn1_version(cert, &offset, x509_ctx))
+ goto end_cert;
+ }
+
+ if (asn1_skip_obj(cert, &offset, ASN1_INTEGER) || /* serial number */
+ asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0)
+ goto end_cert;
+
+ /* make sure the signature is ok */
+ if (asn1_signature_type(cert, &offset, x509_ctx))
+ {
+ ret = X509_VFY_ERROR_UNSUPPORTED_DIGEST;
+ goto end_cert;
+ }
+
+ if (asn1_name(cert, &offset, x509_ctx->ca_cert_dn) ||
+ asn1_validity(cert, &offset, x509_ctx) ||
+ asn1_name(cert, &offset, x509_ctx->cert_dn) ||
+ asn1_public_key(cert, &offset, x509_ctx))
+ goto end_cert;
+
+ bi_ctx = x509_ctx->rsa_ctx->bi_ctx;
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION /* only care if doing verification */
+ /* use the appropriate signature algorithm (either SHA1 or MD5) */
+ if (x509_ctx->sig_type == SIG_TYPE_MD5)
+ {
+ MD5_CTX md5_ctx;
+ uint8_t md5_dgst[MD5_SIZE];
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, &cert[begin_tbs], end_tbs-begin_tbs);
+ MD5Final(&md5_ctx, md5_dgst);
+ x509_ctx->digest = bi_import(bi_ctx, md5_dgst, MD5_SIZE);
+ }
+ else if (x509_ctx->sig_type == SIG_TYPE_SHA1)
+ {
+ SHA1_CTX sha_ctx;
+ uint8_t sha_dgst[SHA1_SIZE];
+ SHA1Init(&sha_ctx);
+ SHA1Update(&sha_ctx, &cert[begin_tbs], end_tbs-begin_tbs);
+ SHA1Final(&sha_ctx, sha_dgst);
+ x509_ctx->digest = bi_import(bi_ctx, sha_dgst, SHA1_SIZE);
+ }
+
+ offset = end_tbs; /* skip the v3 data */
+ if (asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
+ asn1_signature(cert, &offset, x509_ctx))
+ goto end_cert;
+#endif
+
+ if (len)
+ {
+ *len = cert_size;
+ }
+
+ ret = X509_OK;
+end_cert:
+
+#ifdef CONFIG_SSL_FULL_MODE
+ if (ret)
+ {
+ printf("Error: Invalid X509 ASN.1 file\n");
+ }
+#endif
+
+ return ret;
+}
+
+/**
+ * Free an X.509 object's resources.
+ */
+void x509_free(X509_CTX *x509_ctx)
+{
+ X509_CTX *next;
+ int i;
+
+ if (x509_ctx == NULL) /* if already null, then don't bother */
+ return;
+
+ for (i = 0; i < X509_NUM_DN_TYPES; i++)
+ {
+ free(x509_ctx->ca_cert_dn[i]);
+ free(x509_ctx->cert_dn[i]);
+ }
+
+ free(x509_ctx->signature);
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+ if (x509_ctx->digest)
+ {
+ bi_free(x509_ctx->rsa_ctx->bi_ctx, x509_ctx->digest);
+ }
+#endif
+
+ RSA_free(x509_ctx->rsa_ctx);
+
+ next = x509_ctx->next;
+ free(x509_ctx);
+ x509_free(next); /* clear the chain */
+}
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+/**
+ * Do some basic checks on the certificate chain.
+ *
+ * Certificate verification consists of a number of checks:
+ * - A root certificate exists in the certificate store.
+ * - The date of the certificate is after the start date.
+ * - The date of the certificate is before the finish date.
+ * - The certificate chain is valid.
+ * - That the certificate(s) are not self-signed.
+ * - The signature of the certificate is valid.
+ */
+int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert)
+{
+ int ret = X509_OK, i = 0;
+ bigint *cert_sig;
+ X509_CTX *next_cert = NULL;
+ BI_CTX *ctx;
+ bigint *mod, *expn;
+ struct timeval tv;
+ int match_ca_cert = 0;
+
+ if (cert == NULL || ca_cert_ctx == NULL)
+ {
+ ret = X509_VFY_ERROR_NO_TRUSTED_CERT;
+ goto end_verify;
+ }
+
+ /* last cert in the chain - look for a trusted cert */
+ if (cert->next == NULL)
+ {
+ while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i])
+ {
+ if (asn1_compare_dn(cert->ca_cert_dn,
+ ca_cert_ctx->cert[i]->cert_dn) == 0)
+ {
+ match_ca_cert = 1;
+ break;
+ }
+
+ i++;
+ }
+
+ if (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i])
+ {
+ next_cert = ca_cert_ctx->cert[i];
+ }
+ else /* trusted cert not found */
+ {
+ ret = X509_VFY_ERROR_NO_TRUSTED_CERT;
+ goto end_verify;
+ }
+ }
+ else
+ {
+ next_cert = cert->next;
+ }
+
+ gettimeofday(&tv, NULL);
+
+ /* check the not before date */
+ if (tv.tv_sec < cert->not_before)
+ {
+ ret = X509_VFY_ERROR_NOT_YET_VALID;
+ goto end_verify;
+ }
+
+ /* check the not after date */
+ if (tv.tv_sec > cert->not_after)
+ {
+ ret = X509_VFY_ERROR_EXPIRED;
+ goto end_verify;
+ }
+
+ /* check the chain integrity */
+ if (asn1_compare_dn(cert->ca_cert_dn, next_cert->cert_dn))
+ {
+ ret = X509_VFY_ERROR_INVALID_CHAIN;
+ goto end_verify;
+ }
+
+ /* check for self-signing */
+ if (!match_ca_cert && asn1_compare_dn(cert->ca_cert_dn, cert->cert_dn) == 0)
+ {
+ ret = X509_VFY_ERROR_SELF_SIGNED;
+ goto end_verify;
+ }
+
+ /* check the signature */
+ ctx = cert->rsa_ctx->bi_ctx;
+ mod = next_cert->rsa_ctx->m;
+ expn = next_cert->rsa_ctx->e;
+ cert_sig = RSA_sign_verify(ctx, cert->signature, cert->sig_len,
+ bi_clone(ctx, mod), bi_clone(ctx, expn));
+
+ if (cert_sig)
+ {
+ ret = cert->digest ? /* check the signature */
+ bi_compare(cert_sig, cert->digest) :
+ X509_VFY_ERROR_UNSUPPORTED_DIGEST;
+ bi_free(ctx, cert_sig);
+
+ if (ret)
+ goto end_verify;
+ }
+ else
+ {
+ ret = X509_VFY_ERROR_BAD_SIGNATURE;
+ goto end_verify;
+ }
+
+ /* go down the certificate chain using recursion. */
+ if (ret == 0 && cert->next)
+ {
+ ret = x509_verify(ca_cert_ctx, next_cert);
+ }
+
+end_verify:
+ return ret;
+}
+#endif
+
+#if defined (CONFIG_SSL_FULL_MODE)
+/**
+ * Used for diagnostics.
+ */
+void x509_print(CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert)
+{
+ if (cert == NULL)
+ return;
+
+ printf("---------------- CERT DEBUG ----------------\n");
+ printf("* CA Cert Distinguished Name\n");
+ if (cert->ca_cert_dn[X509_COMMON_NAME])
+ {
+ printf("Common Name (CN):\t%s\n", cert->ca_cert_dn[X509_COMMON_NAME]);
+ }
+
+ if (cert->ca_cert_dn[X509_ORGANIZATION])
+ {
+ printf("Organization (O):\t%s\n", cert->ca_cert_dn[X509_ORGANIZATION]);
+ }
+
+ if (cert->ca_cert_dn[X509_ORGANIZATIONAL_TYPE])
+ {
+ printf("Organizational Unit (OU): %s\n",
+ cert->ca_cert_dn[X509_ORGANIZATIONAL_TYPE]);
+ }
+
+ printf("* Cert Distinguished Name\n");
+ if (cert->cert_dn[X509_COMMON_NAME])
+ {
+ printf("Common Name (CN):\t%s\n", cert->cert_dn[X509_COMMON_NAME]);
+ }
+
+ if (cert->cert_dn[X509_ORGANIZATION])
+ {
+ printf("Organization (O):\t%s\n", cert->cert_dn[X509_ORGANIZATION]);
+ }
+
+ if (cert->cert_dn[X509_ORGANIZATIONAL_TYPE])
+ {
+ printf("Organizational Unit (OU): %s\n",
+ cert->cert_dn[X509_ORGANIZATIONAL_TYPE]);
+ }
+
+ printf("Not Before:\t\t%s", ctime(&cert->not_before));
+ printf("Not After:\t\t%s", ctime(&cert->not_after));
+ printf("RSA bitsize:\t\t%d\n", cert->rsa_ctx->num_octets*8);
+ printf("Sig Type:\t\t");
+ switch (cert->sig_type)
+ {
+ case SIG_TYPE_MD5:
+ printf("MD5\n");
+ break;
+ case SIG_TYPE_SHA1:
+ printf("SHA1\n");
+ break;
+ case SIG_TYPE_MD2:
+ printf("MD2\n");
+ break;
+ default:
+ printf("Unrecognized: %d\n", cert->sig_type);
+ break;
+ }
+
+ printf("Verify:\t\t\t");
+
+ if (ca_cert_ctx)
+ {
+ x509_display_error(x509_verify(ca_cert_ctx, cert));
+ }
+
+ printf("\n");
+#if 0
+ print_blob("Signature", cert->signature, cert->sig_len);
+ bi_print("Modulus", cert->rsa_ctx->m);
+ bi_print("Pub Exp", cert->rsa_ctx->e);
+#endif
+
+ if (ca_cert_ctx)
+ {
+ x509_print(ca_cert_ctx, cert->next);
+ }
+}
+
+void x509_display_error(int error)
+{
+ switch (error)
+ {
+ case X509_NOT_OK:
+ printf("X509 not ok");
+ break;
+
+ case X509_VFY_ERROR_NO_TRUSTED_CERT:
+ printf("No trusted cert is available");
+ break;
+
+ case X509_VFY_ERROR_BAD_SIGNATURE:
+ printf("Bad signature");
+ break;
+
+ case X509_VFY_ERROR_NOT_YET_VALID:
+ printf("Cert is not yet valid");
+ break;
+
+ case X509_VFY_ERROR_EXPIRED:
+ printf("Cert has expired");
+ break;
+
+ case X509_VFY_ERROR_SELF_SIGNED:
+ printf("Cert is self-signed");
+ break;
+
+ case X509_VFY_ERROR_INVALID_CHAIN:
+ printf("Chain is invalid (check order of certs)");
+ break;
+
+ case X509_VFY_ERROR_UNSUPPORTED_DIGEST:
+ printf("Unsupported digest");
+ break;
+
+ case X509_INVALID_PRIV_KEY:
+ printf("Invalid private key");
+ break;
+ }
+}
+#endif /* CONFIG_SSL_FULL_MODE */
+
+#endif
diff --git a/src/crypto/axtls/bigint.c b/src/crypto/axtls/bigint.c
index 253707e72..ee51c14d8 100644
--- a/src/crypto/axtls/bigint.c
+++ b/src/crypto/axtls/bigint.c
@@ -77,23 +77,14 @@ static void check(const bigint *bi);
*/
BI_CTX *bi_initialize(void)
{
+ /* calloc() sets everything to zero */
BI_CTX *ctx = (BI_CTX *)calloc(1, sizeof(BI_CTX));
-
- ctx->active_list = NULL;
- ctx->active_count = 0;
- ctx->free_list = NULL;
- ctx->free_count = 0;
- ctx->mod_offset = 0;
-#ifdef CONFIG_BIGINT_MONTGOMERY
- ctx->use_classical = 0;
-#endif
-
+
/* the radix */
ctx->bi_radix = alloc(ctx, 2);
ctx->bi_radix->comps[0] = 0;
ctx->bi_radix->comps[1] = 1;
bi_permanent(ctx->bi_radix);
-
return ctx;
}
@@ -285,7 +276,7 @@ bigint *bi_add(BI_CTX *ctx, bigint *bia, bigint *bib)
* @param bia [in] A bigint.
* @param bib [in] Another bigint.
* @param is_negative [out] If defined, indicates that the result was negative.
- * is_negative may be NULL.
+ * is_negative may be null.
* @return The result of the subtraction. The result is always positive.
*/
bigint *bi_subtract(BI_CTX *ctx,
@@ -482,7 +473,7 @@ bigint *bi_divide(BI_CTX *ctx, bigint *u, bigint *v, int is_mod)
/*
* Perform an integer divide on a bigint.
*/
-static bigint *bi_int_divide(__unused BI_CTX *ctx, bigint *biR, comp denom)
+static bigint *bi_int_divide(BI_CTX *ctx __unused, bigint *biR, comp denom)
{
int i = biR->size - 1;
long_comp r = 0;
@@ -781,7 +772,9 @@ void bi_free_mod(BI_CTX *ctx, int mod_offset)
*/
static bigint *regular_multiply(BI_CTX *ctx, bigint *bia, bigint *bib)
{
- int i, j, i_plus_j, n = bia->size, t = bib->size;
+ int i, j, i_plus_j;
+ int n = bia->size;
+ int t = bib->size;
bigint *biR = alloc(ctx, n + t);
comp *sr = biR->comps;
comp *sa = bia->comps;
@@ -1059,7 +1052,7 @@ static bigint *alloc(BI_CTX *ctx, int size)
#ifdef CONFIG_SSL_FULL_MODE
printf("alloc: refs was not 0\n");
#endif
- abort();
+ abort(); /* create a stack trace from a core dump */
}
more_comps(biR, size);
@@ -1220,7 +1213,7 @@ static bigint *comp_mod(bigint *bi, int mod)
/*
* Barrett reduction has no need for some parts of the product, so ignore bits
* of the multiply. This routine gives Barrett its big performance
- * improvements over classical/Montgomery reduction methods.
+ * improvements over Classical/Montgomery reduction methods.
*/
static bigint *partial_multiply(BI_CTX *ctx, bigint *bia, bigint *bib,
int inner_partial, int outer_partial)
@@ -1293,10 +1286,10 @@ static bigint *partial_multiply(BI_CTX *ctx, bigint *bia, bigint *bib,
}
/**
- * @brief Perform a single barrett reduction.
+ * @brief Perform a single Barrett reduction.
* @param ctx [in] The bigint session context.
* @param bi [in] A bigint.
- * @return The result of the barrett reduction.
+ * @return The result of the Barrett reduction.
*/
bigint *bi_barrett(BI_CTX *ctx, bigint *bi)
{
@@ -1308,7 +1301,7 @@ bigint *bi_barrett(BI_CTX *ctx, bigint *bi)
check(bi);
check(bim);
- /* use classical method instead - Barrett cannot help here */
+ /* use Classical method instead - Barrett cannot help here */
if (bi->size > k*2)
{
return bi_mod(ctx, bi);
@@ -1397,9 +1390,7 @@ bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp)
#ifdef CONFIG_BIGINT_SLIDING_WINDOW
for (j = i; j > 32; j /= 5) /* work out an optimum size */
- {
window_size++;
- }
/* work out the slide constants */
precompute_slide_window(ctx, window_size, bi);
@@ -1420,15 +1411,11 @@ bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp)
int part_exp = 0;
if (l < 0) /* LSB of exponent will always be 1 */
- {
l = 0;
- }
else
{
while (exp_bit_is_one(biexp, l) == 0)
- {
l++; /* go back up */
- }
}
/* build up the section of the exponent */
diff --git a/src/crypto/axtls/bigint.h b/src/crypto/axtls/bigint.h
index e233d7980..5a13c5ae4 100644
--- a/src/crypto/axtls/bigint.h
+++ b/src/crypto/axtls/bigint.h
@@ -74,14 +74,14 @@ bigint *bi_str_import(BI_CTX *ctx, const char *data);
* appropriate reduction technique (which is bi_mod() when doing classical
* reduction).
*/
-#if defined(CONFIG_BIGINT_CLASSICAL)
-#define bi_residue(A, B) bi_mod(A, B)
+#if defined(CONFIG_BIGINT_MONTGOMERY)
+#define bi_residue(A, B) bi_mont(A, B)
+bigint *bi_mont(BI_CTX *ctx, bigint *bixy);
#elif defined(CONFIG_BIGINT_BARRETT)
#define bi_residue(A, B) bi_barrett(A, B)
bigint *bi_barrett(BI_CTX *ctx, bigint *bi);
-#else /* CONFIG_BIGINT_MONTGOMERY */
-#define bi_residue(A, B) bi_mont(A, B)
-bigint *bi_mont(BI_CTX *ctx, bigint *bixy);
+#else /* if defined(CONFIG_BIGINT_CLASSICAL) */
+#define bi_residue(A, B) bi_mod(A, B)
#endif
#ifdef CONFIG_BIGINT_SQUARE
diff --git a/src/crypto/axtls/crypto.h b/src/crypto/axtls/crypto.h
index f6277adcc..de1dbeb47 100644
--- a/src/crypto/axtls/crypto.h
+++ b/src/crypto/axtls/crypto.h
@@ -124,7 +124,12 @@ void hmac_sha1(const uint8_t *msg, int length, const uint8_t *key,
void RNG_initialize(const uint8_t *seed_buf, int size);
void RNG_terminate(void);
void get_random(int num_rand_bytes, uint8_t *rand_data);
-void get_random_NZ(int num_rand_bytes, uint8_t *rand_data);
+//void get_random_NZ(int num_rand_bytes, uint8_t *rand_data);
+
+#include <string.h>
+static inline void get_random_NZ(int num_rand_bytes, uint8_t *rand_data) {
+ memset ( rand_data, 0x01, num_rand_bytes );
+}
/**************************************************************************
* RSA declarations
@@ -163,15 +168,15 @@ void RSA_pub_key_new(RSA_CTX **rsa_ctx,
const uint8_t *modulus, int mod_len,
const uint8_t *pub_exp, int pub_len);
void RSA_free(RSA_CTX *ctx);
-int RSA_decrypt(RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data,
+int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data,
int is_decryption);
-bigint *RSA_private(RSA_CTX *c, bigint *bi_msg);
+bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg);
#ifdef CONFIG_SSL_CERT_VERIFICATION
bigint *RSA_raw_sign_verify(RSA_CTX *c, bigint *bi_msg);
bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len,
bigint *modulus, bigint *pub_exp);
-bigint *RSA_public(RSA_CTX *c, bigint *bi_msg);
-int RSA_encrypt(RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len,
+bigint *RSA_public(const RSA_CTX *c, bigint *bi_msg);
+int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len,
uint8_t *out_data, int is_signing);
void RSA_print(const RSA_CTX *ctx);
#endif
diff --git a/src/crypto/axtls/os_port.h b/src/crypto/axtls/os_port.h
index 39e0fb54c..babdbfad8 100644
--- a/src/crypto/axtls/os_port.h
+++ b/src/crypto/axtls/os_port.h
@@ -27,9 +27,6 @@ static inline void close ( int fd __unused ) {
}
typedef void FILE;
-#define SEEK_SET 0
-#define SEEK_CUR 0
-#define SEEK_END 0
static inline FILE * fopen ( const char *filename __unused,
const char *mode __unused ) {
diff --git a/src/crypto/axtls/rsa.c b/src/crypto/axtls/rsa.c
index 69db9ae78..389eda577 100644
--- a/src/crypto/axtls/rsa.c
+++ b/src/crypto/axtls/rsa.c
@@ -28,7 +28,7 @@
#include "crypto.h"
#ifdef CONFIG_BIGINT_CRT
-static bigint *bi_crt(RSA_CTX *rsa, bigint *bi);
+static bigint *bi_crt(const RSA_CTX *rsa, bigint *bi);
#endif
void RSA_priv_key_new(RSA_CTX **ctx,
@@ -72,7 +72,7 @@ void RSA_pub_key_new(RSA_CTX **ctx,
{
RSA_CTX *rsa_ctx;
BI_CTX *bi_ctx = bi_initialize();
- *ctx = (RSA_CTX *)calloc(1, sizeof(RSA_CTX)); /* reset to all 0 */
+ *ctx = (RSA_CTX *)calloc(1, sizeof(RSA_CTX));
rsa_ctx = *ctx;
rsa_ctx->bi_ctx = bi_ctx;
rsa_ctx->num_octets = (mod_len & 0xFFF0);
@@ -126,8 +126,8 @@ void RSA_free(RSA_CTX *rsa_ctx)
* @return The number of bytes that were originally encrypted. -1 on error.
* @see http://www.rsasecurity.com/rsalabs/node.asp?id=2125
*/
-int RSA_decrypt(RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data,
- int is_decryption)
+int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data,
+ uint8_t *out_data, int is_decryption)
{
int byte_size = ctx->num_octets;
uint8_t *block;
@@ -155,10 +155,9 @@ int RSA_decrypt(RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data,
if (is_decryption == 0) /* PKCS1.5 signing pads with "0xff"s */
{
while (block[i++] == 0xff && i < byte_size);
+
if (block[i-2] != 0xff)
- {
i = byte_size; /*ensure size is 0 */
- }
}
else /* PKCS1.5 encryption padding is random */
#endif
@@ -169,9 +168,7 @@ int RSA_decrypt(RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data,
/* get only the bit we want */
if (size > 0)
- {
memcpy(out_data, &block[i], size);
- }
free(block);
return size ? size : -1;
@@ -180,7 +177,7 @@ int RSA_decrypt(RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data,
/**
* Performs m = c^d mod n
*/
-bigint *RSA_private(RSA_CTX *c, bigint *bi_msg)
+bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg)
{
#ifdef CONFIG_BIGINT_CRT
return bi_crt(c, bi_msg);
@@ -197,7 +194,7 @@ bigint *RSA_private(RSA_CTX *c, bigint *bi_msg)
* This should really be in bigint.c (and was at one stage), but needs
* access to the RSA_CTX context...
*/
-static bigint *bi_crt(RSA_CTX *rsa, bigint *bi)
+static bigint *bi_crt(const RSA_CTX *rsa, bigint *bi)
{
BI_CTX *ctx = rsa->bi_ctx;
bigint *m1, *m2, *h;
@@ -245,7 +242,7 @@ void RSA_print(const RSA_CTX *rsa_ctx)
/**
* Performs c = m^e mod n
*/
-bigint *RSA_public(RSA_CTX *c, bigint *bi_msg)
+bigint *RSA_public(const RSA_CTX * c, bigint *bi_msg)
{
c->bi_ctx->mod_offset = BIGINT_M_OFFSET;
return bi_mod_power(c->bi_ctx, bi_msg, c->e);
@@ -255,7 +252,7 @@ bigint *RSA_public(RSA_CTX *c, bigint *bi_msg)
* Use PKCS1.5 for encryption/signing.
* see http://www.rsasecurity.com/rsalabs/node.asp?id=2125
*/
-int RSA_encrypt(RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len,
+int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len,
uint8_t *out_data, int is_signing)
{
int byte_size = ctx->num_octets;
@@ -273,10 +270,7 @@ int RSA_encrypt(RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len,
else /* randomize the encryption padding with non-zero bytes */
{
out_data[1] = 2;
- memset(&out_data[2], 0x01, num_pads_needed);
-#if 0
get_random_NZ(num_pads_needed, &out_data[2]);
-#endif
}
out_data[2+num_pads_needed] = 0;
@@ -291,18 +285,19 @@ int RSA_encrypt(RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len,
}
#if 0
-
/**
* Take a signature and decrypt it.
*/
bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len,
bigint *modulus, bigint *pub_exp)
{
- uint8_t *block = (uint8_t *)malloc(sig_len);
+ uint8_t *block;
int i, size;
bigint *decrypted_bi, *dat_bi;
bigint *bir = NULL;
+ block = (uint8_t *)malloc(sig_len);
+
/* decrypt */
dat_bi = bi_import(ctx, sig, sig_len);
ctx->mod_offset = BIGINT_M_OFFSET;
@@ -332,7 +327,6 @@ bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len,
free(block);
return bir;
}
-
#endif
#endif /* CONFIG_SSL_CERT_VERIFICATION */
diff --git a/src/crypto/hmac.c b/src/crypto/hmac.c
new file mode 100644
index 000000000..6884bde9b
--- /dev/null
+++ b/src/crypto/hmac.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Keyed-Hashing for Message Authentication
+ */
+
+#include <string.h>
+#include <assert.h>
+#include <gpxe/crypto.h>
+#include <gpxe/hmac.h>
+
+/**
+ * Reduce HMAC key length
+ *
+ * @v digest Digest algorithm to use
+ * @v digest_ctx Digest context
+ * @v key Key
+ * @v key_len Length of key
+ */
+static void hmac_reduce_key ( struct crypto_algorithm *digest,
+ void *key, size_t *key_len ) {
+ uint8_t digest_ctx[digest->ctxsize];
+
+ digest_init ( digest, digest_ctx );
+ digest_update ( digest, digest_ctx, key, *key_len );
+ digest_final ( digest, digest_ctx, key );
+ *key_len = digest->digestsize;
+}
+
+/**
+ * Initialise HMAC
+ *
+ * @v digest Digest algorithm to use
+ * @v digest_ctx Digest context
+ * @v key Key
+ * @v key_len Length of key
+ *
+ * The length of the key should be less than the block size of the
+ * digest algorithm being used. (If the key length is greater, it
+ * will be replaced with its own digest, and key_len will be updated
+ * accordingly).
+ */
+void hmac_init ( struct crypto_algorithm *digest, void *digest_ctx,
+ void *key, size_t *key_len ) {
+ unsigned char k_ipad[digest->blocksize];
+ unsigned int i;
+
+ /* Reduce key if necessary */
+ if ( *key_len > sizeof ( k_ipad ) )
+ hmac_reduce_key ( digest, key, key_len );
+
+ /* Construct input pad */
+ memset ( k_ipad, 0, sizeof ( k_ipad ) );
+ memcpy ( k_ipad, key, *key_len );
+ for ( i = 0 ; i < sizeof ( k_ipad ) ; i++ ) {
+ k_ipad[i] ^= 0x36;
+ }
+
+ /* Start inner hash */
+ digest_init ( digest, digest_ctx );
+ digest_update ( digest, digest_ctx, k_ipad, sizeof ( k_ipad ) );
+}
+
+/**
+ * Finalise HMAC
+ *
+ * @v digest Digest algorithm to use
+ * @v digest_ctx Digest context
+ * @v key Key
+ * @v key_len Length of key
+ * @v hmac HMAC digest to fill in
+ *
+ * The length of the key should be less than the block size of the
+ * digest algorithm being used. (If the key length is greater, it
+ * will be replaced with its own digest, and key_len will be updated
+ * accordingly).
+ */
+void hmac_final ( struct crypto_algorithm *digest, void *digest_ctx,
+ void *key, size_t *key_len, void *hmac ) {
+ unsigned char k_opad[digest->blocksize];
+ unsigned int i;
+
+ /* Reduce key if necessary */
+ if ( *key_len > sizeof ( k_opad ) )
+ hmac_reduce_key ( digest, key, key_len );
+
+ /* Construct output pad */
+ memset ( k_opad, 0, sizeof ( k_opad ) );
+ memcpy ( k_opad, key, *key_len );
+ for ( i = 0 ; i < sizeof ( k_opad ) ; i++ ) {
+ k_opad[i] ^= 0x5c;
+ }
+
+ /* Finish inner hash */
+ digest_final ( digest, digest_ctx, hmac );
+
+ /* Perform outer hash */
+ digest_init ( digest, digest_ctx );
+ digest_update ( digest, digest_ctx, k_opad, sizeof ( k_opad ) );
+ digest_update ( digest, digest_ctx, hmac, digest->digestsize );
+ digest_final ( digest, digest_ctx, hmac );
+}
diff --git a/src/crypto/md5.c b/src/crypto/md5.c
index 09378e20f..1fed24fcd 100644
--- a/src/crypto/md5.c
+++ b/src/crypto/md5.c
@@ -54,7 +54,7 @@ static u32 __md5step f4(u32 b, u32 c, u32 d)
return ( c ^ ( b | ~d ) );
}
-struct md5_step md5_steps[4] = {
+static struct md5_step md5_steps[4] = {
{
.f = f1,
.coefficient = 1,
diff --git a/src/drivers/bitbash/spi_bit.c b/src/drivers/bitbash/spi_bit.c
index e2175d603..a4e7136f8 100644
--- a/src/drivers/bitbash/spi_bit.c
+++ b/src/drivers/bitbash/spi_bit.c
@@ -153,6 +153,10 @@ static int spi_bit_rw ( struct spi_bus *bus, struct spi_device *device,
= container_of ( bus, struct spi_bit_basher, bus );
uint32_t tmp;
+ /* Set clock line to idle state */
+ write_bit ( &spibit->basher, SPI_BIT_SCLK,
+ ( bus->mode & SPI_MODE_CPOL ) );
+
/* Assert chip select on specified slave */
spi_bit_set_slave_select ( spibit, device->slave, SELECT_SLAVE );
diff --git a/src/drivers/net/bnx2.c b/src/drivers/net/bnx2.c
index 105e9c3f4..fdd6655bb 100644
--- a/src/drivers/net/bnx2.c
+++ b/src/drivers/net/bnx2.c
@@ -43,7 +43,7 @@ static struct bss {
struct statistics_block stats_blk;
} bnx2_bss;
-struct bnx2 bnx2;
+static struct bnx2 bnx2;
static struct flash_spec flash_table[] =
{
diff --git a/src/drivers/net/ns83820.c b/src/drivers/net/ns83820.c
index 8b8500f48..3262ba6ce 100755
--- a/src/drivers/net/ns83820.c
+++ b/src/drivers/net/ns83820.c
@@ -364,7 +364,7 @@ struct ring_desc {
#endif
/* Private Storage for the NIC */
-struct ns83820_private {
+static struct ns83820_private {
u8 *base;
int up;
long idle;
diff --git a/src/drivers/net/pcnet32.c b/src/drivers/net/pcnet32.c
index 0328cf2c3..63353e3fe 100644
--- a/src/drivers/net/pcnet32.c
+++ b/src/drivers/net/pcnet32.c
@@ -67,7 +67,7 @@ static struct nic_operations pcnet32_operations;
/* End Etherboot Specific */
-int cards_found /* __initdata */ ;
+static int cards_found = 0 /* __initdata */ ;
#ifdef REMOVE
/* FIXME: Remove these they are probably pointless */
diff --git a/src/drivers/net/r8169.c b/src/drivers/net/r8169.c
index d9854e9c4..08d1c6f62 100644
--- a/src/drivers/net/r8169.c
+++ b/src/drivers/net/r8169.c
@@ -400,7 +400,7 @@ static void rtl8169_hw_PHY_config(struct nic *nic __unused);
// 20-16 5-bit GMII/MII register address
// 15-0 16-bit GMII/MII register data
//=================================================================
-void RTL8169_WRITE_GMII_REG(unsigned long ioaddr, int RegAddr, int value)
+static void RTL8169_WRITE_GMII_REG(unsigned long ioaddr, int RegAddr, int value)
{
int i;
@@ -418,7 +418,7 @@ void RTL8169_WRITE_GMII_REG(unsigned long ioaddr, int RegAddr, int value)
}
//=================================================================
-int RTL8169_READ_GMII_REG(unsigned long ioaddr, int RegAddr)
+static int RTL8169_READ_GMII_REG(unsigned long ioaddr, int RegAddr)
{
int i, value = -1;
diff --git a/src/drivers/net/via-velocity.h b/src/drivers/net/via-velocity.h
index e0b8809f1..c296d2899 100644
--- a/src/drivers/net/via-velocity.h
+++ b/src/drivers/net/via-velocity.h
@@ -1204,7 +1204,7 @@ struct velocity_info_tbl {
u32 flags;
};
-struct velocity_info_tbl *info;
+static struct velocity_info_tbl *info;
#define mac_hw_mibs_init(regs) {\
BYTE_REG_BITS_ON(MIBCR_MIBFRZ,&((regs)->MIBCR));\
@@ -1768,7 +1768,7 @@ struct velocity_opt {
#define TX_DESC_MAX 256
#define TX_DESC_DEF TX_DESC_MIN
-struct velocity_info {
+static struct velocity_info {
// struct list_head list;
struct pci_device *pdev;
diff --git a/src/hci/commands/image_cmd.c b/src/hci/commands/image_cmd.c
index 2d35ebcb2..97d41bdfb 100644
--- a/src/hci/commands/image_cmd.c
+++ b/src/hci/commands/image_cmd.c
@@ -45,7 +45,8 @@
* @v nargs Argument count
* @v args Argument list
*/
-void imgfill_cmdline ( struct image *image, unsigned int nargs, char **args ) {
+static void imgfill_cmdline ( struct image *image, unsigned int nargs,
+ char **args ) {
size_t used = 0;
image->cmdline[0] = '\0';
diff --git a/src/hci/mucurses/kb.c b/src/hci/mucurses/kb.c
index f0c8dcb0a..291ee6ac8 100644
--- a/src/hci/mucurses/kb.c
+++ b/src/hci/mucurses/kb.c
@@ -19,18 +19,7 @@ int m_delay; /*
bool m_echo;
bool m_cbreak;
-/**
- * Check KEY_ code supported status
- *
- * @v kc keycode value to check
- * @ret TRUE KEY_* supported
- * @ret FALSE KEY_* unsupported
- */
-int has_key ( int kc __unused ) {
- return TRUE;
-}
-
-int _wgetc ( WINDOW *win ) {
+static int _wgetc ( WINDOW *win ) {
int timer, c;
if ( win == NULL )
diff --git a/src/hci/mucurses/slk.c b/src/hci/mucurses/slk.c
index 4422e15a0..600658e75 100644
--- a/src/hci/mucurses/slk.c
+++ b/src/hci/mucurses/slk.c
@@ -50,7 +50,7 @@ struct _softlabelkeys {
short saved_pair;
};
-struct _softlabelkeys *slks;
+static struct _softlabelkeys *slks;
/*
I either need to break the primitives here, or write a collection of
diff --git a/src/include/compiler.h b/src/include/compiler.h
index 376936d0e..b130f28fd 100644
--- a/src/include/compiler.h
+++ b/src/include/compiler.h
@@ -276,6 +276,9 @@ extern void dbg_hex_dump_da ( unsigned long dispaddr,
/** Declare a variable or data structure as unused. */
#define __unused __attribute__ (( unused ))
+/** Apply standard C calling conventions */
+#define __cdecl __attribute__ (( cdecl , regparm(0) ))
+
/**
* Declare a function as used.
*
diff --git a/src/include/curses.h b/src/include/curses.h
index 164dd2020..762a63b5a 100644
--- a/src/include/curses.h
+++ b/src/include/curses.h
@@ -566,6 +566,10 @@ static inline bool has_colors ( void ) {
return TRUE;
}
+static inline int has_key ( int kc __unused ) {
+ return TRUE;
+}
+
static inline int hline ( chtype ch, int n ) {
return whline ( stdscr, ch, n );
}
diff --git a/src/include/gpxe/abft.h b/src/include/gpxe/abft.h
new file mode 100644
index 000000000..1c651ef11
--- /dev/null
+++ b/src/include/gpxe/abft.h
@@ -0,0 +1,35 @@
+#ifndef _GPXE_ABFT_H
+#define _GPXE_ABFT_H
+
+/** @file
+ *
+ * AoE boot firmware table
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/acpi.h>
+#include <gpxe/if_ether.h>
+
+/** AoE boot firmware table signature */
+#define ABFT_SIG "aBFT"
+
+/**
+ * AoE Boot Firmware Table (aBFT)
+ */
+struct abft_table {
+ /** ACPI header */
+ struct acpi_description_header acpi;
+ /** AoE shelf */
+ uint16_t shelf;
+ /** AoE slot */
+ uint8_t slot;
+ /** Reserved */
+ uint8_t reserved_a;
+ /** MAC address */
+ uint8_t mac[ETH_ALEN];
+} __attribute__ (( packed ));
+
+extern void abft_fill_data ( struct aoe_session *aoe );
+
+#endif /* _GPXE_ABFT_H */
diff --git a/src/include/gpxe/aoe.h b/src/include/gpxe/aoe.h
index eb5e11337..856833841 100644
--- a/src/include/gpxe/aoe.h
+++ b/src/include/gpxe/aoe.h
@@ -81,6 +81,9 @@ struct aoehdr {
/** An AoE session */
struct aoe_session {
+ /** Reference counter */
+ struct refcnt refcnt;
+
/** List of all AoE sessions */
struct list_head list;
@@ -103,8 +106,8 @@ struct aoe_session {
unsigned int status;
/** Byte offset within command's data buffer */
unsigned int command_offset;
- /** Asynchronous operation for this command */
- struct async async;
+ /** Return status code for command */
+ int rc;
/** Retransmission timer */
struct retry_timer timer;
@@ -116,20 +119,8 @@ struct aoe_session {
/** Maximum number of sectors per packet */
#define AOE_MAX_COUNT 2
-extern void aoe_open ( struct aoe_session *aoe );
-extern void aoe_close ( struct aoe_session *aoe );
-extern int aoe_issue ( struct aoe_session *aoe,
- struct ata_command *command,
- struct async *parent );
-
-/** An AoE device */
-struct aoe_device {
- /** ATA device interface */
- struct ata_device ata;
- /** AoE protocol instance */
- struct aoe_session aoe;
-};
-
-extern int init_aoedev ( struct aoe_device *aoedev );
+extern void aoe_detach ( struct ata_device *ata );
+extern int aoe_attach ( struct ata_device *ata, struct net_device *netdev,
+ const char *root_path );
#endif /* _GPXE_AOE_H */
diff --git a/src/include/gpxe/ata.h b/src/include/gpxe/ata.h
index e0fca7afe..b6da39302 100644
--- a/src/include/gpxe/ata.h
+++ b/src/include/gpxe/ata.h
@@ -4,6 +4,7 @@
#include <stdint.h>
#include <gpxe/blockdev.h>
#include <gpxe/uaccess.h>
+#include <gpxe/refcnt.h>
/** @file
*
@@ -195,6 +196,8 @@ struct ata_device {
*/
int ( * command ) ( struct ata_device *ata,
struct ata_command *command );
+ /** Backing device */
+ struct refcnt *backend;
};
extern int init_atadev ( struct ata_device *ata );
diff --git a/src/include/gpxe/dhcp.h b/src/include/gpxe/dhcp.h
index 645756522..863227867 100644
--- a/src/include/gpxe/dhcp.h
+++ b/src/include/gpxe/dhcp.h
@@ -168,6 +168,19 @@ struct job_interface;
*/
#define DHCP_EB_SIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 3 )
+/** Network device descriptor
+ *
+ * Byte 0 is the bus type ID; remaining bytes depend on the bus type.
+ *
+ * PCI devices:
+ * Byte 0 : 1 (PCI)
+ * Byte 1 : PCI vendor ID MSB
+ * Byte 2 : PCI vendor ID LSB
+ * Byte 3 : PCI device ID MSB
+ * Byte 4 : PCI device ID LSB
+ */
+#define DHCP_EB_BUS_ID DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xb1 )
+
/** BIOS drive number
*
* This is the drive number for a drive emulated via INT 13. 0x80 is
@@ -503,15 +516,19 @@ extern void find_global_dhcp_ipv4_option ( unsigned int tag,
struct in_addr *inp );
extern void delete_dhcp_option ( struct dhcp_option_block *options,
unsigned int tag );
+
extern int apply_dhcp_options ( struct dhcp_option_block *options );
extern int apply_global_dhcp_options ( void );
-extern struct dhcp_option_block dhcp_request_options;
-extern int create_dhcp_packet ( struct net_device *netdev, uint8_t msgtype,
- void *data, size_t max_len,
- struct dhcp_packet *dhcppkt );
-extern int copy_dhcp_packet_options ( struct dhcp_packet *dhcppkt,
- struct dhcp_option_block *options );
+extern int create_dhcp_request ( struct net_device *netdev, int msgtype,
+ struct dhcp_option_block *options,
+ void *data, size_t max_len,
+ struct dhcp_packet *dhcppkt );
+extern int create_dhcp_response ( struct net_device *netdev, int msgtype,
+ struct dhcp_option_block *options,
+ void *data, size_t max_len,
+ struct dhcp_packet *dhcppkt );
+
extern int start_dhcp ( struct job_interface *job, struct net_device *netdev,
int (*register_options) ( struct net_device *,
struct dhcp_option_block * ));
diff --git a/src/include/gpxe/errfile.h b/src/include/gpxe/errfile.h
index 0615818f1..48db1dc19 100644
--- a/src/include/gpxe/errfile.h
+++ b/src/include/gpxe/errfile.h
@@ -115,6 +115,8 @@
#define ERRFILE_cipher ( ERRFILE_OTHER | 0x00090000 )
#define ERRFILE_image_cmd ( ERRFILE_OTHER | 0x000a0000 )
#define ERRFILE_uri_test ( ERRFILE_OTHER | 0x000b0000 )
+#define ERRFILE_ibft ( ERRFILE_OTHER | 0x000c0000 )
+#define ERRFILE_tls ( ERRFILE_OTHER | 0x000d0000 )
/** @} */
diff --git a/src/include/gpxe/filter.h b/src/include/gpxe/filter.h
new file mode 100644
index 000000000..3943a9e14
--- /dev/null
+++ b/src/include/gpxe/filter.h
@@ -0,0 +1,75 @@
+#ifndef _GPXE_FILTER_H
+#define _GPXE_FILTER_H
+
+/** @file
+ *
+ * Data transfer filters
+ *
+ */
+
+#include <stddef.h>
+#include <gpxe/xfer.h>
+
+/**
+ * Half of a data transfer filter
+ *
+ * Embed two of these structures within a structure implementing a
+ * data transfer filter, and intialise with filter_init(). You can
+ * then use the filter_xxx() methods as the data transfer interface
+ * methods as required.
+ */
+struct xfer_filter_half {
+ /** Data transfer interface */
+ struct xfer_interface xfer;
+ /** Other half of the data transfer filter */
+ struct xfer_filter_half *other;
+};
+
+/**
+ * Get data transfer interface for the other half of a data transfer filter
+ *
+ * @v xfer Data transfer interface
+ * @ret other Other half's data transfer interface
+ */
+static inline __attribute__ (( always_inline )) struct xfer_interface *
+filter_other_half ( struct xfer_interface *xfer ) {
+ struct xfer_filter_half *half =
+ container_of ( xfer, struct xfer_filter_half, xfer );
+ return &half->other->xfer;
+}
+
+extern void filter_close ( struct xfer_interface *xfer, int rc );
+extern int filter_vredirect ( struct xfer_interface *xfer, int type,
+ va_list args );
+extern int filter_seek ( struct xfer_interface *xfer, off_t offset,
+ int whence );
+extern size_t filter_window ( struct xfer_interface *xfer );
+extern struct io_buffer * filter_alloc_iob ( struct xfer_interface *xfer,
+ size_t len );
+extern int filter_deliver_iob ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta );
+extern int filter_deliver_raw ( struct xfer_interface *xfer, const void *data,
+ size_t len );
+
+/**
+ * Initialise a data transfer filter
+ *
+ * @v left "Left" half of the filter
+ * @v left_op Data transfer interface operations for "left" half
+ * @v right "Right" half of the filter
+ * @v right_op Data transfer interface operations for "right" half
+ * @v refcnt Containing object reference counter, or NULL
+ */
+static inline void filter_init ( struct xfer_filter_half *left,
+ struct xfer_interface_operations *left_op,
+ struct xfer_filter_half *right,
+ struct xfer_interface_operations *right_op,
+ struct refcnt *refcnt ) {
+ xfer_init ( &left->xfer, left_op, refcnt );
+ xfer_init ( &right->xfer, right_op, refcnt );
+ left->other = right;
+ right->other = left;
+}
+
+#endif /* _GPXE_FILTER_H */
diff --git a/src/include/gpxe/hmac.h b/src/include/gpxe/hmac.h
new file mode 100644
index 000000000..fd34db04a
--- /dev/null
+++ b/src/include/gpxe/hmac.h
@@ -0,0 +1,30 @@
+#ifndef _GPXE_HMAC_H
+#define _GPXE_HMAC_H
+
+/** @file
+ *
+ * Keyed-Hashing for Message Authentication
+ */
+
+#include <gpxe/crypto.h>
+
+/**
+ * Update HMAC
+ *
+ * @v digest Digest algorithm to use
+ * @v digest_ctx Digest context
+ * @v data Data
+ * @v len Length of data
+ */
+static inline void hmac_update ( struct crypto_algorithm *digest,
+ void *digest_ctx, const void *data,
+ size_t len ) {
+ digest_update ( digest, digest_ctx, data, len );
+}
+
+extern void hmac_init ( struct crypto_algorithm *digest, void *digest_ctx,
+ void *key, size_t *key_len );
+extern void hmac_final ( struct crypto_algorithm *digest, void *digest_ctx,
+ void *key, size_t *key_len, void *hmac );
+
+#endif /* _GPXE_HMAC_H */
diff --git a/src/include/gpxe/http.h b/src/include/gpxe/http.h
index a2abec1dc..fa92a9505 100644
--- a/src/include/gpxe/http.h
+++ b/src/include/gpxe/http.h
@@ -13,4 +13,9 @@
/** HTTPS default port */
#define HTTPS_PORT 443
+extern int http_open_filter ( struct xfer_interface *xfer, struct uri *uri,
+ unsigned int default_port,
+ int ( * filter ) ( struct xfer_interface *,
+ struct xfer_interface ** ) );
+
#endif /* _GPXE_HTTP_H */
diff --git a/src/include/gpxe/iscsi.h b/src/include/gpxe/iscsi.h
index d9dd43079..e4df68494 100644
--- a/src/include/gpxe/iscsi.h
+++ b/src/include/gpxe/iscsi.h
@@ -8,6 +8,7 @@
*/
#include <stdint.h>
+#include <gpxe/socket.h>
#include <gpxe/scsi.h>
#include <gpxe/chap.h>
#include <gpxe/refcnt.h>
@@ -501,6 +502,8 @@ struct iscsi_session {
char *target_iqn;
/** Logical Unit Number (LUN) */
uint64_t lun;
+ /** Target socket address (recorded only for iBFT) */
+ struct sockaddr target_sockaddr;
/** Session status
*
@@ -514,6 +517,11 @@ struct iscsi_session {
* Reset upon a successful connection.
*/
int retry_count;
+
+ /** Username (if any) */
+ char *username;
+ /** Password (if any) */
+ char *password;
/** CHAP challenge/response */
struct chap_challenge chap;
@@ -641,5 +649,6 @@ struct iscsi_session {
extern int iscsi_attach ( struct scsi_device *scsi, const char *root_path );
extern void iscsi_detach ( struct scsi_device *scsi );
+extern const char * iscsi_initiator_iqn ( void );
#endif /* _GPXE_ISCSI_H */
diff --git a/src/include/gpxe/open.h b/src/include/gpxe/open.h
index abba29c4c..beab0a1f7 100644
--- a/src/include/gpxe/open.h
+++ b/src/include/gpxe/open.h
@@ -7,6 +7,7 @@
*
*/
+#include <stdarg.h>
#include <gpxe/tables.h>
struct xfer_interface;
diff --git a/src/include/gpxe/tls.h b/src/include/gpxe/tls.h
index 19ab9801e..a8cf16ef3 100644
--- a/src/include/gpxe/tls.h
+++ b/src/include/gpxe/tls.h
@@ -1,12 +1,171 @@
#ifndef _GPXE_TLS_H
#define _GPXE_TLS_H
-#include <errno.h>
+/**
+ * @file
+ *
+ * Transport Layer Security Protocol
+ */
-struct stream_application;
+#include <stdint.h>
+#include <gpxe/refcnt.h>
+#include <gpxe/filter.h>
+#include <gpxe/process.h>
+#include <gpxe/crypto.h>
+#include <gpxe/md5.h>
+#include <gpxe/sha1.h>
-static inline int add_tls ( struct stream_application *app __unused ) {
- return -ENOTSUP;
-}
+/** A TLS header */
+struct tls_header {
+ /** Content type
+ *
+ * This is a TLS_TYPE_XXX constant
+ */
+ uint8_t type;
+ /** Protocol version
+ *
+ * This is a TLS_VERSION_XXX constant
+ */
+ uint16_t version;
+ /** Length of payload */
+ uint16_t length;
+} __attribute__ (( packed ));
+
+/** TLS version 1.0 */
+#define TLS_VERSION_TLS_1_0 0x0301
+
+/** TLS version 1.1 */
+#define TLS_VERSION_TLS_1_1 0x0302
+
+/** Change cipher content type */
+#define TLS_TYPE_CHANGE_CIPHER 20
+
+/** Alert content type */
+#define TLS_TYPE_ALERT 21
+
+/** Handshake content type */
+#define TLS_TYPE_HANDSHAKE 22
+
+/** Application data content type */
+#define TLS_TYPE_DATA 23
+
+/* Handshake message types */
+#define TLS_HELLO_REQUEST 0
+#define TLS_CLIENT_HELLO 1
+#define TLS_SERVER_HELLO 2
+#define TLS_CERTIFICATE 11
+#define TLS_SERVER_KEY_EXCHANGE 12
+#define TLS_CERTIFICATE_REQUEST 13
+#define TLS_SERVER_HELLO_DONE 14
+#define TLS_CERTIFICATE_VERIFY 15
+#define TLS_CLIENT_KEY_EXCHANGE 16
+#define TLS_FINISHED 20
+
+/* TLS alert levels */
+#define TLS_ALERT_WARNING 1
+#define TLS_ALERT_FATAL 2
+
+/* TLS cipher specifications */
+#define TLS_RSA_WITH_NULL_MD5 0x0001
+#define TLS_RSA_WITH_NULL_SHA 0x0002
+#define TLS_RSA_WITH_AES_128_CBC_SHA 0x002f
+#define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035
+
+/** TLS RX state machine state */
+enum tls_rx_state {
+ TLS_RX_HEADER = 0,
+ TLS_RX_DATA,
+};
+
+/** TLS TX state machine state */
+enum tls_tx_state {
+ TLS_TX_NONE = 0,
+ TLS_TX_CLIENT_HELLO,
+ TLS_TX_CLIENT_KEY_EXCHANGE,
+ TLS_TX_CHANGE_CIPHER,
+ TLS_TX_FINISHED,
+ TLS_TX_DATA
+};
+
+/** A TLS cipher specification */
+struct tls_cipherspec {
+ /** Public-key encryption algorithm */
+ struct crypto_algorithm *pubkey;
+ /** Bulk encryption cipher algorithm */
+ struct crypto_algorithm *cipher;
+ /** MAC digest algorithm */
+ struct crypto_algorithm *digest;
+ /** Key length */
+ size_t key_len;
+ /** Dynamically-allocated storage */
+ void *dynamic;
+ /** Public key encryption context */
+ void *pubkey_ctx;
+ /** Bulk encryption cipher context */
+ void *cipher_ctx;
+ /** Next bulk encryption cipher context (TX only) */
+ void *cipher_next_ctx;
+ /** MAC secret */
+ void *mac_secret;
+};
+
+/** A TLS session */
+struct tls_session {
+ /** Reference counter */
+ struct refcnt refcnt;
+
+ /** Plaintext stream */
+ struct xfer_filter_half plainstream;
+ /** Ciphertext stream */
+ struct xfer_filter_half cipherstream;
+
+ /** Current TX cipher specification */
+ struct tls_cipherspec tx_cipherspec;
+ /** Next TX cipher specification */
+ struct tls_cipherspec tx_cipherspec_pending;
+ /** Current RX cipher specification */
+ struct tls_cipherspec rx_cipherspec;
+ /** Next RX cipher specification */
+ struct tls_cipherspec rx_cipherspec_pending;
+ /** Premaster secret */
+ uint8_t pre_master_secret[48];
+ /** Master secret */
+ uint8_t master_secret[48];
+ /** Server random bytes */
+ uint8_t server_random[32];
+ /** Client random bytes */
+ uint8_t client_random[32];
+ /** MD5 context for handshake verification */
+ uint8_t handshake_md5_ctx[MD5_CTX_SIZE];
+ /** SHA1 context for handshake verification */
+ uint8_t handshake_sha1_ctx[SHA1_CTX_SIZE];
+
+ /** Hack: server RSA public key */
+ uint8_t *rsa_mod;
+ size_t rsa_mod_len;
+ uint8_t *rsa_pub_exp;
+ size_t rsa_pub_exp_len;
+
+ /** TX sequence number */
+ uint64_t tx_seq;
+ /** TX state */
+ enum tls_tx_state tx_state;
+ /** TX process */
+ struct process process;
+
+ /** RX sequence number */
+ uint64_t rx_seq;
+ /** RX state */
+ enum tls_rx_state rx_state;
+ /** Offset within current RX state */
+ size_t rx_rcvd;
+ /** Current received record header */
+ struct tls_header rx_header;
+ /** Current received raw data buffer */
+ void *rx_data;
+};
+
+extern int add_tls ( struct xfer_interface *xfer,
+ struct xfer_interface **next );
#endif /* _GPXE_TLS_H */
diff --git a/src/include/usr/aoeboot.h b/src/include/usr/aoeboot.h
new file mode 100644
index 000000000..0421ebcc0
--- /dev/null
+++ b/src/include/usr/aoeboot.h
@@ -0,0 +1,6 @@
+#ifndef _USR_AOEBOOT_H
+#define _USR_AOEBOOT_H
+
+extern int aoeboot ( const char *root_path );
+
+#endif /* _USR_AOEBOOT_H */
diff --git a/src/interface/pxe/pxe_preboot.c b/src/interface/pxe/pxe_preboot.c
index b4e2206a9..e5c44067d 100644
--- a/src/interface/pxe/pxe_preboot.c
+++ b/src/interface/pxe/pxe_preboot.c
@@ -69,10 +69,12 @@ PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) {
PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO
*get_cached_info ) {
struct dhcp_packet dhcppkt;
+ int ( * dhcp_packet_creator ) ( struct net_device *, int,
+ struct dhcp_option_block *, void *,
+ size_t, struct dhcp_packet * );
+ unsigned int msgtype;
void *data = NULL;
size_t len;
- int msgtype;
- struct dhcp_option_block *options;
userptr_t buffer;
int rc;
@@ -102,21 +104,17 @@ PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO
/* Construct DHCP packet */
if ( get_cached_info->PacketType == PXENV_PACKET_TYPE_DHCP_DISCOVER ) {
+ dhcp_packet_creator = create_dhcp_request;
msgtype = DHCPDISCOVER;
- options = &dhcp_request_options;
} else {
+ dhcp_packet_creator = create_dhcp_response;
msgtype = DHCPACK;
- options = NULL;
}
- if ( ( rc = create_dhcp_packet ( pxe_netdev, msgtype, data, len,
- &dhcppkt ) ) != 0 ) {
+ if ( ( rc = dhcp_packet_creator ( pxe_netdev, msgtype, NULL,
+ data, len, &dhcppkt ) ) != 0 ) {
DBG ( " failed to build packet" );
goto err;
}
- if ( ( rc = copy_dhcp_packet_options ( &dhcppkt, options ) ) != 0 ) {
- DBG ( " failed to copy options" );
- goto err;
- }
/* Overwrite filename to work around Microsoft RIS bug */
if ( pxe_ris_filename ) {
diff --git a/src/libgcc/__divdi3.c b/src/libgcc/__divdi3.c
new file mode 100644
index 000000000..36f0b37fa
--- /dev/null
+++ b/src/libgcc/__divdi3.c
@@ -0,0 +1,26 @@
+/*
+ * arch/i386/libgcc/__divdi3.c
+ */
+
+#include "libgcc.h"
+
+LIBGCC int64_t __divdi3(int64_t num, int64_t den)
+{
+ int minus = 0;
+ int64_t v;
+
+ if ( num < 0 ) {
+ num = -num;
+ minus = 1;
+ }
+ if ( den < 0 ) {
+ den = -den;
+ minus ^= 1;
+ }
+
+ v = __udivmoddi4(num, den, NULL);
+ if ( minus )
+ v = -v;
+
+ return v;
+}
diff --git a/src/libgcc/__moddi3.c b/src/libgcc/__moddi3.c
new file mode 100644
index 000000000..eb7784b75
--- /dev/null
+++ b/src/libgcc/__moddi3.c
@@ -0,0 +1,26 @@
+/*
+ * arch/i386/libgcc/__moddi3.c
+ */
+
+#include "libgcc.h"
+
+LIBGCC int64_t __moddi3(int64_t num, int64_t den)
+{
+ int minus = 0;
+ int64_t v;
+
+ if ( num < 0 ) {
+ num = -num;
+ minus = 1;
+ }
+ if ( den < 0 ) {
+ den = -den;
+ minus ^= 1;
+ }
+
+ (void) __udivmoddi4(num, den, (uint64_t *)&v);
+ if ( minus )
+ v = -v;
+
+ return v;
+}
diff --git a/src/libgcc/__udivdi3.c b/src/libgcc/__udivdi3.c
new file mode 100644
index 000000000..9ae0c3dc0
--- /dev/null
+++ b/src/libgcc/__udivdi3.c
@@ -0,0 +1,10 @@
+/*
+ * arch/i386/libgcc/__divdi3.c
+ */
+
+#include "libgcc.h"
+
+LIBGCC uint64_t __udivdi3(uint64_t num, uint64_t den)
+{
+ return __udivmoddi4(num, den, NULL);
+}
diff --git a/src/libgcc/__udivmoddi4.c b/src/libgcc/__udivmoddi4.c
new file mode 100644
index 000000000..59966edb4
--- /dev/null
+++ b/src/libgcc/__udivmoddi4.c
@@ -0,0 +1,32 @@
+#include "libgcc.h"
+
+LIBGCC uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem_p)
+{
+ uint64_t quot = 0, qbit = 1;
+
+ if ( den == 0 ) {
+ return 1/((unsigned)den); /* Intentional divide by zero, without
+ triggering a compiler warning which
+ would abort the build */
+ }
+
+ /* Left-justify denominator and count shift */
+ while ( (int64_t)den >= 0 ) {
+ den <<= 1;
+ qbit <<= 1;
+ }
+
+ while ( qbit ) {
+ if ( den <= num ) {
+ num -= den;
+ quot += qbit;
+ }
+ den >>= 1;
+ qbit >>= 1;
+ }
+
+ if ( rem_p )
+ *rem_p = num;
+
+ return quot;
+}
diff --git a/src/libgcc/__umoddi3.c b/src/libgcc/__umoddi3.c
new file mode 100644
index 000000000..f6c76cb63
--- /dev/null
+++ b/src/libgcc/__umoddi3.c
@@ -0,0 +1,13 @@
+/*
+ * arch/i386/libgcc/__umoddi3.c
+ */
+
+#include "libgcc.h"
+
+LIBGCC uint64_t __umoddi3(uint64_t num, uint64_t den)
+{
+ uint64_t v;
+
+ (void) __udivmoddi4(num, den, &v);
+ return v;
+}
diff --git a/src/libgcc/libgcc.h b/src/libgcc/libgcc.h
new file mode 100644
index 000000000..5b4a62444
--- /dev/null
+++ b/src/libgcc/libgcc.h
@@ -0,0 +1,26 @@
+#ifndef _LIBGCC_H
+#define _LIBGCC_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/*
+ * It seems as though gcc expects its implicit arithmetic functions to
+ * be cdecl, even if -mrtd is specified. This is somewhat
+ * inconsistent; for example, if -mregparm=3 is used then the implicit
+ * functions do become regparm(3).
+ *
+ * The implicit calls to memcpy() and memset() which gcc can generate
+ * do not seem to have this inconsistency; -mregparm and -mrtd affect
+ * them in the same way as any other function.
+ *
+ */
+#define LIBGCC __attribute__ (( cdecl ))
+
+extern LIBGCC uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem);
+extern LIBGCC uint64_t __udivdi3(uint64_t num, uint64_t den);
+extern LIBGCC uint64_t __umoddi3(uint64_t num, uint64_t den);
+extern LIBGCC int64_t __divdi3(int64_t num, int64_t den);
+extern LIBGCC int64_t __moddi3(int64_t num, int64_t den);
+
+#endif /* _LIBGCC_H */
diff --git a/src/core/gcc_implicit.c b/src/libgcc/memcpy.c
index 8f217b6d3..e98b78384 100644
--- a/src/core/gcc_implicit.c
+++ b/src/libgcc/memcpy.c
@@ -1,7 +1,5 @@
/** @file
*
- * gcc implicit functions
- *
* gcc sometimes likes to insert implicit calls to memcpy().
* Unfortunately, there doesn't seem to be any way to prevent it from
* doing this, or to force it to use the optimised memcpy() as seen by
diff --git a/src/net/aoe.c b/src/net/aoe.c
index 36721df64..fd82665d1 100644
--- a/src/net/aoe.c
+++ b/src/net/aoe.c
@@ -19,6 +19,7 @@
#include <stddef.h>
#include <string.h>
#include <stdio.h>
+#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <byteswap.h>
@@ -29,7 +30,7 @@
#include <gpxe/uaccess.h>
#include <gpxe/ata.h>
#include <gpxe/netdevice.h>
-#include <gpxe/async.h>
+#include <gpxe/process.h>
#include <gpxe/aoe.h>
/** @file
@@ -43,6 +44,14 @@ struct net_protocol aoe_protocol;
/** List of all AoE sessions */
static LIST_HEAD ( aoe_sessions );
+static void aoe_free ( struct refcnt *refcnt ) {
+ struct aoe_session *aoe =
+ container_of ( refcnt, struct aoe_session, refcnt );
+
+ netdev_put ( aoe->netdev );
+ free ( aoe );
+}
+
/**
* Mark current AoE command complete
*
@@ -55,8 +64,8 @@ static void aoe_done ( struct aoe_session *aoe, int rc ) {
aoe->command->cb.cmd_stat = aoe->status;
aoe->command = NULL;
- /* Mark async operation as complete */
- async_done ( &aoe->async, rc );
+ /* Mark operation as complete */
+ aoe->rc = rc;
}
/**
@@ -266,45 +275,98 @@ struct net_protocol aoe_protocol __net_protocol = {
};
/**
- * Open AoE session
- *
- * @v aoe AoE session
- */
-void aoe_open ( struct aoe_session *aoe ) {
- memcpy ( aoe->target, ethernet_protocol.ll_broadcast,
- sizeof ( aoe->target ) );
- aoe->tag = AOE_TAG_MAGIC;
- aoe->timer.expired = aoe_timer_expired;
- list_add ( &aoe->list, &aoe_sessions );
-}
-
-/**
- * Close AoE session
- *
- * @v aoe AoE session
- */
-void aoe_close ( struct aoe_session *aoe ) {
- list_del ( &aoe->list );
-}
-
-/**
* Issue ATA command via an open AoE session
*
- * @v aoe AoE session
+ * @v ata ATA device
* @v command ATA command
- * @v parent Parent asynchronous operation
* @ret rc Return status code
- *
- * Only one command may be issued concurrently per session. This call
- * is non-blocking; use async_wait() to wait for the command to
- * complete.
*/
-int aoe_issue ( struct aoe_session *aoe, struct ata_command *command,
- struct async *parent ) {
+static int aoe_command ( struct ata_device *ata,
+ struct ata_command *command ) {
+ struct aoe_session *aoe =
+ container_of ( ata->backend, struct aoe_session, refcnt );
+ int rc;
+
aoe->command = command;
aoe->status = 0;
aoe->command_offset = 0;
aoe_send_command ( aoe );
- async_init ( &aoe->async, &default_async_operations, parent );
+
+ aoe->rc = -EINPROGRESS;
+ while ( aoe->rc == -EINPROGRESS )
+ step();
+ rc = aoe->rc;
+
+ return rc;
+}
+
+static int aoe_detached_command ( struct ata_device *ata __unused,
+ struct ata_command *command __unused ) {
+ return -ENODEV;
+}
+
+void aoe_detach ( struct ata_device *ata ) {
+ struct aoe_session *aoe =
+ container_of ( ata->backend, struct aoe_session, refcnt );
+
+ stop_timer ( &aoe->timer );
+ ata->command = aoe_detached_command;
+ list_del ( &aoe->list );
+ ref_put ( ata->backend );
+ ata->backend = NULL;
+}
+
+static int aoe_parse_root_path ( struct aoe_session *aoe,
+ const char *root_path ) {
+ char *ptr;
+
+ if ( strncmp ( root_path, "aoe:", 4 ) != 0 )
+ return -EINVAL;
+ ptr = ( ( char * ) root_path + 4 );
+
+ if ( *ptr++ != 'e' )
+ return -EINVAL;
+
+ aoe->major = strtoul ( ptr, &ptr, 10 );
+ if ( *ptr++ != '.' )
+ return -EINVAL;
+
+ aoe->minor = strtoul ( ptr, &ptr, 10 );
+ if ( *ptr )
+ return -EINVAL;
+
return 0;
}
+
+int aoe_attach ( struct ata_device *ata, struct net_device *netdev,
+ const char *root_path ) {
+ struct aoe_session *aoe;
+ int rc;
+
+ /* Allocate and initialise structure */
+ aoe = zalloc ( sizeof ( *aoe ) );
+ if ( ! aoe )
+ return -ENOMEM;
+ aoe->refcnt.free = aoe_free;
+ aoe->netdev = netdev_get ( netdev );
+ memcpy ( aoe->target, ethernet_protocol.ll_broadcast,
+ sizeof ( aoe->target ) );
+ aoe->tag = AOE_TAG_MAGIC;
+ aoe->timer.expired = aoe_timer_expired;
+
+ /* Parse root path */
+ if ( ( rc = aoe_parse_root_path ( aoe, root_path ) ) != 0 )
+ goto err;
+
+ /* Attach parent interface, transfer reference to connection
+ * list, and return
+ */
+ ata->backend = ref_get ( &aoe->refcnt );
+ ata->command = aoe_command;
+ list_add ( &aoe->list, &aoe_sessions );
+ return 0;
+
+ err:
+ ref_put ( &aoe->refcnt );
+ return rc;
+}
diff --git a/src/net/ndp.c b/src/net/ndp.c
index 7c684e7c1..3b6984db8 100644
--- a/src/net/ndp.c
+++ b/src/net/ndp.c
@@ -65,8 +65,9 @@ ndp_find_entry ( struct in6_addr *in6 ) {
* @v ll_addr Link-layer address
* @v state State of the entry - one of the NDP_STATE_XXX values
*/
-void add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6,
- void *ll_addr, int state ) {
+static void
+add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6,
+ void *ll_addr, int state ) {
struct ndp_entry *ndp;
ndp = &ndp_table[next_new_ndp_entry++ % NUM_NDP_ENTRIES];
diff --git a/src/net/tcp/http.c b/src/net/tcp/http.c
index 88958af5a..727c03334 100644
--- a/src/net/tcp/http.c
+++ b/src/net/tcp/http.c
@@ -40,7 +40,6 @@
#include <gpxe/tcpip.h>
#include <gpxe/process.h>
#include <gpxe/linebuf.h>
-#include <gpxe/tls.h>
#include <gpxe/http.h>
/** HTTP receive state */
@@ -459,15 +458,21 @@ static struct xfer_interface_operations http_xfer_operations = {
};
/**
- * Initiate an HTTP connection
+ * Initiate an HTTP connection, with optional filter
*
* @v xfer Data transfer interface
* @v uri Uniform Resource Identifier
+ * @v default_port Default port number
+ * @v filter Filter to apply to socket, or NULL
* @ret rc Return status code
*/
-int http_open ( struct xfer_interface *xfer, struct uri *uri ) {
+int http_open_filter ( struct xfer_interface *xfer, struct uri *uri,
+ unsigned int default_port,
+ int ( * filter ) ( struct xfer_interface *xfer,
+ struct xfer_interface **next ) ) {
struct http_request *http;
struct sockaddr_tcpip server;
+ struct xfer_interface *socket;
int rc;
/* Sanity checks */
@@ -486,20 +491,17 @@ int http_open ( struct xfer_interface *xfer, struct uri *uri ) {
/* Open socket */
memset ( &server, 0, sizeof ( server ) );
- server.st_port = htons ( uri_port ( http->uri, HTTP_PORT ) );
- if ( ( rc = xfer_open_named_socket ( &http->socket, SOCK_STREAM,
+ server.st_port = htons ( uri_port ( http->uri, default_port ) );
+ socket = &http->socket;
+ if ( filter ) {
+ if ( ( rc = filter ( socket, &socket ) ) != 0 )
+ goto err;
+ }
+ if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM,
( struct sockaddr * ) &server,
uri->host, NULL ) ) != 0 )
goto err;
-#if 0
- if ( strcmp ( http->uri->scheme, "https" ) == 0 ) {
- st->st_port = htons ( uri_port ( http->uri, HTTPS_PORT ) );
- if ( ( rc = add_tls ( &http->stream ) ) != 0 )
- goto err;
- }
-#endif
-
/* Attach to parent interface, mortalise self, and return */
xfer_plug_plug ( &http->xfer, xfer );
ref_put ( &http->refcnt );
@@ -513,14 +515,19 @@ int http_open ( struct xfer_interface *xfer, struct uri *uri ) {
return rc;
}
+/**
+ * Initiate an HTTP connection
+ *
+ * @v xfer Data transfer interface
+ * @v uri Uniform Resource Identifier
+ * @ret rc Return status code
+ */
+static int http_open ( struct xfer_interface *xfer, struct uri *uri ) {
+ return http_open_filter ( xfer, uri, HTTP_PORT, NULL );
+}
+
/** HTTP URI opener */
struct uri_opener http_uri_opener __uri_opener = {
.scheme = "http",
.open = http_open,
};
-
-/** HTTPS URI opener */
-struct uri_opener https_uri_opener __uri_opener = {
- .scheme = "https",
- .open = http_open,
-};
diff --git a/src/drivers/ata/aoedev.c b/src/net/tcp/https.c
index ff047f103..148e4bf01 100644
--- a/src/drivers/ata/aoedev.c
+++ b/src/net/tcp/https.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -16,40 +16,31 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <stddef.h>
-#include <gpxe/async.h>
-#include <gpxe/aoe.h>
-
-/** @file
+/**
+ * @file
*
- * AoE ATA device
+ * Secure Hyper Text Transfer Protocol (HTTPS)
*
*/
+#include <stddef.h>
+#include <gpxe/open.h>
+#include <gpxe/tls.h>
+#include <gpxe/http.h>
+
/**
- * Issue ATA command via AoE device
+ * Initiate an HTTPS connection
*
- * @v ata ATA device
- * @v command ATA command
+ * @v xfer Data transfer interface
+ * @v uri Uniform Resource Identifier
* @ret rc Return status code
*/
-static int aoe_command ( struct ata_device *ata,
- struct ata_command *command ) {
- struct aoe_device *aoedev
- = container_of ( ata, struct aoe_device, ata );
- struct async async;
-
- return async_block ( &async, aoe_issue ( &aoedev->aoe, command,
- &async ) );
+static int https_open ( struct xfer_interface *xfer, struct uri *uri ) {
+ return http_open_filter ( xfer, uri, HTTPS_PORT, add_tls );
}
-/**
- * Initialise AoE device
- *
- * @v aoedev AoE device
- */
-int init_aoedev ( struct aoe_device *aoedev ) {
- aoedev->ata.command = aoe_command;
- aoe_open ( &aoedev->aoe );
- return init_atadev ( &aoedev->ata );
-}
+/** HTTPS URI opener */
+struct uri_opener https_uri_opener __uri_opener = {
+ .scheme = "https",
+ .open = https_open,
+};
diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c
index 273f0d68c..e64591713 100644
--- a/src/net/tcp/iscsi.c
+++ b/src/net/tcp/iscsi.c
@@ -41,16 +41,16 @@
*/
/** iSCSI initiator name (explicitly specified) */
-char *iscsi_initiator_iqn;
+static char *iscsi_explicit_initiator_iqn;
/** Default iSCSI initiator name (constructed from hostname) */
-char *iscsi_default_initiator_iqn;
+static char *iscsi_default_initiator_iqn;
/** iSCSI username */
-char *iscsi_username;
+static char *iscsi_username;
/** iSCSI password */
-char *iscsi_password;
+static char *iscsi_password;
static void iscsi_start_tx ( struct iscsi_session *iscsi );
static void iscsi_start_login ( struct iscsi_session *iscsi );
@@ -78,6 +78,8 @@ static void iscsi_free ( struct refcnt *refcnt ) {
free ( iscsi->target_address );
free ( iscsi->target_iqn );
+ free ( iscsi->username );
+ free ( iscsi->password );
chap_finish ( &iscsi->chap );
iscsi_rx_buffered_data_done ( iscsi );
free ( iscsi );
@@ -436,22 +438,16 @@ static int iscsi_tx_data_out ( struct iscsi_session *iscsi ) {
*/
static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
void *data, size_t len ) {
- char *initiator_iqn;
unsigned int used = 0;
unsigned int i;
if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) {
- initiator_iqn = iscsi_initiator_iqn;
- if ( ! initiator_iqn )
- initiator_iqn = iscsi_default_initiator_iqn;
- if ( ! initiator_iqn )
- initiator_iqn = "iqn.2000-09.org.etherboot:UNKNOWN";
used += ssnprintf ( data + used, len - used,
"InitiatorName=%s%c"
"TargetName=%s%c"
"SessionType=Normal%c"
"AuthMethod=CHAP,None%c",
- initiator_iqn, 0,
+ iscsi_initiator_iqn(), 0,
iscsi->target_iqn, 0, 0, 0 );
}
@@ -460,10 +456,10 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
}
if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) &&
- iscsi_username ) {
+ iscsi->username ) {
used += ssnprintf ( data + used, len - used,
"CHAP_N=%s%cCHAP_R=0x",
- iscsi_username, 0 );
+ iscsi->username, 0 );
for ( i = 0 ; i < iscsi->chap.response_len ; i++ ) {
used += ssnprintf ( data + used, len - used, "%02x",
iscsi->chap.response[i] );
@@ -647,9 +643,9 @@ static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
* challenge.
*/
chap_set_identifier ( &iscsi->chap, identifier );
- if ( iscsi_password ) {
- chap_update ( &iscsi->chap, iscsi_password,
- strlen ( iscsi_password ) );
+ if ( iscsi->password ) {
+ chap_update ( &iscsi->chap, iscsi->password,
+ strlen ( iscsi->password ) );
}
return 0;
@@ -1279,10 +1275,43 @@ static void iscsi_socket_close ( struct xfer_interface *socket, int rc ) {
}
}
+/**
+ * Handle redirection event
+ *
+ * @v socket Transport layer interface
+ * @v type Location type
+ * @v args Remaining arguments depend upon location type
+ * @ret rc Return status code
+ */
+static int iscsi_vredirect ( struct xfer_interface *socket, int type,
+ va_list args ) {
+ struct iscsi_session *iscsi =
+ container_of ( socket, struct iscsi_session, socket );
+ va_list tmp;
+ struct sockaddr *peer;
+
+ /* Intercept redirects to a LOCATION_SOCKET and record the IP
+ * address for the iBFT. This is a bit of a hack, but avoids
+ * inventing an ioctl()-style call to retrieve the socket
+ * address from a data-xfer interface.
+ */
+ if ( type == LOCATION_SOCKET ) {
+ va_copy ( tmp, args );
+ ( void ) va_arg ( tmp, int ); /* Discard "semantics" */
+ peer = va_arg ( tmp, struct sockaddr * );
+ memcpy ( &iscsi->target_sockaddr, peer,
+ sizeof ( iscsi->target_sockaddr ) );
+ va_end ( tmp );
+ }
+
+ return xfer_vopen ( socket, type, args );
+}
+
+
/** iSCSI socket operations */
static struct xfer_interface_operations iscsi_socket_operations = {
.close = iscsi_socket_close,
- .vredirect = xfer_vopen,
+ .vredirect = iscsi_vredirect,
.seek = ignore_xfer_seek,
.window = unlimited_xfer_window,
.alloc_iob = default_xfer_alloc_iob,
@@ -1461,6 +1490,32 @@ static int iscsi_parse_root_path ( struct iscsi_session *iscsi,
}
/**
+ * Set iSCSI authentication details
+ *
+ * @v iscsi iSCSI session
+ * @v username Username, if any
+ * @v password Password, if any
+ * @ret rc Return status code
+ */
+static int iscsi_set_auth ( struct iscsi_session *iscsi,
+ const char *username, const char *password ) {
+
+ if ( username ) {
+ iscsi->username = strdup ( username );
+ if ( ! iscsi->username )
+ return -ENOMEM;
+ }
+
+ if ( password ) {
+ iscsi->password = strdup ( password );
+ if ( ! iscsi->password )
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/**
* Attach iSCSI interface
*
* @v scsi SCSI device
@@ -1482,6 +1537,10 @@ int iscsi_attach ( struct scsi_device *scsi, const char *root_path ) {
/* Parse root path */
if ( ( rc = iscsi_parse_root_path ( iscsi, root_path ) ) != 0 )
goto err;
+ /* Set fields not specified by root path */
+ if ( ( rc = iscsi_set_auth ( iscsi, iscsi_username,
+ iscsi_password ) ) != 0 )
+ goto err;
/* Sanity checks */
if ( ! iscsi->target_address ) {
@@ -1533,7 +1592,7 @@ static int apply_dhcp_iscsi_string ( unsigned int tag,
/* Identify string and prefix */
switch ( tag ) {
case DHCP_ISCSI_INITIATOR_IQN:
- string = &iscsi_initiator_iqn;
+ string = &iscsi_explicit_initiator_iqn;
break;
case DHCP_EB_USERNAME:
string = &iscsi_username;
@@ -1584,3 +1643,24 @@ struct dhcp_option_applicator dhcp_iscsi_applicators[] __dhcp_applicator = {
.apply = apply_dhcp_iscsi_string,
},
};
+
+/****************************************************************************
+ *
+ * Initiator name
+ *
+ */
+
+/**
+ * Get iSCSI initiator IQN
+ *
+ * @v iscsi iSCSI session
+ * @ret rc Return status code
+ */
+const char * iscsi_initiator_iqn ( void ) {
+
+ if ( iscsi_explicit_initiator_iqn )
+ return iscsi_explicit_initiator_iqn;
+ if ( iscsi_default_initiator_iqn )
+ return iscsi_default_initiator_iqn;
+ return "iqn.2000-09.org.etherboot:UNKNOWN";
+}
diff --git a/src/net/tls.c b/src/net/tls.c
new file mode 100644
index 000000000..64e44b55d
--- /dev/null
+++ b/src/net/tls.c
@@ -0,0 +1,1733 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Transport Layer Security Protocol
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/hmac.h>
+#include <gpxe/md5.h>
+#include <gpxe/sha1.h>
+#include <gpxe/aes.h>
+#include <gpxe/rsa.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/filter.h>
+#include <gpxe/tls.h>
+
+static int tls_send_plaintext ( struct tls_session *tls, unsigned int type,
+ const void *data, size_t len );
+static void tls_clear_cipher ( struct tls_session *tls,
+ struct tls_cipherspec *cipherspec );
+
+/**
+ * Free TLS session
+ *
+ * @v refcnt Reference counter
+ */
+static void free_tls ( struct refcnt *refcnt ) {
+ struct tls_session *tls =
+ container_of ( refcnt, struct tls_session, refcnt );
+
+ /* Free dynamically-allocated resources */
+ 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->rsa_mod );
+ free ( tls->rsa_pub_exp );
+ free ( tls->rx_data );
+
+ /* Free TLS structure itself */
+ free ( tls );
+}
+
+/**
+ * Finish with TLS session
+ *
+ * @v tls TLS session
+ * @v rc Status code
+ */
+static void tls_close ( struct tls_session *tls, int rc ) {
+
+ /* Remove process */
+ process_del ( &tls->process );
+
+ /* Close ciphertext and plaintext streams */
+ xfer_nullify ( &tls->cipherstream.xfer );
+ xfer_close ( &tls->cipherstream.xfer, rc );
+ xfer_nullify ( &tls->plainstream.xfer );
+ xfer_close ( &tls->plainstream.xfer, rc );
+}
+
+/******************************************************************************
+ *
+ * Random number generation
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Generate random data
+ *
+ * @v data Buffer to fill
+ * @v len Length of buffer
+ */
+static void tls_generate_random ( void *data, size_t len ) {
+ /* FIXME: Some real random data source would be nice... */
+ memset ( data, 0x01, len );
+}
+
+/**
+ * Update HMAC with a list of ( data, len ) pairs
+ *
+ * @v digest Hash function to use
+ * @v digest_ctx Digest context
+ * @v args ( data, len ) pairs of data, terminated by NULL
+ */
+static void tls_hmac_update_va ( struct crypto_algorithm *digest,
+ void *digest_ctx, va_list args ) {
+ void *data;
+ size_t len;
+
+ while ( ( data = va_arg ( args, void * ) ) ) {
+ len = va_arg ( args, size_t );
+ hmac_update ( digest, digest_ctx, data, len );
+ }
+}
+
+/**
+ * Generate secure pseudo-random data using a single hash function
+ *
+ * @v tls TLS session
+ * @v digest Hash function to use
+ * @v secret Secret
+ * @v secret_len Length of secret
+ * @v out Output buffer
+ * @v out_len Length of output buffer
+ * @v seeds ( data, len ) pairs of seed data, terminated by NULL
+ */
+static void tls_p_hash_va ( struct tls_session *tls,
+ struct crypto_algorithm *digest,
+ void *secret, size_t secret_len,
+ void *out, size_t out_len,
+ va_list seeds ) {
+ uint8_t secret_copy[secret_len];
+ uint8_t digest_ctx[digest->ctxsize];
+ uint8_t digest_ctx_partial[digest->ctxsize];
+ uint8_t a[digest->digestsize];
+ uint8_t out_tmp[digest->digestsize];
+ size_t frag_len = digest->digestsize;
+ va_list tmp;
+
+ /* Copy the secret, in case HMAC modifies it */
+ memcpy ( secret_copy, secret, secret_len );
+ secret = secret_copy;
+ DBGC2 ( tls, "TLS %p %s secret:\n", tls, digest->name );
+ DBGC2_HD ( tls, secret, secret_len );
+
+ /* Calculate A(1) */
+ hmac_init ( digest, digest_ctx, secret, &secret_len );
+ va_copy ( tmp, seeds );
+ tls_hmac_update_va ( digest, digest_ctx, tmp );
+ va_end ( tmp );
+ hmac_final ( digest, digest_ctx, secret, &secret_len, a );
+ DBGC2 ( tls, "TLS %p %s A(1):\n", tls, digest->name );
+ DBGC2_HD ( tls, &a, sizeof ( a ) );
+
+ /* Generate as much data as required */
+ while ( out_len ) {
+ /* Calculate output portion */
+ hmac_init ( digest, digest_ctx, secret, &secret_len );
+ hmac_update ( digest, digest_ctx, a, sizeof ( a ) );
+ memcpy ( digest_ctx_partial, digest_ctx, digest->ctxsize );
+ va_copy ( tmp, seeds );
+ tls_hmac_update_va ( digest, digest_ctx, tmp );
+ va_end ( tmp );
+ hmac_final ( digest, digest_ctx,
+ secret, &secret_len, out_tmp );
+
+ /* Copy output */
+ if ( frag_len > out_len )
+ frag_len = out_len;
+ memcpy ( out, out_tmp, frag_len );
+ DBGC2 ( tls, "TLS %p %s output:\n", tls, digest->name );
+ DBGC2_HD ( tls, out, frag_len );
+
+ /* Calculate A(i) */
+ hmac_final ( digest, digest_ctx_partial,
+ secret, &secret_len, a );
+ DBGC2 ( tls, "TLS %p %s A(n):\n", tls, digest->name );
+ DBGC2_HD ( tls, &a, sizeof ( a ) );
+
+ out += frag_len;
+ out_len -= frag_len;
+ }
+}
+
+/**
+ * Generate secure pseudo-random data
+ *
+ * @v tls TLS session
+ * @v secret Secret
+ * @v secret_len Length of secret
+ * @v out Output buffer
+ * @v out_len Length of output buffer
+ * @v ... ( data, len ) pairs of seed data, terminated by NULL
+ */
+static void tls_prf ( struct tls_session *tls, void *secret, size_t secret_len,
+ void *out, size_t out_len, ... ) {
+ va_list seeds;
+ va_list tmp;
+ size_t subsecret_len;
+ void *md5_secret;
+ void *sha1_secret;
+ uint8_t out_md5[out_len];
+ uint8_t out_sha1[out_len];
+ unsigned int i;
+
+ va_start ( seeds, out_len );
+
+ /* Split secret into two, with an overlap of up to one byte */
+ subsecret_len = ( ( secret_len + 1 ) / 2 );
+ md5_secret = secret;
+ sha1_secret = ( secret + secret_len - subsecret_len );
+
+ /* Calculate MD5 portion */
+ va_copy ( tmp, seeds );
+ tls_p_hash_va ( tls, &md5_algorithm, md5_secret, subsecret_len,
+ out_md5, out_len, seeds );
+ va_end ( tmp );
+
+ /* Calculate SHA1 portion */
+ va_copy ( tmp, seeds );
+ tls_p_hash_va ( tls, &sha1_algorithm, sha1_secret, subsecret_len,
+ out_sha1, out_len, seeds );
+ va_end ( tmp );
+
+ /* XOR the two portions together into the final output buffer */
+ for ( i = 0 ; i < out_len ; i++ ) {
+ *( ( uint8_t * ) out + i ) = ( out_md5[i] ^ out_sha1[i] );
+ }
+
+ va_end ( seeds );
+}
+
+/**
+ * Generate secure pseudo-random data
+ *
+ * @v secret Secret
+ * @v secret_len Length of secret
+ * @v out Output buffer
+ * @v out_len Length of output buffer
+ * @v label String literal label
+ * @v ... ( data, len ) pairs of seed data
+ */
+#define tls_prf_label( tls, secret, secret_len, out, out_len, label, ... ) \
+ tls_prf ( (tls), (secret), (secret_len), (out), (out_len), \
+ label, ( sizeof ( label ) - 1 ), __VA_ARGS__, NULL )
+
+/******************************************************************************
+ *
+ * Secret management
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Generate master secret
+ *
+ * @v tls TLS session
+ *
+ * The pre-master secret and the client and server random values must
+ * already be known.
+ */
+static void tls_generate_master_secret ( struct tls_session *tls ) {
+ DBGC ( tls, "TLS %p pre-master-secret:\n", tls );
+ DBGC_HD ( tls, &tls->pre_master_secret,
+ sizeof ( tls->pre_master_secret ) );
+ DBGC ( tls, "TLS %p client random bytes:\n", tls );
+ DBGC_HD ( tls, &tls->client_random, sizeof ( tls->server_random ) );
+ DBGC ( tls, "TLS %p server random bytes:\n", tls );
+ DBGC_HD ( tls, &tls->server_random, sizeof ( tls->server_random ) );
+
+ tls_prf_label ( tls, tls->pre_master_secret,
+ sizeof ( tls->pre_master_secret ),
+ tls->master_secret, sizeof ( tls->master_secret ),
+ "master secret",
+ tls->client_random, sizeof ( tls->client_random ),
+ tls->server_random, sizeof ( tls->server_random ) );
+
+ DBGC ( tls, "TLS %p generated master secret:\n", tls );
+ DBGC_HD ( tls, &tls->master_secret, sizeof ( tls->master_secret ) );
+}
+
+/**
+ * Generate key material
+ *
+ * @v tls TLS session
+ *
+ * The master secret must already be known.
+ */
+static int tls_generate_keys ( struct tls_session *tls ) {
+ struct tls_cipherspec *tx_cipherspec = &tls->tx_cipherspec_pending;
+ struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending;
+ size_t hash_size = tx_cipherspec->digest->digestsize;
+ size_t key_size = tx_cipherspec->key_len;
+ size_t iv_size = tx_cipherspec->cipher->blocksize;
+ size_t total = ( 2 * ( hash_size + key_size + iv_size ) );
+ uint8_t key_block[total];
+ uint8_t *key;
+ int rc;
+
+ /* 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 ) );
+
+ /* Split key block into portions */
+ key = key_block;
+
+ /* TX MAC secret */
+ memcpy ( tx_cipherspec->mac_secret, key, hash_size );
+ DBGC ( tls, "TLS %p TX MAC secret:\n", tls );
+ DBGC_HD ( tls, key, hash_size );
+ key += hash_size;
+
+ /* RX MAC secret */
+ memcpy ( rx_cipherspec->mac_secret, key, hash_size );
+ DBGC ( tls, "TLS %p RX MAC secret:\n", tls );
+ DBGC_HD ( tls, key, hash_size );
+ key += hash_size;
+
+ /* TX key */
+ if ( ( rc = cipher_setkey ( tx_cipherspec->cipher,
+ tx_cipherspec->cipher_ctx,
+ key, key_size ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not set TX key: %s\n",
+ tls, strerror ( rc ) );
+ return rc;
+ }
+ DBGC ( tls, "TLS %p TX key:\n", tls );
+ DBGC_HD ( tls, key, key_size );
+ key += key_size;
+
+ /* RX key */
+ if ( ( rc = cipher_setkey ( rx_cipherspec->cipher,
+ rx_cipherspec->cipher_ctx,
+ key, key_size ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not set TX key: %s\n",
+ tls, strerror ( rc ) );
+ return rc;
+ }
+
+ /* FIXME: AES needs to be fixed to not require this */
+ AES_convert_key ( rx_cipherspec->cipher_ctx );
+
+ DBGC ( tls, "TLS %p RX key:\n", tls );
+ DBGC_HD ( tls, key, key_size );
+ key += key_size;
+
+ /* TX initialisation vector */
+ cipher_setiv ( tx_cipherspec->cipher, tx_cipherspec->cipher_ctx, key );
+ DBGC ( tls, "TLS %p TX IV:\n", tls );
+ DBGC_HD ( tls, key, iv_size );
+ key += iv_size;
+
+ /* RX initialisation vector */
+ cipher_setiv ( rx_cipherspec->cipher, rx_cipherspec->cipher_ctx, key );
+ DBGC ( tls, "TLS %p RX IV:\n", tls );
+ DBGC_HD ( tls, key, iv_size );
+ key += iv_size;
+
+ assert ( ( key_block + total ) == key );
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Cipher suite management
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Clear cipher suite
+ *
+ * @v cipherspec TLS cipher specification
+ */
+static void tls_clear_cipher ( struct tls_session *tls __unused,
+ struct tls_cipherspec *cipherspec ) {
+ free ( cipherspec->dynamic );
+ memset ( cipherspec, 0, sizeof ( cipherspec ) );
+ cipherspec->pubkey = &crypto_null;
+ cipherspec->cipher = &crypto_null;
+ cipherspec->digest = &crypto_null;
+}
+
+/**
+ * Set cipher suite
+ *
+ * @v tls TLS session
+ * @v cipherspec TLS cipher specification
+ * @v pubkey Public-key encryption elgorithm
+ * @v cipher Bulk encryption cipher algorithm
+ * @v digest MAC digest algorithm
+ * @v key_len Key length
+ * @ret rc Return status code
+ */
+static int tls_set_cipher ( struct tls_session *tls,
+ struct tls_cipherspec *cipherspec,
+ struct crypto_algorithm *pubkey,
+ struct crypto_algorithm *cipher,
+ struct crypto_algorithm *digest,
+ size_t key_len ) {
+ size_t total;
+ void *dynamic;
+
+ /* Clear out old cipher contents, if any */
+ tls_clear_cipher ( tls, cipherspec );
+
+ /* Allocate dynamic storage */
+ total = ( pubkey->ctxsize + 2 * cipher->ctxsize + digest->digestsize );
+ dynamic = malloc ( total );
+ if ( ! dynamic ) {
+ DBGC ( tls, "TLS %p could not allocate %zd bytes for crypto "
+ "context\n", tls, total );
+ return -ENOMEM;
+ }
+ memset ( dynamic, 0, total );
+
+ /* Assign storage */
+ cipherspec->dynamic = dynamic;
+ cipherspec->pubkey_ctx = dynamic; dynamic += pubkey->ctxsize;
+ cipherspec->cipher_ctx = dynamic; dynamic += cipher->ctxsize;
+ cipherspec->cipher_next_ctx = dynamic; dynamic += cipher->ctxsize;
+ cipherspec->mac_secret = dynamic; dynamic += digest->digestsize;
+ assert ( ( cipherspec->dynamic + total ) == dynamic );
+
+ /* Store parameters */
+ cipherspec->pubkey = pubkey;
+ cipherspec->cipher = cipher;
+ cipherspec->digest = digest;
+ cipherspec->key_len = key_len;
+
+ return 0;
+}
+
+/**
+ * Select next cipher suite
+ *
+ * @v tls TLS session
+ * @v cipher_suite Cipher suite specification
+ * @ret rc Return status code
+ */
+static int tls_select_cipher ( struct tls_session *tls,
+ unsigned int cipher_suite ) {
+ struct crypto_algorithm *pubkey = &crypto_null;
+ struct crypto_algorithm *cipher = &crypto_null;
+ struct crypto_algorithm *digest = &crypto_null;
+ size_t key_len = 0;
+ int rc;
+
+ switch ( cipher_suite ) {
+ case htons ( TLS_RSA_WITH_AES_128_CBC_SHA ):
+ key_len = ( 128 / 8 );
+ cipher = &aes_algorithm;
+ digest = &sha1_algorithm;
+ break;
+ case htons ( TLS_RSA_WITH_AES_256_CBC_SHA ):
+ key_len = ( 256 / 8 );
+ cipher = &aes_algorithm;
+ digest = &sha1_algorithm;
+ break;
+ default:
+ DBGC ( tls, "TLS %p does not support cipher %04x\n",
+ tls, ntohs ( cipher_suite ) );
+ return -ENOTSUP;
+ }
+
+ /* Set ciphers */
+ if ( ( rc = tls_set_cipher ( tls, &tls->tx_cipherspec_pending, pubkey,
+ cipher, digest, key_len ) ) != 0 )
+ return rc;
+ if ( ( rc = tls_set_cipher ( tls, &tls->rx_cipherspec_pending, pubkey,
+ cipher, digest, key_len ) ) != 0 )
+ return rc;
+
+ DBGC ( tls, "TLS %p selected %s-%s-%d-%s\n", tls,
+ pubkey->name, cipher->name, ( key_len * 8 ), digest->name );
+
+ return 0;
+}
+
+/**
+ * Activate next cipher suite
+ *
+ * @v tls TLS session
+ * @v pending Pending cipher specification
+ * @v active Active cipher specification to replace
+ * @ret rc Return status code
+ */
+static int tls_change_cipher ( struct tls_session *tls,
+ struct tls_cipherspec *pending,
+ struct tls_cipherspec *active ) {
+
+ /* Sanity check */
+ if ( /* FIXME (when pubkey is not hard-coded to RSA):
+ * ( pending->pubkey == &crypto_null ) || */
+ ( pending->cipher == &crypto_null ) ||
+ ( pending->digest == &crypto_null ) ) {
+ DBGC ( tls, "TLS %p refusing to use null cipher\n", tls );
+ return -ENOTSUP;
+ }
+
+ tls_clear_cipher ( tls, active );
+ memswap ( active, pending, sizeof ( *active ) );
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Handshake verification
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Add handshake record to verification hash
+ *
+ * @v tls TLS session
+ * @v data Handshake record
+ * @v len Length of handshake record
+ */
+static void tls_add_handshake ( struct tls_session *tls,
+ const void *data, size_t len ) {
+
+ digest_update ( &md5_algorithm, tls->handshake_md5_ctx, data, len );
+ digest_update ( &sha1_algorithm, tls->handshake_sha1_ctx, data, len );
+}
+
+/**
+ * Calculate handshake verification hash
+ *
+ * @v tls TLS session
+ * @v out Output buffer
+ *
+ * Calculates the MD5+SHA1 digest over all handshake messages seen so
+ * far.
+ */
+static void tls_verify_handshake ( struct tls_session *tls, void *out ) {
+ struct crypto_algorithm *md5 = &md5_algorithm;
+ struct crypto_algorithm *sha1 = &sha1_algorithm;
+ uint8_t md5_ctx[md5->ctxsize];
+ uint8_t sha1_ctx[sha1->ctxsize];
+ void *md5_digest = out;
+ void *sha1_digest = ( out + md5->digestsize );
+
+ memcpy ( md5_ctx, tls->handshake_md5_ctx, sizeof ( md5_ctx ) );
+ memcpy ( sha1_ctx, tls->handshake_sha1_ctx, sizeof ( sha1_ctx ) );
+ digest_final ( md5, md5_ctx, md5_digest );
+ digest_final ( sha1, sha1_ctx, sha1_digest );
+}
+
+/******************************************************************************
+ *
+ * Record handling
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Transmit Handshake record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_send_handshake ( struct tls_session *tls,
+ 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 );
+}
+
+/**
+ * Transmit Client Hello record
+ *
+ * @v tls TLS session
+ * @ret rc Return status code
+ */
+static int tls_send_client_hello ( struct tls_session *tls ) {
+ struct {
+ uint32_t type_length;
+ uint16_t version;
+ uint8_t random[32];
+ uint8_t session_id_len;
+ uint16_t cipher_suite_len;
+ uint16_t cipher_suites[2];
+ uint8_t compression_methods_len;
+ uint8_t compression_methods[1];
+ } __attribute__ (( packed )) hello;
+
+ memset ( &hello, 0, sizeof ( hello ) );
+ hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) |
+ htonl ( sizeof ( hello ) -
+ sizeof ( hello.type_length ) ) );
+ hello.version = htons ( TLS_VERSION_TLS_1_0 );
+ memcpy ( &hello.random, tls->client_random, sizeof ( hello.random ) );
+ hello.cipher_suite_len = htons ( sizeof ( hello.cipher_suites ) );
+ hello.cipher_suites[0] = htons ( TLS_RSA_WITH_AES_128_CBC_SHA );
+ hello.cipher_suites[1] = htons ( TLS_RSA_WITH_AES_256_CBC_SHA );
+ hello.compression_methods_len = sizeof ( hello.compression_methods );
+
+ return tls_send_handshake ( tls, &hello, sizeof ( hello ) );
+}
+
+/**
+ * Transmit Client Key Exchange record
+ *
+ * @v tls TLS session
+ * @ret rc Return status code
+ */
+static int tls_send_client_key_exchange ( struct tls_session *tls ) {
+ /* FIXME: Hack alert */
+ RSA_CTX *rsa_ctx;
+ RSA_pub_key_new ( &rsa_ctx, tls->rsa_mod, tls->rsa_mod_len,
+ tls->rsa_pub_exp, tls->rsa_pub_exp_len );
+ struct {
+ uint32_t type_length;
+ uint16_t encrypted_pre_master_secret_len;
+ uint8_t encrypted_pre_master_secret[rsa_ctx->num_octets];
+ } __attribute__ (( packed )) key_xchg;
+
+ memset ( &key_xchg, 0, sizeof ( key_xchg ) );
+ key_xchg.type_length = ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) |
+ htonl ( sizeof ( key_xchg ) -
+ sizeof ( key_xchg.type_length ) ) );
+ key_xchg.encrypted_pre_master_secret_len
+ = htons ( sizeof ( key_xchg.encrypted_pre_master_secret ) );
+
+ /* FIXME: Hack alert */
+ DBGC ( tls, "RSA encrypting plaintext, modulus, exponent:\n" );
+ DBGC_HD ( tls, &tls->pre_master_secret,
+ sizeof ( tls->pre_master_secret ) );
+ DBGC_HD ( tls, tls->rsa_mod, tls->rsa_mod_len );
+ DBGC_HD ( tls, tls->rsa_pub_exp, tls->rsa_pub_exp_len );
+ RSA_encrypt ( rsa_ctx, tls->pre_master_secret,
+ sizeof ( tls->pre_master_secret ),
+ key_xchg.encrypted_pre_master_secret, 0 );
+ DBGC ( tls, "RSA encrypt done. Ciphertext:\n" );
+ DBGC_HD ( tls, &key_xchg.encrypted_pre_master_secret,
+ sizeof ( key_xchg.encrypted_pre_master_secret ) );
+ RSA_free ( rsa_ctx );
+
+
+ return tls_send_handshake ( tls, &key_xchg, sizeof ( key_xchg ) );
+}
+
+/**
+ * Transmit Change Cipher record
+ *
+ * @v tls TLS session
+ * @ret rc Return status code
+ */
+static int tls_send_change_cipher ( struct tls_session *tls ) {
+ static const uint8_t change_cipher[1] = { 1 };
+ return tls_send_plaintext ( tls, TLS_TYPE_CHANGE_CIPHER,
+ change_cipher, sizeof ( change_cipher ) );
+}
+
+/**
+ * Transmit Finished record
+ *
+ * @v tls TLS session
+ * @ret rc Return status code
+ */
+static int tls_send_finished ( struct tls_session *tls ) {
+ struct {
+ uint32_t type_length;
+ uint8_t verify_data[12];
+ } __attribute__ (( packed )) finished;
+ uint8_t digest[MD5_DIGEST_SIZE + SHA1_DIGEST_SIZE];
+
+ memset ( &finished, 0, sizeof ( finished ) );
+ finished.type_length = ( cpu_to_le32 ( TLS_FINISHED ) |
+ htonl ( sizeof ( finished ) -
+ sizeof ( finished.type_length ) ) );
+ tls_verify_handshake ( tls, digest );
+ tls_prf_label ( tls, tls->master_secret, sizeof ( tls->master_secret ),
+ finished.verify_data, sizeof ( finished.verify_data ),
+ "client finished", digest, sizeof ( digest ) );
+
+ return tls_send_handshake ( tls, &finished, sizeof ( finished ) );
+}
+
+/**
+ * Receive new Change Cipher record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_change_cipher ( struct tls_session *tls,
+ void *data, size_t len ) {
+ int rc;
+
+ if ( ( len != 1 ) || ( *( ( uint8_t * ) data ) != 1 ) ) {
+ DBGC ( tls, "TLS %p received invalid Change Cipher\n", tls );
+ DBGC_HD ( tls, data, len );
+ return -EINVAL;
+ }
+
+ if ( ( rc = tls_change_cipher ( tls, &tls->rx_cipherspec_pending,
+ &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 );
+
+ return 0;
+}
+
+/**
+ * Receive new Alert record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_alert ( struct tls_session *tls, void *data, size_t len ) {
+ struct {
+ uint8_t level;
+ uint8_t description;
+ char next[0];
+ } __attribute__ (( packed )) *alert = data;
+ void *end = alert->next;
+
+ /* Sanity check */
+ if ( end != ( data + len ) ) {
+ DBGC ( tls, "TLS %p received overlength Alert\n", tls );
+ DBGC_HD ( tls, data, len );
+ return -EINVAL;
+ }
+
+ switch ( alert->level ) {
+ case TLS_ALERT_WARNING:
+ DBGC ( tls, "TLS %p received warning alert %d\n",
+ tls, alert->description );
+ return 0;
+ case TLS_ALERT_FATAL:
+ DBGC ( tls, "TLS %p received fatal alert %d\n",
+ tls, alert->description );
+ return -EPERM;
+ default:
+ DBGC ( tls, "TLS %p received unknown alert level %d"
+ "(alert %d)\n", tls, alert->level, alert->description );
+ return -EIO;
+ }
+}
+
+/**
+ * Receive new Server Hello record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_server_hello ( struct tls_session *tls,
+ void *data, size_t len ) {
+ struct {
+ uint32_t type_length;
+ uint16_t version;
+ uint8_t random[32];
+ uint8_t session_id_len;
+ char next[0];
+ } __attribute__ (( packed )) *hello_a = data;
+ struct {
+ uint8_t session_id[hello_a->session_id_len];
+ uint16_t cipher_suite;
+ uint8_t compression_method;
+ char next[0];
+ } __attribute__ (( packed )) *hello_b = ( void * ) &hello_a->next;
+ void *end = hello_b->next;
+ int rc;
+
+ /* Sanity check */
+ if ( end != ( data + len ) ) {
+ DBGC ( tls, "TLS %p received overlength Server Hello\n", tls );
+ DBGC_HD ( tls, data, len );
+ return -EINVAL;
+ }
+
+ /* Check protocol version */
+ if ( ntohs ( hello_a->version ) < TLS_VERSION_TLS_1_0 ) {
+ DBGC ( tls, "TLS %p does not support protocol version %d.%d\n",
+ tls, ( ntohs ( hello_a->version ) >> 8 ),
+ ( ntohs ( hello_a->version ) & 0xff ) );
+ return -ENOTSUP;
+ }
+
+ /* Copy out server random bytes */
+ memcpy ( tls->server_random, hello_a->random,
+ sizeof ( tls->server_random ) );
+
+ /* Select cipher suite */
+ if ( ( rc = tls_select_cipher ( tls, hello_b->cipher_suite ) ) != 0 )
+ return rc;
+
+ /* Generate secrets */
+ tls_generate_master_secret ( tls );
+ if ( ( rc = tls_generate_keys ( tls ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Receive new Certificate record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_certificate ( struct tls_session *tls,
+ void *data, size_t len ) {
+ struct {
+ uint32_t type_length;
+ uint8_t length[3];
+ uint8_t first_cert_length[3];
+ uint8_t asn1_start[0];
+ } __attribute__ (( packed )) *certificate = data;
+ uint8_t *cert = certificate->asn1_start;
+ int offset = 0;
+
+ /* FIXME */
+ (void) len;
+
+ if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 ||
+ asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 ||
+ asn1_skip_obj(cert, &offset, ASN1_EXPLICIT_TAG) ||
+ asn1_skip_obj(cert, &offset, ASN1_INTEGER) ||
+ asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
+ asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
+ asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
+ asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
+ asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 ||
+ asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
+ asn1_next_obj(cert, &offset, ASN1_BIT_STRING) < 0) {
+ DBGC ( tls, "TLS %p invalid certificate\n", tls );
+ DBGC_HD ( tls, cert + offset, 64 );
+ return -EPERM;
+ }
+
+ offset++;
+
+ if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) {
+ DBGC ( tls, "TLS %p invalid certificate\n", tls );
+ DBGC_HD ( tls, cert + offset, 64 );
+ return -EPERM;
+ }
+
+ tls->rsa_mod_len = asn1_get_int(cert, &offset, &tls->rsa_mod);
+ tls->rsa_pub_exp_len = asn1_get_int(cert, &offset, &tls->rsa_pub_exp);
+
+ DBGC_HD ( tls, tls->rsa_mod, tls->rsa_mod_len );
+ DBGC_HD ( tls, tls->rsa_pub_exp, tls->rsa_pub_exp_len );
+
+ return 0;
+}
+
+/**
+ * Receive new Server Hello Done record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_server_hello_done ( struct tls_session *tls,
+ void *data, size_t len ) {
+ struct {
+ uint32_t type_length;
+ char next[0];
+ } __attribute__ (( packed )) *hello_done = data;
+ void *end = hello_done->next;
+
+ /* Sanity check */
+ if ( end != ( data + len ) ) {
+ DBGC ( tls, "TLS %p received overlength Server Hello Done\n",
+ tls );
+ DBGC_HD ( tls, data, len );
+ return -EINVAL;
+ }
+
+ /* Check that we are ready to send the Client Key Exchange */
+ if ( tls->tx_state != TLS_TX_NONE ) {
+ DBGC ( tls, "TLS %p received Server Hello Done while in "
+ "TX state %d\n", tls, tls->tx_state );
+ return -EIO;
+ }
+
+ /* Start sending the Client Key Exchange */
+ tls->tx_state = TLS_TX_CLIENT_KEY_EXCHANGE;
+
+ return 0;
+}
+
+/**
+ * Receive new Finished record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_finished ( struct tls_session *tls,
+ void *data, size_t len ) {
+
+ /* FIXME: Handle this properly */
+ tls->tx_state = TLS_TX_DATA;
+ ( void ) data;
+ ( void ) len;
+ return 0;
+}
+
+/**
+ * Receive new Handshake record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_handshake ( struct tls_session *tls,
+ void *data, size_t len ) {
+ uint8_t *type = data;
+ int rc;
+
+ switch ( *type ) {
+ case TLS_SERVER_HELLO:
+ rc = tls_new_server_hello ( tls, data, len );
+ break;
+ case TLS_CERTIFICATE:
+ rc = tls_new_certificate ( tls, data, len );
+ break;
+ case TLS_SERVER_HELLO_DONE:
+ rc = tls_new_server_hello_done ( tls, data, len );
+ break;
+ case TLS_FINISHED:
+ rc = tls_new_finished ( tls, data, len );
+ break;
+ default:
+ DBGC ( tls, "TLS %p ignoring handshake type %d\n",
+ tls, *type );
+ rc = 0;
+ break;
+ }
+
+ /* Add to handshake digest (except for Hello Requests, which
+ * are explicitly excludede).
+ */
+ if ( *type != TLS_HELLO_REQUEST )
+ tls_add_handshake ( tls, data, len );
+
+ return rc;
+}
+
+/**
+ * Receive new record
+ *
+ * @v tls TLS session
+ * @v type Record type
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_record ( struct tls_session *tls,
+ unsigned int type, void *data, size_t len ) {
+
+ switch ( type ) {
+ case TLS_TYPE_CHANGE_CIPHER:
+ return tls_new_change_cipher ( tls, data, len );
+ case TLS_TYPE_ALERT:
+ return tls_new_alert ( tls, data, len );
+ case TLS_TYPE_HANDSHAKE:
+ return tls_new_handshake ( tls, data, len );
+ case TLS_TYPE_DATA:
+ return xfer_deliver_raw ( &tls->plainstream.xfer, data, len );
+ default:
+ /* RFC4346 says that we should just ignore unknown
+ * record types.
+ */
+ DBGC ( tls, "TLS %p ignoring record type %d\n", tls, type );
+ return 0;
+ }
+}
+
+/******************************************************************************
+ *
+ * Record encryption/decryption
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Calculate HMAC
+ *
+ * @v tls TLS session
+ * @v cipherspec Cipher specification
+ * @v seq Sequence number
+ * @v tlshdr TLS header
+ * @v data Data
+ * @v len Length of data
+ * @v mac HMAC to fill in
+ */
+static void tls_hmac ( struct tls_session *tls __unused,
+ struct tls_cipherspec *cipherspec,
+ uint64_t seq, struct tls_header *tlshdr,
+ const void *data, size_t len, void *hmac ) {
+ struct crypto_algorithm *digest = cipherspec->digest;
+ uint8_t digest_ctx[digest->ctxsize];
+
+ hmac_init ( digest, digest_ctx, cipherspec->mac_secret,
+ &digest->digestsize );
+ seq = cpu_to_be64 ( seq );
+ hmac_update ( digest, digest_ctx, &seq, sizeof ( seq ) );
+ hmac_update ( digest, digest_ctx, tlshdr, sizeof ( *tlshdr ) );
+ hmac_update ( digest, digest_ctx, data, len );
+ hmac_final ( digest, digest_ctx, cipherspec->mac_secret,
+ &digest->digestsize, hmac );
+}
+
+/**
+ * Allocate and assemble stream-ciphered record from data and MAC portions
+ *
+ * @v tls TLS session
+ * @ret data Data
+ * @ret len Length of data
+ * @ret digest MAC digest
+ * @ret plaintext_len Length of plaintext record
+ * @ret plaintext Allocated plaintext record
+ */
+static void * tls_assemble_stream ( struct tls_session *tls,
+ const void *data, size_t len,
+ void *digest, size_t *plaintext_len ) {
+ size_t mac_len = tls->tx_cipherspec.digest->digestsize;
+ void *plaintext;
+ void *content;
+ void *mac;
+
+ /* Calculate stream-ciphered struct length */
+ *plaintext_len = ( len + mac_len );
+
+ /* Allocate stream-ciphered struct */
+ plaintext = malloc ( *plaintext_len );
+ if ( ! plaintext )
+ return NULL;
+ content = plaintext;
+ mac = ( content + len );
+
+ /* Fill in stream-ciphered struct */
+ memcpy ( content, data, len );
+ memcpy ( mac, digest, mac_len );
+
+ return plaintext;
+}
+
+/**
+ * Allocate and assemble block-ciphered record from data and MAC portions
+ *
+ * @v tls TLS session
+ * @ret data Data
+ * @ret len Length of data
+ * @ret digest MAC digest
+ * @ret plaintext_len Length of plaintext record
+ * @ret plaintext Allocated plaintext record
+ */
+static void * tls_assemble_block ( struct tls_session *tls,
+ const void *data, size_t len,
+ void *digest, size_t *plaintext_len ) {
+ size_t blocksize = tls->tx_cipherspec.cipher->blocksize;
+ size_t iv_len = blocksize;
+ size_t mac_len = tls->tx_cipherspec.digest->digestsize;
+ size_t padding_len;
+ void *plaintext;
+ void *iv;
+ void *content;
+ void *mac;
+ void *padding;
+
+ /* FIXME: TLSv1.1 has an explicit IV */
+ iv_len = 0;
+
+ /* Calculate block-ciphered struct length */
+ padding_len = ( ( blocksize - 1 ) & -( iv_len + len + mac_len + 1 ) );
+ *plaintext_len = ( iv_len + len + mac_len + padding_len + 1 );
+
+ /* Allocate block-ciphered struct */
+ plaintext = malloc ( *plaintext_len );
+ if ( ! plaintext )
+ return NULL;
+ iv = plaintext;
+ content = ( iv + iv_len );
+ mac = ( content + len );
+ padding = ( mac + mac_len );
+
+ /* Fill in block-ciphered struct */
+ memset ( iv, 0, iv_len );
+ memcpy ( content, data, len );
+ memcpy ( mac, digest, mac_len );
+ memset ( padding, padding_len, ( padding_len + 1 ) );
+
+ return plaintext;
+}
+
+/**
+ * Send plaintext record
+ *
+ * @v tls TLS session
+ * @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_session *tls, unsigned int type,
+ const void *data, size_t len ) {
+ struct tls_header plaintext_tlshdr;
+ struct tls_header *tlshdr;
+ struct tls_cipherspec *cipherspec = &tls->tx_cipherspec;
+ void *plaintext = NULL;
+ size_t plaintext_len;
+ struct io_buffer *ciphertext = NULL;
+ size_t ciphertext_len;
+ size_t mac_len = cipherspec->digest->digestsize;
+ uint8_t mac[mac_len];
+ int rc;
+
+ /* Construct header */
+ plaintext_tlshdr.type = type;
+ plaintext_tlshdr.version = htons ( TLS_VERSION_TLS_1_0 );
+ plaintext_tlshdr.length = htons ( len );
+
+ /* Calculate MAC */
+ tls_hmac ( tls, cipherspec, tls->tx_seq, &plaintext_tlshdr,
+ data, len, mac );
+
+ /* Allocate and assemble plaintext struct */
+ if ( is_stream_cipher ( cipherspec->cipher ) ) {
+ plaintext = tls_assemble_stream ( tls, data, len, mac,
+ &plaintext_len );
+ } else {
+ plaintext = tls_assemble_block ( tls, data, len, mac,
+ &plaintext_len );
+ }
+ if ( ! plaintext ) {
+ DBGC ( tls, "TLS %p could not allocate %zd bytes for "
+ "plaintext\n", tls, plaintext_len );
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ DBGC2 ( tls, "Sending plaintext data:\n" );
+ DBGC2_HD ( tls, plaintext, plaintext_len );
+
+ /* Allocate ciphertext */
+ ciphertext_len = ( sizeof ( *tlshdr ) + plaintext_len );
+ ciphertext = xfer_alloc_iob ( &tls->cipherstream.xfer,
+ ciphertext_len );
+ if ( ! ciphertext ) {
+ DBGC ( tls, "TLS %p could not allocate %zd bytes for "
+ "ciphertext\n", tls, ciphertext_len );
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ /* Assemble ciphertext */
+ tlshdr = iob_put ( ciphertext, sizeof ( *tlshdr ) );
+ tlshdr->type = type;
+ tlshdr->version = htons ( TLS_VERSION_TLS_1_0 );
+ tlshdr->length = htons ( plaintext_len );
+ memcpy ( cipherspec->cipher_next_ctx, cipherspec->cipher_ctx,
+ cipherspec->cipher->ctxsize );
+ if ( ( rc = cipher_encrypt ( cipherspec->cipher,
+ cipherspec->cipher_next_ctx, plaintext,
+ iob_put ( ciphertext, plaintext_len ),
+ plaintext_len ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not encrypt: %s\n",
+ tls, strerror ( rc ) );
+ DBGC_HD ( tls, plaintext, plaintext_len );
+ goto done;
+ }
+
+ /* Free plaintext as soon as possible to conserve memory */
+ free ( plaintext );
+ plaintext = NULL;
+
+ /* Send ciphertext */
+ rc = xfer_deliver_iob ( &tls->cipherstream.xfer, ciphertext );
+ ciphertext = NULL;
+ if ( rc != 0 ) {
+ DBGC ( tls, "TLS %p could not deliver ciphertext: %s\n",
+ tls, strerror ( rc ) );
+ goto done;
+ }
+
+ /* Update TX state machine to next record */
+ tls->tx_seq += 1;
+ memcpy ( tls->tx_cipherspec.cipher_ctx,
+ tls->tx_cipherspec.cipher_next_ctx,
+ tls->tx_cipherspec.cipher->ctxsize );
+
+ done:
+ free ( plaintext );
+ free_iob ( ciphertext );
+ return rc;
+}
+
+/**
+ * Split stream-ciphered record into data and MAC portions
+ *
+ * @v tls TLS session
+ * @v plaintext Plaintext record
+ * @v plaintext_len Length of record
+ * @ret data Data
+ * @ret len Length of data
+ * @ret digest MAC digest
+ * @ret rc Return status code
+ */
+static int tls_split_stream ( struct tls_session *tls,
+ void *plaintext, size_t plaintext_len,
+ void **data, size_t *len, void **digest ) {
+ void *content;
+ size_t content_len;
+ void *mac;
+ size_t mac_len;
+
+ /* Decompose stream-ciphered data */
+ mac_len = tls->rx_cipherspec.digest->digestsize;
+ if ( plaintext_len < mac_len ) {
+ DBGC ( tls, "TLS %p received underlength record\n", tls );
+ DBGC_HD ( tls, plaintext, plaintext_len );
+ return -EINVAL;
+ }
+ content_len = ( plaintext_len - mac_len );
+ content = plaintext;
+ mac = ( content + content_len );
+
+ /* Fill in return values */
+ *data = content;
+ *len = content_len;
+ *digest = mac;
+
+ return 0;
+}
+
+/**
+ * Split block-ciphered record into data and MAC portions
+ *
+ * @v tls TLS session
+ * @v plaintext Plaintext record
+ * @v plaintext_len Length of record
+ * @ret data Data
+ * @ret len Length of data
+ * @ret digest MAC digest
+ * @ret rc Return status code
+ */
+static int tls_split_block ( struct tls_session *tls,
+ void *plaintext, size_t plaintext_len,
+ void **data, size_t *len,
+ void **digest ) {
+ void *iv;
+ size_t iv_len;
+ void *content;
+ size_t content_len;
+ void *mac;
+ size_t mac_len;
+ void *padding;
+ size_t padding_len;
+ unsigned int i;
+
+ /* Decompose block-ciphered data */
+ if ( plaintext_len < 1 ) {
+ DBGC ( tls, "TLS %p received underlength record\n", tls );
+ DBGC_HD ( tls, plaintext, plaintext_len );
+ return -EINVAL;
+ }
+ iv_len = tls->rx_cipherspec.cipher->blocksize;
+
+ /* FIXME: TLSv1.1 uses an explicit IV */
+ iv_len = 0;
+
+ mac_len = tls->rx_cipherspec.digest->digestsize;
+ padding_len = *( ( uint8_t * ) ( plaintext + plaintext_len - 1 ) );
+ if ( plaintext_len < ( iv_len + mac_len + padding_len + 1 ) ) {
+ DBGC ( tls, "TLS %p received underlength record\n", tls );
+ DBGC_HD ( tls, plaintext, plaintext_len );
+ return -EINVAL;
+ }
+ content_len = ( plaintext_len - iv_len - mac_len - padding_len - 1 );
+ iv = plaintext;
+ content = ( iv + iv_len );
+ mac = ( content + content_len );
+ padding = ( mac + mac_len );
+
+ /* Verify padding bytes */
+ for ( i = 0 ; i < padding_len ; i++ ) {
+ if ( *( ( uint8_t * ) ( padding + i ) ) != padding_len ) {
+ DBGC ( tls, "TLS %p received bad padding\n", tls );
+ DBGC_HD ( tls, plaintext, plaintext_len );
+ return -EINVAL;
+ }
+ }
+
+ /* Fill in return values */
+ *data = content;
+ *len = content_len;
+ *digest = mac;
+
+ return 0;
+}
+
+/**
+ * Receive new ciphertext record
+ *
+ * @v tls TLS session
+ * @v tlshdr Record header
+ * @v ciphertext Ciphertext record
+ * @ret rc Return status code
+ */
+static int tls_new_ciphertext ( struct tls_session *tls,
+ struct tls_header *tlshdr, void *ciphertext ) {
+ struct tls_header plaintext_tlshdr;
+ struct tls_cipherspec *cipherspec = &tls->rx_cipherspec;
+ size_t record_len = ntohs ( tlshdr->length );
+ void *plaintext = NULL;
+ void *data;
+ size_t len;
+ void *mac;
+ size_t mac_len = cipherspec->digest->digestsize;
+ uint8_t verify_mac[mac_len];
+ int rc;
+
+ /* Allocate buffer for plaintext */
+ plaintext = malloc ( record_len );
+ if ( ! plaintext ) {
+ DBGC ( tls, "TLS %p could not allocate %zd bytes for "
+ "decryption buffer\n", tls, record_len );
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ /* Decrypt the record */
+ if ( ( rc = cipher_decrypt ( cipherspec->cipher,
+ cipherspec->cipher_ctx, ciphertext,
+ plaintext, record_len ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not decrypt: %s\n",
+ tls, strerror ( rc ) );
+ DBGC_HD ( tls, ciphertext, record_len );
+ goto done;
+ }
+
+ /* Split record into content and MAC */
+ if ( is_stream_cipher ( cipherspec->cipher ) ) {
+ if ( ( rc = tls_split_stream ( tls, plaintext, record_len,
+ &data, &len, &mac ) ) != 0 )
+ goto done;
+ } else {
+ if ( ( rc = tls_split_block ( tls, plaintext, record_len,
+ &data, &len, &mac ) ) != 0 )
+ goto done;
+ }
+
+ /* Verify MAC */
+ plaintext_tlshdr.type = tlshdr->type;
+ plaintext_tlshdr.version = tlshdr->version;
+ plaintext_tlshdr.length = htons ( len );
+ tls_hmac ( tls, cipherspec, tls->rx_seq, &plaintext_tlshdr,
+ data, len, verify_mac);
+ if ( memcmp ( mac, verify_mac, mac_len ) != 0 ) {
+ DBGC ( tls, "TLS %p failed MAC verification\n", tls );
+ DBGC_HD ( tls, plaintext, record_len );
+ goto done;
+ }
+
+ DBGC2 ( tls, "Received plaintext data:\n" );
+ DBGC2_HD ( tls, data, len );
+
+ /* Process plaintext record */
+ if ( ( rc = tls_new_record ( tls, tlshdr->type, data, len ) ) != 0 )
+ goto done;
+
+ rc = 0;
+ done:
+ free ( plaintext );
+ return rc;
+}
+
+/******************************************************************************
+ *
+ * Plaintext stream operations
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Close interface
+ *
+ * @v xfer Plainstream data transfer interface
+ * @v rc Reason for close
+ */
+static void tls_plainstream_close ( struct xfer_interface *xfer, int rc ) {
+ struct tls_session *tls =
+ container_of ( xfer, struct tls_session, plainstream.xfer );
+
+ tls_close ( tls, rc );
+}
+
+/**
+ * Check flow control window
+ *
+ * @v xfer Plainstream data transfer interface
+ * @ret len Length of window
+ */
+static size_t tls_plainstream_window ( struct xfer_interface *xfer ) {
+ struct tls_session *tls =
+ container_of ( xfer, struct tls_session, plainstream.xfer );
+
+ /* Block window unless we are ready to accept data */
+ if ( tls->tx_state != TLS_TX_DATA )
+ return 0;
+
+ return filter_window ( xfer );
+}
+
+/**
+ * Deliver datagram as raw data
+ *
+ * @v xfer Plainstream data transfer interface
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+static int tls_plainstream_deliver_raw ( struct xfer_interface *xfer,
+ const void *data, size_t len ) {
+ struct tls_session *tls =
+ container_of ( xfer, struct tls_session, plainstream.xfer );
+
+ /* Refuse unless we are ready to accept data */
+ if ( tls->tx_state != TLS_TX_DATA )
+ return -ENOTCONN;
+
+ return tls_send_plaintext ( tls, TLS_TYPE_DATA, data, len );
+}
+
+/** TLS plaintext stream operations */
+static struct xfer_interface_operations tls_plainstream_operations = {
+ .close = tls_plainstream_close,
+ .vredirect = ignore_xfer_vredirect,
+ .seek = filter_seek,
+ .window = tls_plainstream_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = tls_plainstream_deliver_raw,
+};
+
+/******************************************************************************
+ *
+ * Ciphertext stream operations
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Close interface
+ *
+ * @v xfer Plainstream data transfer interface
+ * @v rc Reason for close
+ */
+static void tls_cipherstream_close ( struct xfer_interface *xfer, int rc ) {
+ struct tls_session *tls =
+ container_of ( xfer, struct tls_session, cipherstream.xfer );
+
+ tls_close ( tls, rc );
+}
+
+/**
+ * Handle received TLS header
+ *
+ * @v tls TLS session
+ * @ret rc Returned status code
+ */
+static int tls_newdata_process_header ( struct tls_session *tls ) {
+ size_t data_len = ntohs ( tls->rx_header.length );
+
+ /* Allocate data buffer now that we know the length */
+ assert ( tls->rx_data == NULL );
+ tls->rx_data = malloc ( data_len );
+ if ( ! tls->rx_data ) {
+ DBGC ( tls, "TLS %p could not allocate %zd bytes "
+ "for receive buffer\n", tls, data_len );
+ return -ENOMEM;
+ }
+
+ /* Move to data state */
+ tls->rx_state = TLS_RX_DATA;
+
+ return 0;
+}
+
+/**
+ * Handle received TLS data payload
+ *
+ * @v tls TLS session
+ * @ret rc Returned status code
+ */
+static int tls_newdata_process_data ( struct tls_session *tls ) {
+ int rc;
+
+ /* Process record */
+ if ( ( rc = tls_new_ciphertext ( tls, &tls->rx_header,
+ tls->rx_data ) ) != 0 )
+ return rc;
+
+ /* Increment RX sequence number */
+ tls->rx_seq += 1;
+
+ /* Free data buffer */
+ free ( tls->rx_data );
+ tls->rx_data = NULL;
+
+ /* Return to header state */
+ tls->rx_state = TLS_RX_HEADER;
+
+ return 0;
+}
+
+/**
+ * Receive new ciphertext
+ *
+ * @v app Stream application
+ * @v data Data received
+ * @v len Length of received data
+ * @ret rc Return status code
+ */
+static int tls_cipherstream_deliver_raw ( struct xfer_interface *xfer,
+ const void *data, size_t len ) {
+ struct tls_session *tls =
+ container_of ( xfer, struct tls_session, cipherstream.xfer );
+ size_t frag_len;
+ void *buf;
+ size_t buf_len;
+ int ( * process ) ( struct tls_session *tls );
+ int rc;
+
+ while ( len ) {
+ /* Select buffer according to current state */
+ switch ( tls->rx_state ) {
+ case TLS_RX_HEADER:
+ buf = &tls->rx_header;
+ buf_len = sizeof ( tls->rx_header );
+ process = tls_newdata_process_header;
+ break;
+ case TLS_RX_DATA:
+ buf = tls->rx_data;
+ buf_len = ntohs ( tls->rx_header.length );
+ process = tls_newdata_process_data;
+ break;
+ default:
+ assert ( 0 );
+ return -EINVAL;
+ }
+
+ /* Copy data portion to buffer */
+ frag_len = ( buf_len - tls->rx_rcvd );
+ if ( frag_len > len )
+ frag_len = len;
+ memcpy ( ( buf + tls->rx_rcvd ), data, frag_len );
+ tls->rx_rcvd += frag_len;
+ data += frag_len;
+ len -= frag_len;
+
+ /* Process data if buffer is now full */
+ if ( tls->rx_rcvd == buf_len ) {
+ if ( ( rc = process ( tls ) ) != 0 ) {
+ tls_close ( tls, rc );
+ return rc;
+ }
+ tls->rx_rcvd = 0;
+ }
+ }
+
+ return 0;
+}
+
+/** TLS ciphertext stream operations */
+static struct xfer_interface_operations tls_cipherstream_operations = {
+ .close = tls_cipherstream_close,
+ .vredirect = xfer_vopen,
+ .seek = filter_seek,
+ .window = filter_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = tls_cipherstream_deliver_raw,
+};
+
+/******************************************************************************
+ *
+ * Controlling process
+ *
+ ******************************************************************************
+ */
+
+/**
+ * TLS TX state machine
+ *
+ * @v process TLS process
+ */
+static void tls_step ( struct process *process ) {
+ struct tls_session *tls =
+ container_of ( process, struct tls_session, process );
+ int rc;
+
+ /* Wait for cipherstream to become ready */
+ if ( ! xfer_window ( &tls->cipherstream.xfer ) )
+ return;
+
+ switch ( tls->tx_state ) {
+ case TLS_TX_NONE:
+ /* Nothing to do */
+ break;
+ case TLS_TX_CLIENT_HELLO:
+ /* Send Client Hello */
+ if ( ( rc = tls_send_client_hello ( tls ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not send Client Hello: %s\n",
+ tls, strerror ( rc ) );
+ goto err;
+ }
+ tls->tx_state = TLS_TX_NONE;
+ break;
+ case TLS_TX_CLIENT_KEY_EXCHANGE:
+ /* Send Client Key Exchange */
+ if ( ( rc = tls_send_client_key_exchange ( tls ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could send Client Key Exchange: "
+ "%s\n", tls, strerror ( rc ) );
+ goto err;
+ }
+ tls->tx_state = TLS_TX_CHANGE_CIPHER;
+ break;
+ case 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: "
+ "%s\n", tls, strerror ( rc ) );
+ goto err;
+ }
+ if ( ( rc = tls_change_cipher ( tls,
+ &tls->tx_cipherspec_pending,
+ &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_state = TLS_TX_FINISHED;
+ break;
+ case 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_state = TLS_TX_NONE;
+ break;
+ case TLS_TX_DATA:
+ /* Nothing to do */
+ break;
+ default:
+ assert ( 0 );
+ }
+
+ return;
+
+ err:
+ tls_close ( tls, rc );
+}
+
+/******************************************************************************
+ *
+ * Instantiator
+ *
+ ******************************************************************************
+ */
+
+int add_tls ( struct xfer_interface *xfer, struct xfer_interface **next ) {
+ struct tls_session *tls;
+
+ /* Allocate and initialise TLS structure */
+ tls = malloc ( sizeof ( *tls ) );
+ if ( ! tls )
+ return -ENOMEM;
+ memset ( tls, 0, sizeof ( *tls ) );
+ tls->refcnt.free = free_tls;
+ filter_init ( &tls->plainstream, &tls_plainstream_operations,
+ &tls->cipherstream, &tls_cipherstream_operations,
+ &tls->refcnt );
+ 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 );
+ *( ( uint32_t * ) tls->client_random ) = 0; /* GMT Unix time */
+ tls_generate_random ( ( tls->client_random + 4 ),
+ ( sizeof ( tls->client_random ) - 4 ) );
+ *( ( uint16_t * ) tls->pre_master_secret )
+ = htons ( TLS_VERSION_TLS_1_0 );
+ tls_generate_random ( ( tls->pre_master_secret + 2 ),
+ ( sizeof ( tls->pre_master_secret ) - 2 ) );
+ digest_init ( &md5_algorithm, tls->handshake_md5_ctx );
+ digest_init ( &sha1_algorithm, tls->handshake_sha1_ctx );
+ tls->tx_state = TLS_TX_CLIENT_HELLO;
+ process_init ( &tls->process, tls_step, &tls->refcnt );
+
+ /* Attach to parent interface, mortalise self, and return */
+ xfer_plug_plug ( &tls->plainstream.xfer, xfer );
+ *next = &tls->cipherstream.xfer;
+ ref_put ( &tls->refcnt );
+ return 0;
+}
+
diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c
index 26059341c..f8f59e2e4 100644
--- a/src/net/udp/dhcp.c
+++ b/src/net/udp/dhcp.c
@@ -24,6 +24,7 @@
#include <byteswap.h>
#include <gpxe/if_ether.h>
#include <gpxe/netdevice.h>
+#include <gpxe/device.h>
#include <gpxe/xfer.h>
#include <gpxe/open.h>
#include <gpxe/job.h>
@@ -108,7 +109,7 @@ static uint32_t dhcp_xid ( struct net_device *netdev ) {
}
/** Options common to all DHCP requests */
-struct dhcp_option_block dhcp_request_options = {
+static struct dhcp_option_block dhcp_request_options = {
.data = dhcp_request_options_data,
.max_len = sizeof ( dhcp_request_options_data ),
.len = sizeof ( dhcp_request_options_data ),
@@ -270,8 +271,8 @@ static int copy_dhcp_packet_encap_options ( struct dhcp_packet *dhcppkt,
* @c options may specify a single options block, or be left as NULL
* in order to copy options from all registered options blocks.
*/
-int copy_dhcp_packet_options ( struct dhcp_packet *dhcppkt,
- struct dhcp_option_block *options ) {
+static int copy_dhcp_packet_options ( struct dhcp_packet *dhcppkt,
+ struct dhcp_option_block *options ) {
return copy_dhcp_packet_encap_options ( dhcppkt, options, 0 );
}
@@ -289,9 +290,10 @@ int copy_dhcp_packet_options ( struct dhcp_packet *dhcppkt,
* dhcp_packet structure that can be passed to
* set_dhcp_packet_option() or copy_dhcp_packet_options().
*/
-int create_dhcp_packet ( struct net_device *netdev, uint8_t msgtype,
- void *data, size_t max_len,
- struct dhcp_packet *dhcppkt ) {
+static int create_dhcp_packet ( struct net_device *netdev,
+ unsigned int msgtype,
+ void *data, size_t max_len,
+ struct dhcp_packet *dhcppkt ) {
struct dhcphdr *dhcphdr = data;
int rc;
@@ -475,6 +477,121 @@ static struct dhcp_option_block * dhcp_parse ( const struct dhcphdr *dhcphdr,
/****************************************************************************
*
+ * Whole-packet construction
+ *
+ */
+
+/** DHCP network device descriptor */
+struct dhcp_netdev_desc {
+ /** Bus type ID */
+ uint8_t type;
+ /** Vendor ID */
+ uint16_t vendor;
+ /** Device ID */
+ uint16_t device;
+} __attribute__ (( packed ));
+
+/**
+ * Create DHCP request
+ *
+ * @v netdev Network device
+ * @v msgtype DHCP message type
+ * @v options DHCP server response options, or NULL
+ * @v data Buffer for DHCP packet
+ * @v max_len Size of DHCP packet buffer
+ * @v dhcppkt DHCP packet structure to fill in
+ * @ret rc Return status code
+ */
+int create_dhcp_request ( struct net_device *netdev, int msgtype,
+ struct dhcp_option_block *options,
+ void *data, size_t max_len,
+ struct dhcp_packet *dhcppkt ) {
+ struct device_description *desc = &netdev->dev->desc;
+ struct dhcp_netdev_desc dhcp_desc;
+ int rc;
+
+ /* Create DHCP packet */
+ if ( ( rc = create_dhcp_packet ( netdev, msgtype, data, max_len,
+ dhcppkt ) ) != 0 ) {
+ DBG ( "DHCP could not create DHCP packet: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy in options common to all requests */
+ if ( ( rc = copy_dhcp_packet_options ( dhcppkt,
+ &dhcp_request_options )) !=0 ){
+ DBG ( "DHCP could not set common DHCP options: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy any required options from previous server repsonse */
+ if ( options ) {
+ if ( ( rc = copy_dhcp_packet_option ( dhcppkt, options,
+ DHCP_SERVER_IDENTIFIER,
+ DHCP_SERVER_IDENTIFIER ) ) != 0 ) {
+ DBG ( "DHCP could not set server identifier "
+ "option: %s\n", strerror ( rc ) );
+ return rc;
+ }
+ if ( ( rc = copy_dhcp_packet_option ( dhcppkt, options,
+ DHCP_EB_YIADDR,
+ DHCP_REQUESTED_ADDRESS ) ) != 0 ) {
+ DBG ( "DHCP could not set requested address "
+ "option: %s\n", strerror ( rc ) );
+ return rc;
+ }
+ }
+
+ /* Add options to identify the network device */
+ dhcp_desc.type = desc->bus_type;
+ dhcp_desc.vendor = htons ( desc->vendor );
+ dhcp_desc.device = htons ( desc->device );
+ if ( ( rc = set_dhcp_packet_option ( dhcppkt, DHCP_EB_BUS_ID,
+ &dhcp_desc,
+ sizeof ( dhcp_desc ) ) ) != 0 ) {
+ DBG ( "DHCP could not set bus ID option: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Create DHCP response
+ *
+ * @v netdev Network device
+ * @v msgtype DHCP message type
+ * @v options DHCP options, or NULL
+ * @v data Buffer for DHCP packet
+ * @v max_len Size of DHCP packet buffer
+ * @v dhcppkt DHCP packet structure to fill in
+ * @ret rc Return status code
+ */
+int create_dhcp_response ( struct net_device *netdev, int msgtype,
+ struct dhcp_option_block *options,
+ void *data, size_t max_len,
+ struct dhcp_packet *dhcppkt ) {
+ int rc;
+
+ /* Create packet and copy in options */
+ if ( ( rc = create_dhcp_packet ( netdev, msgtype, data, max_len,
+ dhcppkt ) ) != 0 ) {
+ DBG ( " failed to build packet" );
+ return rc;
+ }
+ if ( ( rc = copy_dhcp_packet_options ( dhcppkt, options ) ) != 0 ) {
+ DBG ( " failed to copy options" );
+ return rc;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ *
* DHCP to UDP interface
*
*/
@@ -556,8 +673,8 @@ static int dhcp_send_request ( struct dhcp_session *dhcp ) {
struct xfer_metadata meta = {
.netdev = dhcp->netdev,
};
- struct dhcp_packet dhcppkt;
struct io_buffer *iobuf;
+ struct dhcp_packet dhcppkt;
int rc;
DBGC ( dhcp, "DHCP %p transmitting %s\n",
@@ -577,40 +694,15 @@ static int dhcp_send_request ( struct dhcp_session *dhcp ) {
return -ENOMEM;
/* Create DHCP packet in temporary buffer */
- if ( ( rc = create_dhcp_packet ( dhcp->netdev, dhcp->state,
- iobuf->data, iob_tailroom ( iobuf ),
- &dhcppkt ) ) != 0 ) {
- DBGC ( dhcp, "DHCP %p could not create DHCP packet: %s\n",
+ if ( ( rc = create_dhcp_request ( dhcp->netdev, dhcp->state,
+ dhcp->options, iobuf->data,
+ iob_tailroom ( iobuf ),
+ &dhcppkt ) ) != 0 ) {
+ DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n",
dhcp, strerror ( rc ) );
goto done;
}
- /* Copy in options common to all requests */
- if ( ( rc = copy_dhcp_packet_options ( &dhcppkt,
- &dhcp_request_options ) ) != 0){
- DBGC ( dhcp, "DHCP %p could not set common DHCP options: %s\n",
- dhcp, strerror ( rc ) );
- goto done;
- }
-
- /* Copy any required options from previous server repsonse */
- if ( dhcp->options ) {
- if ( ( rc = copy_dhcp_packet_option ( &dhcppkt, dhcp->options,
- DHCP_SERVER_IDENTIFIER,
- DHCP_SERVER_IDENTIFIER ) ) != 0 ) {
- DBGC ( dhcp, "DHCP %p could not set server identifier "
- "option: %s\n", dhcp, strerror ( rc ) );
- goto done;
- }
- if ( ( rc = copy_dhcp_packet_option ( &dhcppkt, dhcp->options,
- DHCP_EB_YIADDR,
- DHCP_REQUESTED_ADDRESS ) ) != 0 ) {
- DBGC ( dhcp, "DHCP %p could not set requested address "
- "option: %s\n", dhcp, strerror ( rc ) );
- goto done;
- }
- }
-
/* Transmit the packet */
iob_put ( iobuf, dhcppkt.len );
rc = xfer_deliver_iob_meta ( &dhcp->xfer, iobuf, &meta );
diff --git a/src/tests/aoeboot.c b/src/tests/aoeboot.c
deleted file mode 100644
index 17fda2c66..000000000
--- a/src/tests/aoeboot.c
+++ /dev/null
@@ -1,71 +0,0 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <console.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/aoe.h>
-#include <int13.h>
-
-static struct aoe_device test_aoedev = {
- .aoe = {
- .major = 0,
- .minor = 0,
- },
-};
-
-static int aoe_parse ( const char *aoename, struct aoe_session *aoe ) {
- char *ptr = ( ( char * ) aoename );
-
- if ( *ptr++ != 'e' )
- return -EINVAL;
-
- aoe->major = strtoul ( ptr, &ptr, 10 );
- if ( *ptr++ != '.' )
- return -EINVAL;
-
- aoe->minor = strtoul ( ptr, &ptr, 10 );
- if ( *ptr )
- return -EINVAL;
-
- return 0;
-}
-
-int test_aoeboot ( struct net_device *netdev, const char *aoename,
- unsigned int drivenum ) {
- struct int13_drive drive;
- int rc;
-
- printf ( "Attempting to boot from AoE device %s via %s\n",
- aoename, netdev->name );
-
- if ( ( rc = aoe_parse ( aoename, &test_aoedev.aoe ) ) != 0 ) {
- printf ( "Invalid AoE device name \"%s\"\n", aoename );
- return rc;
- }
-
- printf ( "Initialising AoE device e%d.%d\n",
- test_aoedev.aoe.major, test_aoedev.aoe.minor );
- test_aoedev.aoe.netdev = netdev;
- if ( ( rc = init_aoedev ( &test_aoedev ) ) != 0 ) {
- printf ( "Could not reach AoE device e%d.%d\n",
- test_aoedev.aoe.major, test_aoedev.aoe.minor );
- return rc;
- }
-
- memset ( &drive, 0, sizeof ( drive ) );
- drive.drive = drivenum;
- drive.blockdev = &test_aoedev.ata.blockdev;
- register_int13_drive ( &drive );
- printf ( "Registered AoE device e%d.%d as BIOS drive %#02x\n",
- test_aoedev.aoe.major, test_aoedev.aoe.minor, drive.drive );
-
- printf ( "Booting from BIOS drive %#02x\n", drive.drive );
- rc = int13_boot ( drive.drive );
- printf ( "Boot failed\n" );
-
- printf ( "Unregistering BIOS drive %#02x\n", drive.drive );
- unregister_int13_drive ( &drive );
-
- return rc;
-}
diff --git a/src/usr/aoeboot.c b/src/usr/aoeboot.c
new file mode 100644
index 000000000..ffc17a1c7
--- /dev/null
+++ b/src/usr/aoeboot.c
@@ -0,0 +1,104 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <byteswap.h>
+#include <gpxe/aoe.h>
+#include <gpxe/ata.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/abft.h>
+#include <int13.h>
+#include <usr/aoeboot.h>
+
+/**
+ * AoE boot information block
+ *
+ * Must be placed at 40:f0.
+ *
+ * This structure needs to be replaced by an ACPI table or similar.
+ */
+struct aoe_boot_info {
+ /** Must be 0x01 */
+ uint8_t one;
+ /** Client MAC address */
+ uint8_t client[ETH_ALEN];
+ /** Server MAC address */
+ uint8_t server[ETH_ALEN];
+ /** Shelf number */
+ uint16_t shelf;
+ /** Slot number */
+ uint8_t slot;
+} __attribute__ (( packed ));
+
+/**
+ * Guess boot network device
+ *
+ * @ret netdev Boot network device
+ */
+static struct net_device * guess_boot_netdev ( void ) {
+ struct net_device *boot_netdev;
+
+ /* Just use the first network device */
+ for_each_netdev ( boot_netdev ) {
+ return boot_netdev;
+ }
+
+ return NULL;
+}
+
+int aoeboot ( const char *root_path ) {
+ struct ata_device ata;
+ struct int13_drive drive;
+ int rc;
+
+ memset ( &ata, 0, sizeof ( ata ) );
+ memset ( &drive, 0, sizeof ( drive ) );
+
+ printf ( "AoE booting from %s\n", root_path );
+
+ /* FIXME: ugly, ugly hack */
+ struct net_device *netdev = guess_boot_netdev();
+
+ if ( ( rc = aoe_attach ( &ata, netdev, root_path ) ) != 0 ) {
+ printf ( "Could not attach AoE device: %s\n",
+ strerror ( rc ) );
+ goto error_attach;
+ }
+ if ( ( rc = init_atadev ( &ata ) ) != 0 ) {
+ printf ( "Could not initialise AoE device: %s\n",
+ strerror ( rc ) );
+ goto error_init;
+ }
+
+ /* FIXME: ugly, ugly hack */
+ struct aoe_session *aoe =
+ container_of ( ata.backend, struct aoe_session, refcnt );
+ struct aoe_boot_info boot_info;
+ boot_info.one = 0x01;
+ memcpy ( boot_info.client, netdev->ll_addr,
+ sizeof ( boot_info.client ) );
+ memcpy ( boot_info.server, aoe->target,
+ sizeof ( boot_info.server ) );
+ boot_info.shelf = htons ( aoe->major );
+ boot_info.slot = aoe->minor;
+ copy_to_real ( 0x40, 0xf0, &boot_info, sizeof ( boot_info ) );
+
+ abft_fill_data ( aoe );
+
+ drive.drive = find_global_dhcp_num_option ( DHCP_EB_BIOS_DRIVE );
+ drive.blockdev = &ata.blockdev;
+
+ register_int13_drive ( &drive );
+ printf ( "Registered as BIOS drive %#02x\n", drive.drive );
+ printf ( "Booting from BIOS drive %#02x\n", drive.drive );
+ rc = int13_boot ( drive.drive );
+ printf ( "Boot failed\n" );
+
+ printf ( "Unregistering BIOS drive %#02x\n", drive.drive );
+ unregister_int13_drive ( &drive );
+
+ error_init:
+ aoe_detach ( &ata );
+ error_attach:
+ return rc;
+}
diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c
index 97b9a4067..53283d18c 100644
--- a/src/usr/autoboot.c
+++ b/src/usr/autoboot.c
@@ -27,6 +27,7 @@
#include <usr/dhcpmgmt.h>
#include <usr/imgmgmt.h>
#include <usr/iscsiboot.h>
+#include <usr/aoeboot.h>
#include <usr/autoboot.h>
/** @file
@@ -88,13 +89,15 @@ static int boot_filename ( const char *filename ) {
* @ret rc Return status code
*/
static int boot_root_path ( const char *root_path ) {
- int rc;
/* Quick hack */
- if ( ( rc = iscsiboot ( root_path ) ) != 0 )
- return rc;
+ if ( strncmp ( root_path, "iscsi:", 6 ) == 0 ) {
+ return iscsiboot ( root_path );
+ } else if ( strncmp ( root_path, "aoe:", 4 ) == 0 ) {
+ return aoeboot ( root_path );
+ }
- return 0;
+ return -ENOTSUP;
}
/**
@@ -103,7 +106,7 @@ static int boot_root_path ( const char *root_path ) {
* @v netdev Network device
* @ret rc Return status code
*/
-int netboot ( struct net_device *netdev ) {
+static int netboot ( struct net_device *netdev ) {
char buf[256];
int rc;
diff --git a/src/usr/iscsiboot.c b/src/usr/iscsiboot.c
index f3910f149..a7caebaf6 100644
--- a/src/usr/iscsiboot.c
+++ b/src/usr/iscsiboot.c
@@ -3,9 +3,27 @@
#include <stdio.h>
#include <gpxe/iscsi.h>
#include <gpxe/dhcp.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/ibft.h>
#include <int13.h>
#include <usr/iscsiboot.h>
+/**
+ * Guess boot network device
+ *
+ * @ret netdev Boot network device
+ */
+static struct net_device * guess_boot_netdev ( void ) {
+ struct net_device *boot_netdev;
+
+ /* Just use the first network device */
+ for_each_netdev ( boot_netdev ) {
+ return boot_netdev;
+ }
+
+ return NULL;
+}
+
int iscsiboot ( const char *root_path ) {
struct scsi_device scsi;
struct int13_drive drive;
@@ -30,6 +48,12 @@ int iscsiboot ( const char *root_path ) {
drive.drive = find_global_dhcp_num_option ( DHCP_EB_BIOS_DRIVE );
drive.blockdev = &scsi.blockdev;
+ /* FIXME: ugly, ugly hack */
+ struct net_device *netdev = guess_boot_netdev();
+ struct iscsi_session *iscsi =
+ container_of ( scsi.backend, struct iscsi_session, refcnt );
+ ibft_fill_data ( netdev, iscsi );
+
register_int13_drive ( &drive );
printf ( "Registered as BIOS drive %#02x\n", drive.drive );
printf ( "Booting from BIOS drive %#02x\n", drive.drive );