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