diff options
| author | Simon Rettberg | 2026-01-28 12:53:53 +0100 |
|---|---|---|
| committer | Simon Rettberg | 2026-01-28 12:53:53 +0100 |
| commit | 8e82785c584dc13e20f9229decb95bd17bbe9cd1 (patch) | |
| tree | a8b359e59196be5b2e3862bed189107f4bc9975f /src/net/tls.c | |
| parent | Merge branch 'master' into openslx (diff) | |
| parent | [prefix] Make unlzma.S compatible with 386 class CPUs (diff) | |
| download | ipxe-openslx.tar.gz ipxe-openslx.tar.xz ipxe-openslx.zip | |
Merge branch 'master' into openslxopenslx
Diffstat (limited to 'src/net/tls.c')
| -rw-r--r-- | src/net/tls.c | 1044 |
1 files changed, 595 insertions, 449 deletions
diff --git a/src/net/tls.c b/src/net/tls.c index 5f89be452..4f8ea2692 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -18,6 +18,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); /** * @file @@ -50,6 +51,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/validator.h> #include <ipxe/job.h> #include <ipxe/dhe.h> +#include <ipxe/ecdhe.h> #include <ipxe/tls.h> #include <config/crypto.h> @@ -170,10 +172,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_EPERM_VERIFY \ __einfo_uniqify ( EINFO_EPERM, 0x02, \ "Handshake verification failed" ) -#define EPERM_CLIENT_CERT __einfo_error ( EINFO_EPERM_CLIENT_CERT ) -#define EINFO_EPERM_CLIENT_CERT \ - __einfo_uniqify ( EINFO_EPERM, 0x03, \ - "No suitable client certificate available" ) #define EPERM_RENEG_INSECURE __einfo_error ( EINFO_EPERM_RENEG_INSECURE ) #define EINFO_EPERM_RENEG_INSECURE \ __einfo_uniqify ( EINFO_EPERM, 0x04, \ @@ -186,6 +184,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_EPERM_KEY_EXCHANGE \ __einfo_uniqify ( EINFO_EPERM, 0x06, \ "ServerKeyExchange verification failed" ) +#define EPERM_EMS __einfo_error ( EINFO_EPERM_EMS ) +#define EINFO_EPERM_EMS \ + __einfo_uniqify ( EINFO_EPERM, 0x07, \ + "Extended master secret extension mismatch" ) #define EPROTO_VERSION __einfo_error ( EINFO_EPROTO_VERSION ) #define EINFO_EPROTO_VERSION \ __einfo_uniqify ( EINFO_EPROTO, 0x01, \ @@ -195,10 +197,15 @@ FILE_LICENCE ( GPL2_OR_LATER ); static LIST_HEAD ( tls_sessions ); static void tls_tx_resume_all ( struct tls_session *session ); +static struct io_buffer * tls_alloc_iob ( struct tls_connection *tls, + size_t len ); +static int tls_send_record ( struct tls_connection *tls, unsigned int type, + struct io_buffer *iobuf ); static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, const void *data, size_t len ); static void tls_clear_cipher ( struct tls_connection *tls, struct tls_cipherspec *cipherspec ); +static void tls_verify_handshake ( struct tls_connection *tls, void *out ); /****************************************************************************** * @@ -251,8 +258,8 @@ static void tls_set_uint24 ( tls24_t *field24, unsigned long value ) { * @ret is_ready TLS connection is ready */ static int tls_ready ( struct tls_connection *tls ) { - return ( ( ! is_pending ( &tls->client_negotiation ) ) && - ( ! is_pending ( &tls->server_negotiation ) ) ); + return ( ( ! is_pending ( &tls->client.negotiation ) ) && + ( ! is_pending ( &tls->server.negotiation ) ) ); } /** @@ -382,21 +389,21 @@ static void free_tls ( struct refcnt *refcnt ) { /* Free dynamically-allocated resources */ free ( tls->new_session_ticket ); - 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->server_key ); + tls_clear_cipher ( tls, &tls->tx.cipherspec.active ); + tls_clear_cipher ( tls, &tls->tx.cipherspec.pending ); + tls_clear_cipher ( tls, &tls->rx.cipherspec.active ); + tls_clear_cipher ( tls, &tls->rx.cipherspec.pending ); + free ( tls->server.exchange ); free ( tls->handshake_ctx ); - list_for_each_entry_safe ( iobuf, tmp, &tls->rx_data, list ) { + list_for_each_entry_safe ( iobuf, tmp, &tls->rx.data, list ) { list_del ( &iobuf->list ); free_iob ( iobuf ); } - free_iob ( tls->rx_handshake ); - x509_chain_put ( tls->certs ); - x509_chain_put ( tls->chain ); - x509_root_put ( tls->root ); - privkey_put ( tls->key ); + free_iob ( tls->rx.handshake ); + privkey_put ( tls->client.key ); + x509_chain_put ( tls->client.chain ); + x509_chain_put ( tls->server.chain ); + x509_root_put ( tls->server.root ); /* Drop reference to session */ assert ( list_empty ( &tls->list ) ); @@ -415,17 +422,17 @@ static void free_tls ( struct refcnt *refcnt ) { static void tls_close ( struct tls_connection *tls, int rc ) { /* Remove pending operations, if applicable */ - pending_put ( &tls->client_negotiation ); - pending_put ( &tls->server_negotiation ); - pending_put ( &tls->validation ); + pending_put ( &tls->client.negotiation ); + pending_put ( &tls->server.negotiation ); + pending_put ( &tls->server.validation ); /* Remove process */ - process_del ( &tls->process ); + process_del ( &tls->tx.process ); /* Close all interfaces */ intf_shutdown ( &tls->cipherstream, rc ); intf_shutdown ( &tls->plainstream, rc ); - intf_shutdown ( &tls->validator, rc ); + intf_shutdown ( &tls->server.validator, rc ); /* Remove from session */ list_del ( &tls->list ); @@ -636,21 +643,43 @@ static void tls_prf ( struct tls_connection *tls, const void *secret, static void tls_generate_master_secret ( struct tls_connection *tls, const void *pre_master_secret, size_t pre_master_secret_len ) { + struct digest_algorithm *digest = tls->handshake_digest; + uint8_t digest_out[ digest->digestsize ]; + + /* Generate handshake digest */ + tls_verify_handshake ( tls, digest_out ); - DBGC ( tls, "TLS %p pre-master-secret:\n", tls ); + /* Show inputs */ + DBGC ( tls, "TLS %p pre-master secret:\n", tls ); DBGC_HD ( tls, pre_master_secret, pre_master_secret_len ); DBGC ( tls, "TLS %p client random bytes:\n", tls ); - DBGC_HD ( tls, &tls->client_random, sizeof ( tls->client_random ) ); + DBGC_HD ( tls, &tls->client.random, sizeof ( tls->client.random ) ); DBGC ( tls, "TLS %p server random bytes:\n", tls ); - DBGC_HD ( tls, &tls->server_random, sizeof ( tls->server_random ) ); - - tls_prf_label ( tls, pre_master_secret, pre_master_secret_len, - &tls->master_secret, sizeof ( tls->master_secret ), - "master secret", - &tls->client_random, sizeof ( tls->client_random ), - &tls->server_random, sizeof ( tls->server_random ) ); + DBGC_HD ( tls, &tls->server.random, sizeof ( tls->server.random ) ); + DBGC ( tls, "TLS %p session hash:\n", tls ); + DBGC_HD ( tls, digest_out, sizeof ( digest_out ) ); - DBGC ( tls, "TLS %p generated master secret:\n", tls ); + /* Generate master secret */ + if ( tls->extended_master_secret ) { + tls_prf_label ( tls, pre_master_secret, pre_master_secret_len, + &tls->master_secret, + sizeof ( tls->master_secret ), + "extended master secret", + digest_out, sizeof ( digest_out ) ); + } else { + tls_prf_label ( tls, pre_master_secret, pre_master_secret_len, + &tls->master_secret, + sizeof ( tls->master_secret ), + "master secret", + &tls->client.random, + sizeof ( tls->client.random ), + &tls->server.random, + sizeof ( tls->server.random ) ); + } + + /* Show output */ + DBGC ( tls, "TLS %p generated %smaster secret:\n", tls, + ( tls->extended_master_secret ? "extended ": "" ) ); DBGC_HD ( tls, &tls->master_secret, sizeof ( tls->master_secret ) ); } @@ -662,8 +691,8 @@ static void tls_generate_master_secret ( struct tls_connection *tls, * The master secret must already be known. */ static int tls_generate_keys ( struct tls_connection *tls ) { - struct tls_cipherspec *tx_cipherspec = &tls->tx_cipherspec_pending; - struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending; + struct tls_cipherspec *tx_cipherspec = &tls->tx.cipherspec.pending; + struct tls_cipherspec *rx_cipherspec = &tls->rx.cipherspec.pending; size_t hash_size = tx_cipherspec->suite->mac_len; size_t key_size = tx_cipherspec->suite->key_len; size_t iv_size = tx_cipherspec->suite->fixed_iv_len; @@ -675,8 +704,8 @@ static int tls_generate_keys ( struct tls_connection *tls ) { /* 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 ) ); + &tls->server.random, sizeof ( tls->server.random ), + &tls->client.random, sizeof ( tls->client.random ) ); /* Split key block into portions */ key = key_block; @@ -856,10 +885,6 @@ tls_find_cipher_suite ( unsigned int cipher_suite ) { static void tls_clear_cipher ( struct tls_connection *tls __unused, struct tls_cipherspec *cipherspec ) { - if ( cipherspec->suite ) { - pubkey_final ( cipherspec->suite->pubkey, - cipherspec->pubkey_ctx ); - } free ( cipherspec->dynamic ); memset ( cipherspec, 0, sizeof ( *cipherspec ) ); cipherspec->suite = &tls_cipher_suite_null; @@ -876,7 +901,6 @@ static void tls_clear_cipher ( struct tls_connection *tls __unused, static int tls_set_cipher ( struct tls_connection *tls, struct tls_cipherspec *cipherspec, struct tls_cipher_suite *suite ) { - struct pubkey_algorithm *pubkey = suite->pubkey; struct cipher_algorithm *cipher = suite->cipher; size_t total; void *dynamic; @@ -885,8 +909,7 @@ static int tls_set_cipher ( struct tls_connection *tls, tls_clear_cipher ( tls, cipherspec ); /* Allocate dynamic storage */ - total = ( pubkey->ctxsize + cipher->ctxsize + suite->mac_len + - suite->fixed_iv_len ); + total = ( cipher->ctxsize + suite->mac_len + suite->fixed_iv_len ); dynamic = zalloc ( total ); if ( ! dynamic ) { DBGC ( tls, "TLS %p could not allocate %zd bytes for crypto " @@ -896,7 +919,6 @@ static int tls_set_cipher ( struct tls_connection *tls, /* Assign storage */ cipherspec->dynamic = dynamic; - cipherspec->pubkey_ctx = dynamic; dynamic += pubkey->ctxsize; cipherspec->cipher_ctx = dynamic; dynamic += cipher->ctxsize; cipherspec->mac_secret = dynamic; dynamic += suite->mac_len; cipherspec->fixed_iv = dynamic; dynamic += suite->fixed_iv_len; @@ -936,10 +958,10 @@ static int tls_select_cipher ( struct tls_connection *tls, return rc; /* Set ciphers */ - if ( ( rc = tls_set_cipher ( tls, &tls->tx_cipherspec_pending, + if ( ( rc = tls_set_cipher ( tls, &tls->tx.cipherspec.pending, suite ) ) != 0 ) return rc; - if ( ( rc = tls_set_cipher ( tls, &tls->rx_cipherspec_pending, + if ( ( rc = tls_set_cipher ( tls, &tls->rx.cipherspec.pending, suite ) ) != 0 ) return rc; @@ -955,22 +977,20 @@ static int tls_select_cipher ( struct tls_connection *tls, * Activate next cipher suite * * @v tls TLS connection - * @v pending Pending cipher specification - * @v active Active cipher specification to replace + * @v pair Cipher specification pair * @ret rc Return status code */ static int tls_change_cipher ( struct tls_connection *tls, - struct tls_cipherspec *pending, - struct tls_cipherspec *active ) { + struct tls_cipherspec_pair *pair ) { /* Sanity check */ - if ( pending->suite == &tls_cipher_suite_null ) { + if ( pair->pending.suite == &tls_cipher_suite_null ) { DBGC ( tls, "TLS %p refusing to use null cipher\n", tls ); return -ENOTSUP_NULL; } - tls_clear_cipher ( tls, active ); - memswap ( active, pending, sizeof ( *active ) ); + tls_clear_cipher ( tls, &pair->active ); + memswap ( &pair->active, &pair->pending, sizeof ( pair->active ) ); return 0; } @@ -1088,7 +1108,7 @@ tls_find_named_curve ( unsigned int named_curve ) { * @v tls TLS connection */ static void tls_tx_resume ( struct tls_connection *tls ) { - process_add ( &tls->process ); + process_add ( &tls->tx.process ); } /** @@ -1111,16 +1131,16 @@ static void tls_tx_resume_all ( struct tls_session *session ) { static void tls_restart ( struct tls_connection *tls ) { /* Sanity check */ - assert ( ! tls->tx_pending ); - assert ( ! is_pending ( &tls->client_negotiation ) ); - assert ( ! is_pending ( &tls->server_negotiation ) ); - assert ( ! is_pending ( &tls->validation ) ); + assert ( ! tls->tx.pending ); + assert ( ! is_pending ( &tls->client.negotiation ) ); + assert ( ! is_pending ( &tls->server.negotiation ) ); + assert ( ! is_pending ( &tls->server.validation ) ); /* (Re)start negotiation */ - tls->tx_pending = TLS_TX_CLIENT_HELLO; + tls->tx.pending = TLS_TX_CLIENT_HELLO; tls_tx_resume ( tls ); - pending_get ( &tls->client_negotiation ); - pending_get ( &tls->server_negotiation ); + pending_get ( &tls->client.negotiation ); + pending_get ( &tls->server.negotiation ); } /** @@ -1134,9 +1154,6 @@ static void tls_restart ( struct tls_connection *tls ) { static int tls_send_handshake ( struct tls_connection *tls, const 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 ); } @@ -1207,11 +1224,16 @@ static int tls_client_hello ( struct tls_connection *tls, } __attribute__ (( packed )) data; } __attribute__ (( packed )) *named_curve_ext; struct { + uint16_t type; + uint16_t len; + } __attribute__ (( packed )) *extended_master_secret_ext; + struct { typeof ( *server_name_ext ) server_name; typeof ( *max_fragment_length_ext ) max_fragment_length; typeof ( *signature_algorithms_ext ) signature_algorithms; typeof ( *renegotiation_info_ext ) renegotiation_info; typeof ( *session_ticket_ext ) session_ticket; + typeof ( *extended_master_secret_ext ) extended_master_secret; typeof ( *named_curve_ext ) named_curve[TLS_NUM_NAMED_CURVES ? 1 : 0]; } __attribute__ (( packed )) *extensions; @@ -1239,7 +1261,7 @@ static int tls_client_hello ( struct tls_connection *tls, htonl ( sizeof ( hello ) - sizeof ( hello.type_length ) ) ); hello.version = htons ( TLS_VERSION_MAX ); - memcpy ( &hello.random, &tls->client_random, sizeof ( hello.random ) ); + memcpy ( &hello.random, &tls->client.random, sizeof ( hello.random ) ); hello.session_id_len = tls->session_id_len; memcpy ( hello.session_id, tls->session_id, sizeof ( hello.session_id ) ); @@ -1267,7 +1289,7 @@ static int tls_client_hello ( struct tls_connection *tls, max_fragment_length_ext->type = htons ( TLS_MAX_FRAGMENT_LENGTH ); max_fragment_length_ext->len = htons ( sizeof ( max_fragment_length_ext->data ) ); - max_fragment_length_ext->data.max = TLS_MAX_FRAGMENT_LENGTH_4096; + max_fragment_length_ext->data.max = TLS_MAX_FRAGMENT_LENGTH_VALUE; /* Construct supported signature algorithms extension */ signature_algorithms_ext = &extensions->signature_algorithms; @@ -1297,6 +1319,12 @@ static int tls_client_hello ( struct tls_connection *tls, memcpy ( session_ticket_ext->data.data, session->ticket, sizeof ( session_ticket_ext->data.data ) ); + /* Construct extended master secret extension */ + extended_master_secret_ext = &extensions->extended_master_secret; + extended_master_secret_ext->type + = htons ( TLS_EXTENDED_MASTER_SECRET ); + extended_master_secret_ext->len = 0; + /* Construct named curves extension, if applicable */ if ( sizeof ( extensions->named_curve ) ) { named_curve_ext = &extensions->named_curve[0]; @@ -1341,12 +1369,12 @@ static int tls_send_certificate ( struct tls_connection *tls ) { } __attribute__ (( packed )) *certificates; struct x509_link *link; struct x509_certificate *cert; + struct io_buffer *iobuf; size_t len; - int rc; /* Calculate length of client certificates */ len = 0; - list_for_each_entry ( link, &tls->certs->links, list ) { + list_for_each_entry ( link, &tls->client.chain->links, list ) { cert = link->cert; len += ( sizeof ( *certificate ) + cert->raw.len ); DBGC ( tls, "TLS %p sending client certificate %s\n", @@ -1356,33 +1384,28 @@ static int tls_send_certificate ( struct tls_connection *tls ) { /* Allocate storage for Certificate record (which may be too * large for the stack). */ - certificates = zalloc ( sizeof ( *certificates ) + len ); - if ( ! certificates ) + iobuf = tls_alloc_iob ( tls, ( sizeof ( *certificates ) + len ) ); + if ( ! iobuf ) return -ENOMEM_CERTIFICATE; /* Populate record */ + certificates = iob_put ( iobuf, sizeof ( *certificates ) ); certificates->type_length = ( cpu_to_le32 ( TLS_CERTIFICATE ) | htonl ( sizeof ( *certificates ) + len - sizeof ( certificates->type_length ) ) ); tls_set_uint24 ( &certificates->length, len ); - certificate = &certificates->certificates[0]; - list_for_each_entry ( link, &tls->certs->links, list ) { + list_for_each_entry ( link, &tls->client.chain->links, list ) { cert = link->cert; + certificate = iob_put ( iobuf, sizeof ( *certificate ) ); tls_set_uint24 ( &certificate->length, cert->raw.len ); - memcpy ( certificate->data, cert->raw.data, cert->raw.len ); - certificate = ( ( ( void * ) certificate->data ) + - cert->raw.len ); + memcpy ( iob_put ( iobuf, cert->raw.len ), cert->raw.data, + cert->raw.len ); } /* Transmit record */ - rc = tls_send_handshake ( tls, certificates, - ( sizeof ( *certificates ) + len ) ); - - /* Free record */ - free ( certificates ); - - return rc; + return tls_send_record ( tls, TLS_TYPE_HANDSHAKE, + iob_disown ( iobuf ) ); } /** @@ -1392,55 +1415,71 @@ static int tls_send_certificate ( struct tls_connection *tls ) { * @ret rc Return status code */ static int tls_send_client_key_exchange_pubkey ( struct tls_connection *tls ) { - struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; + struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.pending; struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; - size_t max_len = pubkey_max_len ( pubkey, cipherspec->pubkey_ctx ); struct { uint16_t version; uint8_t random[46]; } __attribute__ (( packed )) pre_master_secret; - struct { - uint32_t type_length; - uint16_t encrypted_pre_master_secret_len; - uint8_t encrypted_pre_master_secret[max_len]; - } __attribute__ (( packed )) key_xchg; - size_t unused; - int len; + struct asn1_cursor cursor = { + .data = &pre_master_secret, + .len = sizeof ( pre_master_secret ), + }; + struct asn1_builder builder = { NULL, 0 }; int rc; /* Generate pre-master secret */ pre_master_secret.version = htons ( TLS_VERSION_MAX ); if ( ( rc = tls_generate_random ( tls, &pre_master_secret.random, ( sizeof ( pre_master_secret.random ) ) ) ) != 0 ) { - return rc; + goto err_random; } - /* Generate master secret */ - tls_generate_master_secret ( tls, &pre_master_secret, - sizeof ( pre_master_secret ) ); - /* Encrypt pre-master secret using server's public key */ - memset ( &key_xchg, 0, sizeof ( key_xchg ) ); - len = pubkey_encrypt ( pubkey, cipherspec->pubkey_ctx, - &pre_master_secret, sizeof ( pre_master_secret ), - key_xchg.encrypted_pre_master_secret ); - if ( len < 0 ) { - rc = len; + if ( ( rc = pubkey_encrypt ( pubkey, &tls->server.key, &cursor, + &builder ) ) != 0 ) { DBGC ( tls, "TLS %p could not encrypt pre-master secret: %s\n", tls, strerror ( rc ) ); - return rc; + goto err_encrypt; + } + + /* Construct Client Key Exchange record */ + { + struct { + uint32_t type_length; + uint16_t encrypted_pre_master_secret_len; + } __attribute__ (( packed )) header; + + header.type_length = + ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) | + htonl ( builder.len + sizeof ( header ) - + sizeof ( header.type_length ) ) ); + header.encrypted_pre_master_secret_len = htons ( builder.len ); + + if ( ( rc = asn1_prepend_raw ( &builder, &header, + sizeof ( header ) ) ) != 0 ) { + DBGC ( tls, "TLS %p could not construct Client Key " + "Exchange: %s\n", tls, strerror ( rc ) ); + goto err_prepend; + } } - unused = ( max_len - len ); - key_xchg.type_length = - ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) | - htonl ( sizeof ( key_xchg ) - - sizeof ( key_xchg.type_length ) - unused ) ); - key_xchg.encrypted_pre_master_secret_len = - htons ( sizeof ( key_xchg.encrypted_pre_master_secret ) - - unused ); - return tls_send_handshake ( tls, &key_xchg, - ( sizeof ( key_xchg ) - unused ) ); + /* Transmit Client Key Exchange record */ + if ( ( rc = tls_send_handshake ( tls, builder.data, + builder.len ) ) != 0 ) { + goto err_send; + } + + /* Generate master secret */ + tls_generate_master_secret ( tls, &pre_master_secret, + sizeof ( pre_master_secret ) ); + + err_random: + err_encrypt: + err_prepend: + err_send: + free ( builder.data ); + return rc; } /** Public key exchange algorithm */ @@ -1458,7 +1497,7 @@ struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm = { */ static int tls_verify_dh_params ( struct tls_connection *tls, size_t param_len ) { - struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; + struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.pending; struct pubkey_algorithm *pubkey; struct digest_algorithm *digest; int use_sig_hash = tls_version ( tls, TLS_VERSION_TLS_1_2 ); @@ -1467,14 +1506,15 @@ static int tls_verify_dh_params ( struct tls_connection *tls, uint16_t signature_len; uint8_t signature[0]; } __attribute__ (( packed )) *sig; + struct asn1_cursor signature; const void *data; size_t remaining; int rc; /* Signature follows parameters */ - assert ( param_len <= tls->server_key_len ); - data = ( tls->server_key + param_len ); - remaining = ( tls->server_key_len - param_len ); + assert ( param_len <= tls->server.exchange_len ); + data = ( tls->server.exchange + param_len ); + remaining = ( tls->server.exchange_len - param_len ); /* Parse signature from ServerKeyExchange */ sig = data; @@ -1483,9 +1523,12 @@ static int tls_verify_dh_params ( struct tls_connection *tls, sizeof ( *sig ) ) ) ) { DBGC ( tls, "TLS %p received underlength ServerKeyExchange\n", tls ); - DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len ); + DBGC_HDA ( tls, 0, tls->server.exchange, + tls->server.exchange_len ); return -EINVAL_KEY_EXCHANGE; } + signature.data = sig->signature; + signature.len = ntohs ( sig->signature_len ); /* Identify signature and hash algorithm */ if ( use_sig_hash ) { @@ -1509,28 +1552,25 @@ static int tls_verify_dh_params ( struct tls_connection *tls, /* Verify signature */ { - const void *signature = sig->signature; - size_t signature_len = ntohs ( sig->signature_len ); uint8_t ctx[digest->ctxsize]; uint8_t hash[digest->digestsize]; /* Calculate digest */ digest_init ( digest, ctx ); - digest_update ( digest, ctx, &tls->client_random, - sizeof ( tls->client_random ) ); - digest_update ( digest, ctx, tls->server_random, - sizeof ( tls->server_random ) ); - digest_update ( digest, ctx, tls->server_key, param_len ); + digest_update ( digest, ctx, &tls->client.random, + sizeof ( tls->client.random ) ); + digest_update ( digest, ctx, tls->server.random, + sizeof ( tls->server.random ) ); + digest_update ( digest, ctx, tls->server.exchange, param_len ); digest_final ( digest, ctx, hash ); /* Verify signature */ - if ( ( rc = pubkey_verify ( pubkey, cipherspec->pubkey_ctx, - digest, hash, signature, - signature_len ) ) != 0 ) { + if ( ( rc = pubkey_verify ( pubkey, &tls->server.key, digest, + hash, &signature ) ) != 0 ) { DBGC ( tls, "TLS %p ServerKeyExchange failed " "verification\n", tls ); - DBGC_HDA ( tls, 0, tls->server_key, - tls->server_key_len ); + DBGC_HDA ( tls, 0, tls->server.exchange, + tls->server.exchange_len ); return -EPERM_KEY_EXCHANGE; } } @@ -1545,7 +1585,7 @@ static int tls_verify_dh_params ( struct tls_connection *tls, * @ret rc Return status code */ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { - uint8_t private[ sizeof ( tls->client_random.random ) ]; + uint8_t private[ sizeof ( tls->client.random.random ) ]; const struct { uint16_t len; uint8_t data[0]; @@ -1558,8 +1598,8 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { int rc; /* Parse ServerKeyExchange */ - data = tls->server_key; - remaining = tls->server_key_len; + data = tls->server.exchange; + remaining = tls->server.exchange_len; for ( i = 0 ; i < ( sizeof ( dh_val ) / sizeof ( dh_val[0] ) ) ; i++ ){ dh_val[i] = data; if ( ( sizeof ( *dh_val[i] ) > remaining ) || @@ -1567,8 +1607,8 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { sizeof ( *dh_val[i] ) ) )){ DBGC ( tls, "TLS %p received underlength " "ServerKeyExchange\n", tls ); - DBGC_HDA ( tls, 0, tls->server_key, - tls->server_key_len ); + DBGC_HDA ( tls, 0, tls->server.exchange, + tls->server.exchange_len ); rc = -EINVAL_KEY_EXCHANGE; goto err_header; } @@ -1576,7 +1616,7 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { data += frag_len; remaining -= frag_len; } - param_len = ( tls->server_key_len - remaining ); + param_len = ( tls->server.exchange_len - remaining ); /* Verify parameter signature */ if ( ( rc = tls_verify_dh_params ( tls, param_len ) ) != 0 ) @@ -1637,15 +1677,15 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { len--; } - /* Generate master secret */ - tls_generate_master_secret ( tls, pre_master_secret, len ); - /* Transmit Client Key Exchange record */ if ( ( rc = tls_send_handshake ( tls, key_xchg, sizeof ( *key_xchg ) ) ) !=0){ goto err_send_handshake; } + /* Generate master secret */ + tls_generate_master_secret ( tls, pre_master_secret, len ); + err_send_handshake: err_dhe_key: free ( dynamic ); @@ -1678,15 +1718,20 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) { uint8_t public[0]; } __attribute__ (( packed )) *ecdh; size_t param_len; + size_t pointsize; + size_t keysize; + size_t offset; int rc; /* Parse ServerKeyExchange record */ - ecdh = tls->server_key; - if ( ( sizeof ( *ecdh ) > tls->server_key_len ) || - ( ecdh->public_len > ( tls->server_key_len - sizeof ( *ecdh ) ))){ + ecdh = tls->server.exchange; + if ( ( sizeof ( *ecdh ) > tls->server.exchange_len ) || + ( ecdh->public_len > ( tls->server.exchange_len - + sizeof ( *ecdh ) ) ) ) { DBGC ( tls, "TLS %p received underlength ServerKeyExchange\n", tls ); - DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len ); + DBGC_HDA ( tls, 0, tls->server.exchange, + tls->server.exchange_len ); return -EINVAL_KEY_EXCHANGE; } param_len = ( sizeof ( *ecdh ) + ecdh->public_len ); @@ -1699,34 +1744,49 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) { if ( ecdh->curve_type != TLS_NAMED_CURVE_TYPE ) { DBGC ( tls, "TLS %p unsupported curve type %d\n", tls, ecdh->curve_type ); - DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len ); + DBGC_HDA ( tls, 0, tls->server.exchange, + tls->server.exchange_len ); return -ENOTSUP_CURVE; } curve = tls_find_named_curve ( ecdh->named_curve ); if ( ! curve ) { DBGC ( tls, "TLS %p unsupported named curve %d\n", tls, ntohs ( ecdh->named_curve ) ); - DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len ); + DBGC_HDA ( tls, 0, tls->server.exchange, + tls->server.exchange_len ); return -ENOTSUP_CURVE; } + DBGC ( tls, "TLS %p using named curve %s\n", tls, curve->curve->name ); + pointsize = curve->curve->pointsize; + keysize = curve->curve->keysize; + offset = ( curve->format ? 1 : 0 ); /* Check key length */ - if ( ecdh->public_len != curve->curve->keysize ) { + if ( ecdh->public_len != ( offset + pointsize ) ) { DBGC ( tls, "TLS %p invalid %s key\n", tls, curve->curve->name ); - DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len ); + DBGC_HDA ( tls, 0, tls->server.exchange, + tls->server.exchange_len ); + return -EINVAL_KEY_EXCHANGE; + } + + /* Check curve point format byte (if present) */ + if ( curve->format && ( ecdh->public[0] != curve->format ) ) { + DBGC ( tls, "TLS %p invalid %s curve point format\n", + tls, curve->curve->name ); + DBGC_HDA ( tls, 0, tls->server.exchange, + tls->server.exchange_len ); return -EINVAL_KEY_EXCHANGE; } /* Construct pre-master secret and ClientKeyExchange record */ { - size_t len = curve->curve->keysize; - uint8_t private[len]; - uint8_t pre_master_secret[len]; + uint8_t private[keysize]; + uint8_t pre_master_secret[pointsize]; struct { uint32_t type_length; uint8_t public_len; - uint8_t public[len]; + uint8_t public[ecdh->public_len]; } __attribute__ (( packed )) key_xchg; /* Generate ephemeral private key */ @@ -1735,36 +1795,33 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) { return rc; } - /* Calculate pre-master secret */ - if ( ( rc = elliptic_multiply ( curve->curve, - ecdh->public, private, - pre_master_secret ) ) != 0 ) { + /* Exchange keys */ + if ( ( rc = ecdhe_key ( curve->curve, ( ecdh->public + offset ), + private, ( key_xchg.public + offset ), + pre_master_secret ) ) != 0 ) { DBGC ( tls, "TLS %p could not exchange ECDHE key: %s\n", tls, strerror ( rc ) ); return rc; } - /* Generate master secret */ - tls_generate_master_secret ( tls, pre_master_secret, len ); - /* Generate Client Key Exchange record */ key_xchg.type_length = ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) | htonl ( sizeof ( key_xchg ) - sizeof ( key_xchg.type_length ) ) ); - key_xchg.public_len = len; - if ( ( rc = elliptic_multiply ( curve->curve, NULL, private, - key_xchg.public ) ) != 0 ) { - DBGC ( tls, "TLS %p could not generate ECDHE key: %s\n", - tls, strerror ( rc ) ); - return rc; - } + key_xchg.public_len = sizeof ( key_xchg.public ); + if ( curve->format ) + key_xchg.public[0] = curve->format; /* Transmit Client Key Exchange record */ if ( ( rc = tls_send_handshake ( tls, &key_xchg, sizeof ( key_xchg ) ) ) !=0){ return rc; } + + /* Generate master secret */ + tls_generate_master_secret ( tls, pre_master_secret, + curve->pre_master_secret_len ); } return 0; @@ -1783,7 +1840,7 @@ struct tls_key_exchange_algorithm tls_ecdhe_exchange_algorithm = { * @ret rc Return status code */ static int tls_send_client_key_exchange ( struct tls_connection *tls ) { - struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; + struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.pending; struct tls_cipher_suite *suite = cipherspec->suite; int rc; @@ -1812,24 +1869,17 @@ static int tls_send_client_key_exchange ( struct tls_connection *tls ) { */ static int tls_send_certificate_verify ( struct tls_connection *tls ) { struct digest_algorithm *digest = tls->handshake_digest; - struct x509_certificate *cert = x509_first ( tls->certs ); + struct x509_certificate *cert = x509_first ( tls->client.chain ); struct pubkey_algorithm *pubkey = cert->signature_algorithm->pubkey; - struct asn1_cursor *key = privkey_cursor ( tls->key ); + struct asn1_cursor *key = privkey_cursor ( tls->client.key ); uint8_t digest_out[ digest->digestsize ]; - uint8_t ctx[ pubkey->ctxsize ]; struct tls_signature_hash_algorithm *sig_hash = NULL; + struct asn1_builder builder = { NULL, 0 }; int rc; /* Generate digest to be signed */ tls_verify_handshake ( tls, digest_out ); - /* Initialise public-key algorithm */ - if ( ( rc = pubkey_init ( pubkey, ctx, key->data, key->len ) ) != 0 ) { - DBGC ( tls, "TLS %p could not initialise %s client private " - "key: %s\n", tls, pubkey->name, strerror ( rc ) ); - goto err_pubkey_init; - } - /* TLSv1.2 and later use explicit algorithm identifiers */ if ( tls_version ( tls, TLS_VERSION_TLS_1_2 ) ) { sig_hash = tls_signature_hash_algorithm ( pubkey, digest ); @@ -1842,55 +1892,53 @@ static int tls_send_certificate_verify ( struct tls_connection *tls ) { } } - /* Generate and transmit record */ + /* Sign digest */ + if ( ( rc = pubkey_sign ( pubkey, key, digest, digest_out, + &builder ) ) != 0 ) { + DBGC ( tls, "TLS %p could not sign %s digest using %s client " + "private key: %s\n", tls, digest->name, pubkey->name, + strerror ( rc ) ); + goto err_pubkey_sign; + } + + /* Construct Certificate Verify record */ { - size_t max_len = pubkey_max_len ( pubkey, ctx ); int use_sig_hash = ( ( sig_hash == NULL ) ? 0 : 1 ); struct { uint32_t type_length; struct tls_signature_hash_id sig_hash[use_sig_hash]; uint16_t signature_len; - uint8_t signature[max_len]; - } __attribute__ (( packed )) certificate_verify; - size_t unused; - int len; - - /* Sign digest */ - len = pubkey_sign ( pubkey, ctx, digest, digest_out, - certificate_verify.signature ); - if ( len < 0 ) { - rc = len; - DBGC ( tls, "TLS %p could not sign %s digest using %s " - "client private key: %s\n", tls, digest->name, - pubkey->name, strerror ( rc ) ); - goto err_pubkey_sign; - } - unused = ( max_len - len ); - - /* Construct Certificate Verify record */ - certificate_verify.type_length = - ( cpu_to_le32 ( TLS_CERTIFICATE_VERIFY ) | - htonl ( sizeof ( certificate_verify ) - - sizeof ( certificate_verify.type_length ) - - unused ) ); + } __attribute__ (( packed )) header; + + header.type_length = ( cpu_to_le32 ( TLS_CERTIFICATE_VERIFY ) | + htonl ( builder.len + + sizeof ( header ) - + sizeof ( header.type_length ))); if ( use_sig_hash ) { - memcpy ( &certificate_verify.sig_hash[0], - &sig_hash->code, - sizeof ( certificate_verify.sig_hash[0] ) ); + memcpy ( &header.sig_hash[0], &sig_hash->code, + sizeof ( header.sig_hash[0] ) ); } - certificate_verify.signature_len = - htons ( sizeof ( certificate_verify.signature ) - - unused ); + header.signature_len = htons ( builder.len ); - /* Transmit record */ - rc = tls_send_handshake ( tls, &certificate_verify, - ( sizeof ( certificate_verify ) - unused ) ); + if ( ( rc = asn1_prepend_raw ( &builder, &header, + sizeof ( header ) ) ) != 0 ) { + DBGC ( tls, "TLS %p could not construct Certificate " + "Verify: %s\n", tls, strerror ( rc ) ); + goto err_prepend; + } } + /* Transmit record */ + if ( ( rc = tls_send_handshake ( tls, builder.data, + builder.len ) ) != 0 ) { + goto err_send; + } + + err_send: + err_prepend: err_pubkey_sign: err_sig_hash: - pubkey_final ( pubkey, ctx ); - err_pubkey_init: + free ( builder.data ); return rc; } @@ -1946,7 +1994,7 @@ static int tls_send_finished ( struct tls_connection *tls ) { return rc; /* Mark client as finished */ - pending_put ( &tls->client_negotiation ); + pending_put ( &tls->client.negotiation ); return 0; } @@ -1976,13 +2024,12 @@ static int tls_new_change_cipher ( struct tls_connection *tls, iob_pull ( iobuf, sizeof ( *change_cipher ) ); /* Change receive cipher spec */ - if ( ( rc = tls_change_cipher ( tls, &tls->rx_cipherspec_pending, - &tls->rx_cipherspec ) ) != 0 ) { + if ( ( rc = tls_change_cipher ( tls, &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 ); + tls->rx.seq = ~( ( uint64_t ) 0 ); return 0; } @@ -2047,7 +2094,7 @@ static int tls_new_hello_request ( struct tls_connection *tls, } /* Fail unless server supports secure renegotiation */ - if ( ! tls->secure_renegotiation ) { + if ( ! ( tls->secure_renegotiation && tls->extended_master_secret ) ) { DBGC ( tls, "TLS %p refusing to renegotiate insecurely\n", tls ); return -EPERM_RENEG_INSECURE; @@ -2094,6 +2141,9 @@ static int tls_new_server_hello ( struct tls_connection *tls, uint8_t len; uint8_t data[0]; } __attribute__ (( packed )) *reneg = NULL; + const struct { + uint8_t data[0]; + } __attribute__ (( packed )) *ems = NULL; uint16_t version; size_t exts_len; size_t ext_len; @@ -2158,6 +2208,9 @@ static int tls_new_server_hello ( struct tls_connection *tls, return -EINVAL_HELLO; } break; + case htons ( TLS_EXTENDED_MASTER_SECRET ) : + ems = ( ( void * ) ext->data ); + break; } } } @@ -2188,8 +2241,11 @@ static int tls_new_server_hello ( struct tls_connection *tls, return rc; /* Copy out server random bytes */ - memcpy ( &tls->server_random, &hello_a->random, - sizeof ( tls->server_random ) ); + memcpy ( &tls->server.random, &hello_a->random, + sizeof ( tls->server.random ) ); + + /* Handle extended master secret */ + tls->extended_master_secret = ( !! ems ); /* Check session ID */ if ( hello_a->session_id_len && @@ -2203,6 +2259,14 @@ static int tls_new_server_hello ( struct tls_connection *tls, if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) return rc; + /* Ensure master secret generation method matches */ + if ( tls->extended_master_secret != + tls->session->extended_master_secret ) { + DBGC ( tls, "TLS %p mismatched extended master secret " + "extension\n", tls ); + return -EPERM_EMS; + } + } else { /* Record new session ID, if present */ @@ -2309,12 +2373,13 @@ static int tls_parse_chain ( struct tls_connection *tls, int rc; /* Free any existing certificate chain */ - x509_chain_put ( tls->chain ); - tls->chain = NULL; + memset ( &tls->server.key, 0, sizeof ( tls->server.key ) ); + x509_chain_put ( tls->server.chain ); + tls->server.chain = NULL; /* Create certificate chain */ - tls->chain = x509_alloc_chain(); - if ( ! tls->chain ) { + tls->server.chain = x509_alloc_chain(); + if ( ! tls->server.chain ) { rc = -ENOMEM_CHAIN; goto err_alloc_chain; } @@ -2346,14 +2411,15 @@ static int tls_parse_chain ( struct tls_connection *tls, record_len = ( sizeof ( *certificate ) + certificate_len ); /* Add certificate to chain */ - if ( ( rc = x509_append_raw ( tls->chain, certificate->data, + if ( ( rc = x509_append_raw ( tls->server.chain, + certificate->data, certificate_len ) ) != 0 ) { DBGC ( tls, "TLS %p could not append certificate: %s\n", tls, strerror ( rc ) ); DBGC_HDA ( tls, 0, data, remaining ); goto err_parse; } - cert = x509_last ( tls->chain ); + cert = x509_last ( tls->server.chain ); DBGC ( tls, "TLS %p found certificate %s\n", tls, x509_name ( cert ) ); @@ -2367,8 +2433,9 @@ static int tls_parse_chain ( struct tls_connection *tls, err_parse: err_overlength: err_underlength: - x509_chain_put ( tls->chain ); - tls->chain = NULL; + memset ( &tls->server.key, 0, sizeof ( tls->server.key ) ); + x509_chain_put ( tls->server.chain ); + tls->server.chain = NULL; err_alloc_chain: return rc; } @@ -2425,12 +2492,12 @@ static int tls_new_server_key_exchange ( struct tls_connection *tls, const void *data, size_t len ) { /* Free any existing server key exchange record */ - free ( tls->server_key ); - tls->server_key_len = 0; + free ( tls->server.exchange ); + tls->server.exchange_len = 0; /* Allocate copy of server key exchange record */ - tls->server_key = malloc ( len ); - if ( ! tls->server_key ) + tls->server.exchange = malloc ( len ); + if ( ! tls->server.exchange ) return -ENOMEM; /* Store copy of server key exchange record for later @@ -2438,8 +2505,8 @@ static int tls_new_server_key_exchange ( struct tls_connection *tls, * since the certificate validation will not yet have * completed. */ - memcpy ( tls->server_key, data, len ); - tls->server_key_len = len; + memcpy ( tls->server.exchange, data, len ); + tls->server.exchange_len = len; return 0; } @@ -2463,48 +2530,44 @@ static int tls_new_certificate_request ( struct tls_connection *tls, */ /* Free any existing client certificate chain */ - x509_chain_put ( tls->certs ); - tls->certs = NULL; - - /* Determine client certificate to be sent */ - cert = certstore_find_key ( tls->key ); - if ( ! cert ) { - DBGC ( tls, "TLS %p could not find certificate corresponding " - "to private key\n", tls ); - rc = -EPERM_CLIENT_CERT; - goto err_find; - } - x509_get ( cert ); - DBGC ( tls, "TLS %p selected client certificate %s\n", - tls, x509_name ( cert ) ); + x509_chain_put ( tls->client.chain ); + tls->client.chain = NULL; /* Create client certificate chain */ - tls->certs = x509_alloc_chain(); - if ( ! tls->certs ) { + tls->client.chain = x509_alloc_chain(); + if ( ! tls->client.chain ) { rc = -ENOMEM; goto err_alloc; } - /* Append client certificate to chain */ - if ( ( rc = x509_append ( tls->certs, cert ) ) != 0 ) - goto err_append; + /* Determine client certificate to be sent, if any */ + cert = x509_find_key ( NULL, tls->client.key ); + if ( cert ) { + DBGC ( tls, "TLS %p selected client certificate %s\n", + tls, x509_name ( cert ) ); - /* Append any relevant issuer certificates */ - if ( ( rc = x509_auto_append ( tls->certs, &certstore ) ) != 0 ) - goto err_auto_append; + /* Append client certificate to chain */ + if ( ( rc = x509_append ( tls->client.chain, cert ) ) != 0 ) + goto err_append; - /* Drop local reference to client certificate */ - x509_put ( cert ); + /* Append any relevant issuer certificates */ + if ( ( rc = x509_auto_append ( tls->client.chain, + &certstore ) ) != 0 ) + goto err_auto_append; + } else { + + /* Send an empty certificate chain */ + DBGC ( tls, "TLS %p could not find certificate corresponding " + "to private key\n", tls ); + } return 0; err_auto_append: err_append: - x509_chain_put ( tls->certs ); - tls->certs = NULL; + x509_chain_put ( tls->client.chain ); + tls->client.chain = NULL; err_alloc: - x509_put ( cert ); - err_find: return rc; } @@ -2532,13 +2595,14 @@ static int tls_new_server_hello_done ( struct tls_connection *tls, } /* Begin certificate validation */ - if ( ( rc = create_validator ( &tls->validator, tls->chain, - tls->root ) ) != 0 ) { + if ( ( rc = create_validator ( &tls->server.validator, + tls->server.chain, + tls->server.root ) ) != 0 ) { DBGC ( tls, "TLS %p could not start certificate validation: " "%s\n", tls, strerror ( rc ) ); return rc; } - pending_get ( &tls->validation ); + pending_get ( &tls->server.validation ); return 0; } @@ -2580,14 +2644,14 @@ static int tls_new_finished ( struct tls_connection *tls, } /* Mark server as finished */ - pending_put ( &tls->server_negotiation ); + pending_put ( &tls->server.negotiation ); /* If we are resuming a session (i.e. if the server Finished * arrives before the client Finished is sent), then schedule * transmission of Change Cipher and Finished. */ - if ( is_pending ( &tls->client_negotiation ) ) { - tls->tx_pending |= ( TLS_TX_CHANGE_CIPHER | TLS_TX_FINISHED ); + if ( is_pending ( &tls->client.negotiation ) ) { + tls->tx.pending |= ( TLS_TX_CHANGE_CIPHER | TLS_TX_FINISHED ); tls_tx_resume ( tls ); } @@ -2595,6 +2659,7 @@ static int tls_new_finished ( struct tls_connection *tls, if ( tls->session_id_len || tls->new_session_ticket_len ) { memcpy ( session->master_secret, tls->master_secret, sizeof ( session->master_secret ) ); + session->extended_master_secret = tls->extended_master_secret; } if ( tls->session_id_len ) { session->id_len = tls->session_id_len; @@ -2788,7 +2853,7 @@ static int tls_new_record ( struct tls_connection *tls, unsigned int type, break; case TLS_TYPE_HANDSHAKE: handler = tls_new_handshake; - iobuf = &tls->rx_handshake; + iobuf = &tls->rx.handshake; break; default: DBGC ( tls, "TLS %p unknown record type %d\n", tls, type ); @@ -2925,143 +2990,228 @@ static void tls_hmac_list ( struct tls_cipherspec *cipherspec, } /** - * Send plaintext record + * Calculate maximum additional length required for transmitted record(s) + * + * @v tls TLS connection + * @v len I/O buffer payload length + * @ret reserve Maximum additional length to reserve + */ +static size_t tls_iob_reserved ( struct tls_connection *tls, size_t len ) { + struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.active; + struct tls_cipher_suite *suite = cipherspec->suite; + struct cipher_algorithm *cipher = suite->cipher; + struct tls_header *tlshdr; + unsigned int count; + size_t each; + + /* Calculate number of records (allowing for zero-length records) */ + count = ( len ? ( ( len + TLS_TX_BUFSIZE - 1 ) / TLS_TX_BUFSIZE ) : 1 ); + + /* Calculate maximum additional length per record */ + each = ( sizeof ( *tlshdr ) + suite->record_iv_len + suite->mac_len + + ( is_block_cipher ( cipher ) ? cipher->blocksize : 0 ) + + cipher->authsize ); + + /* Calculate maximum total additional length */ + return ( count * each ); +} + +/** + * Allocate I/O buffer for transmitted record(s) + * + * @v tls TLS connection + * @v len I/O buffer payload length + * @ret iobuf I/O buffer + */ +static struct io_buffer * tls_alloc_iob ( struct tls_connection *tls, + size_t len ) { + struct io_buffer *iobuf; + size_t reserve; + + /* Calculate maximum additional length to reserve */ + reserve = tls_iob_reserved ( tls, len ); + + /* Allocate I/O buffer */ + iobuf = xfer_alloc_iob ( &tls->cipherstream, ( reserve + len ) ); + if ( ! iobuf ) + return NULL; + + /* Reserve space */ + iob_reserve ( iobuf, reserve ); + + return iobuf; +} + +/** + * Send plaintext record(s) * * @v tls TLS connection * @v type Record type - * @v data Plaintext record - * @v len Length of plaintext record + * @v iobuf I/O buffer * @ret rc Return status code */ -static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, - const void *data, size_t len ) { - struct tls_cipherspec *cipherspec = &tls->tx_cipherspec; +static int tls_send_record ( struct tls_connection *tls, unsigned int type, + struct io_buffer *iobuf ) { + struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.active; struct tls_cipher_suite *suite = cipherspec->suite; struct cipher_algorithm *cipher = suite->cipher; struct digest_algorithm *digest = suite->digest; struct { uint8_t fixed[suite->fixed_iv_len]; - uint8_t record[suite->record_iv_len]; + uint8_t rec[suite->record_iv_len]; } __attribute__ (( packed )) iv; struct tls_auth_header authhdr; struct tls_header *tlshdr; - void *plaintext; - size_t plaintext_len; - struct io_buffer *ciphertext; - size_t ciphertext_len; - size_t padding_len; uint8_t mac[digest->digestsize]; - void *tmp; + const void *plaintext; + const void *encrypt; + void *ciphertext; + size_t record_len; + size_t encrypt_len; + size_t pad_len; + size_t len; int rc; - /* Construct initialisation vector */ - memcpy ( iv.fixed, cipherspec->fixed_iv, sizeof ( iv.fixed ) ); - if ( ( rc = tls_generate_random ( tls, iv.record, - sizeof ( iv.record ) ) ) != 0 ) { - goto err_random; - } - - /* Construct authentication data */ - authhdr.seq = cpu_to_be64 ( tls->tx_seq ); - authhdr.header.type = type; - authhdr.header.version = htons ( tls->version ); - authhdr.header.length = htons ( len ); + /* Record plaintext pointer and length */ + plaintext = iobuf->data; + len = iob_len ( iobuf ); + + /* Add to handshake digest if applicable */ + if ( type == TLS_TYPE_HANDSHAKE ) + tls_add_handshake ( tls, plaintext, len ); + + /* Start constructing ciphertext at start of reserved space */ + iob_push ( iobuf, tls_iob_reserved ( tls, len ) ); + iob_unput ( iobuf, iob_len ( iobuf ) ); + + /* Construct records */ + do { + /* Limit length of this record (may be zero) */ + record_len = len; + if ( record_len > TLS_TX_BUFSIZE ) + record_len = TLS_TX_BUFSIZE; + + /* Construct and set initialisation vector */ + memcpy ( iv.fixed, cipherspec->fixed_iv, sizeof ( iv.fixed ) ); + if ( ( rc = tls_generate_random ( tls, iv.rec, + sizeof ( iv.rec ) ) ) != 0 ) { + goto err_random; + } + cipher_setiv ( cipher, cipherspec->cipher_ctx, &iv, + sizeof ( iv ) ); + + /* Construct and process authentication data */ + authhdr.seq = cpu_to_be64 ( tls->tx.seq ); + authhdr.header.type = type; + authhdr.header.version = htons ( tls->version ); + authhdr.header.length = htons ( record_len ); + if ( suite->mac_len ) { + tls_hmac ( cipherspec, &authhdr, plaintext, record_len, + mac ); + } + if ( is_auth_cipher ( cipher ) ) { + cipher_encrypt ( cipher, cipherspec->cipher_ctx, + &authhdr, NULL, sizeof ( authhdr ) ); + } - /* Calculate padding length */ - plaintext_len = ( len + suite->mac_len ); - if ( is_block_cipher ( cipher ) ) { - padding_len = ( ( ( cipher->blocksize - 1 ) & - -( plaintext_len + 1 ) ) + 1 ); - } else { - padding_len = 0; - } - plaintext_len += padding_len; + /* Calculate encryption length */ + encrypt_len = ( record_len + suite->mac_len ); + if ( is_block_cipher ( cipher ) ) { + pad_len = ( ( ( cipher->blocksize - 1 ) & + -( encrypt_len + 1 ) ) + 1 ); + } else { + pad_len = 0; + } + encrypt_len += pad_len; + + /* Add record header */ + tlshdr = iob_put ( iobuf, sizeof ( *tlshdr ) ); + tlshdr->type = type; + tlshdr->version = htons ( tls->version ); + tlshdr->length = htons ( sizeof ( iv.rec ) + encrypt_len + + cipher->authsize ); + + /* Add record initialisation vector, if applicable */ + memcpy ( iob_put ( iobuf, sizeof ( iv.rec ) ), iv.rec, + sizeof ( iv.rec ) ); + + /* Copy plaintext data if necessary */ + ciphertext = iob_put ( iobuf, record_len ); + assert ( ciphertext <= plaintext ); + if ( encrypt_len > record_len ) { + memmove ( ciphertext, plaintext, record_len ); + encrypt = ciphertext; + } else { + encrypt = plaintext; + } - /* Allocate plaintext */ - plaintext = malloc ( plaintext_len ); - if ( ! plaintext ) { - DBGC ( tls, "TLS %p could not allocate %zd bytes for " - "plaintext\n", tls, plaintext_len ); - rc = -ENOMEM_TX_PLAINTEXT; - goto err_plaintext; - } + /* Add MAC, if applicable */ + memcpy ( iob_put ( iobuf, suite->mac_len ), mac, + suite->mac_len ); - /* Assemble plaintext */ - tmp = plaintext; - memcpy ( tmp, data, len ); - tmp += len; - if ( suite->mac_len ) - tls_hmac ( cipherspec, &authhdr, data, len, mac ); - memcpy ( tmp, mac, suite->mac_len ); - tmp += suite->mac_len; - memset ( tmp, ( padding_len - 1 ), padding_len ); - tmp += padding_len; - assert ( tmp == ( plaintext + plaintext_len ) ); - DBGC2 ( tls, "Sending plaintext data:\n" ); - DBGC2_HD ( tls, plaintext, plaintext_len ); + /* Add padding, if applicable */ + memset ( iob_put ( iobuf, pad_len ), ( pad_len - 1 ), pad_len ); - /* Set initialisation vector */ - cipher_setiv ( cipher, cipherspec->cipher_ctx, &iv, sizeof ( iv ) ); + /* Encrypt data and append authentication tag */ + DBGC2 ( tls, "Sending plaintext data:\n" ); + DBGC2_HDA ( tls, 0, encrypt, encrypt_len ); + cipher_encrypt ( cipher, cipherspec->cipher_ctx, encrypt, + ciphertext, encrypt_len ); + cipher_auth ( cipher, cipherspec->cipher_ctx, + iob_put ( iobuf, cipher->authsize ) ); - /* Process authentication data, if applicable */ - if ( is_auth_cipher ( cipher ) ) { - cipher_encrypt ( cipher, cipherspec->cipher_ctx, &authhdr, - NULL, sizeof ( authhdr ) ); - } + /* Move to next record */ + tls->tx.seq += 1; + plaintext += record_len; + len -= record_len; - /* Allocate ciphertext */ - ciphertext_len = ( sizeof ( *tlshdr ) + sizeof ( iv.record ) + - plaintext_len + cipher->authsize ); - ciphertext = xfer_alloc_iob ( &tls->cipherstream, ciphertext_len ); - if ( ! ciphertext ) { - DBGC ( tls, "TLS %p could not allocate %zd bytes for " - "ciphertext\n", tls, ciphertext_len ); - rc = -ENOMEM_TX_CIPHERTEXT; - goto err_ciphertext; - } - - /* Assemble ciphertext */ - tlshdr = iob_put ( ciphertext, sizeof ( *tlshdr ) ); - tlshdr->type = type; - tlshdr->version = htons ( tls->version ); - tlshdr->length = htons ( ciphertext_len - sizeof ( *tlshdr ) ); - memcpy ( iob_put ( ciphertext, sizeof ( iv.record ) ), iv.record, - sizeof ( iv.record ) ); - cipher_encrypt ( cipher, cipherspec->cipher_ctx, plaintext, - iob_put ( ciphertext, plaintext_len ), plaintext_len ); - cipher_auth ( cipher, cipherspec->cipher_ctx, - iob_put ( ciphertext, cipher->authsize ) ); - assert ( iob_len ( ciphertext ) == ciphertext_len ); - - /* Free plaintext as soon as possible to conserve memory */ - free ( plaintext ); - plaintext = NULL; + } while ( len ); /* Send ciphertext */ if ( ( rc = xfer_deliver_iob ( &tls->cipherstream, - iob_disown ( ciphertext ) ) ) != 0 ) { + iob_disown ( iobuf ) ) ) != 0 ) { DBGC ( tls, "TLS %p could not deliver ciphertext: %s\n", tls, strerror ( rc ) ); goto err_deliver; } - /* Update TX state machine to next record */ - tls->tx_seq += 1; - - assert ( plaintext == NULL ); - assert ( ciphertext == NULL ); + assert ( iobuf == NULL ); return 0; err_deliver: - free_iob ( ciphertext ); - err_ciphertext: - free ( plaintext ); - err_plaintext: err_random: + free_iob ( iobuf ); return rc; } /** + * Send plaintext record + * + * @v tls TLS connection + * @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_connection *tls, unsigned int type, + const void *data, size_t len ) { + struct io_buffer *iobuf; + int rc; + + /* Allocate I/O buffer */ + iobuf = tls_alloc_iob ( tls, len ); + if ( ! iobuf ) + return -ENOMEM_TX_PLAINTEXT; + memcpy ( iob_put ( iobuf, len ), data, len ); + + /* Transmit I/O buffer */ + if ( ( rc = tls_send_record ( tls, type, iob_disown ( iobuf ) ) ) != 0 ) + return rc; + + return 0; +} + +/** * Verify block padding * * @v tls TLS connection @@ -3107,7 +3257,7 @@ static int tls_verify_padding ( struct tls_connection *tls, static int tls_new_ciphertext ( struct tls_connection *tls, struct tls_header *tlshdr, struct list_head *rx_data ) { - struct tls_cipherspec *cipherspec = &tls->rx_cipherspec; + struct tls_cipherspec *cipherspec = &tls->rx.cipherspec.active; struct tls_cipher_suite *suite = cipherspec->suite; struct cipher_algorithm *cipher = suite->cipher; struct digest_algorithm *digest = suite->digest; @@ -3156,7 +3306,7 @@ static int tls_new_ciphertext ( struct tls_connection *tls, auth = last->tail; /* Construct authentication data */ - authhdr.seq = cpu_to_be64 ( tls->rx_seq ); + authhdr.seq = cpu_to_be64 ( tls->rx.seq ); authhdr.header.type = tlshdr->type; authhdr.header.version = tlshdr->version; authhdr.header.length = htons ( len ); @@ -3172,7 +3322,7 @@ static int tls_new_ciphertext ( struct tls_connection *tls, /* Decrypt the received data */ check_len = 0; - list_for_each_entry ( iobuf, &tls->rx_data, list ) { + list_for_each_entry ( iobuf, &tls->rx.data, list ) { cipher_decrypt ( cipher, cipherspec->cipher_ctx, iobuf->data, iobuf->data, iob_len ( iobuf ) ); check_len += iob_len ( iobuf ); @@ -3278,8 +3428,9 @@ static int tls_plainstream_deliver ( struct tls_connection *tls, goto done; } - if ( ( rc = tls_send_plaintext ( tls, TLS_TYPE_DATA, iobuf->data, - iob_len ( iobuf ) ) ) != 0 ) + /* Send data record */ + if ( ( rc = tls_send_record ( tls, TLS_TYPE_DATA, + iob_disown ( iobuf ) ) ) != 0 ) goto done; done: @@ -3298,8 +3449,8 @@ static int tls_progress ( struct tls_connection *tls, struct job_progress *progress ) { /* Return cipherstream or validator progress as applicable */ - if ( is_pending ( &tls->validation ) ) { - return job_progress ( &tls->validator, progress ); + if ( is_pending ( &tls->server.validation ) ) { + return job_progress ( &tls->server.validator, progress ); } else { return job_progress ( &tls->cipherstream, progress ); } @@ -3307,6 +3458,7 @@ static int tls_progress ( struct tls_connection *tls, /** TLS plaintext stream interface operations */ static struct interface_operation tls_plainstream_ops[] = { + INTF_OP ( xfer_alloc_iob, struct tls_connection *, tls_alloc_iob ), INTF_OP ( xfer_deliver, struct tls_connection *, tls_plainstream_deliver ), INTF_OP ( xfer_window, struct tls_connection *, @@ -3334,10 +3486,10 @@ static struct interface_descriptor tls_plainstream_desc = * @ret rc Returned status code */ static int tls_newdata_process_header ( struct tls_connection *tls ) { - struct tls_cipherspec *cipherspec = &tls->rx_cipherspec; + struct tls_cipherspec *cipherspec = &tls->rx.cipherspec.active; struct cipher_algorithm *cipher = cipherspec->suite->cipher; size_t iv_len = cipherspec->suite->record_iv_len; - size_t data_len = ntohs ( tls->rx_header.length ); + size_t data_len = ntohs ( tls->rx.header.length ); size_t remaining = data_len; size_t frag_len; size_t reserve; @@ -3353,7 +3505,7 @@ static int tls_newdata_process_header ( struct tls_connection *tls ) { remaining += reserve; /* Allocate data buffers now that we know the length */ - assert ( list_empty ( &tls->rx_data ) ); + assert ( list_empty ( &tls->rx.data ) ); while ( remaining ) { /* Calculate fragment length. Ensure that no block is @@ -3394,16 +3546,16 @@ static int tls_newdata_process_header ( struct tls_connection *tls ) { reserve = 0; /* Add I/O buffer to list */ - list_add_tail ( &iobuf->list, &tls->rx_data ); + list_add_tail ( &iobuf->list, &tls->rx.data ); } /* Move to data state */ - tls->rx_state = TLS_RX_DATA; + tls->rx.state = TLS_RX_DATA; return 0; err: - list_for_each_entry_safe ( iobuf, tmp, &tls->rx_data, list ) { + list_for_each_entry_safe ( iobuf, tmp, &tls->rx.data, list ) { list_del ( &iobuf->list ); free_iob ( iobuf ); } @@ -3421,27 +3573,27 @@ static int tls_newdata_process_data ( struct tls_connection *tls ) { int rc; /* Move current buffer to end of list */ - iobuf = list_first_entry ( &tls->rx_data, struct io_buffer, list ); + iobuf = list_first_entry ( &tls->rx.data, struct io_buffer, list ); list_del ( &iobuf->list ); - list_add_tail ( &iobuf->list, &tls->rx_data ); + list_add_tail ( &iobuf->list, &tls->rx.data ); /* Continue receiving data if any space remains */ - iobuf = list_first_entry ( &tls->rx_data, struct io_buffer, list ); + iobuf = list_first_entry ( &tls->rx.data, struct io_buffer, list ); if ( iob_tailroom ( iobuf ) ) return 0; /* Process record */ - if ( ( rc = tls_new_ciphertext ( tls, &tls->rx_header, - &tls->rx_data ) ) != 0 ) + if ( ( rc = tls_new_ciphertext ( tls, &tls->rx.header, + &tls->rx.data ) ) != 0 ) return rc; /* Increment RX sequence number */ - tls->rx_seq += 1; + tls->rx.seq += 1; /* Return to header state */ - assert ( list_empty ( &tls->rx_data ) ); - tls->rx_state = TLS_RX_HEADER; - iob_unput ( &tls->rx_header_iobuf, sizeof ( tls->rx_header ) ); + assert ( list_empty ( &tls->rx.data ) ); + tls->rx.state = TLS_RX_HEADER; + iob_unput ( &tls->rx.iobuf, sizeof ( tls->rx.header ) ); return 0; } @@ -3480,13 +3632,13 @@ static int tls_cipherstream_deliver ( struct tls_connection *tls, while ( iob_len ( iobuf ) ) { /* Select buffer according to current state */ - switch ( tls->rx_state ) { + switch ( tls->rx.state ) { case TLS_RX_HEADER: - dest = &tls->rx_header_iobuf; + dest = &tls->rx.iobuf; process = tls_newdata_process_header; break; case TLS_RX_DATA: - dest = list_first_entry ( &tls->rx_data, + dest = list_first_entry ( &tls->rx.data, struct io_buffer, list ); assert ( dest != NULL ); process = tls_newdata_process_data; @@ -3550,15 +3702,13 @@ static struct interface_descriptor tls_cipherstream_desc = */ static void tls_validator_done ( struct tls_connection *tls, int rc ) { struct tls_session *session = tls->session; - struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; - struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; struct x509_certificate *cert; /* Mark validation as complete */ - pending_put ( &tls->validation ); + pending_put ( &tls->server.validation ); /* Close validator interface */ - intf_restart ( &tls->validator, rc ); + intf_restart ( &tls->server.validator, rc ); /* Check for validation failure */ if ( rc != 0 ) { @@ -3569,7 +3719,7 @@ static void tls_validator_done ( struct tls_connection *tls, int rc ) { DBGC ( tls, "TLS %p certificate validation succeeded\n", tls ); /* Extract first certificate */ - cert = x509_first ( tls->chain ); + cert = x509_first ( tls->server.chain ); assert ( cert != NULL ); /* Verify server name */ @@ -3579,22 +3729,18 @@ static void tls_validator_done ( struct tls_connection *tls, int rc ) { goto err; } - /* Initialise public key algorithm */ - if ( ( rc = pubkey_init ( pubkey, cipherspec->pubkey_ctx, - cert->subject.public_key.raw.data, - cert->subject.public_key.raw.len ) ) != 0 ) { - DBGC ( tls, "TLS %p cannot initialise public key: %s\n", - tls, strerror ( rc ) ); - goto err; - } + /* Extract the now trusted server public key */ + memcpy ( &tls->server.key, &cert->subject.public_key.raw, + sizeof ( tls->server.key ) ); - /* Schedule Client Key Exchange, Change Cipher, and Finished */ - tls->tx_pending |= ( TLS_TX_CLIENT_KEY_EXCHANGE | + /* Schedule transmission of applicable handshake messages */ + tls->tx.pending |= ( TLS_TX_CLIENT_KEY_EXCHANGE | TLS_TX_CHANGE_CIPHER | TLS_TX_FINISHED ); - if ( tls->certs ) { - tls->tx_pending |= ( TLS_TX_CERTIFICATE | - TLS_TX_CERTIFICATE_VERIFY ); + if ( tls->client.chain ) { + tls->tx.pending |= TLS_TX_CERTIFICATE; + if ( ! list_empty ( &tls->client.chain->links ) ) + tls->tx.pending |= TLS_TX_CERTIFICATE_VERIFY; } tls_tx_resume ( tls ); @@ -3612,7 +3758,8 @@ static struct interface_operation tls_validator_ops[] = { /** TLS certificate validator interface descriptor */ static struct interface_descriptor tls_validator_desc = - INTF_DESC ( struct tls_connection, validator, tls_validator_ops ); + INTF_DESC ( struct tls_connection, server.validator, + tls_validator_ops ); /****************************************************************************** * @@ -3636,7 +3783,7 @@ static void tls_tx_step ( struct tls_connection *tls ) { return; /* Send first pending transmission */ - if ( tls->tx_pending & TLS_TX_CLIENT_HELLO ) { + if ( tls->tx.pending & TLS_TX_CLIENT_HELLO ) { /* Serialise server negotiations within a session, to * provide a consistent view of session IDs and * session tickets. @@ -3644,7 +3791,7 @@ static void tls_tx_step ( struct tls_connection *tls ) { list_for_each_entry ( conn, &session->conn, list ) { if ( conn == tls ) break; - if ( is_pending ( &conn->server_negotiation ) ) + if ( is_pending ( &conn->server.negotiation ) ) return; } /* Record or generate session ID and associated master secret */ @@ -3658,8 +3805,8 @@ static void tls_tx_step ( struct tls_connection *tls ) { } else { /* No existing session: use a random session ID */ assert ( sizeof ( tls->session_id ) == - sizeof ( tls->client_random ) ); - memcpy ( tls->session_id, &tls->client_random, + sizeof ( tls->client.random ) ); + memcpy ( tls->session_id, &tls->client.random, sizeof ( tls->session_id ) ); tls->session_id_len = sizeof ( tls->session_id ); } @@ -3669,32 +3816,32 @@ static void tls_tx_step ( struct tls_connection *tls ) { tls, strerror ( rc ) ); goto err; } - tls->tx_pending &= ~TLS_TX_CLIENT_HELLO; - } else if ( tls->tx_pending & TLS_TX_CERTIFICATE ) { + tls->tx.pending &= ~TLS_TX_CLIENT_HELLO; + } else if ( tls->tx.pending & TLS_TX_CERTIFICATE ) { /* Send Certificate */ if ( ( rc = tls_send_certificate ( tls ) ) != 0 ) { DBGC ( tls, "TLS %p cold not send Certificate: %s\n", tls, strerror ( rc ) ); goto err; } - tls->tx_pending &= ~TLS_TX_CERTIFICATE; - } else if ( tls->tx_pending & TLS_TX_CLIENT_KEY_EXCHANGE ) { + tls->tx.pending &= ~TLS_TX_CERTIFICATE; + } else if ( tls->tx.pending & TLS_TX_CLIENT_KEY_EXCHANGE ) { /* Send Client Key Exchange */ if ( ( rc = tls_send_client_key_exchange ( tls ) ) != 0 ) { DBGC ( tls, "TLS %p could not send Client Key " "Exchange: %s\n", tls, strerror ( rc ) ); goto err; } - tls->tx_pending &= ~TLS_TX_CLIENT_KEY_EXCHANGE; - } else if ( tls->tx_pending & TLS_TX_CERTIFICATE_VERIFY ) { + tls->tx.pending &= ~TLS_TX_CLIENT_KEY_EXCHANGE; + } else if ( tls->tx.pending & TLS_TX_CERTIFICATE_VERIFY ) { /* Send Certificate Verify */ if ( ( rc = tls_send_certificate_verify ( tls ) ) != 0 ) { DBGC ( tls, "TLS %p could not send Certificate " "Verify: %s\n", tls, strerror ( rc ) ); goto err; } - tls->tx_pending &= ~TLS_TX_CERTIFICATE_VERIFY; - } else if ( tls->tx_pending & TLS_TX_CHANGE_CIPHER ) { + tls->tx.pending &= ~TLS_TX_CERTIFICATE_VERIFY; + } else if ( tls->tx.pending & 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: " @@ -3702,28 +3849,27 @@ static void tls_tx_step ( struct tls_connection *tls ) { goto err; } if ( ( rc = tls_change_cipher ( tls, - &tls->tx_cipherspec_pending, - &tls->tx_cipherspec )) != 0 ){ + &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_pending &= ~TLS_TX_CHANGE_CIPHER; - } else if ( tls->tx_pending & TLS_TX_FINISHED ) { + tls->tx.seq = 0; + tls->tx.pending &= ~TLS_TX_CHANGE_CIPHER; + } else if ( tls->tx.pending & 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_pending &= ~TLS_TX_FINISHED; + tls->tx.pending &= ~TLS_TX_FINISHED; } /* Reschedule process if pending transmissions remain, * otherwise send notification of a window change. */ - if ( tls->tx_pending ) { + if ( tls->tx.pending ) { tls_tx_resume ( tls ); } else { xfer_window_changed ( &tls->plainstream ); @@ -3737,7 +3883,7 @@ static void tls_tx_step ( struct tls_connection *tls ) { /** TLS TX process descriptor */ static struct process_descriptor tls_process_desc = - PROC_DESC_ONCE ( struct tls_connection, process, tls_tx_step ); + PROC_DESC_ONCE ( struct tls_connection, tx.process, tls_tx_step ); /****************************************************************************** * @@ -3761,8 +3907,8 @@ static int tls_session ( struct tls_connection *tls, const char *name ) { /* Find existing matching session, if any */ list_for_each_entry ( session, &tls_sessions, list ) { if ( ( strcmp ( name, session->name ) == 0 ) && - ( tls->root == session->root ) && - ( tls->key == session->key ) ) { + ( tls->server.root == session->root ) && + ( tls->client.key == session->key ) ) { ref_get ( &session->refcnt ); tls->session = session; DBGC ( tls, "TLS %p joining session %s\n", tls, name ); @@ -3781,8 +3927,8 @@ static int tls_session ( struct tls_connection *tls, const char *name ) { name_copy = ( ( ( void * ) session ) + sizeof ( *session ) ); strcpy ( name_copy, name ); session->name = name_copy; - session->root = x509_root_get ( tls->root ); - session->key = privkey_get ( tls->key ); + session->root = x509_root_get ( tls->server.root ); + session->key = privkey_get ( tls->client.key ); INIT_LIST_HEAD ( &session->conn ); list_add ( &session->list, &tls_sessions ); @@ -3829,23 +3975,23 @@ int add_tls ( struct interface *xfer, const char *name, INIT_LIST_HEAD ( &tls->list ); intf_init ( &tls->plainstream, &tls_plainstream_desc, &tls->refcnt ); intf_init ( &tls->cipherstream, &tls_cipherstream_desc, &tls->refcnt ); - intf_init ( &tls->validator, &tls_validator_desc, &tls->refcnt ); - process_init_stopped ( &tls->process, &tls_process_desc, + intf_init ( &tls->server.validator, &tls_validator_desc, &tls->refcnt ); + process_init_stopped ( &tls->tx.process, &tls_process_desc, &tls->refcnt ); - tls->key = privkey_get ( key ? key : &private_key ); - tls->root = x509_root_get ( root ? root : &root_certificates ); + tls->client.key = privkey_get ( key ? key : &private_key ); + tls->server.root = x509_root_get ( root ? root : &root_certificates ); tls->version = TLS_VERSION_MAX; - 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 ); + tls_clear_cipher ( tls, &tls->tx.cipherspec.active ); + tls_clear_cipher ( tls, &tls->tx.cipherspec.pending ); + tls_clear_cipher ( tls, &tls->rx.cipherspec.active ); + tls_clear_cipher ( tls, &tls->rx.cipherspec.pending ); tls_clear_handshake ( tls ); - tls->client_random.gmt_unix_time = time ( NULL ); - iob_populate ( &tls->rx_header_iobuf, &tls->rx_header, 0, - sizeof ( tls->rx_header ) ); - INIT_LIST_HEAD ( &tls->rx_data ); - if ( ( rc = tls_generate_random ( tls, &tls->client_random.random, - ( sizeof ( tls->client_random.random ) ) ) ) != 0 ) { + tls->client.random.gmt_unix_time = time ( NULL ); + iob_populate ( &tls->rx.iobuf, &tls->rx.header, 0, + sizeof ( tls->rx.header ) ); + INIT_LIST_HEAD ( &tls->rx.data ); + if ( ( rc = tls_generate_random ( tls, &tls->client.random.random, + ( sizeof ( tls->client.random.random ) ) ) ) != 0 ) { goto err_random; } if ( ( rc = tls_session ( tls, name ) ) != 0 ) |
