-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for fragmenting of DTLS handshake messages #1939
Add support for fragmenting of DTLS handshake messages #1939
Conversation
`mbedtls_ssl_get_record_expansion()` is supposed to return the maximum difference between the size of a protected record and the size of the encapsulated plaintext. It had the following two bugs: (1) It did not consider the new ChaChaPoly ciphersuites, returning the error code #MBEDTLS_ERR_SSL_INTERNAL_ERROR in this case. (2) It did not correctly estimate the maximum record expansion in case of CBC ciphersuites in (D)TLS versions 1.1 and higher, in which case the ciphertext is prefixed by an explicit IV. This commit fixes both bugs.
/* | ||
* Check if a smaller max length was negotiated | ||
*/ | ||
/* Check if a smaller max length was negotiated */ | ||
if( ssl->session_out != NULL && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some tests fail here because of #1941.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this was fixed by basing this PR on #1942.
a078c8c
to
8c75a5d
Compare
include/mbedtls/ssl.h
Outdated
@@ -958,6 +958,10 @@ struct mbedtls_ssl_config | |||
unsigned int dhm_min_bitlen; /*!< min. bit length of the DHM prime */ | |||
#endif | |||
|
|||
#if defined(MBEDTLS_SSL_PROTO_DTLS) | |||
uint16_t mtu; /*!< path mtu, used to fragment outoing messages */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, fixed.
include/mbedtls/ssl.h
Outdated
* | ||
* \note This only controls the size of the packet we send. | ||
* Client-side, you can request the server to use smaller | ||
* records with \c mbedtls_conf_max_frag_len(). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the Doxygen links are automatically inserted when using the func_name()
syntax, without \c
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Autolinks also work with the \c
, so I'll keep it for consistency with the rest of the file. In that case, the link didn't work because the function name was wrong - missing the ssl_
part.
include/mbedtls/ssl.h
Outdated
* Client-side, you can request the server to use smaller | ||
* records with \c mbedtls_conf_max_frag_len(). | ||
* | ||
* \note If both a MTU and a maximum fragment length have been |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This slightly reads as if MTU and MFL would limit the same thing, but MTU limits the maximum size of (the list of) protected records (within the current datagram), while MFL limits the maximum payload size for each single record (as far as I understand).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try to improve the wording here.
include/mbedtls/ssl.h
Outdated
* configured (or negotiated with the peer), the lower limit | ||
* is used. | ||
* | ||
* \note Values larger than \c MBEDTLS_SSL_OUT_CONTENT_LEN have no |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather expect MBEDTLS_SSL_OUT_BUFFER_LEN
here, because MBEDTLS_SSL_OUT_CONTENT_LEN
is about the unprotected payload.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm rephrasing that too.
/** | ||
* \brief Return the current maximum outgoing record payload in bytes. | ||
* This takes into account the config.h setting \c | ||
* MBEDTLS_SSL_OUT_CONTENT_LEN, the configured and negotiated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Doxygen links are inserted when using #MBEDTLS_SSL_OUT_CONTENT_LEN
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, fixing.
library/ssl_tls.c
Outdated
* - ssl->out_msg[0]: the handshake type (ClientHello, ServerHello, etc) | ||
* - ssl->out_msg + 4: the handshake message body | ||
* | ||
* Ouputs, ie state before passing to flight_append() or write_record(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, fixing.
- take advantage of the fact that we're only called for first send - put all sanity checks at the top - rename and constify shortcut variables - improve comments
This will allow fragmentation to always happen in the same place, always from a buffer distinct from ssl->out_msg, and with the same way of resuming after returning WANT_WRITE
Note: no interop tests in ssl-opt.sh for now, as some of them make us run into bugs in (the CI's default versions of) OpenSSL and GnuTLS, so interop tests will be added later once the situation is clarified. <- TODO
retest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general I approve the PR. I am just curious about the local vs "global" nature of the pointers in the SSL context structure. I'd be happy to see it clarified.
Depending on the settings of the local machine, gnutls-cli will either try IPv4 or IPv6 when trying to connect to localhost. With TLS, whatever it tries first, it will notice if any failure happens and try the other protocol if necessary. With DTLS it can't do that. Unfortunately for now there isn't really any good way to specify an address and hostname independently, though that might come soon: https://gitlab.com/gnutls/gnutls/issues/344 A work around is to specify an address directly and then use --insecure to ignore certificate hostname mismatch; that is OK for tests that are completely unrelated to certificate verification (such as the recent fragmenting tests) but unacceptable for others. For that reason, don't specify a default hostname for gnutls-cli, but instead let each test choose between `--insecure 127.0.0.1` and `localhost` (or `--insecure '::1'` if desired). Alternatives include: - having test certificates with 127.0.0.1 as the hostname, but having an IP as the CN is unusual, and we would need to change our test certs; - have our server open two sockets under the hood and listen on both IPv4 and IPv6 (that's what gnutls-serv does, and IMO it's a good thing) but that obviously requires development and testing (esp. for windows compatibility) - wait for a newer version of GnuTLS to be released, install it on the CI and developer machines, and use that in all tests - quite satisfying but can't be done now (and puts stronger requirements on test environment).
In SSLv3, the client sends a NoCertificate alert in response to a CertificateRequest if it doesn't have a CRT. This previously lead to failure in ssl_write_handshake_msg() which only accepted handshake or CCS records.
The previous code appended messages to flights only if their handshake type, as derived from the first byte in the message, was different from MBEDTLS_SSL_HS_HELLO_REQUEST. This check should only be performed for handshake records, while CCS records should immediately be appended.
@hanno-arm @k-stachowiak I quick-fixed the gnutls-cli tests so that they pass also on machines where IPv6 does not work or where @hanno-arm Please be aware that my commit changes the |
Note: |
retest |
Hi @mpg. 'needs CI' means, the CI hasn't passed or is yet to run. In this instance, the CI (I think) had failed. Right now it's still failing but the build seems to be stale so I've triggered the build again. |
I checked the output of the "mbed TLS build & test" CI and it appears the only failing test is the timing suite, which is known to be flaky, see #1517, so is not a blocker. |
retest |
The new CI run failed again only in the timing test. I'm not triggering a new run, though feel free to do so if you feel it's useful. |
retest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't given this a deep nor full review, but I have some reservations and have given some feedback. I don't want to hold up merging the PR, so @mpg, could you please look at my review feedback and submit a second PR that addresses my review feedback? So we can get this merged, and do fixes as a followup.
Treat the review feedback as normal - and decide for yourself whether you agree or not to the suggestions. Reply to this PR for anything you disagree with.
(Also - apologies in advance if I've given feedback on a commit introduced by a different PR).
* encapsulation and encryption/authentication if any. | ||
* | ||
* \note This can be called at any point during the connection, for | ||
* example when a PMTU estimate becomes available from other |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PMTU would be better expanded as:
Path Maximum Transfer Unit (PMTU)
Not a blocker.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in hanno-becker@eefe084.
include/mbedtls/ssl.h
Outdated
* Client-side, you can request the server to use smaller | ||
* records with \c mbedtls_ssl_conf_max_frag_len(). | ||
* \note This setting only controls the size of the packets we send, | ||
* and does not restrict the size of the datagrams we're |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's perfectly acceptable and breaks no language rules. You don't contract when you want to place emphasis, speak clearly or sound like a robot.
|
||
/** | ||
* \brief Return the current maximum outgoing record payload in bytes. | ||
* This takes into account the config.h setting \c |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have to, and probably shouldn't call it the config.h setting MBEDTLS_SSL_OUT_CONTENT_LEN
but instead got with the setting MBEDTLS_SSL_OUT_CONTENT_LEN
. Just makes the documentation easier to maintain, and it's not necessary.
Not a blocker.
memset( ssl->out_msg + 6, 0x00, 3 ); | ||
memcpy( ssl->out_msg + 9, ssl->out_msg + 1, 3 ); | ||
} | ||
#endif /* MBEDTLS_SSL_PROTO_DTLS */ | ||
|
||
if( out_msg_type != MBEDTLS_SSL_HS_HELLO_REQUEST ) | ||
ssl->handshake->update_checksum( ssl, ssl->out_msg, len ); | ||
/* Update running hashes of hanshake messages seen */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo - 'hanshake' -> 'handshake'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in hanno-becker@0207e53.
* | ||
* Need to remember the current message in case flush_output returns | ||
* WANT_WRITE, causing us to exit this function and come back later. | ||
* This function must be called until state is no longer SENDING. | ||
*/ | ||
int mbedtls_ssl_resend( mbedtls_ssl_context *ssl ) | ||
int mbedtls_ssl_flight_transmit( mbedtls_ssl_context *ssl ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this function would have made more sense called mbedtls_ssl_transmit_flight()
, to make it verb noun, rather than noun verb, but it's not a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sbutcher-arm I prefer this naming scheme because it allows to broadly categorize functions according to the parts of their name. For example, there are also (pre-existing) functions mbedtls_ssl_flight_append()
and mbedtls_ssl_flight_free()
, and for all of them the mbedtls_ssl_flight
prefix indicates that they're about flight handling.
ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT ) ) | ||
#endif /* MBEDTLS_SSL_PROTO_SSL3 && MBEDTLS_SSL_SRV_C */ | ||
{ | ||
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should aim for better than "should never happen" messages in debug. yes, I know the code is full of them already, but they can be really irritating when you see them in debug output (and unhelpful), and would have once got us a spot on the Daily WTF.
Instead we should state the impossible condition and/or why it's invalid. It would be better to say "invalid state - not a handshake message" or something similar.
Not a blocker.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be addressed as a separate issue, going through all uses of should never happen
we have in the library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hs_type != MBEDTLS_SSL_HS_HELLO_REQUEST && | ||
ssl->handshake == NULL ) | ||
{ | ||
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, we should aim for better than "should never happen" error messages.
|
||
#if defined(MBEDTLS_SSL_PROTO_DTLS) | ||
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && | ||
ssl->handshake != NULL && | ||
ssl->handshake->retransmit_state == MBEDTLS_SSL_RETRANS_SENDING ) | ||
{ | ||
; /* Skip special handshake treatment when resending */ | ||
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, we should aim for better than "should never happen" error messages.
-C "error" | ||
|
||
# This ensures things still work after session_reset(), | ||
# for example it would have caught #1941. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't reference GitHub issues in the test code, and especially not without explaining it's a GitHub issue. Instead if it's worth stating we should name the issue and explain it.
However - I'm not sure we have to - we should just state the test case and why we have it without mentioning bugs we've had before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in hanno-becker@b841b4f.
## https://gitlab.com/gnutls/gnutls/issues/543 | ||
## We can re-enable them when a fixed version fo GnuTLS is available | ||
## and installed in our CI system. | ||
## |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather we didn't comment these tests out, but instead skipped them, so they're included in the stats of what test cases we have, but also so as 'skipped' tests.
I suggest we add a forcing function of test_disabled
which sets SKIP_NEXT="YES"
to force the test to be skipped. Better than commenting it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Followed your suggestion in hanno-becker@3b8b40c.
retest |
Note to self: when creating the follow-up PR, see also #1918 (comment) |
Description
Add support for fragmenting of DTLS handshake messages based on maximum fragment length and/or an explicit MTU setting.
Supersedes and continues #1879
Partially fixes #387 (for DTLS only).
Note to gatekeeper: please close internal ref IOTSSL-592 when merging this.
Based on #1942 and merging #1915 and #1955 along the way as they're all necessary for tests to pass here.
Reviewers, please only review new commits in this branch.
Note to gatekeeper: you probably want to merge #1942, #1915 and #1955 before this one for clarity.
Status
READY
Requires Backporting
NO
Migrations
Compatible API extensions
ABI change in libmbedtls (added
mtu
field tostuct mbedtls_ssl_config
).