From 47a86bca2d2241a5126350e70d117564890a3f01 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 26 Jul 2007 02:13:38 +0100 Subject: Prepare for iBFT merge when possible. iscsiboot.c contains a really, really ugly hack at present, but that doesn't hugely matter since I'm aiming to change the interface to iSCSI devices anyway within the next week. --- src/include/gpxe/errfile.h | 1 + src/include/gpxe/iscsi.h | 9 +++++++++ 2 files changed, 10 insertions(+) (limited to 'src/include') diff --git a/src/include/gpxe/errfile.h b/src/include/gpxe/errfile.h index 0615818f1..744ae5b26 100644 --- a/src/include/gpxe/errfile.h +++ b/src/include/gpxe/errfile.h @@ -115,6 +115,7 @@ #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 ) /** @} */ diff --git a/src/include/gpxe/iscsi.h b/src/include/gpxe/iscsi.h index d9dd43079..e4df68494 100644 --- a/src/include/gpxe/iscsi.h +++ b/src/include/gpxe/iscsi.h @@ -8,6 +8,7 @@ */ #include +#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 */ -- cgit v1.2.3-55-g7522 From 0d568ac2197428cbf17a4d83574fa2ff22fd7e72 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 28 Jul 2007 22:55:17 +0100 Subject: Make has_key() a static inline, rather than omitting it altogether. --- src/hci/mucurses/kb.c | 16 +--------------- src/include/curses.h | 4 ++++ 2 files changed, 5 insertions(+), 15 deletions(-) (limited to 'src/include') diff --git a/src/hci/mucurses/kb.c b/src/hci/mucurses/kb.c index a68cf26a7..291ee6ac8 100644 --- a/src/hci/mucurses/kb.c +++ b/src/hci/mucurses/kb.c @@ -19,21 +19,7 @@ int m_delay; /* bool m_echo; bool m_cbreak; -#if 0 -/** - * 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; -} -#endif - -static -int _wgetc ( WINDOW *win ) { +static int _wgetc ( WINDOW *win ) { int timer, c; if ( win == NULL ) diff --git a/src/include/curses.h b/src/include/curses.h index 164dd2020..762a63b5a 100644 --- a/src/include/curses.h +++ b/src/include/curses.h @@ -566,6 +566,10 @@ static inline bool has_colors ( void ) { return TRUE; } +static inline int has_key ( int kc __unused ) { + return TRUE; +} + static inline int hline ( chtype ch, int n ) { return whline ( stdscr, ch, n ); } -- cgit v1.2.3-55-g7522 From a6a1052096c72660a24fead2e1e7d580ea4f8bc2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 29 Jul 2007 00:17:25 +0100 Subject: Applied a modified version of holger's regparm patches. --- src/arch/i386/Config | 12 ++++++++++++ src/arch/i386/core/relocate.c | 2 +- src/arch/i386/interface/pcbios/int13.c | 2 +- src/arch/i386/interface/pxe/pxe_call.c | 4 ++-- src/core/main.c | 2 +- src/include/compiler.h | 3 +++ 6 files changed, 20 insertions(+), 5 deletions(-) (limited to 'src/include') diff --git a/src/arch/i386/Config b/src/arch/i386/Config index 877913210..16de411b1 100644 --- a/src/arch/i386/Config +++ b/src/arch/i386/Config @@ -130,6 +130,18 @@ endif # this is almost always a win. the kernel uses it, too. CFLAGS+= -mpreferred-stack-boundary=2 +# use regparm for all functions - C functions called from assembly (or +# vice versa) need __cdecl now +CFLAGS+= -mregparm=3 + +# use -mrtd (same __cdecl requirements as above) +CFLAGS+= -mrtd + +# this is the logical complement to -mregparm=3. +# it doesn't currently buy us anything, but if anything ever tries +# to return small structures, let's be prepared +CFLAGS+= -freg-struct-return + LDFLAGS+= -N --no-check-sections ifeq "$(shell uname -s)" "FreeBSD" diff --git a/src/arch/i386/core/relocate.c b/src/arch/i386/core/relocate.c index d3b42d0da..39d00b098 100644 --- a/src/arch/i386/core/relocate.c +++ b/src/arch/i386/core/relocate.c @@ -39,7 +39,7 @@ extern char _end[]; * address space, and returns the physical address of the new location * to the prefix in %edi. */ -void relocate ( struct i386_all_regs *ix86 ) { +__cdecl void relocate ( struct i386_all_regs *ix86 ) { struct memory_map memmap; unsigned long start, end, size, padded_size; unsigned long new_start, new_end; diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c index f7cb67958..53817c7a8 100644 --- a/src/arch/i386/interface/pcbios/int13.c +++ b/src/arch/i386/interface/pcbios/int13.c @@ -321,7 +321,7 @@ static int int13_get_extended_parameters ( struct int13_drive *drive, * INT 13 handler * */ -static void int13 ( struct i386_all_regs *ix86 ) { +static __cdecl void int13 ( struct i386_all_regs *ix86 ) { int command = ix86->regs.ah; unsigned int bios_drive = ix86->regs.dl; unsigned int original_bios_drive = bios_drive; diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/i386/interface/pxe/pxe_call.c index 834ca7380..1c1b5066c 100644 --- a/src/arch/i386/interface/pxe/pxe_call.c +++ b/src/arch/i386/interface/pxe/pxe_call.c @@ -112,7 +112,7 @@ static PXENV_EXIT_t pxenv_unknown ( struct s_PXENV_UNKNOWN *pxenv_unknown ) { * @v es:di Address of PXE parameter block * @ret ax PXE exit code */ -void pxe_api_call ( struct i386_all_regs *ix86 ) { +__cdecl void pxe_api_call ( struct i386_all_regs *ix86 ) { int opcode = ix86->regs.bx; userptr_t parameters = real_to_user ( ix86->segs.es, ix86->regs.di ); size_t param_len; @@ -304,7 +304,7 @@ void pxe_api_call ( struct i386_all_regs *ix86 ) { * @v es:di Address of PXE parameter block * @ret ax PXE exit code */ -void pxe_loader_call ( struct i386_all_regs *ix86 ) { +__cdecl void pxe_loader_call ( struct i386_all_regs *ix86 ) { userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di ); struct s_UNDI_LOADER params; PXENV_EXIT_t ret; diff --git a/src/core/main.c b/src/core/main.c index 09dccc761..88fbb5794 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -24,7 +24,7 @@ Literature dealing with the network protocols: * * @ret rc Return status code */ -int main ( void ) { +__cdecl int main ( void ) { initialise(); startup(); diff --git a/src/include/compiler.h b/src/include/compiler.h index 376936d0e..b130f28fd 100644 --- a/src/include/compiler.h +++ b/src/include/compiler.h @@ -276,6 +276,9 @@ extern void dbg_hex_dump_da ( unsigned long dispaddr, /** Declare a variable or data structure as unused. */ #define __unused __attribute__ (( unused )) +/** Apply standard C calling conventions */ +#define __cdecl __attribute__ (( cdecl , regparm(0) )) + /** * Declare a function as used. * -- cgit v1.2.3-55-g7522 From 43013da9bf02439b4726d8afef15f7ce97d1c469 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 29 Jul 2007 02:31:14 +0100 Subject: Quick hack to get AoE back in to the tree, on a par with the current iSCSI hack. --- src/drivers/ata/aoedev.c | 55 -------------------- src/include/gpxe/aoe.h | 25 +++------ src/include/gpxe/ata.h | 3 ++ src/include/usr/aoeboot.h | 6 +++ src/net/aoe.c | 130 ++++++++++++++++++++++++++++++++++------------ src/tests/aoeboot.c | 71 ------------------------- src/usr/aoeboot.c | 67 ++++++++++++++++++++++++ src/usr/autoboot.c | 11 ++-- 8 files changed, 187 insertions(+), 181 deletions(-) delete mode 100644 src/drivers/ata/aoedev.c create mode 100644 src/include/usr/aoeboot.h delete mode 100644 src/tests/aoeboot.c create mode 100644 src/usr/aoeboot.c (limited to 'src/include') diff --git a/src/drivers/ata/aoedev.c b/src/drivers/ata/aoedev.c deleted file mode 100644 index ff047f103..000000000 --- a/src/drivers/ata/aoedev.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2006 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 - -/** @file - * - * AoE ATA device - * - */ - -/** - * Issue ATA command via AoE device - * - * @v ata ATA device - * @v command ATA command - * @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 ) ); -} - -/** - * 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 ); -} diff --git a/src/include/gpxe/aoe.h b/src/include/gpxe/aoe.h index eb5e11337..856833841 100644 --- a/src/include/gpxe/aoe.h +++ b/src/include/gpxe/aoe.h @@ -81,6 +81,9 @@ struct aoehdr { /** An AoE session */ struct aoe_session { + /** Reference counter */ + struct refcnt refcnt; + /** List of all AoE sessions */ struct list_head list; @@ -103,8 +106,8 @@ struct aoe_session { unsigned int status; /** Byte offset within command's data buffer */ unsigned int command_offset; - /** Asynchronous operation for this command */ - struct async async; + /** Return status code for command */ + int rc; /** Retransmission timer */ struct retry_timer timer; @@ -116,20 +119,8 @@ struct aoe_session { /** Maximum number of sectors per packet */ #define AOE_MAX_COUNT 2 -extern void aoe_open ( struct aoe_session *aoe ); -extern void aoe_close ( struct aoe_session *aoe ); -extern int aoe_issue ( struct aoe_session *aoe, - struct ata_command *command, - struct async *parent ); - -/** An AoE device */ -struct aoe_device { - /** ATA device interface */ - struct ata_device ata; - /** AoE protocol instance */ - struct aoe_session aoe; -}; - -extern int init_aoedev ( struct aoe_device *aoedev ); +extern void aoe_detach ( struct ata_device *ata ); +extern int aoe_attach ( struct ata_device *ata, struct net_device *netdev, + const char *root_path ); #endif /* _GPXE_AOE_H */ diff --git a/src/include/gpxe/ata.h b/src/include/gpxe/ata.h index e0fca7afe..b6da39302 100644 --- a/src/include/gpxe/ata.h +++ b/src/include/gpxe/ata.h @@ -4,6 +4,7 @@ #include #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/usr/aoeboot.h b/src/include/usr/aoeboot.h new file mode 100644 index 000000000..0421ebcc0 --- /dev/null +++ b/src/include/usr/aoeboot.h @@ -0,0 +1,6 @@ +#ifndef _USR_AOEBOOT_H +#define _USR_AOEBOOT_H + +extern int aoeboot ( const char *root_path ); + +#endif /* _USR_AOEBOOT_H */ diff --git a/src/net/aoe.c b/src/net/aoe.c index 36721df64..fd82665d1 100644 --- a/src/net/aoe.c +++ b/src/net/aoe.c @@ -19,6 +19,7 @@ #include #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/tests/aoeboot.c b/src/tests/aoeboot.c deleted file mode 100644 index 17fda2c66..000000000 --- a/src/tests/aoeboot.c +++ /dev/null @@ -1,71 +0,0 @@ -#include -#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 000000000..54bb5d47b --- /dev/null +++ b/src/usr/aoeboot.c @@ -0,0 +1,67 @@ +#include +#include +#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 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; + } + + 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 4bc43d5a7..53283d18c 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; } /** -- cgit v1.2.3-55-g7522 From 218314e712fdb44683fcb9f974b6cd071dcc4a32 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 29 Jul 2007 03:09:00 +0100 Subject: Added HMAC code from TLS project --- src/crypto/hmac.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++ src/include/gpxe/hmac.h | 30 ++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 src/crypto/hmac.c create mode 100644 src/include/gpxe/hmac.h (limited to 'src/include') diff --git a/src/crypto/hmac.c b/src/crypto/hmac.c new file mode 100644 index 000000000..6884bde9b --- /dev/null +++ b/src/crypto/hmac.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * 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/include/gpxe/hmac.h b/src/include/gpxe/hmac.h new file mode 100644 index 000000000..fd34db04a --- /dev/null +++ b/src/include/gpxe/hmac.h @@ -0,0 +1,30 @@ +#ifndef _GPXE_HMAC_H +#define _GPXE_HMAC_H + +/** @file + * + * Keyed-Hashing for Message Authentication + */ + +#include + +/** + * 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 */ -- cgit v1.2.3-55-g7522 From a6467c99a021d2740b195dd29fbe920ea7338967 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 29 Jul 2007 16:05:14 +0100 Subject: Added a simple pass-through filter layer for data transfer interfaces. --- src/core/filter.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++ src/include/gpxe/filter.h | 75 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 src/core/filter.c create mode 100644 src/include/gpxe/filter.h (limited to 'src/include') diff --git a/src/core/filter.c b/src/core/filter.c new file mode 100644 index 000000000..51ec5c4bc --- /dev/null +++ b/src/core/filter.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * 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/include/gpxe/filter.h b/src/include/gpxe/filter.h new file mode 100644 index 000000000..3943a9e14 --- /dev/null +++ b/src/include/gpxe/filter.h @@ -0,0 +1,75 @@ +#ifndef _GPXE_FILTER_H +#define _GPXE_FILTER_H + +/** @file + * + * Data transfer filters + * + */ + +#include +#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 */ -- cgit v1.2.3-55-g7522 From 3b9bf40682e78a3a20c09e26dfc1a8465884b836 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 29 Jul 2007 17:16:00 +0100 Subject: Preparation for adding tls.c --- src/include/gpxe/errfile.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src/include') diff --git a/src/include/gpxe/errfile.h b/src/include/gpxe/errfile.h index 744ae5b26..48db1dc19 100644 --- a/src/include/gpxe/errfile.h +++ b/src/include/gpxe/errfile.h @@ -116,6 +116,7 @@ #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 ) /** @} */ -- cgit v1.2.3-55-g7522 From 6fc9ed167e3f8bd3fc94cd2e21bd108f90a736d6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Jul 2007 02:48:38 +0100 Subject: TLS now working again. --- src/include/gpxe/tls.h | 169 ++++- src/net/tcp/http.c | 17 +- src/net/tls.c | 1732 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1904 insertions(+), 14 deletions(-) create mode 100644 src/net/tls.c (limited to 'src/include') diff --git a/src/include/gpxe/tls.h b/src/include/gpxe/tls.h index 19ab9801e..a8cf16ef3 100644 --- a/src/include/gpxe/tls.h +++ b/src/include/gpxe/tls.h @@ -1,12 +1,171 @@ #ifndef _GPXE_TLS_H #define _GPXE_TLS_H -#include +/** + * @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/net/tcp/http.c b/src/net/tcp/http.c index 9c2436816..bdd791ebb 100644 --- a/src/net/tcp/http.c +++ b/src/net/tcp/http.c @@ -468,6 +468,7 @@ static struct xfer_interface_operations http_xfer_operations = { static int http_open ( struct xfer_interface *xfer, struct uri *uri ) { struct http_request *http; struct sockaddr_tcpip server; + struct xfer_interface *socket; int rc; /* Sanity checks */ @@ -487,18 +488,16 @@ static 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, - ( struct sockaddr * ) &server, - uri->host, NULL ) ) != 0 ) - goto err; - -#if 0 + socket = &http->socket; if ( strcmp ( http->uri->scheme, "https" ) == 0 ) { - st->st_port = htons ( uri_port ( http->uri, HTTPS_PORT ) ); - if ( ( rc = add_tls ( &http->stream ) ) != 0 ) + server.st_port = htons ( uri_port ( http->uri, HTTPS_PORT ) ); + if ( ( rc = add_tls ( socket, &socket ) ) != 0 ) goto err; } -#endif + if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM, + ( struct sockaddr * ) &server, + uri->host, NULL ) ) != 0 ) + goto err; /* Attach to parent interface, mortalise self, and return */ xfer_plug_plug ( &http->xfer, xfer ); diff --git a/src/net/tls.c b/src/net/tls.c new file mode 100644 index 000000000..1cd995aed --- /dev/null +++ b/src/net/tls.c @@ -0,0 +1,1732 @@ +/* + * 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 ) { +#warning "Placeholder" + 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; + } + +#warning "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 ) { + +#warning "Why is this disabled?" +#if 0 + /* Sanity check */ + if ( ( 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; + } +#endif + + 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 ) { +#warning "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 ) ); + +#warning "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; + + 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 ) { + +#warning "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; + +#warning "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; + +#warning "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; +} + -- cgit v1.2.3-55-g7522 From 3b8b06ae90cc73137585ec93d554f3bd351c6d6a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Jul 2007 03:00:15 +0100 Subject: Added missing #include --- src/include/gpxe/open.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src/include') diff --git a/src/include/gpxe/open.h b/src/include/gpxe/open.h index abba29c4c..beab0a1f7 100644 --- a/src/include/gpxe/open.h +++ b/src/include/gpxe/open.h @@ -7,6 +7,7 @@ * */ +#include #include struct xfer_interface; -- cgit v1.2.3-55-g7522 From bf3d8fb1aa08d13e3d15257ba165cf0128f2c5b2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Jul 2007 03:01:04 +0100 Subject: Allowed HTTPS to be a separately configurable feature. --- src/config.h | 1 + src/core/config.c | 3 +++ src/include/gpxe/http.h | 5 +++++ src/net/tcp/http.c | 34 +++++++++++++++++++++------------- src/net/tcp/https.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 13 deletions(-) create mode 100644 src/net/tcp/https.c (limited to 'src/include') diff --git a/src/config.h b/src/config.h index 55409b0eb..c436d100c 100644 --- a/src/config.h +++ b/src/config.h @@ -73,6 +73,7 @@ #define DOWNLOAD_PROTO_TFTP /* Trivial File Transfer Protocol */ #undef DOWNLOAD_PROTO_NFS /* Network File System */ #define DOWNLOAD_PROTO_HTTP /* Hypertext Transfer Protocol */ +#undef DOWNLOAD_PROTO_HTTPS /* Secure Hypertext Transfer Protocol */ #undef DOWNLOAD_PROTO_FTP /* File Transfer Protocol */ #undef DOWNLOAD_PROTO_TFTM /* Multicast Trivial File Transfer Protocol */ #undef DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */ diff --git a/src/core/config.c b/src/core/config.c index a2194e8dc..7e70c12a1 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -87,6 +87,9 @@ REQUIRE_OBJECT ( nfs ); #ifdef DOWNLOAD_PROTO_HTTP REQUIRE_OBJECT ( http ); #endif +#ifdef DOWNLOAD_PROTO_HTTPS +REQUIRE_OBJECT ( https ); +#endif #ifdef DOWNLOAD_PROTO_FTP REQUIRE_OBJECT ( ftp ); #endif diff --git a/src/include/gpxe/http.h b/src/include/gpxe/http.h index a2abec1dc..fa92a9505 100644 --- a/src/include/gpxe/http.h +++ b/src/include/gpxe/http.h @@ -13,4 +13,9 @@ /** HTTPS default port */ #define HTTPS_PORT 443 +extern int http_open_filter ( struct xfer_interface *xfer, struct uri *uri, + unsigned int default_port, + int ( * filter ) ( struct xfer_interface *, + struct xfer_interface ** ) ); + #endif /* _GPXE_HTTP_H */ diff --git a/src/net/tcp/http.c b/src/net/tcp/http.c index bdd791ebb..727c03334 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,13 +458,18 @@ 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 */ -static 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; @@ -487,11 +491,10 @@ static 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 ) ); + server.st_port = htons ( uri_port ( http->uri, default_port ) ); socket = &http->socket; - if ( strcmp ( http->uri->scheme, "https" ) == 0 ) { - server.st_port = htons ( uri_port ( http->uri, HTTPS_PORT ) ); - if ( ( rc = add_tls ( socket, &socket ) ) != 0 ) + if ( filter ) { + if ( ( rc = filter ( socket, &socket ) ) != 0 ) goto err; } if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM, @@ -512,14 +515,19 @@ static 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/net/tcp/https.c b/src/net/tcp/https.c new file mode 100644 index 000000000..148e4bf01 --- /dev/null +++ b/src/net/tcp/https.c @@ -0,0 +1,46 @@ +/* + * 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 + * + * Secure Hyper Text Transfer Protocol (HTTPS) + * + */ + +#include +#include +#include +#include + +/** + * Initiate an HTTPS connection + * + * @v xfer Data transfer interface + * @v uri Uniform Resource Identifier + * @ret rc Return status code + */ +static int https_open ( struct xfer_interface *xfer, struct uri *uri ) { + return http_open_filter ( xfer, uri, HTTPS_PORT, add_tls ); +} + +/** HTTPS URI opener */ +struct uri_opener https_uri_opener __uri_opener = { + .scheme = "https", + .open = https_open, +}; -- cgit v1.2.3-55-g7522 From 5e26df03251378c2990e74ad0c45eb90e4a98256 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 31 Jul 2007 03:02:21 +0100 Subject: Centralise construction of the DHCP request and response packets. --- src/arch/i386/image/nbi.c | 11 ++-- src/include/gpxe/dhcp.h | 16 +++-- src/interface/pxe/pxe_preboot.c | 18 +++-- src/net/udp/dhcp.c | 141 +++++++++++++++++++++++++++++----------- 4 files changed, 126 insertions(+), 60 deletions(-) (limited to 'src/include') diff --git a/src/arch/i386/image/nbi.c b/src/arch/i386/image/nbi.c index 42596f057..2de381d07 100644 --- a/src/arch/i386/image/nbi.c +++ b/src/arch/i386/image/nbi.c @@ -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/include/gpxe/dhcp.h b/src/include/gpxe/dhcp.h index 645756522..a7cac228d 100644 --- a/src/include/gpxe/dhcp.h +++ b/src/include/gpxe/dhcp.h @@ -503,15 +503,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/interface/pxe/pxe_preboot.c b/src/interface/pxe/pxe_preboot.c index b4e2206a9..e5c44067d 100644 --- a/src/interface/pxe/pxe_preboot.c +++ b/src/interface/pxe/pxe_preboot.c @@ -69,10 +69,12 @@ PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) { PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO *get_cached_info ) { struct dhcp_packet dhcppkt; + int ( * dhcp_packet_creator ) ( struct net_device *, int, + struct dhcp_option_block *, void *, + size_t, struct dhcp_packet * ); + unsigned int msgtype; void *data = NULL; size_t len; - int msgtype; - struct dhcp_option_block *options; userptr_t buffer; int rc; @@ -102,21 +104,17 @@ PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO /* Construct DHCP packet */ if ( get_cached_info->PacketType == PXENV_PACKET_TYPE_DHCP_DISCOVER ) { + dhcp_packet_creator = create_dhcp_request; msgtype = DHCPDISCOVER; - options = &dhcp_request_options; } else { + dhcp_packet_creator = create_dhcp_response; msgtype = DHCPACK; - options = NULL; } - if ( ( rc = create_dhcp_packet ( pxe_netdev, msgtype, data, len, - &dhcppkt ) ) != 0 ) { + if ( ( rc = dhcp_packet_creator ( pxe_netdev, msgtype, NULL, + data, len, &dhcppkt ) ) != 0 ) { DBG ( " failed to build packet" ); goto err; } - if ( ( rc = copy_dhcp_packet_options ( &dhcppkt, options ) ) != 0 ) { - DBG ( " failed to copy options" ); - goto err; - } /* Overwrite filename to work around Microsoft RIS bug */ if ( pxe_ris_filename ) { diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index 26059341c..b82db1a42 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -108,7 +108,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 +270,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 +289,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 +474,97 @@ static struct dhcp_option_block * dhcp_parse ( const struct dhcphdr *dhcphdr, return options; } +/**************************************************************************** + * + * Whole-packet construction + * + */ + +/** + * 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 ) { + 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; + } + } + + 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 +648,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 +669,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", - 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", + 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 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 ); -- cgit v1.2.3-55-g7522 From 79691961bae9a0eaf261615aeff472b5d186dfc0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 31 Jul 2007 03:32:22 +0100 Subject: Add identifier for the network device into the DHCP request. --- src/include/gpxe/dhcp.h | 13 +++++++++++++ src/net/udp/dhcp.c | 25 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+) (limited to 'src/include') diff --git a/src/include/gpxe/dhcp.h b/src/include/gpxe/dhcp.h index a7cac228d..863227867 100644 --- a/src/include/gpxe/dhcp.h +++ b/src/include/gpxe/dhcp.h @@ -168,6 +168,19 @@ struct job_interface; */ #define DHCP_EB_SIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 3 ) +/** Network device descriptor + * + * Byte 0 is the bus type ID; remaining bytes depend on the bus type. + * + * PCI devices: + * Byte 0 : 1 (PCI) + * Byte 1 : PCI vendor ID MSB + * Byte 2 : PCI vendor ID LSB + * Byte 3 : PCI device ID MSB + * Byte 4 : PCI device ID LSB + */ +#define DHCP_EB_BUS_ID DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xb1 ) + /** BIOS drive number * * This is the drive number for a drive emulated via INT 13. 0x80 is diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index b82db1a42..f8f59e2e4 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -480,6 +481,16 @@ static struct dhcp_option_block * dhcp_parse ( const struct dhcphdr *dhcphdr, * */ +/** 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 * @@ -495,6 +506,8 @@ 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 */ @@ -531,6 +544,18 @@ int create_dhcp_request ( struct net_device *netdev, int msgtype, } } + /* 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; } -- cgit v1.2.3-55-g7522 From 291d69d7fd618d11fdcae010af319094cdf4d973 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 31 Jul 2007 14:05:03 +0100 Subject: Added support for draft version of the AoE Boot Firmware Table. --- src/core/abft.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ src/include/gpxe/abft.h | 35 +++++++++++++++++++++++++++++ src/usr/aoeboot.c | 3 +++ 3 files changed, 98 insertions(+) create mode 100644 src/core/abft.c create mode 100644 src/include/gpxe/abft.h (limited to 'src/include') diff --git a/src/core/abft.c b/src/core/abft.c new file mode 100644 index 000000000..af28bbcfb --- /dev/null +++ b/src/core/abft.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * 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/include/gpxe/abft.h b/src/include/gpxe/abft.h new file mode 100644 index 000000000..1c651ef11 --- /dev/null +++ b/src/include/gpxe/abft.h @@ -0,0 +1,35 @@ +#ifndef _GPXE_ABFT_H +#define _GPXE_ABFT_H + +/** @file + * + * AoE boot firmware table + * + */ + +#include +#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/usr/aoeboot.c b/src/usr/aoeboot.c index 1ed4ce49f..ffc17a1c7 100644 --- a/src/usr/aoeboot.c +++ b/src/usr/aoeboot.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -82,6 +83,8 @@ int aoeboot ( const char *root_path ) { 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; -- cgit v1.2.3-55-g7522