Merge branch 'master' into strings
authorHolger Lubitz <hal@duncan.ol.sub.de>
Wed, 1 Aug 2007 22:13:40 +0000 (00:13 +0200)
committerHolger Lubitz <hal@duncan.ol.sub.de>
Wed, 1 Aug 2007 22:13:40 +0000 (00:13 +0200)
66 files changed:
src/Makefile
src/arch/i386/Config
src/arch/i386/core/relocate.c
src/arch/i386/core/udivmod64.c [deleted file]
src/arch/i386/drivers/net/undinet.c
src/arch/i386/image/bzimage.c
src/arch/i386/image/eltorito.c
src/arch/i386/image/multiboot.c
src/arch/i386/image/nbi.c
src/arch/i386/interface/pcbios/int13.c
src/arch/i386/interface/pxe/pxe_call.c
src/config.h
src/core/abft.c [new file with mode: 0644]
src/core/config.c
src/core/filter.c [new file with mode: 0644]
src/core/main.c
src/crypto/axtls/axtls_asn1.c [new file with mode: 0644]
src/crypto/axtls/bigint.c
src/crypto/axtls/bigint.h
src/crypto/axtls/crypto.h
src/crypto/axtls/os_port.h
src/crypto/axtls/rsa.c
src/crypto/hmac.c [new file with mode: 0644]
src/crypto/md5.c
src/drivers/bitbash/spi_bit.c
src/drivers/net/bnx2.c
src/drivers/net/ns83820.c
src/drivers/net/pcnet32.c
src/drivers/net/r8169.c
src/drivers/net/via-velocity.h
src/hci/commands/image_cmd.c
src/hci/mucurses/kb.c
src/hci/mucurses/slk.c
src/include/compiler.h
src/include/curses.h
src/include/gpxe/abft.h [new file with mode: 0644]
src/include/gpxe/aoe.h
src/include/gpxe/ata.h
src/include/gpxe/dhcp.h
src/include/gpxe/errfile.h
src/include/gpxe/filter.h [new file with mode: 0644]
src/include/gpxe/hmac.h [new file with mode: 0644]
src/include/gpxe/http.h
src/include/gpxe/iscsi.h
src/include/gpxe/open.h
src/include/gpxe/tls.h
src/include/usr/aoeboot.h [new file with mode: 0644]
src/interface/pxe/pxe_preboot.c
src/libgcc/__divdi3.c [new file with mode: 0644]
src/libgcc/__moddi3.c [new file with mode: 0644]
src/libgcc/__udivdi3.c [new file with mode: 0644]
src/libgcc/__udivmoddi4.c [new file with mode: 0644]
src/libgcc/__umoddi3.c [new file with mode: 0644]
src/libgcc/libgcc.h [new file with mode: 0644]
src/libgcc/memcpy.c [moved from src/core/gcc_implicit.c with 95% similarity]
src/net/aoe.c
src/net/ndp.c
src/net/tcp/http.c
src/net/tcp/https.c [moved from src/drivers/ata/aoedev.c with 52% similarity]
src/net/tcp/iscsi.c
src/net/tls.c [new file with mode: 0644]
src/net/udp/dhcp.c
src/tests/aoeboot.c [deleted file]
src/usr/aoeboot.c [new file with mode: 0644]
src/usr/autoboot.c
src/usr/iscsiboot.c

index a567b92..8e0c8be 100644 (file)
@@ -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
index 8779132..16de411 100644 (file)
@@ -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"
index d3b42d0..39d00b0 100644 (file)
@@ -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 (file)
index faf6fd8..0000000
+++ /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;
-}
index 38032ab..a5746ce 100644 (file)
@@ -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;
index 3a3e82e..0d01f6a 100644 (file)
@@ -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;
index 53e25ca..9d57310 100644 (file)
@@ -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;
index ab0b55a..546de36 100644 (file)
@@ -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;
 
index 3a66e9c..2de381d 100644 (file)
@@ -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;
 }
index f7cb679..53817c7 100644 (file)
@@ -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;
index 834ca73..1c1b506 100644 (file)
@@ -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;
index 55409b0..c436d10 100644 (file)
@@ -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 (file)
index 0000000..af28bbc
--- /dev/null
@@ -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 ) );
+}
index a2194e8..7e70c12 100644 (file)
@@ -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 (file)
index 0000000..51ec5c4
--- /dev/null
@@ -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 );
+}
index 09dccc7..88fbb57 100644 (file)
@@ -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 (file)
index 0000000..74411c7
--- /dev/null
@@ -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
index 253707e..ee51c14 100644 (file)
@@ -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 */
index e233d79..5a13c5a 100644 (file)
@@ -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
index f6277ad..de1dbeb 100644 (file)
@@ -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
index 39e0fb5..babdbfa 100644 (file)
@@ -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 ) {
index 69db9ae..389eda5 100644 (file)
@@ -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 (file)
index 0000000..6884bde
--- /dev/null
@@ -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 );
+}
index 09378e2..1fed24f 100644 (file)
@@ -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,
index e2175d6..a4e7136 100644 (file)
@@ -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 );
 
index 105e9c3..fdd6655 100644 (file)
@@ -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[] =
 {
index 8b8500f..3262ba6 100755 (executable)
@@ -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;
index 0328cf2..63353e3 100644 (file)
@@ -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 */
index d9854e9..08d1c6f 100644 (file)
@@ -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;
 
index e0b8809..c296d28 100644 (file)
@@ -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;
index 2d35ebc..97d41bd 100644 (file)
@@ -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';
index f0c8dcb..291ee6a 100644 (file)
@@ -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 )
index 4422e15..600658e 100644 (file)
@@ -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
index 376936d..b130f28 100644 (file)
@@ -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.
  *
index 164dd20..762a63b 100644 (file)
@@ -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 (file)
index 0000000..1c651ef
--- /dev/null
@@ -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 */
index eb5e113..8568338 100644 (file)
@@ -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 */
index e0fca7a..b6da393 100644 (file)
@@ -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 );
index 6457565..8632278 100644 (file)
@@ -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 * ));
index 0615818..48db1dc 100644 (file)
 #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 (file)
index 0000000..3943a9e
--- /dev/null
@@ -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 (file)
index 0000000..fd34db0
--- /dev/null
@@ -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 */
index a2abec1..fa92a95 100644 (file)
@@ -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 */
index d9dd430..e4df684 100644 (file)
@@ -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 */
index abba29c..beab0a1 100644 (file)
@@ -7,6 +7,7 @@
  *
  */
 
+#include <stdarg.h>
 #include <gpxe/tables.h>
 
 struct xfer_interface;
index 19ab980..a8cf16e 100644 (file)
 #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 (file)
index 0000000..0421ebc
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _USR_AOEBOOT_H
+#define _USR_AOEBOOT_H
+
+extern int aoeboot ( const char *root_path );
+
+#endif /* _USR_AOEBOOT_H */
index b4e2206..e5c4406 100644 (file)
@@ -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 (file)
index 0000000..36f0b37
--- /dev/null
@@ -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 (file)
index 0000000..eb7784b
--- /dev/null
@@ -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 (file)
index 0000000..9ae0c3d
--- /dev/null
@@ -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 (file)
index 0000000..59966ed
--- /dev/null
@@ -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 (file)
index 0000000..f6c76cb
--- /dev/null
@@ -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 (file)
index 0000000..5b4a624
--- /dev/null
@@ -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 */
similarity index 95%
rename from src/core/gcc_implicit.c
rename to src/libgcc/memcpy.c
index 8f217b6..e98b783 100644 (file)
@@ -1,6 +1,4 @@
 /** @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
index 36721df..fd82665 100644 (file)
@@ -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;
 }
 
 /**
@@ -265,46 +274,99 @@ struct net_protocol aoe_protocol __net_protocol = {
        .rx = aoe_rx,
 };
 
-/**
- * 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;
+}
index 7c684e7..3b6984d 100644 (file)
@@ -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];
 
index 88958af..727c033 100644 (file)
@@ -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,
-};
similarity index 52%
rename from src/drivers/ata/aoedev.c
rename to src/net/tcp/https.c
index ff047f1..148e4bf 100644 (file)
@@ -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
  * 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,
+};
index 273f0d6..e645917 100644 (file)
  */
 
 /** 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,
@@ -1460,6 +1489,32 @@ static int iscsi_parse_root_path ( struct iscsi_session *iscsi,
        return 0;
 }
 
+/**
+ * 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
  *
@@ -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 (file)
index 0000000..64e44b5
--- /dev/null
@@ -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;
+}
+
index 2605934..f8f59e2 100644 (file)
@@ -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;
 
@@ -473,6 +475,121 @@ static struct dhcp_option_block * dhcp_parse ( const struct dhcphdr *dhcphdr,
        return options;
 }
 
+/****************************************************************************
+ *
+ * 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 (file)
index 17fda2c..0000000
+++ /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 (file)
index 0000000..ffc17a1
--- /dev/null
@@ -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;
+}
index 97b9a40..53283d1 100644 (file)
@@ -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;
 
index f3910f1..a7caeba 100644 (file)
@@ -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 );