From 47a86bca2d2241a5126350e70d117564890a3f01 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 26 Jul 2007 02:13:38 +0100 Subject: [PATCH 01/50] 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 +++ src/net/tcp/iscsi.c | 116 +++++++++++++++++++++++++++++++------ src/usr/iscsiboot.c | 24 ++++++++ 4 files changed, 132 insertions(+), 18 deletions(-) diff --git a/src/include/gpxe/errfile.h b/src/include/gpxe/errfile.h index 0615818f..744ae5b2 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 d9dd4307..e4df6849 100644 --- a/src/include/gpxe/iscsi.h +++ b/src/include/gpxe/iscsi.h @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -501,6 +502,8 @@ struct iscsi_session { char *target_iqn; /** Logical Unit Number (LUN) */ uint64_t lun; + /** Target socket address (recorded only for iBFT) */ + struct sockaddr target_sockaddr; /** Session status * @@ -514,6 +517,11 @@ struct iscsi_session { * Reset upon a successful connection. */ int retry_count; + + /** Username (if any) */ + char *username; + /** Password (if any) */ + char *password; /** CHAP challenge/response */ struct chap_challenge chap; @@ -641,5 +649,6 @@ struct iscsi_session { extern int iscsi_attach ( struct scsi_device *scsi, const char *root_path ); extern void iscsi_detach ( struct scsi_device *scsi ); +extern const char * iscsi_initiator_iqn ( void ); #endif /* _GPXE_ISCSI_H */ diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index 273f0d68..e6459171 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -41,16 +41,16 @@ */ /** iSCSI initiator name (explicitly specified) */ -char *iscsi_initiator_iqn; +static char *iscsi_explicit_initiator_iqn; /** Default iSCSI initiator name (constructed from hostname) */ -char *iscsi_default_initiator_iqn; +static char *iscsi_default_initiator_iqn; /** iSCSI username */ -char *iscsi_username; +static char *iscsi_username; /** iSCSI password */ -char *iscsi_password; +static char *iscsi_password; static void iscsi_start_tx ( struct iscsi_session *iscsi ); static void iscsi_start_login ( struct iscsi_session *iscsi ); @@ -78,6 +78,8 @@ static void iscsi_free ( struct refcnt *refcnt ) { free ( iscsi->target_address ); free ( iscsi->target_iqn ); + free ( iscsi->username ); + free ( iscsi->password ); chap_finish ( &iscsi->chap ); iscsi_rx_buffered_data_done ( iscsi ); free ( iscsi ); @@ -436,22 +438,16 @@ static int iscsi_tx_data_out ( struct iscsi_session *iscsi ) { */ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, void *data, size_t len ) { - char *initiator_iqn; unsigned int used = 0; unsigned int i; if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) { - initiator_iqn = iscsi_initiator_iqn; - if ( ! initiator_iqn ) - initiator_iqn = iscsi_default_initiator_iqn; - if ( ! initiator_iqn ) - initiator_iqn = "iqn.2000-09.org.etherboot:UNKNOWN"; used += ssnprintf ( data + used, len - used, "InitiatorName=%s%c" "TargetName=%s%c" "SessionType=Normal%c" "AuthMethod=CHAP,None%c", - initiator_iqn, 0, + iscsi_initiator_iqn(), 0, iscsi->target_iqn, 0, 0, 0 ); } @@ -460,10 +456,10 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, } if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) && - iscsi_username ) { + iscsi->username ) { used += ssnprintf ( data + used, len - used, "CHAP_N=%s%cCHAP_R=0x", - iscsi_username, 0 ); + iscsi->username, 0 ); for ( i = 0 ; i < iscsi->chap.response_len ; i++ ) { used += ssnprintf ( data + used, len - used, "%02x", iscsi->chap.response[i] ); @@ -647,9 +643,9 @@ static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi, * challenge. */ chap_set_identifier ( &iscsi->chap, identifier ); - if ( iscsi_password ) { - chap_update ( &iscsi->chap, iscsi_password, - strlen ( iscsi_password ) ); + if ( iscsi->password ) { + chap_update ( &iscsi->chap, iscsi->password, + strlen ( iscsi->password ) ); } return 0; @@ -1279,10 +1275,43 @@ static void iscsi_socket_close ( struct xfer_interface *socket, int rc ) { } } +/** + * Handle redirection event + * + * @v socket Transport layer interface + * @v type Location type + * @v args Remaining arguments depend upon location type + * @ret rc Return status code + */ +static int iscsi_vredirect ( struct xfer_interface *socket, int type, + va_list args ) { + struct iscsi_session *iscsi = + container_of ( socket, struct iscsi_session, socket ); + va_list tmp; + struct sockaddr *peer; + + /* Intercept redirects to a LOCATION_SOCKET and record the IP + * address for the iBFT. This is a bit of a hack, but avoids + * inventing an ioctl()-style call to retrieve the socket + * address from a data-xfer interface. + */ + if ( type == LOCATION_SOCKET ) { + va_copy ( tmp, args ); + ( void ) va_arg ( tmp, int ); /* Discard "semantics" */ + peer = va_arg ( tmp, struct sockaddr * ); + memcpy ( &iscsi->target_sockaddr, peer, + sizeof ( iscsi->target_sockaddr ) ); + va_end ( tmp ); + } + + return xfer_vopen ( socket, type, args ); +} + + /** iSCSI socket operations */ static struct xfer_interface_operations iscsi_socket_operations = { .close = iscsi_socket_close, - .vredirect = xfer_vopen, + .vredirect = iscsi_vredirect, .seek = ignore_xfer_seek, .window = unlimited_xfer_window, .alloc_iob = default_xfer_alloc_iob, @@ -1460,6 +1489,32 @@ static int iscsi_parse_root_path ( struct iscsi_session *iscsi, return 0; } +/** + * Set iSCSI authentication details + * + * @v iscsi iSCSI session + * @v username Username, if any + * @v password Password, if any + * @ret rc Return status code + */ +static int iscsi_set_auth ( struct iscsi_session *iscsi, + const char *username, const char *password ) { + + if ( username ) { + iscsi->username = strdup ( username ); + if ( ! iscsi->username ) + return -ENOMEM; + } + + if ( password ) { + iscsi->password = strdup ( password ); + if ( ! iscsi->password ) + return -ENOMEM; + } + + return 0; +} + /** * Attach iSCSI interface * @@ -1482,6 +1537,10 @@ int iscsi_attach ( struct scsi_device *scsi, const char *root_path ) { /* Parse root path */ if ( ( rc = iscsi_parse_root_path ( iscsi, root_path ) ) != 0 ) goto err; + /* Set fields not specified by root path */ + if ( ( rc = iscsi_set_auth ( iscsi, iscsi_username, + iscsi_password ) ) != 0 ) + goto err; /* Sanity checks */ if ( ! iscsi->target_address ) { @@ -1533,7 +1592,7 @@ static int apply_dhcp_iscsi_string ( unsigned int tag, /* Identify string and prefix */ switch ( tag ) { case DHCP_ISCSI_INITIATOR_IQN: - string = &iscsi_initiator_iqn; + string = &iscsi_explicit_initiator_iqn; break; case DHCP_EB_USERNAME: string = &iscsi_username; @@ -1584,3 +1643,24 @@ struct dhcp_option_applicator dhcp_iscsi_applicators[] __dhcp_applicator = { .apply = apply_dhcp_iscsi_string, }, }; + +/**************************************************************************** + * + * Initiator name + * + */ + +/** + * Get iSCSI initiator IQN + * + * @v iscsi iSCSI session + * @ret rc Return status code + */ +const char * iscsi_initiator_iqn ( void ) { + + if ( iscsi_explicit_initiator_iqn ) + return iscsi_explicit_initiator_iqn; + if ( iscsi_default_initiator_iqn ) + return iscsi_default_initiator_iqn; + return "iqn.2000-09.org.etherboot:UNKNOWN"; +} diff --git a/src/usr/iscsiboot.c b/src/usr/iscsiboot.c index f3910f14..a7caebaf 100644 --- a/src/usr/iscsiboot.c +++ b/src/usr/iscsiboot.c @@ -3,9 +3,27 @@ #include #include #include +#include +#include #include #include +/** + * Guess boot network device + * + * @ret netdev Boot network device + */ +static struct net_device * guess_boot_netdev ( void ) { + struct net_device *boot_netdev; + + /* Just use the first network device */ + for_each_netdev ( boot_netdev ) { + return boot_netdev; + } + + return NULL; +} + int iscsiboot ( const char *root_path ) { struct scsi_device scsi; struct int13_drive drive; @@ -30,6 +48,12 @@ int iscsiboot ( const char *root_path ) { drive.drive = find_global_dhcp_num_option ( DHCP_EB_BIOS_DRIVE ); drive.blockdev = &scsi.blockdev; + /* FIXME: ugly, ugly hack */ + struct net_device *netdev = guess_boot_netdev(); + struct iscsi_session *iscsi = + container_of ( scsi.backend, struct iscsi_session, refcnt ); + ibft_fill_data ( netdev, iscsi ); + register_int13_drive ( &drive ); printf ( "Registered as BIOS drive %#02x\n", drive.drive ); printf ( "Booting from BIOS drive %#02x\n", drive.drive ); From 426c2c150d43f1c2661205c9a0956fcbeeaeacc4 Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:31:11 +0200 Subject: [PATCH 02/50] disable AES_convert_key by #if 0'ing it out (suggested by mcb30) --- src/crypto/axtls/aes.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/crypto/axtls/aes.c b/src/crypto/axtls/aes.c index 9154a515..298e4193 100644 --- a/src/crypto/axtls/aes.c +++ b/src/crypto/axtls/aes.c @@ -238,6 +238,9 @@ void AES_set_key(AES_CTX *ctx, const uint8_t *key, memcpy(ctx->iv, iv, 16); } +#if 0 +/** currently unused function **/ + /** * Change a key for decryption. */ @@ -256,6 +259,7 @@ void AES_convert_key(AES_CTX *ctx) *k++ =w; } } +#endif /** * Encrypt a byte sequence (with a block size 16) using the AES cipher. From 1ec846b0380d292dcf306066d7feb2ed0de2e4db Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:31:45 +0200 Subject: [PATCH 03/50] make netboot() static --- src/usr/autoboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c index 97b9a406..4bc43d5a 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -103,7 +103,7 @@ static int boot_root_path ( const char *root_path ) { * @v netdev Network device * @ret rc Return status code */ -int netboot ( struct net_device *netdev ) { +static int netboot ( struct net_device *netdev ) { char buf[256]; int rc; From 42910594f73d8e58ac2f9b85cedc26073504c1fa Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:33:53 +0200 Subject: [PATCH 04/50] disable AES_convert_key by #if 0'ing it out (suggested by mcb30) --- src/crypto/axtls/crypto.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/crypto/axtls/crypto.h b/src/crypto/axtls/crypto.h index f6277adc..855282fa 100644 --- a/src/crypto/axtls/crypto.h +++ b/src/crypto/axtls/crypto.h @@ -54,7 +54,9 @@ void AES_set_key(AES_CTX *ctx, const uint8_t *key, void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length); void AES_cbc_decrypt(AES_CTX *ks, const uint8_t *in, uint8_t *out, int length); +#if 0 /** currently unused function **/ void AES_convert_key(AES_CTX *ctx); +#endif /************************************************************************** * RC4 declarations From 97aade0d1f39638eab55e3502e3f20997fbed2bd Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:35:17 +0200 Subject: [PATCH 05/50] make imgfill_cmdline static --- src/hci/commands/image_cmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hci/commands/image_cmd.c b/src/hci/commands/image_cmd.c index 2d35ebcb..97d41bdf 100644 --- a/src/hci/commands/image_cmd.c +++ b/src/hci/commands/image_cmd.c @@ -45,7 +45,8 @@ * @v nargs Argument count * @v args Argument list */ -void imgfill_cmdline ( struct image *image, unsigned int nargs, char **args ) { +static void imgfill_cmdline ( struct image *image, unsigned int nargs, + char **args ) { size_t used = 0; image->cmdline[0] = '\0'; From 29134e35dcfe2512e3733da6baf4fc03d1258a4b Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:36:16 +0200 Subject: [PATCH 06/50] make md5_steps static --- src/crypto/md5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/md5.c b/src/crypto/md5.c index 09378e20..1fed24fc 100644 --- a/src/crypto/md5.c +++ b/src/crypto/md5.c @@ -54,7 +54,7 @@ static u32 __md5step f4(u32 b, u32 c, u32 d) return ( c ^ ( b | ~d ) ); } -struct md5_step md5_steps[4] = { +static struct md5_step md5_steps[4] = { { .f = f1, .coefficient = 1, From ed06bd8a89d109c4f884f2aa4c23452d60ff58d0 Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:36:53 +0200 Subject: [PATCH 07/50] make isspace static --- src/core/misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/misc.c b/src/core/misc.c index 4219a36c..af83cb14 100644 --- a/src/core/misc.c +++ b/src/core/misc.c @@ -42,7 +42,7 @@ int inet_aton ( const char *cp, struct in_addr *inp ) { return 0; } -int isspace ( int c ) { +static int isspace ( int c ) { switch ( c ) { case ' ': case '\f': From fa11865de0c90599c3c3d30bdf2d1094b556a79b Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:37:29 +0200 Subject: [PATCH 08/50] make add_ndp_entry static --- src/net/ndp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/net/ndp.c b/src/net/ndp.c index 7c684e7c..3b6984db 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -65,8 +65,9 @@ ndp_find_entry ( struct in6_addr *in6 ) { * @v ll_addr Link-layer address * @v state State of the entry - one of the NDP_STATE_XXX values */ -void add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6, - void *ll_addr, int state ) { +static void +add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6, + void *ll_addr, int state ) { struct ndp_entry *ndp; ndp = &ndp_table[next_new_ndp_entry++ % NUM_NDP_ENTRIES]; From 9280f2649a0337b323a6b11063fb8ecb3e56fc70 Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:38:45 +0200 Subject: [PATCH 09/50] make struct _softlabelkeys *slks static --- src/hci/mucurses/slk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hci/mucurses/slk.c b/src/hci/mucurses/slk.c index 4422e15a..600658e7 100644 --- a/src/hci/mucurses/slk.c +++ b/src/hci/mucurses/slk.c @@ -50,7 +50,7 @@ struct _softlabelkeys { short saved_pair; }; -struct _softlabelkeys *slks; +static struct _softlabelkeys *slks; /* I either need to break the primitives here, or write a collection of From 77ffd636fad639197dc297b6b8913190deedb098 Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:39:31 +0200 Subject: [PATCH 10/50] make RTL8169_READ_GMII_REG, RTL8169_WRITE_GMII_REG static --- src/drivers/net/r8169.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drivers/net/r8169.c b/src/drivers/net/r8169.c index d9854e9c..08d1c6f6 100644 --- a/src/drivers/net/r8169.c +++ b/src/drivers/net/r8169.c @@ -400,7 +400,7 @@ static void rtl8169_hw_PHY_config(struct nic *nic __unused); // 20-16 5-bit GMII/MII register address // 15-0 16-bit GMII/MII register data //================================================================= -void RTL8169_WRITE_GMII_REG(unsigned long ioaddr, int RegAddr, int value) +static void RTL8169_WRITE_GMII_REG(unsigned long ioaddr, int RegAddr, int value) { int i; @@ -418,7 +418,7 @@ void RTL8169_WRITE_GMII_REG(unsigned long ioaddr, int RegAddr, int value) } //================================================================= -int RTL8169_READ_GMII_REG(unsigned long ioaddr, int RegAddr) +static int RTL8169_READ_GMII_REG(unsigned long ioaddr, int RegAddr) { int i, value = -1; From 9e7b165017afe1ba82c29d9e914ae85f6f2798c6 Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:39:58 +0200 Subject: [PATCH 11/50] make __udivmoddi4 static --- src/arch/i386/core/udivmod64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/i386/core/udivmod64.c b/src/arch/i386/core/udivmod64.c index faf6fd8c..e5a626cc 100644 --- a/src/arch/i386/core/udivmod64.c +++ b/src/arch/i386/core/udivmod64.c @@ -294,7 +294,7 @@ static void udivmod64 ( const struct uint64 *x, * @ret r Remainder * @ret q Quotient */ -UDItype __udivmoddi4 ( UDItype x, UDItype d, UDItype *r ) { +static UDItype __udivmoddi4 ( UDItype x, UDItype d, UDItype *r ) { UDItype q; UDItype *_x = &x; UDItype *_d = &d; From 0e0843a872f10a7fc093038700e80248ba7c0e76 Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:45:04 +0200 Subject: [PATCH 12/50] make _wgetc static --- src/hci/mucurses/kb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hci/mucurses/kb.c b/src/hci/mucurses/kb.c index f0c8dcb0..9f5c6bb7 100644 --- a/src/hci/mucurses/kb.c +++ b/src/hci/mucurses/kb.c @@ -30,6 +30,7 @@ int has_key ( int kc __unused ) { return TRUE; } +static int _wgetc ( WINDOW *win ) { int timer, c; From ac1e0aa63edcc5af3178e1f49a7b104adc018e1a Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:46:21 +0200 Subject: [PATCH 13/50] remove has_key by #if 0'ing out --- src/hci/mucurses/kb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hci/mucurses/kb.c b/src/hci/mucurses/kb.c index 9f5c6bb7..a68cf26a 100644 --- a/src/hci/mucurses/kb.c +++ b/src/hci/mucurses/kb.c @@ -19,6 +19,7 @@ int m_delay; /* bool m_echo; bool m_cbreak; +#if 0 /** * Check KEY_ code supported status * @@ -28,7 +29,8 @@ bool m_cbreak; */ int has_key ( int kc __unused ) { return TRUE; -} +} +#endif static int _wgetc ( WINDOW *win ) { From 3cb133d27daf11e77fe48d44e1ce639711f903de Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:47:28 +0200 Subject: [PATCH 14/50] make bool m_echo static --- src/hci/mucurses/kb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hci/mucurses/kb.c b/src/hci/mucurses/kb.c index a68cf26a..8f46bf67 100644 --- a/src/hci/mucurses/kb.c +++ b/src/hci/mucurses/kb.c @@ -16,7 +16,7 @@ int m_delay; /* 0 : non-blocking read > 0 : timed blocking read */ -bool m_echo; +static bool m_echo; bool m_cbreak; #if 0 From 909ad9e80a94e55972255e422c5bf49bdd437d93 Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:50:12 +0200 Subject: [PATCH 15/50] make cards_found static and add missing initialization --- src/drivers/net/pcnet32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/pcnet32.c b/src/drivers/net/pcnet32.c index 0328cf2c..63353e3f 100644 --- a/src/drivers/net/pcnet32.c +++ b/src/drivers/net/pcnet32.c @@ -67,7 +67,7 @@ static struct nic_operations pcnet32_operations; /* End Etherboot Specific */ -int cards_found /* __initdata */ ; +static int cards_found = 0 /* __initdata */ ; #ifdef REMOVE /* FIXME: Remove these they are probably pointless */ From 5ec4e2ad5010923c7793f0be8ddce058f170874c Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:58:31 +0200 Subject: [PATCH 16/50] make eltorito_load static --- src/arch/i386/image/eltorito.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/i386/image/eltorito.c b/src/arch/i386/image/eltorito.c index 53e25ca5..9d573106 100644 --- a/src/arch/i386/image/eltorito.c +++ b/src/arch/i386/image/eltorito.c @@ -298,7 +298,7 @@ static int eltorito_load_disk ( struct image *image, * @v image El Torito file * @ret rc Return status code */ -int eltorito_load ( struct image *image ) { +static int eltorito_load ( struct image *image ) { struct eltorito_boot_entry boot_entry; unsigned long bootcat_offset; int rc; From e6cfa7ced96d17acfe745e94066e7f9de7e2716c Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 21:59:09 +0200 Subject: [PATCH 17/50] make http_open static --- src/net/tcp/http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/tcp/http.c b/src/net/tcp/http.c index 88958af5..9c243681 100644 --- a/src/net/tcp/http.c +++ b/src/net/tcp/http.c @@ -465,7 +465,7 @@ static struct xfer_interface_operations http_xfer_operations = { * @v uri Uniform Resource Identifier * @ret rc Return status code */ -int http_open ( struct xfer_interface *xfer, struct uri *uri ) { +static int http_open ( struct xfer_interface *xfer, struct uri *uri ) { struct http_request *http; struct sockaddr_tcpip server; int rc; From dc38303ff999594b89f8f3cf7e701536413728c3 Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 22:00:51 +0200 Subject: [PATCH 18/50] make nbi_load static --- src/arch/i386/image/nbi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/i386/image/nbi.c b/src/arch/i386/image/nbi.c index 3a66e9cb..42596f05 100644 --- a/src/arch/i386/image/nbi.c +++ b/src/arch/i386/image/nbi.c @@ -244,7 +244,7 @@ static int nbi_process_segments ( struct image *image, * @v image NBI image * @ret rc Return status code */ -int nbi_load ( struct image *image ) { +static int nbi_load ( struct image *image ) { struct imgheader imgheader; int rc; From 9a4790667ad5001589211d5082be2ad2fe3624dc Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 22:01:23 +0200 Subject: [PATCH 19/50] make multiboot_load static --- src/arch/i386/image/multiboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/i386/image/multiboot.c b/src/arch/i386/image/multiboot.c index ab0b55ac..546de365 100644 --- a/src/arch/i386/image/multiboot.c +++ b/src/arch/i386/image/multiboot.c @@ -360,7 +360,7 @@ static int multiboot_load_elf ( struct image *image ) { * @v image Multiboot file * @ret rc Return status code */ -int multiboot_load ( struct image *image ) { +static int multiboot_load ( struct image *image ) { struct multiboot_header_info hdr; int rc; From 4cd21524effbaf5e84dd43debac7e17a1b882d1a Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 22:02:11 +0200 Subject: [PATCH 20/50] make struct bnx2 bnx2 static --- src/drivers/net/bnx2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/bnx2.c b/src/drivers/net/bnx2.c index 105e9c3f..fdd6655b 100644 --- a/src/drivers/net/bnx2.c +++ b/src/drivers/net/bnx2.c @@ -43,7 +43,7 @@ static struct bss { struct statistics_block stats_blk; } bnx2_bss; -struct bnx2 bnx2; +static struct bnx2 bnx2; static struct flash_spec flash_table[] = { From 5a0c70340373c1402f1d4ab6782fc4468ea88254 Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 22:03:04 +0200 Subject: [PATCH 21/50] make struct nsx static --- src/drivers/net/ns83820.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/ns83820.c b/src/drivers/net/ns83820.c index 8b8500f4..3262ba6c 100755 --- a/src/drivers/net/ns83820.c +++ b/src/drivers/net/ns83820.c @@ -364,7 +364,7 @@ struct ring_desc { #endif /* Private Storage for the NIC */ -struct ns83820_private { +static struct ns83820_private { u8 *base; int up; long idle; From 5c486ee75d9ba684fd534a502e9f6ed3166773d6 Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 22:05:47 +0200 Subject: [PATCH 22/50] make struct velocity_info_tbl *info static --- src/drivers/net/via-velocity.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/via-velocity.h b/src/drivers/net/via-velocity.h index e0b8809f..761cb3d4 100644 --- a/src/drivers/net/via-velocity.h +++ b/src/drivers/net/via-velocity.h @@ -1204,7 +1204,7 @@ struct velocity_info_tbl { u32 flags; }; -struct velocity_info_tbl *info; +static struct velocity_info_tbl *info; #define mac_hw_mibs_init(regs) {\ BYTE_REG_BITS_ON(MIBCR_MIBFRZ,&((regs)->MIBCR));\ From 63e489c14d15771bb3bc35c6e4609883994acd58 Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Fri, 27 Jul 2007 22:07:01 +0200 Subject: [PATCH 23/50] make struct velocity_info vptx static --- src/drivers/net/via-velocity.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/via-velocity.h b/src/drivers/net/via-velocity.h index 761cb3d4..c296d289 100644 --- a/src/drivers/net/via-velocity.h +++ b/src/drivers/net/via-velocity.h @@ -1768,7 +1768,7 @@ struct velocity_opt { #define TX_DESC_MAX 256 #define TX_DESC_DEF TX_DESC_MIN -struct velocity_info { +static struct velocity_info { // struct list_head list; struct pci_device *pdev; From ca41159f577ba787d52005af850380259690f933 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 28 Jul 2007 16:05:41 +0100 Subject: [PATCH 24/50] Ensure clock line is in the idle state before asserting chip select. --- src/drivers/bitbash/spi_bit.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/drivers/bitbash/spi_bit.c b/src/drivers/bitbash/spi_bit.c index e2175d60..a4e7136f 100644 --- a/src/drivers/bitbash/spi_bit.c +++ b/src/drivers/bitbash/spi_bit.c @@ -153,6 +153,10 @@ static int spi_bit_rw ( struct spi_bus *bus, struct spi_device *device, = container_of ( bus, struct spi_bit_basher, bus ); uint32_t tmp; + /* Set clock line to idle state */ + write_bit ( &spibit->basher, SPI_BIT_SCLK, + ( bus->mode & SPI_MODE_CPOL ) ); + /* Assert chip select on specified slave */ spi_bit_set_slave_select ( spibit, device->slave, SELECT_SLAVE ); From 0ae6c53bd988269d46501854bb98274e497af8dd Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Sat, 28 Jul 2007 23:20:00 +0200 Subject: [PATCH 25/50] Revert "make __udivmoddi4 static" - gcc links to it implicitly This reverts commit 9e7b165017afe1ba82c29d9e914ae85f6f2798c6. --- src/arch/i386/core/udivmod64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/i386/core/udivmod64.c b/src/arch/i386/core/udivmod64.c index e5a626cc..faf6fd8c 100644 --- a/src/arch/i386/core/udivmod64.c +++ b/src/arch/i386/core/udivmod64.c @@ -294,7 +294,7 @@ static void udivmod64 ( const struct uint64 *x, * @ret r Remainder * @ret q Quotient */ -static UDItype __udivmoddi4 ( UDItype x, UDItype d, UDItype *r ) { +UDItype __udivmoddi4 ( UDItype x, UDItype d, UDItype *r ) { UDItype q; UDItype *_x = &x; UDItype *_d = &d; From 52e5c245652a3c71a43b00d2ea21e8aa6697a17c Mon Sep 17 00:00:00 2001 From: Holger Lubitz Date: Sat, 28 Jul 2007 23:20:44 +0200 Subject: [PATCH 26/50] Revert "make isspace static" - prototype for isspace() is defined by C99, so can't be static This reverts commit ed06bd8a89d109c4f884f2aa4c23452d60ff58d0. --- src/core/misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/misc.c b/src/core/misc.c index af83cb14..4219a36c 100644 --- a/src/core/misc.c +++ b/src/core/misc.c @@ -42,7 +42,7 @@ int inet_aton ( const char *cp, struct in_addr *inp ) { return 0; } -static int isspace ( int c ) { +int isspace ( int c ) { switch ( c ) { case ' ': case '\f': From 3e816c3d569c28d1e6335063775d54d623e34e13 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 28 Jul 2007 22:52:20 +0100 Subject: [PATCH 27/50] Revert "make bool m_echo static" - I suspect this to be defined by spec This reverts commit 3cb133d27daf11e77fe48d44e1ce639711f903de. --- src/hci/mucurses/kb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hci/mucurses/kb.c b/src/hci/mucurses/kb.c index 8f46bf67..a68cf26a 100644 --- a/src/hci/mucurses/kb.c +++ b/src/hci/mucurses/kb.c @@ -16,7 +16,7 @@ int m_delay; /* 0 : non-blocking read > 0 : timed blocking read */ -static bool m_echo; +bool m_echo; bool m_cbreak; #if 0 From 0d568ac2197428cbf17a4d83574fa2ff22fd7e72 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 28 Jul 2007 22:55:17 +0100 Subject: [PATCH 28/50] 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(-) diff --git a/src/hci/mucurses/kb.c b/src/hci/mucurses/kb.c index a68cf26a..291ee6ac 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 164dd202..762a63b5 100644 --- a/src/include/curses.h +++ b/src/include/curses.h @@ -566,6 +566,10 @@ static inline bool has_colors ( void ) { return TRUE; } +static inline int has_key ( int kc __unused ) { + return TRUE; +} + static inline int hline ( chtype ch, int n ) { return whline ( stdscr, ch, n ); } From a6a1052096c72660a24fead2e1e7d580ea4f8bc2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 29 Jul 2007 00:17:25 +0100 Subject: [PATCH 29/50] 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(-) diff --git a/src/arch/i386/Config b/src/arch/i386/Config index 87791321..16de411b 100644 --- a/src/arch/i386/Config +++ b/src/arch/i386/Config @@ -130,6 +130,18 @@ endif # this is almost always a win. the kernel uses it, too. CFLAGS+= -mpreferred-stack-boundary=2 +# use regparm for all functions - C functions called from assembly (or +# vice versa) need __cdecl now +CFLAGS+= -mregparm=3 + +# use -mrtd (same __cdecl requirements as above) +CFLAGS+= -mrtd + +# this is the logical complement to -mregparm=3. +# it doesn't currently buy us anything, but if anything ever tries +# to return small structures, let's be prepared +CFLAGS+= -freg-struct-return + LDFLAGS+= -N --no-check-sections ifeq "$(shell uname -s)" "FreeBSD" diff --git a/src/arch/i386/core/relocate.c b/src/arch/i386/core/relocate.c index d3b42d0d..39d00b09 100644 --- a/src/arch/i386/core/relocate.c +++ b/src/arch/i386/core/relocate.c @@ -39,7 +39,7 @@ extern char _end[]; * address space, and returns the physical address of the new location * to the prefix in %edi. */ -void relocate ( struct i386_all_regs *ix86 ) { +__cdecl void relocate ( struct i386_all_regs *ix86 ) { struct memory_map memmap; unsigned long start, end, size, padded_size; unsigned long new_start, new_end; diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c index f7cb6795..53817c7a 100644 --- a/src/arch/i386/interface/pcbios/int13.c +++ b/src/arch/i386/interface/pcbios/int13.c @@ -321,7 +321,7 @@ static int int13_get_extended_parameters ( struct int13_drive *drive, * INT 13 handler * */ -static void int13 ( struct i386_all_regs *ix86 ) { +static __cdecl void int13 ( struct i386_all_regs *ix86 ) { int command = ix86->regs.ah; unsigned int bios_drive = ix86->regs.dl; unsigned int original_bios_drive = bios_drive; diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/i386/interface/pxe/pxe_call.c index 834ca738..1c1b5066 100644 --- a/src/arch/i386/interface/pxe/pxe_call.c +++ b/src/arch/i386/interface/pxe/pxe_call.c @@ -112,7 +112,7 @@ static PXENV_EXIT_t pxenv_unknown ( struct s_PXENV_UNKNOWN *pxenv_unknown ) { * @v es:di Address of PXE parameter block * @ret ax PXE exit code */ -void pxe_api_call ( struct i386_all_regs *ix86 ) { +__cdecl void pxe_api_call ( struct i386_all_regs *ix86 ) { int opcode = ix86->regs.bx; userptr_t parameters = real_to_user ( ix86->segs.es, ix86->regs.di ); size_t param_len; @@ -304,7 +304,7 @@ void pxe_api_call ( struct i386_all_regs *ix86 ) { * @v es:di Address of PXE parameter block * @ret ax PXE exit code */ -void pxe_loader_call ( struct i386_all_regs *ix86 ) { +__cdecl void pxe_loader_call ( struct i386_all_regs *ix86 ) { userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di ); struct s_UNDI_LOADER params; PXENV_EXIT_t ret; diff --git a/src/core/main.c b/src/core/main.c index 09dccc76..88fbb579 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -24,7 +24,7 @@ Literature dealing with the network protocols: * * @ret rc Return status code */ -int main ( void ) { +__cdecl int main ( void ) { initialise(); startup(); diff --git a/src/include/compiler.h b/src/include/compiler.h index 376936d0..b130f28f 100644 --- a/src/include/compiler.h +++ b/src/include/compiler.h @@ -276,6 +276,9 @@ extern void dbg_hex_dump_da ( unsigned long dispaddr, /** Declare a variable or data structure as unused. */ #define __unused __attribute__ (( unused )) +/** Apply standard C calling conventions */ +#define __cdecl __attribute__ (( cdecl , regparm(0) )) + /** * Declare a function as used. * From 43013da9bf02439b4726d8afef15f7ce97d1c469 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 29 Jul 2007 02:31:14 +0100 Subject: [PATCH 30/50] 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 diff --git a/src/drivers/ata/aoedev.c b/src/drivers/ata/aoedev.c deleted file mode 100644 index ff047f10..00000000 --- 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 eb5e1133..85683384 100644 --- a/src/include/gpxe/aoe.h +++ b/src/include/gpxe/aoe.h @@ -81,6 +81,9 @@ struct aoehdr { /** An AoE session */ struct aoe_session { + /** Reference counter */ + struct refcnt refcnt; + /** List of all AoE sessions */ struct list_head list; @@ -103,8 +106,8 @@ struct aoe_session { unsigned int status; /** Byte offset within command's data buffer */ unsigned int command_offset; - /** Asynchronous operation for this command */ - struct async async; + /** Return status code for command */ + int rc; /** Retransmission timer */ struct retry_timer timer; @@ -116,20 +119,8 @@ struct aoe_session { /** Maximum number of sectors per packet */ #define AOE_MAX_COUNT 2 -extern void aoe_open ( struct aoe_session *aoe ); -extern void aoe_close ( struct aoe_session *aoe ); -extern int aoe_issue ( struct aoe_session *aoe, - struct ata_command *command, - struct async *parent ); - -/** An AoE device */ -struct aoe_device { - /** ATA device interface */ - struct ata_device ata; - /** AoE protocol instance */ - struct aoe_session aoe; -}; - -extern int init_aoedev ( struct aoe_device *aoedev ); +extern void aoe_detach ( struct ata_device *ata ); +extern int aoe_attach ( struct ata_device *ata, struct net_device *netdev, + const char *root_path ); #endif /* _GPXE_AOE_H */ diff --git a/src/include/gpxe/ata.h b/src/include/gpxe/ata.h index e0fca7af..b6da3930 100644 --- a/src/include/gpxe/ata.h +++ b/src/include/gpxe/ata.h @@ -4,6 +4,7 @@ #include #include #include +#include /** @file * @@ -195,6 +196,8 @@ struct ata_device { */ int ( * command ) ( struct ata_device *ata, struct ata_command *command ); + /** Backing device */ + struct refcnt *backend; }; extern int init_atadev ( struct ata_device *ata ); diff --git a/src/include/usr/aoeboot.h b/src/include/usr/aoeboot.h new file mode 100644 index 00000000..0421ebcc --- /dev/null +++ b/src/include/usr/aoeboot.h @@ -0,0 +1,6 @@ +#ifndef _USR_AOEBOOT_H +#define _USR_AOEBOOT_H + +extern int aoeboot ( const char *root_path ); + +#endif /* _USR_AOEBOOT_H */ diff --git a/src/net/aoe.c b/src/net/aoe.c index 36721df6..fd82665d 100644 --- a/src/net/aoe.c +++ b/src/net/aoe.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,7 @@ #include #include #include -#include +#include #include /** @file @@ -43,6 +44,14 @@ struct net_protocol aoe_protocol; /** List of all AoE sessions */ static LIST_HEAD ( aoe_sessions ); +static void aoe_free ( struct refcnt *refcnt ) { + struct aoe_session *aoe = + container_of ( refcnt, struct aoe_session, refcnt ); + + netdev_put ( aoe->netdev ); + free ( aoe ); +} + /** * Mark current AoE command complete * @@ -55,8 +64,8 @@ static void aoe_done ( struct aoe_session *aoe, int rc ) { aoe->command->cb.cmd_stat = aoe->status; aoe->command = NULL; - /* Mark async operation as complete */ - async_done ( &aoe->async, rc ); + /* Mark operation as complete */ + aoe->rc = rc; } /** @@ -265,46 +274,99 @@ struct net_protocol aoe_protocol __net_protocol = { .rx = aoe_rx, }; -/** - * Open AoE session - * - * @v aoe AoE session - */ -void aoe_open ( struct aoe_session *aoe ) { - memcpy ( aoe->target, ethernet_protocol.ll_broadcast, - sizeof ( aoe->target ) ); - aoe->tag = AOE_TAG_MAGIC; - aoe->timer.expired = aoe_timer_expired; - list_add ( &aoe->list, &aoe_sessions ); -} - -/** - * Close AoE session - * - * @v aoe AoE session - */ -void aoe_close ( struct aoe_session *aoe ) { - list_del ( &aoe->list ); -} - /** * Issue ATA command via an open AoE session * - * @v aoe AoE session + * @v ata ATA device * @v command ATA command - * @v parent Parent asynchronous operation * @ret rc Return status code - * - * Only one command may be issued concurrently per session. This call - * is non-blocking; use async_wait() to wait for the command to - * complete. */ -int aoe_issue ( struct aoe_session *aoe, struct ata_command *command, - struct async *parent ) { +static int aoe_command ( struct ata_device *ata, + struct ata_command *command ) { + struct aoe_session *aoe = + container_of ( ata->backend, struct aoe_session, refcnt ); + int rc; + aoe->command = command; aoe->status = 0; aoe->command_offset = 0; aoe_send_command ( aoe ); - async_init ( &aoe->async, &default_async_operations, parent ); + + aoe->rc = -EINPROGRESS; + while ( aoe->rc == -EINPROGRESS ) + step(); + rc = aoe->rc; + + return rc; +} + +static int aoe_detached_command ( struct ata_device *ata __unused, + struct ata_command *command __unused ) { + return -ENODEV; +} + +void aoe_detach ( struct ata_device *ata ) { + struct aoe_session *aoe = + container_of ( ata->backend, struct aoe_session, refcnt ); + + stop_timer ( &aoe->timer ); + ata->command = aoe_detached_command; + list_del ( &aoe->list ); + ref_put ( ata->backend ); + ata->backend = NULL; +} + +static int aoe_parse_root_path ( struct aoe_session *aoe, + const char *root_path ) { + char *ptr; + + if ( strncmp ( root_path, "aoe:", 4 ) != 0 ) + return -EINVAL; + ptr = ( ( char * ) root_path + 4 ); + + if ( *ptr++ != 'e' ) + return -EINVAL; + + aoe->major = strtoul ( ptr, &ptr, 10 ); + if ( *ptr++ != '.' ) + return -EINVAL; + + aoe->minor = strtoul ( ptr, &ptr, 10 ); + if ( *ptr ) + return -EINVAL; + return 0; } + +int aoe_attach ( struct ata_device *ata, struct net_device *netdev, + const char *root_path ) { + struct aoe_session *aoe; + int rc; + + /* Allocate and initialise structure */ + aoe = zalloc ( sizeof ( *aoe ) ); + if ( ! aoe ) + return -ENOMEM; + aoe->refcnt.free = aoe_free; + aoe->netdev = netdev_get ( netdev ); + memcpy ( aoe->target, ethernet_protocol.ll_broadcast, + sizeof ( aoe->target ) ); + aoe->tag = AOE_TAG_MAGIC; + aoe->timer.expired = aoe_timer_expired; + + /* Parse root path */ + if ( ( rc = aoe_parse_root_path ( aoe, root_path ) ) != 0 ) + goto err; + + /* Attach parent interface, transfer reference to connection + * list, and return + */ + ata->backend = ref_get ( &aoe->refcnt ); + ata->command = aoe_command; + list_add ( &aoe->list, &aoe_sessions ); + return 0; + + err: + ref_put ( &aoe->refcnt ); + return rc; +} diff --git a/src/tests/aoeboot.c b/src/tests/aoeboot.c deleted file mode 100644 index 17fda2c6..00000000 --- a/src/tests/aoeboot.c +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -static struct aoe_device test_aoedev = { - .aoe = { - .major = 0, - .minor = 0, - }, -}; - -static int aoe_parse ( const char *aoename, struct aoe_session *aoe ) { - char *ptr = ( ( char * ) aoename ); - - if ( *ptr++ != 'e' ) - return -EINVAL; - - aoe->major = strtoul ( ptr, &ptr, 10 ); - if ( *ptr++ != '.' ) - return -EINVAL; - - aoe->minor = strtoul ( ptr, &ptr, 10 ); - if ( *ptr ) - return -EINVAL; - - return 0; -} - -int test_aoeboot ( struct net_device *netdev, const char *aoename, - unsigned int drivenum ) { - struct int13_drive drive; - int rc; - - printf ( "Attempting to boot from AoE device %s via %s\n", - aoename, netdev->name ); - - if ( ( rc = aoe_parse ( aoename, &test_aoedev.aoe ) ) != 0 ) { - printf ( "Invalid AoE device name \"%s\"\n", aoename ); - return rc; - } - - printf ( "Initialising AoE device e%d.%d\n", - test_aoedev.aoe.major, test_aoedev.aoe.minor ); - test_aoedev.aoe.netdev = netdev; - if ( ( rc = init_aoedev ( &test_aoedev ) ) != 0 ) { - printf ( "Could not reach AoE device e%d.%d\n", - test_aoedev.aoe.major, test_aoedev.aoe.minor ); - return rc; - } - - memset ( &drive, 0, sizeof ( drive ) ); - drive.drive = drivenum; - drive.blockdev = &test_aoedev.ata.blockdev; - register_int13_drive ( &drive ); - printf ( "Registered AoE device e%d.%d as BIOS drive %#02x\n", - test_aoedev.aoe.major, test_aoedev.aoe.minor, drive.drive ); - - printf ( "Booting from BIOS drive %#02x\n", drive.drive ); - rc = int13_boot ( drive.drive ); - printf ( "Boot failed\n" ); - - printf ( "Unregistering BIOS drive %#02x\n", drive.drive ); - unregister_int13_drive ( &drive ); - - return rc; -} diff --git a/src/usr/aoeboot.c b/src/usr/aoeboot.c new file mode 100644 index 00000000..54bb5d47 --- /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 4bc43d5a..53283d18 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -27,6 +27,7 @@ #include #include #include +#include #include /** @file @@ -88,13 +89,15 @@ static int boot_filename ( const char *filename ) { * @ret rc Return status code */ static int boot_root_path ( const char *root_path ) { - int rc; /* Quick hack */ - if ( ( rc = iscsiboot ( root_path ) ) != 0 ) - return rc; + if ( strncmp ( root_path, "iscsi:", 6 ) == 0 ) { + return iscsiboot ( root_path ); + } else if ( strncmp ( root_path, "aoe:", 4 ) == 0 ) { + return aoeboot ( root_path ); + } - return 0; + return -ENOTSUP; } /** From 218314e712fdb44683fcb9f974b6cd071dcc4a32 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 29 Jul 2007 03:09:00 +0100 Subject: [PATCH 31/50] 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 diff --git a/src/crypto/hmac.c b/src/crypto/hmac.c new file mode 100644 index 00000000..6884bde9 --- /dev/null +++ b/src/crypto/hmac.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file + * + * Keyed-Hashing for Message Authentication + */ + +#include +#include +#include +#include + +/** + * Reduce HMAC key length + * + * @v digest Digest algorithm to use + * @v digest_ctx Digest context + * @v key Key + * @v key_len Length of key + */ +static void hmac_reduce_key ( struct crypto_algorithm *digest, + void *key, size_t *key_len ) { + uint8_t digest_ctx[digest->ctxsize]; + + digest_init ( digest, digest_ctx ); + digest_update ( digest, digest_ctx, key, *key_len ); + digest_final ( digest, digest_ctx, key ); + *key_len = digest->digestsize; +} + +/** + * Initialise HMAC + * + * @v digest Digest algorithm to use + * @v digest_ctx Digest context + * @v key Key + * @v key_len Length of key + * + * The length of the key should be less than the block size of the + * digest algorithm being used. (If the key length is greater, it + * will be replaced with its own digest, and key_len will be updated + * accordingly). + */ +void hmac_init ( struct crypto_algorithm *digest, void *digest_ctx, + void *key, size_t *key_len ) { + unsigned char k_ipad[digest->blocksize]; + unsigned int i; + + /* Reduce key if necessary */ + if ( *key_len > sizeof ( k_ipad ) ) + hmac_reduce_key ( digest, key, key_len ); + + /* Construct input pad */ + memset ( k_ipad, 0, sizeof ( k_ipad ) ); + memcpy ( k_ipad, key, *key_len ); + for ( i = 0 ; i < sizeof ( k_ipad ) ; i++ ) { + k_ipad[i] ^= 0x36; + } + + /* Start inner hash */ + digest_init ( digest, digest_ctx ); + digest_update ( digest, digest_ctx, k_ipad, sizeof ( k_ipad ) ); +} + +/** + * Finalise HMAC + * + * @v digest Digest algorithm to use + * @v digest_ctx Digest context + * @v key Key + * @v key_len Length of key + * @v hmac HMAC digest to fill in + * + * The length of the key should be less than the block size of the + * digest algorithm being used. (If the key length is greater, it + * will be replaced with its own digest, and key_len will be updated + * accordingly). + */ +void hmac_final ( struct crypto_algorithm *digest, void *digest_ctx, + void *key, size_t *key_len, void *hmac ) { + unsigned char k_opad[digest->blocksize]; + unsigned int i; + + /* Reduce key if necessary */ + if ( *key_len > sizeof ( k_opad ) ) + hmac_reduce_key ( digest, key, key_len ); + + /* Construct output pad */ + memset ( k_opad, 0, sizeof ( k_opad ) ); + memcpy ( k_opad, key, *key_len ); + for ( i = 0 ; i < sizeof ( k_opad ) ; i++ ) { + k_opad[i] ^= 0x5c; + } + + /* Finish inner hash */ + digest_final ( digest, digest_ctx, hmac ); + + /* Perform outer hash */ + digest_init ( digest, digest_ctx ); + digest_update ( digest, digest_ctx, k_opad, sizeof ( k_opad ) ); + digest_update ( digest, digest_ctx, hmac, digest->digestsize ); + digest_final ( digest, digest_ctx, hmac ); +} diff --git a/src/include/gpxe/hmac.h b/src/include/gpxe/hmac.h new file mode 100644 index 00000000..fd34db04 --- /dev/null +++ b/src/include/gpxe/hmac.h @@ -0,0 +1,30 @@ +#ifndef _GPXE_HMAC_H +#define _GPXE_HMAC_H + +/** @file + * + * Keyed-Hashing for Message Authentication + */ + +#include + +/** + * Update HMAC + * + * @v digest Digest algorithm to use + * @v digest_ctx Digest context + * @v data Data + * @v len Length of data + */ +static inline void hmac_update ( struct crypto_algorithm *digest, + void *digest_ctx, const void *data, + size_t len ) { + digest_update ( digest, digest_ctx, data, len ); +} + +extern void hmac_init ( struct crypto_algorithm *digest, void *digest_ctx, + void *key, size_t *key_len ); +extern void hmac_final ( struct crypto_algorithm *digest, void *digest_ctx, + void *key, size_t *key_len, void *hmac ); + +#endif /* _GPXE_HMAC_H */ From 304d1e9fa59e0a79761d48f168b60c093647b948 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 29 Jul 2007 15:26:50 +0100 Subject: [PATCH 32/50] Don't rely on retry.c's periodically calling currticks() in order to allow the UNDI NIC interrupt to happen. --- src/arch/i386/drivers/net/undinet.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/i386/drivers/net/undinet.c index 38032ab1..a5746ce8 100644 --- a/src/arch/i386/drivers/net/undinet.c +++ b/src/arch/i386/drivers/net/undinet.c @@ -423,8 +423,14 @@ static void undinet_poll ( struct net_device *netdev ) { if ( ! undinic->isr_processing ) { /* Do nothing unless ISR has been triggered */ - if ( ! undinet_isr_triggered() ) + if ( ! undinet_isr_triggered() ) { + /* Allow interrupt to occur */ + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "nop\n\t" + "nop\n\t" + "cli\n\t" ) : : ); return; + } /* Start ISR processing */ undinic->isr_processing = 1; From a6467c99a021d2740b195dd29fbe920ea7338967 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 29 Jul 2007 16:05:14 +0100 Subject: [PATCH 33/50] 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 diff --git a/src/core/filter.c b/src/core/filter.c new file mode 100644 index 00000000..51ec5c4b --- /dev/null +++ b/src/core/filter.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +/** @file + * + * Data transfer filters + * + */ + +/* + * Pass-through methods to be used by filters which don't want to + * intercept all events. + * + */ + +void filter_close ( struct xfer_interface *xfer, int rc ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + xfer_close ( other, rc ); +} + +int filter_vredirect ( struct xfer_interface *xfer, int type, + va_list args ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_vredirect ( other, type, args ); +} + +int filter_seek ( struct xfer_interface *xfer, off_t offset, int whence ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_seek ( other, offset, whence ); +} + +size_t filter_window ( struct xfer_interface *xfer ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_window ( other ); +} + +struct io_buffer * filter_alloc_iob ( struct xfer_interface *xfer, + size_t len ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_alloc_iob ( other, len ); +} + +int filter_deliver_iob ( struct xfer_interface *xfer, struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_deliver_iob_meta ( other, iobuf, meta ); +} + +int filter_deliver_raw ( struct xfer_interface *xfer, const void *data, + size_t len ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_deliver_raw ( other, data, len ); +} diff --git a/src/include/gpxe/filter.h b/src/include/gpxe/filter.h new file mode 100644 index 00000000..3943a9e1 --- /dev/null +++ b/src/include/gpxe/filter.h @@ -0,0 +1,75 @@ +#ifndef _GPXE_FILTER_H +#define _GPXE_FILTER_H + +/** @file + * + * Data transfer filters + * + */ + +#include +#include + +/** + * Half of a data transfer filter + * + * Embed two of these structures within a structure implementing a + * data transfer filter, and intialise with filter_init(). You can + * then use the filter_xxx() methods as the data transfer interface + * methods as required. + */ +struct xfer_filter_half { + /** Data transfer interface */ + struct xfer_interface xfer; + /** Other half of the data transfer filter */ + struct xfer_filter_half *other; +}; + +/** + * Get data transfer interface for the other half of a data transfer filter + * + * @v xfer Data transfer interface + * @ret other Other half's data transfer interface + */ +static inline __attribute__ (( always_inline )) struct xfer_interface * +filter_other_half ( struct xfer_interface *xfer ) { + struct xfer_filter_half *half = + container_of ( xfer, struct xfer_filter_half, xfer ); + return &half->other->xfer; +} + +extern void filter_close ( struct xfer_interface *xfer, int rc ); +extern int filter_vredirect ( struct xfer_interface *xfer, int type, + va_list args ); +extern int filter_seek ( struct xfer_interface *xfer, off_t offset, + int whence ); +extern size_t filter_window ( struct xfer_interface *xfer ); +extern struct io_buffer * filter_alloc_iob ( struct xfer_interface *xfer, + size_t len ); +extern int filter_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ); +extern int filter_deliver_raw ( struct xfer_interface *xfer, const void *data, + size_t len ); + +/** + * Initialise a data transfer filter + * + * @v left "Left" half of the filter + * @v left_op Data transfer interface operations for "left" half + * @v right "Right" half of the filter + * @v right_op Data transfer interface operations for "right" half + * @v refcnt Containing object reference counter, or NULL + */ +static inline void filter_init ( struct xfer_filter_half *left, + struct xfer_interface_operations *left_op, + struct xfer_filter_half *right, + struct xfer_interface_operations *right_op, + struct refcnt *refcnt ) { + xfer_init ( &left->xfer, left_op, refcnt ); + xfer_init ( &right->xfer, right_op, refcnt ); + left->other = right; + right->other = left; +} + +#endif /* _GPXE_FILTER_H */ From 3b9bf40682e78a3a20c09e26dfc1a8465884b836 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 29 Jul 2007 17:16:00 +0100 Subject: [PATCH 34/50] Preparation for adding tls.c --- src/include/gpxe/errfile.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/include/gpxe/errfile.h b/src/include/gpxe/errfile.h index 744ae5b2..48db1dc1 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 ) /** @} */ From f62d6486d8ed99ffff130e71b965b3aff5be70c9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Jul 2007 02:42:32 +0100 Subject: [PATCH 35/50] GCC's optimiser seems to screw up if this is left static... --- src/arch/i386/core/udivmod64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/i386/core/udivmod64.c b/src/arch/i386/core/udivmod64.c index faf6fd8c..6293c8e1 100644 --- a/src/arch/i386/core/udivmod64.c +++ b/src/arch/i386/core/udivmod64.c @@ -273,7 +273,7 @@ static inline void udivmod64_lo ( const struct uint64 *x, : "g" ( d->l ), "0" ( x->l ), "1" ( r_dash ) ); } -static void udivmod64 ( const struct uint64 *x, +void udivmod64 ( const struct uint64 *x, const struct uint64 *d, struct uint64 *q, struct uint64 *r ) { From 4ce8d61a5cb8a0e3f68bff8400ba2b8246c9a58f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Jul 2007 02:37:42 +0100 Subject: [PATCH 36/50] Import various libgcc functions from syslinux. Experimentation reveals that gcc ignores -mrtd for the implicit arithmetic functions (e.g. __udivdi3), but not for the implicit memcpy() and memset() functions. Mark the implicit arithmetic functions with __attribute__((cdecl)) to compensate for this. (Note: we cannot mark with with __cdecl, because we define __cdecl to incorporate regparm(0) as well.) --- src/Makefile | 1 + src/arch/i386/core/udivmod64.c | 336 ------------------- src/libgcc/__divdi3.c | 26 ++ src/libgcc/__moddi3.c | 26 ++ src/libgcc/__udivdi3.c | 10 + src/libgcc/__udivmoddi4.c | 32 ++ src/libgcc/__umoddi3.c | 13 + src/libgcc/libgcc.h | 26 ++ src/{core/gcc_implicit.c => libgcc/memcpy.c} | 2 - 9 files changed, 134 insertions(+), 338 deletions(-) delete mode 100644 src/arch/i386/core/udivmod64.c create mode 100644 src/libgcc/__divdi3.c create mode 100644 src/libgcc/__moddi3.c create mode 100644 src/libgcc/__udivdi3.c create mode 100644 src/libgcc/__udivmoddi4.c create mode 100644 src/libgcc/__umoddi3.c create mode 100644 src/libgcc/libgcc.h rename src/{core/gcc_implicit.c => libgcc/memcpy.c} (95%) diff --git a/src/Makefile b/src/Makefile index a567b922..8e0c8bea 100644 --- a/src/Makefile +++ b/src/Makefile @@ -145,6 +145,7 @@ DEBUG_TARGETS += dbg%.o c s # SRCDIRS lists all directories containing source files. # +SRCDIRS += libgcc SRCDIRS += core SRCDIRS += proto SRCDIRS += net net/tcp net/udp diff --git a/src/arch/i386/core/udivmod64.c b/src/arch/i386/core/udivmod64.c deleted file mode 100644 index 6293c8e1..00000000 --- a/src/arch/i386/core/udivmod64.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown . - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/** @file - * - * 64-bit division - * - * The x86 CPU (386 upwards) has a divl instruction which will perform - * unsigned division of a 64-bit dividend by a 32-bit divisor. If the - * resulting quotient does not fit in 32 bits, then a CPU exception - * will occur. - * - * Unsigned integer division is expressed as solving - * - * x = d.q + r 0 <= q, 0 <= r < d - * - * given the dividend (x) and divisor (d), to find the quotient (q) - * and remainder (r). - * - * The x86 divl instruction will solve - * - * x = d.q + r 0 <= q, 0 <= r < d - * - * given x in the range 0 <= x < 2^64 and 1 <= d < 2^32, and causing a - * hardware exception if the resulting q >= 2^32. - * - * We can therefore use divl only if we can prove that the conditions - * - * 0 <= x < 2^64 - * 1 <= d < 2^32 - * q < 2^32 - * - * are satisfied. - * - * - * Case 1 : 1 <= d < 2^32 - * ====================== - * - * We express x as - * - * x = xh.2^32 + xl 0 <= xh < 2^32, 0 <= xl < 2^32 (1) - * - * i.e. split x into low and high dwords. We then solve - * - * xh = d.qh + r' 0 <= qh, 0 <= r' < d (2) - * - * which we can do using a divl instruction since - * - * 0 <= xh < 2^64 since 0 <= xh < 2^32 from (1) (3) - * - * and - * - * 1 <= d < 2^32 by definition of this Case (4) - * - * and - * - * d.qh = xh - r' from (2) - * d.qh <= xh since r' >= 0 from (2) - * qh <= xh since d >= 1 from (2) - * qh < 2^32 since xh < 2^32 from (1) (5) - * - * Having obtained qh and r', we then solve - * - * ( r'.2^32 + xl ) = d.ql + r 0 <= ql, 0 <= r < d (6) - * - * which we can do using another divl instruction since - * - * xl <= 2^32 - 1 from (1), so - * r'.2^32 + xl <= ( r' + 1 ).2^32 - 1 - * r'.2^32 + xl <= d.2^32 - 1 since r' < d from (2) - * r'.2^32 + xl < d.2^32 (7) - * r'.2^32 + xl < 2^64 since d < 2^32 from (4) (8) - * - * and - * - * 1 <= d < 2^32 by definition of this Case (9) - * - * and - * - * d.ql = ( r'.2^32 + xl ) - r from (6) - * d.ql <= r'.2^32 + xl since r >= 0 from (6) - * d.ql < d.2^32 from (7) - * ql < 2^32 since d >= 1 from (2) (10) - * - * This then gives us - * - * x = xh.2^32 + xl from (1) - * x = ( d.qh + r' ).2^32 + xl from (2) - * x = d.qh.2^32 + ( r'.2^32 + xl ) - * x = d.qh.2^32 + d.ql + r from (3) - * x = d.( qh.2^32 + ql ) + r (11) - * - * Letting - * - * q = qh.2^32 + ql (12) - * - * gives - * - * x = d.q + r from (11) and (12) - * - * which is the solution. - * - * - * This therefore gives us a two-step algorithm: - * - * xh = d.qh + r' 0 <= qh, 0 <= r' < d (2) - * ( r'.2^32 + xl ) = d.ql + r 0 <= ql, 0 <= r < d (6) - * - * which translates to - * - * %edx:%eax = 0:xh - * divl d - * qh = %eax - * r' = %edx - * - * %edx:%eax = r':xl - * divl d - * ql = %eax - * r = %edx - * - * Note that if - * - * xh < d - * - * (which is a fast dword comparison) then the first divl instruction - * can be omitted, since the answer will be - * - * qh = 0 - * r = xh - * - * - * Case 2 : 2^32 <= d < 2^64 - * ========================= - * - * We first express d as - * - * d = dh.2^k + dl 2^31 <= dh < 2^32, - * 0 <= dl < 2^k, 1 <= k <= 32 (1) - * - * i.e. find the highest bit set in d, subtract 32, and split d into - * dh and dl at that point. - * - * We then express x as - * - * x = xh.2^k + xl 0 <= xl < 2^k (2) - * - * giving - * - * xh.2^k = x - xl from (2) - * xh.2^k <= x since xl >= 0 from (1) - * xh.2^k < 2^64 since xh < 2^64 from (1) - * xh < 2^(64-k) (3) - * - * We then solve the division - * - * xh = dh.q' + r' 0 <= r' < dh (4) - * - * which we can do using a divl instruction since - * - * 0 <= xh < 2^64 since x < 2^64 and xh < x - * - * and - * - * 1 <= dh < 2^32 from (1) - * - * and - * - * dh.q' = xh - r' from (4) - * dh.q' <= xh since r' >= 0 from (4) - * dh.q' < 2^(64-k) from (3) (5) - * q'.2^31 <= dh.q' since dh >= 2^31 from (1) (6) - * q'.2^31 < 2^(64-k) from (5) and (6) - * q' < 2^(33-k) - * q' < 2^32 since k >= 1 from (1) (7) - * - * This gives us - * - * xh.2^k = dh.q'.2^k + r'.2^k from (4) - * x - xl = ( d - dl ).q' + r'.2^k from (1) and (2) - * x = d.q' + ( r'.2^k + xl ) - dl.q' (8) - * - * Now - * - * r'.2^k + xl < r'.2^k + 2^k since xl < 2^k from (2) - * r'.2^k + xl < ( r' + 1 ).2^k - * r'.2^k + xl < dh.2^k since r' < dh from (4) - * r'.2^k + xl < ( d - dl ) from (1) (9) - * - * - * (missing) - * - * - * This gives us two cases to consider: - * - * case (a): - * - * dl.q' <= ( r'.2^k + xl ) (15a) - * - * in which case - * - * x = d.q' + ( r'.2^k + xl - dl.q' ) - * - * is a direct solution to the division, since - * - * r'.2^k + xl < d from (9) - * ( r'.2^k + xl - dl.q' ) < d since dl >= 0 and q' >= 0 - * - * and - * - * 0 <= ( r'.2^k + xl - dl.q' ) from (15a) - * - * case (b): - * - * dl.q' > ( r'.2^k + xl ) (15b) - * - * Express - * - * x = d.(q'-1) + ( r'.2^k + xl ) + ( d - dl.q' ) - * - * - * (missing) - * - * - * special case: k = 32 cannot be handled with shifts - * - * (missing) - * - */ - -#include -#include - -typedef uint64_t UDItype; - -struct uint64 { - uint32_t l; - uint32_t h; -}; - -static inline void udivmod64_lo ( const struct uint64 *x, - const struct uint64 *d, - struct uint64 *q, - struct uint64 *r ) { - uint32_t r_dash; - - q->h = 0; - r->h = 0; - r_dash = x->h; - - if ( x->h >= d->l ) { - __asm__ ( "divl %2" - : "=&a" ( q->h ), "=&d" ( r_dash ) - : "g" ( d->l ), "0" ( x->h ), "1" ( 0 ) ); - } - - __asm__ ( "divl %2" - : "=&a" ( q->l ), "=&d" ( r->l ) - : "g" ( d->l ), "0" ( x->l ), "1" ( r_dash ) ); -} - -void udivmod64 ( const struct uint64 *x, - const struct uint64 *d, - struct uint64 *q, - struct uint64 *r ) { - - if ( d->h == 0 ) { - udivmod64_lo ( x, d, q, r ); - } else { - assert ( 0 ); - while ( 1 ) {}; - } -} - -/** - * 64-bit division with remainder - * - * @v x Dividend - * @v d Divisor - * @ret r Remainder - * @ret q Quotient - */ -UDItype __udivmoddi4 ( UDItype x, UDItype d, UDItype *r ) { - UDItype q; - UDItype *_x = &x; - UDItype *_d = &d; - UDItype *_q = &q; - UDItype *_r = r; - - udivmod64 ( ( struct uint64 * ) _x, ( struct uint64 * ) _d, - ( struct uint64 * ) _q, ( struct uint64 * ) _r ); - - assert ( ( x == ( ( d * q ) + (*r) ) ) ); - assert ( (*r) < d ); - - return q; -} - -/** - * 64-bit division - * - * @v x Dividend - * @v d Divisor - * @ret q Quotient - */ -UDItype __udivdi3 ( UDItype x, UDItype d ) { - UDItype r; - return __udivmoddi4 ( x, d, &r ); -} - -/** - * 64-bit modulus - * - * @v x Dividend - * @v d Divisor - * @ret q Quotient - */ -UDItype __umoddi3 ( UDItype x, UDItype d ) { - UDItype r; - __udivmoddi4 ( x, d, &r ); - return r; -} diff --git a/src/libgcc/__divdi3.c b/src/libgcc/__divdi3.c new file mode 100644 index 00000000..36f0b37f --- /dev/null +++ b/src/libgcc/__divdi3.c @@ -0,0 +1,26 @@ +/* + * arch/i386/libgcc/__divdi3.c + */ + +#include "libgcc.h" + +LIBGCC int64_t __divdi3(int64_t num, int64_t den) +{ + int minus = 0; + int64_t v; + + if ( num < 0 ) { + num = -num; + minus = 1; + } + if ( den < 0 ) { + den = -den; + minus ^= 1; + } + + v = __udivmoddi4(num, den, NULL); + if ( minus ) + v = -v; + + return v; +} diff --git a/src/libgcc/__moddi3.c b/src/libgcc/__moddi3.c new file mode 100644 index 00000000..eb7784b7 --- /dev/null +++ b/src/libgcc/__moddi3.c @@ -0,0 +1,26 @@ +/* + * arch/i386/libgcc/__moddi3.c + */ + +#include "libgcc.h" + +LIBGCC int64_t __moddi3(int64_t num, int64_t den) +{ + int minus = 0; + int64_t v; + + if ( num < 0 ) { + num = -num; + minus = 1; + } + if ( den < 0 ) { + den = -den; + minus ^= 1; + } + + (void) __udivmoddi4(num, den, (uint64_t *)&v); + if ( minus ) + v = -v; + + return v; +} diff --git a/src/libgcc/__udivdi3.c b/src/libgcc/__udivdi3.c new file mode 100644 index 00000000..9ae0c3dc --- /dev/null +++ b/src/libgcc/__udivdi3.c @@ -0,0 +1,10 @@ +/* + * arch/i386/libgcc/__divdi3.c + */ + +#include "libgcc.h" + +LIBGCC uint64_t __udivdi3(uint64_t num, uint64_t den) +{ + return __udivmoddi4(num, den, NULL); +} diff --git a/src/libgcc/__udivmoddi4.c b/src/libgcc/__udivmoddi4.c new file mode 100644 index 00000000..59966edb --- /dev/null +++ b/src/libgcc/__udivmoddi4.c @@ -0,0 +1,32 @@ +#include "libgcc.h" + +LIBGCC uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem_p) +{ + uint64_t quot = 0, qbit = 1; + + if ( den == 0 ) { + return 1/((unsigned)den); /* Intentional divide by zero, without + triggering a compiler warning which + would abort the build */ + } + + /* Left-justify denominator and count shift */ + while ( (int64_t)den >= 0 ) { + den <<= 1; + qbit <<= 1; + } + + while ( qbit ) { + if ( den <= num ) { + num -= den; + quot += qbit; + } + den >>= 1; + qbit >>= 1; + } + + if ( rem_p ) + *rem_p = num; + + return quot; +} diff --git a/src/libgcc/__umoddi3.c b/src/libgcc/__umoddi3.c new file mode 100644 index 00000000..f6c76cb6 --- /dev/null +++ b/src/libgcc/__umoddi3.c @@ -0,0 +1,13 @@ +/* + * arch/i386/libgcc/__umoddi3.c + */ + +#include "libgcc.h" + +LIBGCC uint64_t __umoddi3(uint64_t num, uint64_t den) +{ + uint64_t v; + + (void) __udivmoddi4(num, den, &v); + return v; +} diff --git a/src/libgcc/libgcc.h b/src/libgcc/libgcc.h new file mode 100644 index 00000000..5b4a6244 --- /dev/null +++ b/src/libgcc/libgcc.h @@ -0,0 +1,26 @@ +#ifndef _LIBGCC_H +#define _LIBGCC_H + +#include +#include + +/* + * It seems as though gcc expects its implicit arithmetic functions to + * be cdecl, even if -mrtd is specified. This is somewhat + * inconsistent; for example, if -mregparm=3 is used then the implicit + * functions do become regparm(3). + * + * The implicit calls to memcpy() and memset() which gcc can generate + * do not seem to have this inconsistency; -mregparm and -mrtd affect + * them in the same way as any other function. + * + */ +#define LIBGCC __attribute__ (( cdecl )) + +extern LIBGCC uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem); +extern LIBGCC uint64_t __udivdi3(uint64_t num, uint64_t den); +extern LIBGCC uint64_t __umoddi3(uint64_t num, uint64_t den); +extern LIBGCC int64_t __divdi3(int64_t num, int64_t den); +extern LIBGCC int64_t __moddi3(int64_t num, int64_t den); + +#endif /* _LIBGCC_H */ diff --git a/src/core/gcc_implicit.c b/src/libgcc/memcpy.c similarity index 95% rename from src/core/gcc_implicit.c rename to src/libgcc/memcpy.c index 8f217b6d..e98b7838 100644 --- a/src/core/gcc_implicit.c +++ b/src/libgcc/memcpy.c @@ -1,6 +1,4 @@ /** @file - * - * gcc implicit functions * * gcc sometimes likes to insert implicit calls to memcpy(). * Unfortunately, there doesn't seem to be any way to prevent it from From 9a9f46ff583cf276b5885800515e8ffb710d9e07 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Jul 2007 02:48:00 +0100 Subject: [PATCH 37/50] Upgrade AXTLS import to version 1.1.5-a --- src/crypto/axtls/aes.c | 4 - src/crypto/axtls/axtls_asn1.c | 867 ++++++++++++++++++++++++++++++++++ src/crypto/axtls/bigint.c | 37 +- src/crypto/axtls/bigint.h | 10 +- src/crypto/axtls/crypto.h | 17 +- src/crypto/axtls/os_port.h | 3 - src/crypto/axtls/rsa.c | 30 +- 7 files changed, 906 insertions(+), 62 deletions(-) create mode 100644 src/crypto/axtls/axtls_asn1.c diff --git a/src/crypto/axtls/aes.c b/src/crypto/axtls/aes.c index 298e4193..9154a515 100644 --- a/src/crypto/axtls/aes.c +++ b/src/crypto/axtls/aes.c @@ -238,9 +238,6 @@ void AES_set_key(AES_CTX *ctx, const uint8_t *key, memcpy(ctx->iv, iv, 16); } -#if 0 -/** currently unused function **/ - /** * Change a key for decryption. */ @@ -259,7 +256,6 @@ void AES_convert_key(AES_CTX *ctx) *k++ =w; } } -#endif /** * Encrypt a byte sequence (with a block size 16) using the AES cipher. diff --git a/src/crypto/axtls/axtls_asn1.c b/src/crypto/axtls/axtls_asn1.c new file mode 100644 index 00000000..74411c70 --- /dev/null +++ b/src/crypto/axtls/axtls_asn1.c @@ -0,0 +1,867 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @file asn1.c + * + * Some primitive asn methods for extraction rsa modulus information. It also + * is used for retrieving information from X.509 certificates. + */ + +#include +#include +#include +#include +#include "crypto.h" + +#define SIG_OID_PREFIX_SIZE 8 + +#define SIG_TYPE_MD2 0x02 +#define SIG_TYPE_MD5 0x04 +#define SIG_TYPE_SHA1 0x05 + +/* Must be an RSA algorithm with either SHA1 or MD5 for verifying to work */ +static const uint8_t sig_oid_prefix[SIG_OID_PREFIX_SIZE] = +{ + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01 +}; + +/* CN, O, OU */ +static const uint8_t g_dn_types[] = { 3, 10, 11 }; + +static int get_asn1_length(const uint8_t *buf, int *offset) +{ + int len, i; + + if (!(buf[*offset] & 0x80)) /* short form */ + { + len = buf[(*offset)++]; + } + else /* long form */ + { + int length_bytes = buf[(*offset)++]&0x7f; + len = 0; + for (i = 0; i < length_bytes; i++) + { + len <<= 8; + len += buf[(*offset)++]; + } + } + + return len; +} + +/** + * Skip the ASN1.1 object type and its length. Get ready to read the object's + * data. + */ +int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type) +{ + if (buf[*offset] != obj_type) + return X509_NOT_OK; + (*offset)++; + return get_asn1_length(buf, offset); +} + +/** + * Skip over an ASN.1 object type completely. Get ready to read the next + * object. + */ +int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type) +{ + int len; + + if (buf[*offset] != obj_type) + return X509_NOT_OK; + (*offset)++; + len = get_asn1_length(buf, offset); + *offset += len; + return 0; +} + +/** + * Read an integer value for ASN.1 data + * Note: This function allocates memory which must be freed by the user. + */ +int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object) +{ + int len; + + if ((len = asn1_next_obj(buf, offset, ASN1_INTEGER)) < 0) + goto end_int_array; + + *object = (uint8_t *)malloc(len); + memcpy(*object, &buf[*offset], len); + *offset += len; + +end_int_array: + return len; +} + +#if 0 + +/** + * Get all the RSA private key specifics from an ASN.1 encoded file + */ +int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx) +{ + int offset = 7; + uint8_t *modulus, *priv_exp, *pub_exp; + int mod_len, priv_len, pub_len; +#ifdef CONFIG_BIGINT_CRT + uint8_t *p, *q, *dP, *dQ, *qInv; + int p_len, q_len, dP_len, dQ_len, qInv_len; +#endif + + /* not in der format */ + if (buf[0] != ASN1_SEQUENCE) /* basic sanity check */ + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: This is not a valid ASN.1 file\n"); +#endif + return X509_INVALID_PRIV_KEY; + } + + /* initialise the RNG */ + RNG_initialize(buf, len); + + mod_len = asn1_get_int(buf, &offset, &modulus); + pub_len = asn1_get_int(buf, &offset, &pub_exp); + priv_len = asn1_get_int(buf, &offset, &priv_exp); + + if (mod_len <= 0 || pub_len <= 0 || priv_len <= 0) + return X509_INVALID_PRIV_KEY; + +#ifdef CONFIG_BIGINT_CRT + p_len = asn1_get_int(buf, &offset, &p); + q_len = asn1_get_int(buf, &offset, &q); + dP_len = asn1_get_int(buf, &offset, &dP); + dQ_len = asn1_get_int(buf, &offset, &dQ); + qInv_len = asn1_get_int(buf, &offset, &qInv); + + if (p_len <= 0 || q_len <= 0 || dP_len <= 0 || dQ_len <= 0 || qInv_len <= 0) + return X509_INVALID_PRIV_KEY; + + RSA_priv_key_new(rsa_ctx, + modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len, + p, p_len, q, p_len, dP, dP_len, dQ, dQ_len, qInv, qInv_len); + + free(p); + free(q); + free(dP); + free(dQ); + free(qInv); +#else + RSA_priv_key_new(rsa_ctx, + modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len); +#endif + + free(modulus); + free(priv_exp); + free(pub_exp); + return X509_OK; +} + +/** + * Get the time of a certificate. Ignore hours/minutes/seconds. + */ +static int asn1_get_utc_time(const uint8_t *buf, int *offset, time_t *t) +{ + int ret = X509_NOT_OK, len, t_offset; + struct tm tm; + + if (buf[(*offset)++] != ASN1_UTC_TIME) + goto end_utc_time; + len = get_asn1_length(buf, offset); + t_offset = *offset; + + memset(&tm, 0, sizeof(struct tm)); + tm.tm_year = (buf[t_offset] - '0')*10 + (buf[t_offset+1] - '0'); + + if (tm.tm_year <= 50) /* 1951-2050 thing */ + { + tm.tm_year += 100; + } + + tm.tm_mon = (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0') - 1; + tm.tm_mday = (buf[t_offset+4] - '0')*10 + (buf[t_offset+5] - '0'); + *t = mktime(&tm); + *offset += len; + ret = X509_OK; + +end_utc_time: + return ret; +} + +/** + * Get the version type of a certificate (which we don't actually care about) + */ +static int asn1_version(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK; + + (*offset) += 2; /* get past explicit tag */ + if (asn1_skip_obj(cert, offset, ASN1_INTEGER)) + goto end_version; + + ret = X509_OK; +end_version: + return ret; +} + +/** + * Retrieve the notbefore and notafter certificate times. + */ +static int asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + return (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || + asn1_get_utc_time(cert, offset, &x509_ctx->not_before) || + asn1_get_utc_time(cert, offset, &x509_ctx->not_after)); +} + +/** + * Get the components of a distinguished name + */ +static int asn1_get_oid_x520(const uint8_t *buf, int *offset) +{ + int dn_type = 0; + int len; + + if ((len = asn1_next_obj(buf, offset, ASN1_OID)) < 0) + goto end_oid; + + /* expect a sequence of 2.5.4.[x] where x is a one of distinguished name + components we are interested in. */ + if (len == 3 && buf[(*offset)++] == 0x55 && buf[(*offset)++] == 0x04) + dn_type = buf[(*offset)++]; + else + { + *offset += len; /* skip over it */ + } + +end_oid: + return dn_type; +} + +/** + * Obtain an ASN.1 printable string type. + */ +static int asn1_get_printable_str(const uint8_t *buf, int *offset, char **str) +{ + int len = X509_NOT_OK; + + /* some certs have this awful crud in them for some reason */ + if (buf[*offset] != ASN1_PRINTABLE_STR && + buf[*offset] != ASN1_TELETEX_STR && buf[*offset] != ASN1_IA5_STR) + goto end_pnt_str; + + (*offset)++; + len = get_asn1_length(buf, offset); + *str = (char *)malloc(len+1); /* allow for null */ + memcpy(*str, &buf[*offset], len); + (*str)[len] = 0; /* null terminate */ + *offset += len; +end_pnt_str: + return len; +} + +/** + * Get the subject name (or the issuer) of a certificate. + */ +static int asn1_name(const uint8_t *cert, int *offset, char *dn[]) +{ + int ret = X509_NOT_OK; + int dn_type; + char *tmp = NULL; + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0) + goto end_name; + + while (asn1_next_obj(cert, offset, ASN1_SET) >= 0) + { + int i, found = 0; + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || + (dn_type = asn1_get_oid_x520(cert, offset)) < 0) + goto end_name; + + if (asn1_get_printable_str(cert, offset, &tmp) < 0) + { + free(tmp); + goto end_name; + } + + /* find the distinguished named type */ + for (i = 0; i < X509_NUM_DN_TYPES; i++) + { + if (dn_type == g_dn_types[i]) + { + if (dn[i] == NULL) + { + dn[i] = tmp; + found = 1; + break; + } + } + } + + if (found == 0) /* not found so get rid of it */ + { + free(tmp); + } + } + + ret = X509_OK; +end_name: + return ret; +} + +/** + * Read the modulus and public exponent of a certificate. + */ +static int asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK, mod_len, pub_len; + uint8_t *modulus, *pub_exp; + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(cert, offset, ASN1_SEQUENCE) || + asn1_next_obj(cert, offset, ASN1_BIT_STRING) < 0) + goto end_pub_key; + + (*offset)++; + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0) + goto end_pub_key; + + mod_len = asn1_get_int(cert, offset, &modulus); + pub_len = asn1_get_int(cert, offset, &pub_exp); + + RSA_pub_key_new(&x509_ctx->rsa_ctx, modulus, mod_len, pub_exp, pub_len); + + free(modulus); + free(pub_exp); + ret = X509_OK; + +end_pub_key: + return ret; +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Read the signature of the certificate. + */ +static int asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK; + + if (cert[(*offset)++] != ASN1_BIT_STRING) + goto end_sig; + + x509_ctx->sig_len = get_asn1_length(cert, offset); + x509_ctx->signature = (uint8_t *)malloc(x509_ctx->sig_len); + memcpy(x509_ctx->signature, &cert[*offset], x509_ctx->sig_len); + *offset += x509_ctx->sig_len; + ret = X509_OK; + +end_sig: + return ret; +} + +/* + * Compare 2 distinguished name components for equality + * @return 0 if a match + */ +static int asn1_compare_dn_comp(const char *dn1, const char *dn2) +{ + int ret = 1; + + if ((dn1 && dn2 == NULL) || (dn1 == NULL && dn2)) goto err_no_match; + + ret = (dn1 && dn2) ? strcmp(dn1, dn2) : 0; + +err_no_match: + return ret; +} + +/** + * Clean up all of the CA certificates. + */ +void remove_ca_certs(CA_CERT_CTX *ca_cert_ctx) +{ + int i = 0; + + while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) + { + x509_free(ca_cert_ctx->cert[i]); + ca_cert_ctx->cert[i++] = NULL; + } + + free(ca_cert_ctx); +} + +/* + * Compare 2 distinguished names for equality + * @return 0 if a match + */ +static int asn1_compare_dn(char * const dn1[], char * const dn2[]) +{ + int i; + + for (i = 0; i < X509_NUM_DN_TYPES; i++) + { + if (asn1_compare_dn_comp(dn1[i], dn2[i])) + { + return 1; + } + } + + return 0; /* all good */ +} + +/** + * Retrieve the signature from a certificate. + */ +const uint8_t *x509_get_signature(const uint8_t *asn1_sig, int *len) +{ + int offset = 0; + const uint8_t *ptr = NULL; + + if (asn1_next_obj(asn1_sig, &offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(asn1_sig, &offset, ASN1_SEQUENCE)) + goto end_get_sig; + + if (asn1_sig[offset++] != ASN1_OCTET_STRING) + goto end_get_sig; + *len = get_asn1_length(asn1_sig, &offset); + ptr = &asn1_sig[offset]; /* all ok */ + +end_get_sig: + return ptr; +} + +#endif + +/** + * Read the signature type of the certificate. We only support RSA-MD5 and + * RSA-SHA1 signature types. + */ +static int asn1_signature_type(const uint8_t *cert, + int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK, len; + + if (cert[(*offset)++] != ASN1_OID) + goto end_check_sig; + + len = get_asn1_length(cert, offset); + + if (memcmp(sig_oid_prefix, &cert[*offset], SIG_OID_PREFIX_SIZE)) + goto end_check_sig; /* unrecognised cert type */ + + x509_ctx->sig_type = cert[*offset + SIG_OID_PREFIX_SIZE]; + + *offset += len; + if (asn1_skip_obj(cert, offset, ASN1_NULL)) + goto end_check_sig; + ret = X509_OK; + +end_check_sig: + return ret; +} + +/** + * Construct a new x509 object. + * @return 0 if ok. < 0 if there was a problem. + */ +int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx) +{ + int begin_tbs, end_tbs; + int ret = X509_NOT_OK, offset = 0, cert_size = 0; + X509_CTX *x509_ctx; + BI_CTX *bi_ctx; + + *ctx = (X509_CTX *)calloc(1, sizeof(X509_CTX)); + x509_ctx = *ctx; + + /* get the certificate size */ + asn1_skip_obj(cert, &cert_size, ASN1_SEQUENCE); + + if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) + goto end_cert; + + begin_tbs = offset; /* start of the tbs */ + end_tbs = begin_tbs; /* work out the end of the tbs */ + asn1_skip_obj(cert, &end_tbs, ASN1_SEQUENCE); + + if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) + goto end_cert; + + if (cert[offset] == ASN1_EXPLICIT_TAG) /* optional version */ + { + if (asn1_version(cert, &offset, x509_ctx)) + goto end_cert; + } + + if (asn1_skip_obj(cert, &offset, ASN1_INTEGER) || /* serial number */ + asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) + goto end_cert; + + /* make sure the signature is ok */ + if (asn1_signature_type(cert, &offset, x509_ctx)) + { + ret = X509_VFY_ERROR_UNSUPPORTED_DIGEST; + goto end_cert; + } + + if (asn1_name(cert, &offset, x509_ctx->ca_cert_dn) || + asn1_validity(cert, &offset, x509_ctx) || + asn1_name(cert, &offset, x509_ctx->cert_dn) || + asn1_public_key(cert, &offset, x509_ctx)) + goto end_cert; + + bi_ctx = x509_ctx->rsa_ctx->bi_ctx; + +#ifdef CONFIG_SSL_CERT_VERIFICATION /* only care if doing verification */ + /* use the appropriate signature algorithm (either SHA1 or MD5) */ + if (x509_ctx->sig_type == SIG_TYPE_MD5) + { + MD5_CTX md5_ctx; + uint8_t md5_dgst[MD5_SIZE]; + MD5Init(&md5_ctx); + MD5Update(&md5_ctx, &cert[begin_tbs], end_tbs-begin_tbs); + MD5Final(&md5_ctx, md5_dgst); + x509_ctx->digest = bi_import(bi_ctx, md5_dgst, MD5_SIZE); + } + else if (x509_ctx->sig_type == SIG_TYPE_SHA1) + { + SHA1_CTX sha_ctx; + uint8_t sha_dgst[SHA1_SIZE]; + SHA1Init(&sha_ctx); + SHA1Update(&sha_ctx, &cert[begin_tbs], end_tbs-begin_tbs); + SHA1Final(&sha_ctx, sha_dgst); + x509_ctx->digest = bi_import(bi_ctx, sha_dgst, SHA1_SIZE); + } + + offset = end_tbs; /* skip the v3 data */ + if (asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) || + asn1_signature(cert, &offset, x509_ctx)) + goto end_cert; +#endif + + if (len) + { + *len = cert_size; + } + + ret = X509_OK; +end_cert: + +#ifdef CONFIG_SSL_FULL_MODE + if (ret) + { + printf("Error: Invalid X509 ASN.1 file\n"); + } +#endif + + return ret; +} + +/** + * Free an X.509 object's resources. + */ +void x509_free(X509_CTX *x509_ctx) +{ + X509_CTX *next; + int i; + + if (x509_ctx == NULL) /* if already null, then don't bother */ + return; + + for (i = 0; i < X509_NUM_DN_TYPES; i++) + { + free(x509_ctx->ca_cert_dn[i]); + free(x509_ctx->cert_dn[i]); + } + + free(x509_ctx->signature); + +#ifdef CONFIG_SSL_CERT_VERIFICATION + if (x509_ctx->digest) + { + bi_free(x509_ctx->rsa_ctx->bi_ctx, x509_ctx->digest); + } +#endif + + RSA_free(x509_ctx->rsa_ctx); + + next = x509_ctx->next; + free(x509_ctx); + x509_free(next); /* clear the chain */ +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Do some basic checks on the certificate chain. + * + * Certificate verification consists of a number of checks: + * - A root certificate exists in the certificate store. + * - The date of the certificate is after the start date. + * - The date of the certificate is before the finish date. + * - The certificate chain is valid. + * - That the certificate(s) are not self-signed. + * - The signature of the certificate is valid. + */ +int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) +{ + int ret = X509_OK, i = 0; + bigint *cert_sig; + X509_CTX *next_cert = NULL; + BI_CTX *ctx; + bigint *mod, *expn; + struct timeval tv; + int match_ca_cert = 0; + + if (cert == NULL || ca_cert_ctx == NULL) + { + ret = X509_VFY_ERROR_NO_TRUSTED_CERT; + goto end_verify; + } + + /* last cert in the chain - look for a trusted cert */ + if (cert->next == NULL) + { + while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) + { + if (asn1_compare_dn(cert->ca_cert_dn, + ca_cert_ctx->cert[i]->cert_dn) == 0) + { + match_ca_cert = 1; + break; + } + + i++; + } + + if (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) + { + next_cert = ca_cert_ctx->cert[i]; + } + else /* trusted cert not found */ + { + ret = X509_VFY_ERROR_NO_TRUSTED_CERT; + goto end_verify; + } + } + else + { + next_cert = cert->next; + } + + gettimeofday(&tv, NULL); + + /* check the not before date */ + if (tv.tv_sec < cert->not_before) + { + ret = X509_VFY_ERROR_NOT_YET_VALID; + goto end_verify; + } + + /* check the not after date */ + if (tv.tv_sec > cert->not_after) + { + ret = X509_VFY_ERROR_EXPIRED; + goto end_verify; + } + + /* check the chain integrity */ + if (asn1_compare_dn(cert->ca_cert_dn, next_cert->cert_dn)) + { + ret = X509_VFY_ERROR_INVALID_CHAIN; + goto end_verify; + } + + /* check for self-signing */ + if (!match_ca_cert && asn1_compare_dn(cert->ca_cert_dn, cert->cert_dn) == 0) + { + ret = X509_VFY_ERROR_SELF_SIGNED; + goto end_verify; + } + + /* check the signature */ + ctx = cert->rsa_ctx->bi_ctx; + mod = next_cert->rsa_ctx->m; + expn = next_cert->rsa_ctx->e; + cert_sig = RSA_sign_verify(ctx, cert->signature, cert->sig_len, + bi_clone(ctx, mod), bi_clone(ctx, expn)); + + if (cert_sig) + { + ret = cert->digest ? /* check the signature */ + bi_compare(cert_sig, cert->digest) : + X509_VFY_ERROR_UNSUPPORTED_DIGEST; + bi_free(ctx, cert_sig); + + if (ret) + goto end_verify; + } + else + { + ret = X509_VFY_ERROR_BAD_SIGNATURE; + goto end_verify; + } + + /* go down the certificate chain using recursion. */ + if (ret == 0 && cert->next) + { + ret = x509_verify(ca_cert_ctx, next_cert); + } + +end_verify: + return ret; +} +#endif + +#if defined (CONFIG_SSL_FULL_MODE) +/** + * Used for diagnostics. + */ +void x509_print(CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) +{ + if (cert == NULL) + return; + + printf("---------------- CERT DEBUG ----------------\n"); + printf("* CA Cert Distinguished Name\n"); + if (cert->ca_cert_dn[X509_COMMON_NAME]) + { + printf("Common Name (CN):\t%s\n", cert->ca_cert_dn[X509_COMMON_NAME]); + } + + if (cert->ca_cert_dn[X509_ORGANIZATION]) + { + printf("Organization (O):\t%s\n", cert->ca_cert_dn[X509_ORGANIZATION]); + } + + if (cert->ca_cert_dn[X509_ORGANIZATIONAL_TYPE]) + { + printf("Organizational Unit (OU): %s\n", + cert->ca_cert_dn[X509_ORGANIZATIONAL_TYPE]); + } + + printf("* Cert Distinguished Name\n"); + if (cert->cert_dn[X509_COMMON_NAME]) + { + printf("Common Name (CN):\t%s\n", cert->cert_dn[X509_COMMON_NAME]); + } + + if (cert->cert_dn[X509_ORGANIZATION]) + { + printf("Organization (O):\t%s\n", cert->cert_dn[X509_ORGANIZATION]); + } + + if (cert->cert_dn[X509_ORGANIZATIONAL_TYPE]) + { + printf("Organizational Unit (OU): %s\n", + cert->cert_dn[X509_ORGANIZATIONAL_TYPE]); + } + + printf("Not Before:\t\t%s", ctime(&cert->not_before)); + printf("Not After:\t\t%s", ctime(&cert->not_after)); + printf("RSA bitsize:\t\t%d\n", cert->rsa_ctx->num_octets*8); + printf("Sig Type:\t\t"); + switch (cert->sig_type) + { + case SIG_TYPE_MD5: + printf("MD5\n"); + break; + case SIG_TYPE_SHA1: + printf("SHA1\n"); + break; + case SIG_TYPE_MD2: + printf("MD2\n"); + break; + default: + printf("Unrecognized: %d\n", cert->sig_type); + break; + } + + printf("Verify:\t\t\t"); + + if (ca_cert_ctx) + { + x509_display_error(x509_verify(ca_cert_ctx, cert)); + } + + printf("\n"); +#if 0 + print_blob("Signature", cert->signature, cert->sig_len); + bi_print("Modulus", cert->rsa_ctx->m); + bi_print("Pub Exp", cert->rsa_ctx->e); +#endif + + if (ca_cert_ctx) + { + x509_print(ca_cert_ctx, cert->next); + } +} + +void x509_display_error(int error) +{ + switch (error) + { + case X509_NOT_OK: + printf("X509 not ok"); + break; + + case X509_VFY_ERROR_NO_TRUSTED_CERT: + printf("No trusted cert is available"); + break; + + case X509_VFY_ERROR_BAD_SIGNATURE: + printf("Bad signature"); + break; + + case X509_VFY_ERROR_NOT_YET_VALID: + printf("Cert is not yet valid"); + break; + + case X509_VFY_ERROR_EXPIRED: + printf("Cert has expired"); + break; + + case X509_VFY_ERROR_SELF_SIGNED: + printf("Cert is self-signed"); + break; + + case X509_VFY_ERROR_INVALID_CHAIN: + printf("Chain is invalid (check order of certs)"); + break; + + case X509_VFY_ERROR_UNSUPPORTED_DIGEST: + printf("Unsupported digest"); + break; + + case X509_INVALID_PRIV_KEY: + printf("Invalid private key"); + break; + } +} +#endif /* CONFIG_SSL_FULL_MODE */ + +#endif diff --git a/src/crypto/axtls/bigint.c b/src/crypto/axtls/bigint.c index 253707e7..2551f593 100644 --- a/src/crypto/axtls/bigint.c +++ b/src/crypto/axtls/bigint.c @@ -77,23 +77,14 @@ static void check(const bigint *bi); */ BI_CTX *bi_initialize(void) { + /* calloc() sets everything to zero */ BI_CTX *ctx = (BI_CTX *)calloc(1, sizeof(BI_CTX)); - - ctx->active_list = NULL; - ctx->active_count = 0; - ctx->free_list = NULL; - ctx->free_count = 0; - ctx->mod_offset = 0; -#ifdef CONFIG_BIGINT_MONTGOMERY - ctx->use_classical = 0; -#endif - + /* the radix */ ctx->bi_radix = alloc(ctx, 2); ctx->bi_radix->comps[0] = 0; ctx->bi_radix->comps[1] = 1; bi_permanent(ctx->bi_radix); - return ctx; } @@ -285,7 +276,7 @@ bigint *bi_add(BI_CTX *ctx, bigint *bia, bigint *bib) * @param bia [in] A bigint. * @param bib [in] Another bigint. * @param is_negative [out] If defined, indicates that the result was negative. - * is_negative may be NULL. + * is_negative may be null. * @return The result of the subtraction. The result is always positive. */ bigint *bi_subtract(BI_CTX *ctx, @@ -482,7 +473,7 @@ bigint *bi_divide(BI_CTX *ctx, bigint *u, bigint *v, int is_mod) /* * Perform an integer divide on a bigint. */ -static bigint *bi_int_divide(__unused BI_CTX *ctx, bigint *biR, comp denom) +static bigint *bi_int_divide(BI_CTX *ctx, bigint *biR, comp denom) { int i = biR->size - 1; long_comp r = 0; @@ -781,7 +772,9 @@ void bi_free_mod(BI_CTX *ctx, int mod_offset) */ static bigint *regular_multiply(BI_CTX *ctx, bigint *bia, bigint *bib) { - int i, j, i_plus_j, n = bia->size, t = bib->size; + int i, j, i_plus_j; + int n = bia->size; + int t = bib->size; bigint *biR = alloc(ctx, n + t); comp *sr = biR->comps; comp *sa = bia->comps; @@ -1059,7 +1052,7 @@ static bigint *alloc(BI_CTX *ctx, int size) #ifdef CONFIG_SSL_FULL_MODE printf("alloc: refs was not 0\n"); #endif - abort(); + abort(); /* create a stack trace from a core dump */ } more_comps(biR, size); @@ -1220,7 +1213,7 @@ static bigint *comp_mod(bigint *bi, int mod) /* * Barrett reduction has no need for some parts of the product, so ignore bits * of the multiply. This routine gives Barrett its big performance - * improvements over classical/Montgomery reduction methods. + * improvements over Classical/Montgomery reduction methods. */ static bigint *partial_multiply(BI_CTX *ctx, bigint *bia, bigint *bib, int inner_partial, int outer_partial) @@ -1293,10 +1286,10 @@ static bigint *partial_multiply(BI_CTX *ctx, bigint *bia, bigint *bib, } /** - * @brief Perform a single barrett reduction. + * @brief Perform a single Barrett reduction. * @param ctx [in] The bigint session context. * @param bi [in] A bigint. - * @return The result of the barrett reduction. + * @return The result of the Barrett reduction. */ bigint *bi_barrett(BI_CTX *ctx, bigint *bi) { @@ -1308,7 +1301,7 @@ bigint *bi_barrett(BI_CTX *ctx, bigint *bi) check(bi); check(bim); - /* use classical method instead - Barrett cannot help here */ + /* use Classical method instead - Barrett cannot help here */ if (bi->size > k*2) { return bi_mod(ctx, bi); @@ -1397,9 +1390,7 @@ bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp) #ifdef CONFIG_BIGINT_SLIDING_WINDOW for (j = i; j > 32; j /= 5) /* work out an optimum size */ - { window_size++; - } /* work out the slide constants */ precompute_slide_window(ctx, window_size, bi); @@ -1420,15 +1411,11 @@ bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp) int part_exp = 0; if (l < 0) /* LSB of exponent will always be 1 */ - { l = 0; - } else { while (exp_bit_is_one(biexp, l) == 0) - { l++; /* go back up */ - } } /* build up the section of the exponent */ diff --git a/src/crypto/axtls/bigint.h b/src/crypto/axtls/bigint.h index e233d798..5a13c5ae 100644 --- a/src/crypto/axtls/bigint.h +++ b/src/crypto/axtls/bigint.h @@ -74,14 +74,14 @@ bigint *bi_str_import(BI_CTX *ctx, const char *data); * appropriate reduction technique (which is bi_mod() when doing classical * reduction). */ -#if defined(CONFIG_BIGINT_CLASSICAL) -#define bi_residue(A, B) bi_mod(A, B) +#if defined(CONFIG_BIGINT_MONTGOMERY) +#define bi_residue(A, B) bi_mont(A, B) +bigint *bi_mont(BI_CTX *ctx, bigint *bixy); #elif defined(CONFIG_BIGINT_BARRETT) #define bi_residue(A, B) bi_barrett(A, B) bigint *bi_barrett(BI_CTX *ctx, bigint *bi); -#else /* CONFIG_BIGINT_MONTGOMERY */ -#define bi_residue(A, B) bi_mont(A, B) -bigint *bi_mont(BI_CTX *ctx, bigint *bixy); +#else /* if defined(CONFIG_BIGINT_CLASSICAL) */ +#define bi_residue(A, B) bi_mod(A, B) #endif #ifdef CONFIG_BIGINT_SQUARE diff --git a/src/crypto/axtls/crypto.h b/src/crypto/axtls/crypto.h index 855282fa..de1dbeb4 100644 --- a/src/crypto/axtls/crypto.h +++ b/src/crypto/axtls/crypto.h @@ -54,9 +54,7 @@ void AES_set_key(AES_CTX *ctx, const uint8_t *key, void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length); void AES_cbc_decrypt(AES_CTX *ks, const uint8_t *in, uint8_t *out, int length); -#if 0 /** currently unused function **/ void AES_convert_key(AES_CTX *ctx); -#endif /************************************************************************** * RC4 declarations @@ -126,7 +124,12 @@ void hmac_sha1(const uint8_t *msg, int length, const uint8_t *key, void RNG_initialize(const uint8_t *seed_buf, int size); void RNG_terminate(void); void get_random(int num_rand_bytes, uint8_t *rand_data); -void get_random_NZ(int num_rand_bytes, uint8_t *rand_data); +//void get_random_NZ(int num_rand_bytes, uint8_t *rand_data); + +#include +static inline void get_random_NZ(int num_rand_bytes, uint8_t *rand_data) { + memset ( rand_data, 0x01, num_rand_bytes ); +} /************************************************************************** * RSA declarations @@ -165,15 +168,15 @@ void RSA_pub_key_new(RSA_CTX **rsa_ctx, const uint8_t *modulus, int mod_len, const uint8_t *pub_exp, int pub_len); void RSA_free(RSA_CTX *ctx); -int RSA_decrypt(RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data, +int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data, int is_decryption); -bigint *RSA_private(RSA_CTX *c, bigint *bi_msg); +bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg); #ifdef CONFIG_SSL_CERT_VERIFICATION bigint *RSA_raw_sign_verify(RSA_CTX *c, bigint *bi_msg); bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, bigint *modulus, bigint *pub_exp); -bigint *RSA_public(RSA_CTX *c, bigint *bi_msg); -int RSA_encrypt(RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, +bigint *RSA_public(const RSA_CTX *c, bigint *bi_msg); +int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, uint8_t *out_data, int is_signing); void RSA_print(const RSA_CTX *ctx); #endif diff --git a/src/crypto/axtls/os_port.h b/src/crypto/axtls/os_port.h index 39e0fb54..babdbfad 100644 --- a/src/crypto/axtls/os_port.h +++ b/src/crypto/axtls/os_port.h @@ -27,9 +27,6 @@ static inline void close ( int fd __unused ) { } typedef void FILE; -#define SEEK_SET 0 -#define SEEK_CUR 0 -#define SEEK_END 0 static inline FILE * fopen ( const char *filename __unused, const char *mode __unused ) { diff --git a/src/crypto/axtls/rsa.c b/src/crypto/axtls/rsa.c index 69db9ae7..389eda57 100644 --- a/src/crypto/axtls/rsa.c +++ b/src/crypto/axtls/rsa.c @@ -28,7 +28,7 @@ #include "crypto.h" #ifdef CONFIG_BIGINT_CRT -static bigint *bi_crt(RSA_CTX *rsa, bigint *bi); +static bigint *bi_crt(const RSA_CTX *rsa, bigint *bi); #endif void RSA_priv_key_new(RSA_CTX **ctx, @@ -72,7 +72,7 @@ void RSA_pub_key_new(RSA_CTX **ctx, { RSA_CTX *rsa_ctx; BI_CTX *bi_ctx = bi_initialize(); - *ctx = (RSA_CTX *)calloc(1, sizeof(RSA_CTX)); /* reset to all 0 */ + *ctx = (RSA_CTX *)calloc(1, sizeof(RSA_CTX)); rsa_ctx = *ctx; rsa_ctx->bi_ctx = bi_ctx; rsa_ctx->num_octets = (mod_len & 0xFFF0); @@ -126,8 +126,8 @@ void RSA_free(RSA_CTX *rsa_ctx) * @return The number of bytes that were originally encrypted. -1 on error. * @see http://www.rsasecurity.com/rsalabs/node.asp?id=2125 */ -int RSA_decrypt(RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data, - int is_decryption) +int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, + uint8_t *out_data, int is_decryption) { int byte_size = ctx->num_octets; uint8_t *block; @@ -155,10 +155,9 @@ int RSA_decrypt(RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data, if (is_decryption == 0) /* PKCS1.5 signing pads with "0xff"s */ { while (block[i++] == 0xff && i < byte_size); + if (block[i-2] != 0xff) - { i = byte_size; /*ensure size is 0 */ - } } else /* PKCS1.5 encryption padding is random */ #endif @@ -169,9 +168,7 @@ int RSA_decrypt(RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data, /* get only the bit we want */ if (size > 0) - { memcpy(out_data, &block[i], size); - } free(block); return size ? size : -1; @@ -180,7 +177,7 @@ int RSA_decrypt(RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data, /** * Performs m = c^d mod n */ -bigint *RSA_private(RSA_CTX *c, bigint *bi_msg) +bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg) { #ifdef CONFIG_BIGINT_CRT return bi_crt(c, bi_msg); @@ -197,7 +194,7 @@ bigint *RSA_private(RSA_CTX *c, bigint *bi_msg) * This should really be in bigint.c (and was at one stage), but needs * access to the RSA_CTX context... */ -static bigint *bi_crt(RSA_CTX *rsa, bigint *bi) +static bigint *bi_crt(const RSA_CTX *rsa, bigint *bi) { BI_CTX *ctx = rsa->bi_ctx; bigint *m1, *m2, *h; @@ -245,7 +242,7 @@ void RSA_print(const RSA_CTX *rsa_ctx) /** * Performs c = m^e mod n */ -bigint *RSA_public(RSA_CTX *c, bigint *bi_msg) +bigint *RSA_public(const RSA_CTX * c, bigint *bi_msg) { c->bi_ctx->mod_offset = BIGINT_M_OFFSET; return bi_mod_power(c->bi_ctx, bi_msg, c->e); @@ -255,7 +252,7 @@ bigint *RSA_public(RSA_CTX *c, bigint *bi_msg) * Use PKCS1.5 for encryption/signing. * see http://www.rsasecurity.com/rsalabs/node.asp?id=2125 */ -int RSA_encrypt(RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, +int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, uint8_t *out_data, int is_signing) { int byte_size = ctx->num_octets; @@ -273,10 +270,7 @@ int RSA_encrypt(RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, else /* randomize the encryption padding with non-zero bytes */ { out_data[1] = 2; - memset(&out_data[2], 0x01, num_pads_needed); -#if 0 get_random_NZ(num_pads_needed, &out_data[2]); -#endif } out_data[2+num_pads_needed] = 0; @@ -291,18 +285,19 @@ int RSA_encrypt(RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, } #if 0 - /** * Take a signature and decrypt it. */ bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, bigint *modulus, bigint *pub_exp) { - uint8_t *block = (uint8_t *)malloc(sig_len); + uint8_t *block; int i, size; bigint *decrypted_bi, *dat_bi; bigint *bir = NULL; + block = (uint8_t *)malloc(sig_len); + /* decrypt */ dat_bi = bi_import(ctx, sig, sig_len); ctx->mod_offset = BIGINT_M_OFFSET; @@ -332,7 +327,6 @@ bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, free(block); return bir; } - #endif #endif /* CONFIG_SSL_CERT_VERIFICATION */ From 6fc9ed167e3f8bd3fc94cd2e21bd108f90a736d6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Jul 2007 02:48:38 +0100 Subject: [PATCH 38/50] 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 diff --git a/src/include/gpxe/tls.h b/src/include/gpxe/tls.h index 19ab9801..a8cf16ef 100644 --- a/src/include/gpxe/tls.h +++ b/src/include/gpxe/tls.h @@ -1,12 +1,171 @@ #ifndef _GPXE_TLS_H #define _GPXE_TLS_H -#include +/** + * @file + * + * Transport Layer Security Protocol + */ -struct stream_application; +#include +#include +#include +#include +#include +#include +#include -static inline int add_tls ( struct stream_application *app __unused ) { - return -ENOTSUP; -} +/** A TLS header */ +struct tls_header { + /** Content type + * + * This is a TLS_TYPE_XXX constant + */ + uint8_t type; + /** Protocol version + * + * This is a TLS_VERSION_XXX constant + */ + uint16_t version; + /** Length of payload */ + uint16_t length; +} __attribute__ (( packed )); + +/** TLS version 1.0 */ +#define TLS_VERSION_TLS_1_0 0x0301 + +/** TLS version 1.1 */ +#define TLS_VERSION_TLS_1_1 0x0302 + +/** Change cipher content type */ +#define TLS_TYPE_CHANGE_CIPHER 20 + +/** Alert content type */ +#define TLS_TYPE_ALERT 21 + +/** Handshake content type */ +#define TLS_TYPE_HANDSHAKE 22 + +/** Application data content type */ +#define TLS_TYPE_DATA 23 + +/* Handshake message types */ +#define TLS_HELLO_REQUEST 0 +#define TLS_CLIENT_HELLO 1 +#define TLS_SERVER_HELLO 2 +#define TLS_CERTIFICATE 11 +#define TLS_SERVER_KEY_EXCHANGE 12 +#define TLS_CERTIFICATE_REQUEST 13 +#define TLS_SERVER_HELLO_DONE 14 +#define TLS_CERTIFICATE_VERIFY 15 +#define TLS_CLIENT_KEY_EXCHANGE 16 +#define TLS_FINISHED 20 + +/* TLS alert levels */ +#define TLS_ALERT_WARNING 1 +#define TLS_ALERT_FATAL 2 + +/* TLS cipher specifications */ +#define TLS_RSA_WITH_NULL_MD5 0x0001 +#define TLS_RSA_WITH_NULL_SHA 0x0002 +#define TLS_RSA_WITH_AES_128_CBC_SHA 0x002f +#define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035 + +/** TLS RX state machine state */ +enum tls_rx_state { + TLS_RX_HEADER = 0, + TLS_RX_DATA, +}; + +/** TLS TX state machine state */ +enum tls_tx_state { + TLS_TX_NONE = 0, + TLS_TX_CLIENT_HELLO, + TLS_TX_CLIENT_KEY_EXCHANGE, + TLS_TX_CHANGE_CIPHER, + TLS_TX_FINISHED, + TLS_TX_DATA +}; + +/** A TLS cipher specification */ +struct tls_cipherspec { + /** Public-key encryption algorithm */ + struct crypto_algorithm *pubkey; + /** Bulk encryption cipher algorithm */ + struct crypto_algorithm *cipher; + /** MAC digest algorithm */ + struct crypto_algorithm *digest; + /** Key length */ + size_t key_len; + /** Dynamically-allocated storage */ + void *dynamic; + /** Public key encryption context */ + void *pubkey_ctx; + /** Bulk encryption cipher context */ + void *cipher_ctx; + /** Next bulk encryption cipher context (TX only) */ + void *cipher_next_ctx; + /** MAC secret */ + void *mac_secret; +}; + +/** A TLS session */ +struct tls_session { + /** Reference counter */ + struct refcnt refcnt; + + /** Plaintext stream */ + struct xfer_filter_half plainstream; + /** Ciphertext stream */ + struct xfer_filter_half cipherstream; + + /** Current TX cipher specification */ + struct tls_cipherspec tx_cipherspec; + /** Next TX cipher specification */ + struct tls_cipherspec tx_cipherspec_pending; + /** Current RX cipher specification */ + struct tls_cipherspec rx_cipherspec; + /** Next RX cipher specification */ + struct tls_cipherspec rx_cipherspec_pending; + /** Premaster secret */ + uint8_t pre_master_secret[48]; + /** Master secret */ + uint8_t master_secret[48]; + /** Server random bytes */ + uint8_t server_random[32]; + /** Client random bytes */ + uint8_t client_random[32]; + /** MD5 context for handshake verification */ + uint8_t handshake_md5_ctx[MD5_CTX_SIZE]; + /** SHA1 context for handshake verification */ + uint8_t handshake_sha1_ctx[SHA1_CTX_SIZE]; + + /** Hack: server RSA public key */ + uint8_t *rsa_mod; + size_t rsa_mod_len; + uint8_t *rsa_pub_exp; + size_t rsa_pub_exp_len; + + /** TX sequence number */ + uint64_t tx_seq; + /** TX state */ + enum tls_tx_state tx_state; + /** TX process */ + struct process process; + + /** RX sequence number */ + uint64_t rx_seq; + /** RX state */ + enum tls_rx_state rx_state; + /** Offset within current RX state */ + size_t rx_rcvd; + /** Current received record header */ + struct tls_header rx_header; + /** Current received raw data buffer */ + void *rx_data; +}; + +extern int add_tls ( struct xfer_interface *xfer, + struct xfer_interface **next ); #endif /* _GPXE_TLS_H */ diff --git a/src/net/tcp/http.c b/src/net/tcp/http.c index 9c243681..bdd791eb 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,19 +488,17 @@ 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, + 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 ) + goto err; + } + if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM, ( struct sockaddr * ) &server, uri->host, NULL ) ) != 0 ) goto err; -#if 0 - if ( strcmp ( http->uri->scheme, "https" ) == 0 ) { - st->st_port = htons ( uri_port ( http->uri, HTTPS_PORT ) ); - if ( ( rc = add_tls ( &http->stream ) ) != 0 ) - goto err; - } -#endif - /* Attach to parent interface, mortalise self, and return */ xfer_plug_plug ( &http->xfer, xfer ); ref_put ( &http->refcnt ); diff --git a/src/net/tls.c b/src/net/tls.c new file mode 100644 index 00000000..1cd995ae --- /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; +} + From 74ad5014c508a7b1903f2d266f7bbb4198ce78bc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Jul 2007 02:58:22 +0100 Subject: [PATCH 39/50] Inhibit compiler warning --- src/crypto/axtls/bigint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/axtls/bigint.c b/src/crypto/axtls/bigint.c index 2551f593..ee51c14d 100644 --- a/src/crypto/axtls/bigint.c +++ b/src/crypto/axtls/bigint.c @@ -473,7 +473,7 @@ bigint *bi_divide(BI_CTX *ctx, bigint *u, bigint *v, int is_mod) /* * Perform an integer divide on a bigint. */ -static bigint *bi_int_divide(BI_CTX *ctx, bigint *biR, comp denom) +static bigint *bi_int_divide(BI_CTX *ctx __unused, bigint *biR, comp denom) { int i = biR->size - 1; long_comp r = 0; From 3b8b06ae90cc73137585ec93d554f3bd351c6d6a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Jul 2007 03:00:15 +0100 Subject: [PATCH 40/50] Added missing #include --- src/include/gpxe/open.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/include/gpxe/open.h b/src/include/gpxe/open.h index abba29c4..beab0a1f 100644 --- a/src/include/gpxe/open.h +++ b/src/include/gpxe/open.h @@ -7,6 +7,7 @@ * */ +#include #include struct xfer_interface; From bf3d8fb1aa08d13e3d15257ba165cf0128f2c5b2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Jul 2007 03:01:04 +0100 Subject: [PATCH 41/50] 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 diff --git a/src/config.h b/src/config.h index 55409b0e..c436d100 100644 --- a/src/config.h +++ b/src/config.h @@ -73,6 +73,7 @@ #define DOWNLOAD_PROTO_TFTP /* Trivial File Transfer Protocol */ #undef DOWNLOAD_PROTO_NFS /* Network File System */ #define DOWNLOAD_PROTO_HTTP /* Hypertext Transfer Protocol */ +#undef DOWNLOAD_PROTO_HTTPS /* Secure Hypertext Transfer Protocol */ #undef DOWNLOAD_PROTO_FTP /* File Transfer Protocol */ #undef DOWNLOAD_PROTO_TFTM /* Multicast Trivial File Transfer Protocol */ #undef DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */ diff --git a/src/core/config.c b/src/core/config.c index a2194e8d..7e70c12a 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -87,6 +87,9 @@ REQUIRE_OBJECT ( nfs ); #ifdef DOWNLOAD_PROTO_HTTP REQUIRE_OBJECT ( http ); #endif +#ifdef DOWNLOAD_PROTO_HTTPS +REQUIRE_OBJECT ( https ); +#endif #ifdef DOWNLOAD_PROTO_FTP REQUIRE_OBJECT ( ftp ); #endif diff --git a/src/include/gpxe/http.h b/src/include/gpxe/http.h index a2abec1d..fa92a950 100644 --- a/src/include/gpxe/http.h +++ b/src/include/gpxe/http.h @@ -13,4 +13,9 @@ /** HTTPS default port */ #define HTTPS_PORT 443 +extern int http_open_filter ( struct xfer_interface *xfer, struct uri *uri, + unsigned int default_port, + int ( * filter ) ( struct xfer_interface *, + struct xfer_interface ** ) ); + #endif /* _GPXE_HTTP_H */ diff --git a/src/net/tcp/http.c b/src/net/tcp/http.c index bdd791eb..727c0333 100644 --- a/src/net/tcp/http.c +++ b/src/net/tcp/http.c @@ -40,7 +40,6 @@ #include #include #include -#include #include /** HTTP receive state */ @@ -459,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 00000000..148e4bf0 --- /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, +}; From 60e3aa4d753dda70269a4f81376d9fe137c8a80b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Jul 2007 03:06:08 +0100 Subject: [PATCH 42/50] Change #warnings to FIXMEs, so that we can build without NO_WERROR=1 --- src/net/tls.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/net/tls.c b/src/net/tls.c index 1cd995ae..dcdb6601 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -97,7 +97,7 @@ static void tls_close ( struct tls_session *tls, int rc ) { * @v len Length of buffer */ static void tls_generate_random ( void *data, size_t len ) { -#warning "Placeholder" + /* FIXME: Some real random data source would be nice... */ memset ( data, 0x01, len ); } @@ -345,7 +345,7 @@ static int tls_generate_keys ( struct tls_session *tls ) { return rc; } -#warning "AES needs to be fixed to not require this" + /* FIXME: AES needs to be fixed to not require this */ AES_convert_key ( rx_cipherspec->cipher_ctx ); DBGC ( tls, "TLS %p RX key:\n", tls ); @@ -498,7 +498,7 @@ static int tls_change_cipher ( struct tls_session *tls, struct tls_cipherspec *pending, struct tls_cipherspec *active ) { -#warning "Why is this disabled?" + /* FIXME: Why is this disabled? */ #if 0 /* Sanity check */ if ( ( pending->pubkey == &crypto_null ) || @@ -622,7 +622,7 @@ static int tls_send_client_hello ( struct tls_session *tls ) { * @ret rc Return status code */ static int tls_send_client_key_exchange ( struct tls_session *tls ) { -#warning "Hack alert" + /* FIXME: Hack alert */ RSA_CTX *rsa_ctx; RSA_pub_key_new ( &rsa_ctx, tls->rsa_mod, tls->rsa_mod_len, tls->rsa_pub_exp, tls->rsa_pub_exp_len ); @@ -639,7 +639,7 @@ static int tls_send_client_key_exchange ( struct tls_session *tls ) { key_xchg.encrypted_pre_master_secret_len = htons ( sizeof ( key_xchg.encrypted_pre_master_secret ) ); -#warning "Hack alert" + /* FIXME: Hack alert */ DBGC ( tls, "RSA encrypting plaintext, modulus, exponent:\n" ); DBGC_HD ( tls, &tls->pre_master_secret, sizeof ( tls->pre_master_secret ) ); @@ -838,6 +838,9 @@ static int tls_new_certificate ( struct tls_session *tls, uint8_t *cert = certificate->asn1_start; int offset = 0; + /* FIXME */ + (void) len; + if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 || asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 || asn1_skip_obj(cert, &offset, ASN1_EXPLICIT_TAG) || @@ -919,7 +922,7 @@ static int tls_new_server_hello_done ( struct tls_session *tls, static int tls_new_finished ( struct tls_session *tls, void *data, size_t len ) { -#warning "Handle this properly" + /* FIXME: Handle this properly */ tls->tx_state = TLS_TX_DATA; ( void ) data; ( void ) len; @@ -1091,7 +1094,7 @@ static void * tls_assemble_block ( struct tls_session *tls, void *mac; void *padding; -#warning "TLSv1.1 has an explicit IV" + /* FIXME: TLSv1.1 has an explicit IV */ iv_len = 0; /* Calculate block-ciphered struct length */ @@ -1289,7 +1292,7 @@ static int tls_split_block ( struct tls_session *tls, } iv_len = tls->rx_cipherspec.cipher->blocksize; -#warning "TLSv1.1 uses an explicit IV" + /* FIXME: TLSv1.1 uses an explicit IV */ iv_len = 0; mac_len = tls->rx_cipherspec.digest->digestsize; From 847ac4f3363ebdc4e2eb4fa462a09443256a1c38 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Jul 2007 03:30:27 +0100 Subject: [PATCH 43/50] tls_change_cipher() can complain about null cipher and digest algorithms; we only need the pubkey check disabled (and only because pubkey algorithms are not yet integrated into the crypto_algorithm subsystem). --- src/net/tls.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/net/tls.c b/src/net/tls.c index dcdb6601..64e44b55 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -498,16 +498,14 @@ static int tls_change_cipher ( struct tls_session *tls, struct tls_cipherspec *pending, struct tls_cipherspec *active ) { - /* FIXME: Why is this disabled? */ -#if 0 /* Sanity check */ - if ( ( pending->pubkey == &crypto_null ) || + if ( /* FIXME (when pubkey is not hard-coded to RSA): + * ( pending->pubkey == &crypto_null ) || */ ( pending->cipher == &crypto_null ) || ( pending->digest == &crypto_null ) ) { DBGC ( tls, "TLS %p refusing to use null cipher\n", tls ); return -ENOTSUP; } -#endif tls_clear_cipher ( tls, active ); memswap ( active, pending, sizeof ( *active ) ); From d0abffc2f682975171073b856458b31c79ed3eef Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Jul 2007 23:58:59 +0100 Subject: [PATCH 44/50] Added the AoE boot information table as used by Vampyre's AoE initiator. This table needs to be replaced by something similar to iBFT (i.e. scanned for and identified by signature, rather than being at a fixed address), but it works for now. --- src/usr/aoeboot.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/usr/aoeboot.c b/src/usr/aoeboot.c index 54bb5d47..91b80e34 100644 --- a/src/usr/aoeboot.c +++ b/src/usr/aoeboot.c @@ -8,6 +8,26 @@ #include #include +/** + * AoE boot information block + * + * Must be placed at 40:f0. + * + * This structure needs to be replaced by an ACPI table or similar. + */ +struct aoe_boot_info { + /** Must be 0x01 */ + uint8_t one; + /** Client MAC address */ + uint8_t client[ETH_ALEN]; + /** Server MAC address */ + uint8_t server[ETH_ALEN]; + /** Shelf number */ + uint16_t shelf; + /** Slot number */ + uint8_t slot; +} __attribute__ (( packed )); + /** * Guess boot network device * @@ -48,6 +68,19 @@ int aoeboot ( const char *root_path ) { goto error_init; } + /* FIXME: ugly, ugly hack */ + struct aoe_session *aoe = + container_of ( ata.backend, struct aoe_session, refcnt ); + struct aoe_boot_info boot_info; + boot_info.one = 0x01; + memcpy ( boot_info.client, netdev->ll_addr, + sizeof ( boot_info.client ) ); + memcpy ( boot_info.server, aoe->target, + sizeof ( boot_info.server ) ); + boot_info.shelf = aoe->major; + boot_info.slot = aoe->minor; + copy_to_real ( 0x40, 0xf0, &boot_info, sizeof ( boot_info ) ); + drive.drive = find_global_dhcp_num_option ( DHCP_EB_BIOS_DRIVE ); drive.blockdev = &ata.blockdev; From 5e26df03251378c2990e74ad0c45eb90e4a98256 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 31 Jul 2007 03:02:21 +0100 Subject: [PATCH 45/50] 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(-) diff --git a/src/arch/i386/image/nbi.c b/src/arch/i386/image/nbi.c index 42596f05..2de381d0 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 64575652..a7cac228 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 b4e2206a..e5c44067 100644 --- a/src/interface/pxe/pxe_preboot.c +++ b/src/interface/pxe/pxe_preboot.c @@ -69,10 +69,12 @@ PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) { PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO *get_cached_info ) { struct dhcp_packet dhcppkt; + int ( * dhcp_packet_creator ) ( struct net_device *, int, + struct dhcp_option_block *, void *, + size_t, struct dhcp_packet * ); + unsigned int msgtype; void *data = NULL; size_t len; - int msgtype; - struct dhcp_option_block *options; userptr_t buffer; int rc; @@ -102,21 +104,17 @@ PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO /* Construct DHCP packet */ if ( get_cached_info->PacketType == PXENV_PACKET_TYPE_DHCP_DISCOVER ) { + dhcp_packet_creator = create_dhcp_request; msgtype = DHCPDISCOVER; - options = &dhcp_request_options; } else { + dhcp_packet_creator = create_dhcp_response; msgtype = DHCPACK; - options = NULL; } - if ( ( rc = create_dhcp_packet ( pxe_netdev, msgtype, data, len, - &dhcppkt ) ) != 0 ) { + if ( ( rc = dhcp_packet_creator ( pxe_netdev, msgtype, NULL, + data, len, &dhcppkt ) ) != 0 ) { DBG ( " failed to build packet" ); goto err; } - if ( ( rc = copy_dhcp_packet_options ( &dhcppkt, options ) ) != 0 ) { - DBG ( " failed to copy options" ); - goto err; - } /* Overwrite filename to work around Microsoft RIS bug */ if ( pxe_ris_filename ) { diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index 26059341..b82db1a4 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", + if ( ( rc = create_dhcp_request ( dhcp->netdev, dhcp->state, + dhcp->options, iobuf->data, + iob_tailroom ( iobuf ), + &dhcppkt ) ) != 0 ) { + DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n", dhcp, strerror ( rc ) ); goto done; } - /* Copy in options common to all requests */ - if ( ( rc = copy_dhcp_packet_options ( &dhcppkt, - &dhcp_request_options ) ) != 0){ - DBGC ( dhcp, "DHCP %p could not set common DHCP options: %s\n", - dhcp, strerror ( rc ) ); - goto done; - } - - /* Copy any required options from previous server repsonse */ - if ( dhcp->options ) { - if ( ( rc = copy_dhcp_packet_option ( &dhcppkt, dhcp->options, - DHCP_SERVER_IDENTIFIER, - DHCP_SERVER_IDENTIFIER ) ) != 0 ) { - DBGC ( dhcp, "DHCP %p could not set server identifier " - "option: %s\n", dhcp, strerror ( rc ) ); - goto done; - } - if ( ( rc = copy_dhcp_packet_option ( &dhcppkt, dhcp->options, - DHCP_EB_YIADDR, - DHCP_REQUESTED_ADDRESS ) ) != 0 ) { - DBGC ( dhcp, "DHCP %p could not set requested address " - "option: %s\n", dhcp, strerror ( rc ) ); - goto done; - } - } - /* Transmit the packet */ iob_put ( iobuf, dhcppkt.len ); rc = xfer_deliver_iob_meta ( &dhcp->xfer, iobuf, &meta ); From 79691961bae9a0eaf261615aeff472b5d186dfc0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 31 Jul 2007 03:32:22 +0100 Subject: [PATCH 46/50] 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(+) diff --git a/src/include/gpxe/dhcp.h b/src/include/gpxe/dhcp.h index a7cac228..86322786 100644 --- a/src/include/gpxe/dhcp.h +++ b/src/include/gpxe/dhcp.h @@ -168,6 +168,19 @@ struct job_interface; */ #define DHCP_EB_SIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 3 ) +/** Network device descriptor + * + * Byte 0 is the bus type ID; remaining bytes depend on the bus type. + * + * PCI devices: + * Byte 0 : 1 (PCI) + * Byte 1 : PCI vendor ID MSB + * Byte 2 : PCI vendor ID LSB + * Byte 3 : PCI device ID MSB + * Byte 4 : PCI device ID LSB + */ +#define DHCP_EB_BUS_ID DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xb1 ) + /** BIOS drive number * * This is the drive number for a drive emulated via INT 13. 0x80 is diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index b82db1a4..f8f59e2e 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -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; } From dde3f99e4ecbf2fac98dd4da429fb446d543f50e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 31 Jul 2007 03:48:43 +0100 Subject: [PATCH 47/50] Fix endianness of "shelf" field --- src/usr/aoeboot.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/usr/aoeboot.c b/src/usr/aoeboot.c index 91b80e34..1ed4ce49 100644 --- a/src/usr/aoeboot.c +++ b/src/usr/aoeboot.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -77,7 +78,7 @@ int aoeboot ( const char *root_path ) { sizeof ( boot_info.client ) ); memcpy ( boot_info.server, aoe->target, sizeof ( boot_info.server ) ); - boot_info.shelf = aoe->major; + boot_info.shelf = htons ( aoe->major ); boot_info.slot = aoe->minor; copy_to_real ( 0x40, 0xf0, &boot_info, sizeof ( boot_info ) ); From 291d69d7fd618d11fdcae010af319094cdf4d973 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 31 Jul 2007 14:05:03 +0100 Subject: [PATCH 48/50] 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 diff --git a/src/core/abft.c b/src/core/abft.c new file mode 100644 index 00000000..af28bbcf --- /dev/null +++ b/src/core/abft.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +/** @file + * + * AoE Boot Firmware Table + * + */ + +#define abftab __use_data16 ( abftab ) +/** The aBFT used by gPXE */ +struct abft_table __data16 ( abftab ) __attribute__ (( aligned ( 16 ) )) = { + /* ACPI header */ + .acpi = { + .signature = ABFT_SIG, + .length = sizeof ( abftab ), + .revision = 1, + .oem_id = "FENSYS", + .oem_table_id = "gPXE", + }, +}; + +/** + * Fill in all variable portions of aBFT + * + * @v aoe AoE session + */ +void abft_fill_data ( struct aoe_session *aoe ) { + + /* Fill in boot parameters */ + abftab.shelf = aoe->major; + abftab.slot = aoe->minor; + memcpy ( abftab.mac, aoe->netdev->ll_addr, sizeof ( abftab.mac ) ); + + /* Update checksum */ + acpi_fix_checksum ( &abftab.acpi ); + + DBG ( "AoE boot firmware table:\n" ); + DBG_HD ( &abftab, sizeof ( abftab ) ); +} diff --git a/src/include/gpxe/abft.h b/src/include/gpxe/abft.h new file mode 100644 index 00000000..1c651ef1 --- /dev/null +++ b/src/include/gpxe/abft.h @@ -0,0 +1,35 @@ +#ifndef _GPXE_ABFT_H +#define _GPXE_ABFT_H + +/** @file + * + * AoE boot firmware table + * + */ + +#include +#include +#include + +/** AoE boot firmware table signature */ +#define ABFT_SIG "aBFT" + +/** + * AoE Boot Firmware Table (aBFT) + */ +struct abft_table { + /** ACPI header */ + struct acpi_description_header acpi; + /** AoE shelf */ + uint16_t shelf; + /** AoE slot */ + uint8_t slot; + /** Reserved */ + uint8_t reserved_a; + /** MAC address */ + uint8_t mac[ETH_ALEN]; +} __attribute__ (( packed )); + +extern void abft_fill_data ( struct aoe_session *aoe ); + +#endif /* _GPXE_ABFT_H */ diff --git a/src/usr/aoeboot.c b/src/usr/aoeboot.c index 1ed4ce49..ffc17a1c 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; From c5d911406460467f2242ee7bcbffe063038fea64 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 31 Jul 2007 18:09:18 +0100 Subject: [PATCH 49/50] Allow loading of multiple initramfs images. --- src/arch/i386/image/bzimage.c | 113 +++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 51 deletions(-) diff --git a/src/arch/i386/image/bzimage.c b/src/arch/i386/image/bzimage.c index 3a3e82ef..161d2675 100644 --- a/src/arch/i386/image/bzimage.c +++ b/src/arch/i386/image/bzimage.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include @@ -168,58 +167,77 @@ static int bzimage_set_cmdline ( struct image *image, } /** - * Load initrd, if any + * Load initrds, if any * * @v image bzImage image * @v exec_ctx Execution context * @ret rc Return status code */ static int bzimage_load_initrd ( struct image *image, - struct bzimage_exec_context *exec_ctx, - struct image *initrd ) { - physaddr_t start = user_to_phys ( initrd->data, 0 ); + struct bzimage_exec_context *exec_ctx ) { + struct image *initrd; + size_t initrd_len; + size_t total_len = 0; + size_t offset = 0; + physaddr_t start; int rc; - DBGC ( image, "bzImage %p loading initrd %p (%s)\n", - image, initrd, initrd->name ); - - /* Find a suitable start address */ - if ( ( start + initrd->len ) <= exec_ctx->mem_limit ) { - /* Just use initrd in situ */ - DBGC ( image, "bzImage %p using initrd as [%lx,%lx)\n", - image, start, ( start + initrd->len ) ); - } else { - for ( ; ; start -= 0x100000 ) { - /* Check that we're not going to overwrite the - * kernel itself. This check isn't totally - * accurate, but errs on the side of caution. - */ - if ( start <= ( BZI_LOAD_HIGH_ADDR + image->len ) ) { - DBGC ( image, "bzImage %p could not find a " - "location for initrd\n", image ); - return -ENOBUFS; - } - /* Check that we are within the kernel's range */ - if ( ( start + initrd->len ) > exec_ctx->mem_limit ) - continue; - /* Prepare and verify segment */ - if ( ( rc = prep_segment ( phys_to_user ( start ), - initrd->len, - initrd->len ) ) != 0 ) - continue; - /* Copy to segment */ - DBGC ( image, "bzImage %p relocating initrd to " - "[%lx,%lx)\n", image, start, - ( start + initrd->len ) ); - memcpy_user ( phys_to_user ( start ), 0, - initrd->data, 0, initrd->len ); - break; - } + /* Add up length of all initrd images */ + for_each_image ( initrd ) { + if ( initrd->type != &initrd_image_type ) + continue; + initrd_len = ( ( image->len + 0x0f ) & ~0x0f ); + total_len += initrd_len; } + if ( ! total_len ) + return 0; + + /* Find a suitable start address. Try 1MB boundaries, + * starting from the downloaded kernel image itself and + * working downwards until we hit an available region. + */ + for ( start = ( user_to_phys ( image->data, 0 ) & ~0xfffff ) ; ; + start -= 0x100000 ) { + /* Check that we're not going to overwrite the + * kernel itself. This check isn't totally + * accurate, but errs on the side of caution. + */ + if ( start <= ( BZI_LOAD_HIGH_ADDR + image->len ) ) { + DBGC ( image, "bzImage %p could not find a location " + "for initrd\n", image ); + return -ENOBUFS; + } + /* Check that we are within the kernel's range */ + if ( ( start + total_len ) > exec_ctx->mem_limit ) + continue; + /* Prepare and verify segment */ + if ( ( rc = prep_segment ( phys_to_user ( start ), 0, + total_len ) ) != 0 ) + continue; + /* Use this address */ + break; + } + + /* Construct initrd */ + DBGC ( image, "bzImage %p constructing initrd at [%lx,%lx)\n", + image, start, ( start + total_len ) ); + for_each_image ( initrd ) { + if ( initrd->type != &initrd_image_type ) + continue; + initrd_len = ( ( image->len + 0x0f ) & ~0x0f ); + DBGC ( image, "bzImage %p has initrd %p at [%lx,%lx)\n", + image, initrd, ( start + offset ), + ( start + offset + initrd->len ) ); + memcpy_user ( phys_to_user ( start ), offset, + initrd->data, 0, initrd->len ); + offset += initrd_len; + } + assert ( offset == total_len ); + /* Record initrd location */ exec_ctx->ramdisk_image = start; - exec_ctx->ramdisk_size = initrd->len; + exec_ctx->ramdisk_size = total_len; return 0; } @@ -234,7 +252,6 @@ static int bzimage_exec ( struct image *image ) { struct bzimage_exec_context exec_ctx; struct bzimage_header bzhdr; const char *cmdline = ( image->cmdline ? image->cmdline : "" ); - struct image *initrd; int rc; /* Initialise context */ @@ -262,15 +279,9 @@ static int bzimage_exec ( struct image *image ) { if ( ( rc = bzimage_set_cmdline ( image, &exec_ctx, cmdline ) ) != 0 ) return rc; - /* Load an initrd, if one exists */ - for_each_image ( initrd ) { - if ( initrd->type == &initrd_image_type ) { - if ( ( rc = bzimage_load_initrd ( image, &exec_ctx, - initrd ) ) != 0 ) - return rc; - break; - } - } + /* Load any initrds */ + if ( ( rc = bzimage_load_initrd ( image, &exec_ctx ) ) != 0 ) + return rc; /* Update and store kernel header */ bzhdr.vid_mode = exec_ctx.vid_mode; From 24f32a1945537d7e1b5fbff1a09792a671b83bc3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 1 Aug 2007 15:29:15 +0100 Subject: [PATCH 50/50] Initrd concatenation now working --- src/arch/i386/image/bzimage.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/arch/i386/image/bzimage.c b/src/arch/i386/image/bzimage.c index 161d2675..0d01f6a0 100644 --- a/src/arch/i386/image/bzimage.c +++ b/src/arch/i386/image/bzimage.c @@ -186,10 +186,11 @@ static int bzimage_load_initrd ( struct image *image, for_each_image ( initrd ) { if ( initrd->type != &initrd_image_type ) continue; - initrd_len = ( ( image->len + 0x0f ) & ~0x0f ); + initrd_len = ( ( initrd->len + 0x0f ) & ~0x0f ); total_len += initrd_len; } + /* Give up if no initrd images found */ if ( ! total_len ) return 0; @@ -225,7 +226,7 @@ static int bzimage_load_initrd ( struct image *image, for_each_image ( initrd ) { if ( initrd->type != &initrd_image_type ) continue; - initrd_len = ( ( image->len + 0x0f ) & ~0x0f ); + initrd_len = ( ( initrd->len + 0x0f ) & ~0x0f ); DBGC ( image, "bzImage %p has initrd %p at [%lx,%lx)\n", image, initrd, ( start + offset ), ( start + offset + initrd->len ) );