If an attempt for session resumption fails, the `session_negotiate` structure
might be partially filled, and in particular already contain a peer certificate
structure. This certificate structure needs to be freed before parsing the
certificate sent in the `Certificate` message.
This commit moves the code-path taking care of this from the helper
function `ssl_parse_certificate_chain()`, whose purpose should be parsing
only, to the top-level handler `mbedtls_ssl_parse_certificate()`.
The fact that we don't know the state of `ssl->session_negotiate` after
a failed attempt for session resumption is undesirable, and a separate
issue #2414 has been opened to improve on this.
This commit introduces a server-side static helper function
`ssl_srv_check_client_no_crt_notification()`, which checks if
the message we received during the incoming certificate state
notifies the server of the lack of certificate on the client.
For SSLv3, such a notification comes as a specific alert,
while for all other TLS versions, it comes as a `Certificate`
handshake message with an empty CRT list.
So far, we've used the `peer_cert` pointer to detect whether
we're parsing the first CRT, but that will soon be removed
if `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` is unset.
This commit introduces a helper function `ssl_clear_peer_cert()`
which frees all data related to the peer's certificate from an
`mbedtls_ssl_session` structure. Currently, this is the peer's
certificate itself, while eventually, it'll be its digest only.
After mitigating the 'triple handshake attack' by checking that
the peer's end-CRT didn't change during renegotation, the current
code avoids re-parsing the CRT by moving the CRT-pointer from the
old session to the new one. While efficient, this will no longer
work once only the hash of the peer's CRT is stored beyond the
handshake.
This commit removes the code-path moving the old CRT, and instead
frees the entire peer CRT chain from the initial handshake as soon
as the 'triple handshake attack' protection has completed.
Introduce MBEDTLS_X509_INFO to indicate the availability of the
mbedtls_x509_*_info() function and closely related APIs. When this is
not defined, also omit name and description from
mbedtls_oid_descriptor_t, and omit OID arrays, macros, and types that
are entirely unused. This saves several KB of code space.
Change-Id: I056312613379890e0d70e1d08c34171287c0aa17
In a reduced configuration without PEM, PKCS5 or PKCS12, armc5 found that ret
was set but not used. Fixing that lead to a new warning about the variable not
being used at all. Now the variable is only declared when it's needed.
Some TLS-only code paths were not protected by an #ifdef and while some
compiler are happy to just silently remove them, armc5 complains:
Warning: #111-D: statement is unreachable
Let's make armc5 happy.
This is enabled by default as we generally enable things by default unless
there's a reason not to (experimental, deprecated, security risk).
We need a compile-time option because, even though the functions themselves
can be easily garbage-collected by the linker, implementing them will require
saving 64 bytes of Client/ServerHello.random values after the handshake, that
would otherwise not be needed, and people who don't need this feature
shouldn't have to pay the price of increased RAM usage.
A positive option looks better, but comes with the following compatibility
issue: people using a custom config.h that is not based on the default
config.h and need TLS support would need to manually change their config in
order to still get TLS.
Work around that by making the public option negative. Internally the positive
option is used, though.
In the future (when preparing the next major version), we might want to switch
back to a positive option as this would be more consistent with other options
we have.
This commit handles occurrences of case 2 and 3 in the following list:
1. Some DTLS-specific code with no TLS-specific code (most frequent)
2. Some specific code for each protocol
3. Some TLS-specific code with no DTLS-specific code (least frequent)
Case 3 previously had a weird structure in that the TLS-specific code was
always present, but the if structure was conditional on DTLS being enabled.
This is changed by this commit to a more logical structure where both the code
and the test are conditional on TLS being enabled.
Case 2 doesn't require any change in the code structure in general. However,
there is one occurrence where the if/else structure is simplified to assigning
the result of a boolean operation, and one occurrence where I also noticed a
useless use of `ssl_ep_len()` in a TLS-specific branch, that I turned to the
constant 0 as it makes more sense.
Case 1 will be handled in the next commit, as it can easily be handled in an
automated way - only cases 2 and 3 (sometimes) required manual intervention.
The list of occurrences for cases 2 and 3 was established manually by looking
for occurrences of '= MBEDTLS_SSL_TRANSPORT_' in the code and manually
checking if there was a TLS-specific branch.
New sizes (see previous commit for the measuring script):
```
both
text data bss dec hex filename
1820 0 4 1824 720 debug.o (ex library/libmbedtls.a)
0 0 0 0 0 net_sockets.o (ex library/libmbedtls.a)
548 0 0 548 224 ssl_cache.o (ex library/libmbedtls.a)
11155 0 596 11751 2de7 ssl_ciphersuites.o (ex library/libmbedtls.a)
17156 0 0 17156 4304 ssl_cli.o (ex library/libmbedtls.a)
460 0 0 460 1cc ssl_cookie.o (ex library/libmbedtls.a)
17649 0 0 17649 44f1 ssl_srv.o (ex library/libmbedtls.a)
800 0 0 800 320 ssl_ticket.o (ex library/libmbedtls.a)
39286 60 0 39346 99b2 ssl_tls.o (ex library/libmbedtls.a)
88874 60 600 89534 15dbe (TOTALS)
DTLS-only
text data bss dec hex filename
1820 0 4 1824 720 debug.o (ex library/libmbedtls.a)
0 0 0 0 0 net_sockets.o (ex library/libmbedtls.a)
548 0 0 548 224 ssl_cache.o (ex library/libmbedtls.a)
11155 0 596 11751 2de7 ssl_ciphersuites.o (ex library/libmbedtls.a)
17068 0 0 17068 42ac ssl_cli.o (ex library/libmbedtls.a)
460 0 0 460 1cc ssl_cookie.o (ex library/libmbedtls.a)
17553 0 0 17553 4491 ssl_srv.o (ex library/libmbedtls.a)
800 0 0 800 320 ssl_ticket.o (ex library/libmbedtls.a)
38499 60 0 38559 969f ssl_tls.o (ex library/libmbedtls.a)
87903 60 600 88563 159f3 (TOTALS)
TLS-only
text data bss dec hex filename
1820 0 4 1824 720 debug.o (ex library/libmbedtls.a)
0 0 0 0 0 net_sockets.o (ex library/libmbedtls.a)
548 0 0 548 224 ssl_cache.o (ex library/libmbedtls.a)
11155 0 596 11751 2de7 ssl_ciphersuites.o (ex library/libmbedtls.a)
14912 0 0 14912 3a40 ssl_cli.o (ex library/libmbedtls.a)
460 0 0 460 1cc ssl_cookie.o (ex library/libmbedtls.a)
15868 0 0 15868 3dfc ssl_srv.o (ex library/libmbedtls.a)
800 0 0 800 320 ssl_ticket.o (ex library/libmbedtls.a)
27619 60 0 27679 6c1f ssl_tls.o (ex library/libmbedtls.a)
73182 60 600 73842 12072 (TOTALS)
```
And use those tools in a few places. For now the purpose is just to validate
those tools before using them in all occurrences of transport-specific code.
The effect of these changes was measured with the following script:
```
set -eu
build() {
printf "\n$1\n"
CC=arm-none-eabi-gcc CFLAGS='-Werror -Os -march=armv6-m -mthumb' \
AR=arm-none-eabi-ar LD=arm-none-eabi-ld make clean lib >/dev/null
arm-none-eabi-size -t library/libmbedtls.a
}
git checkout -- include/mbedtls/config.h
scripts/config.pl unset MBEDTLS_NET_C
scripts/config.pl unset MBEDTLS_TIMING_C
scripts/config.pl unset MBEDTLS_FS_IO
scripts/config.pl unset MBEDTLS_ENTROPY_NV_SEED
scripts/config.pl set MBEDTLS_NO_PLATFORM_ENTROPY
build "both"
scripts/config.pl unset MBEDTLS_SSL_PROTO_TLS
build "DTLS-only"
scripts/config.pl set MBEDTLS_SSL_PROTO_TLS
scripts/config.pl unset MBEDTLS_SSL_PROTO_DTLS
scripts/config.pl unset MBEDTLS_SSL_DTLS_HELLO_VERIFY
scripts/config.pl unset MBEDTLS_SSL_DTLS_ANTI_REPLAY
scripts/config.pl unset MBEDTLS_SSL_DTLS_BADMAC_LIMIT
scripts/config.pl unset MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE
build "TLS-only"
git checkout -- include/mbedtls/config.h
```
The output of the script is as follows:
```
both
text data bss dec hex filename
1820 0 4 1824 720 debug.o (ex library/libmbedtls.a)
0 0 0 0 0 net_sockets.o (ex library/libmbedtls.a)
548 0 0 548 224 ssl_cache.o (ex library/libmbedtls.a)
11155 0 596 11751 2de7 ssl_ciphersuites.o (ex library/libmbedtls.a)
17160 0 0 17160 4308 ssl_cli.o (ex library/libmbedtls.a)
460 0 0 460 1cc ssl_cookie.o (ex library/libmbedtls.a)
17637 0 0 17637 44e5 ssl_srv.o (ex library/libmbedtls.a)
800 0 0 800 320 ssl_ticket.o (ex library/libmbedtls.a)
39322 60 0 39382 99d6 ssl_tls.o (ex library/libmbedtls.a)
88902 60 600 89562 15dda (TOTALS)
DTLS-only
text data bss dec hex filename
1820 0 4 1824 720 debug.o (ex library/libmbedtls.a)
0 0 0 0 0 net_sockets.o (ex library/libmbedtls.a)
548 0 0 548 224 ssl_cache.o (ex library/libmbedtls.a)
11155 0 596 11751 2de7 ssl_ciphersuites.o (ex library/libmbedtls.a)
17072 0 0 17072 42b0 ssl_cli.o (ex library/libmbedtls.a)
460 0 0 460 1cc ssl_cookie.o (ex library/libmbedtls.a)
17565 0 0 17565 449d ssl_srv.o (ex library/libmbedtls.a)
800 0 0 800 320 ssl_ticket.o (ex library/libmbedtls.a)
38953 60 0 39013 9865 ssl_tls.o (ex library/libmbedtls.a)
88373 60 600 89033 15bc9 (TOTALS)
TLS-only
text data bss dec hex filename
1820 0 4 1824 720 debug.o (ex library/libmbedtls.a)
0 0 0 0 0 net_sockets.o (ex library/libmbedtls.a)
548 0 0 548 224 ssl_cache.o (ex library/libmbedtls.a)
11155 0 596 11751 2de7 ssl_ciphersuites.o (ex library/libmbedtls.a)
14916 0 0 14916 3a44 ssl_cli.o (ex library/libmbedtls.a)
460 0 0 460 1cc ssl_cookie.o (ex library/libmbedtls.a)
15852 0 0 15852 3dec ssl_srv.o (ex library/libmbedtls.a)
800 0 0 800 320 ssl_ticket.o (ex library/libmbedtls.a)
27623 60 0 27683 6c23 ssl_tls.o (ex library/libmbedtls.a)
73174 60 600 73834 1206a (TOTALS)
```
It can be seen that a DTLS-only build is now starting to be a bit smaller than
a dual-mode build, which is the purpose of the new build option.
For now the option has no effect.
Adapted existing example config files. The fact that I needed to do this
highlights that this is a slightly incompatible change: existing users need to
update their existing custom configs (if standalone as opposed to based on the
default config) in order to still get the same behaviour.
The alternative would be to have a negative config option (eg NO_TLS or
DTLS_ONLY) but this doesn't fit as nicely with the existing options, so
hopefully the minor incompatibility is acceptable.
I don't think it's worth adding a new component to all.sh:
- builds with both DTLS and TLS are done in the default (and full) config
- TLS-only builds are done with eg config-suite-b.h in test-ref-configs
- a DTLS-only build is done with config-thread.h in test-ref-configs
- builds with none of them (and SSL_TLS_C enabled) are forbidden
Context: During a handshake, the SSL/TLS handshake logic constructs
an instance of ::mbedtls_ssl_session representing the SSL session
being established. This structure contains information such as the
session's master secret, the peer certificate, or the session ticket
issues by the server (if applicable).
During a renegotiation, the new session is constructed aside the existing
one and destroys and replaces the latter only when the renegotiation is
complete. While conceptually clear, this means that during the renegotiation,
large pieces of information such as the peer's CRT or the session ticket
exist twice in memory, even though the original versions are removed
eventually.
This commit removes the simultaneous presence of two peer CRT chains
in memory during renegotiation, in the following way:
- Unlike in the case of SessionTickets handled in the previous commit,
we cannot simply free the peer's CRT chain from the previous handshake
before parsing the new one, as we need to verify that the peer's end-CRT
hasn't changed to mitigate the 'Triple Handshake Attack'.
- Instead, we perform a binary comparison of the original peer end-CRT
with the one presented during renegotiation, and if it succeeds, we
avoid re-parsing CRT by moving the corresponding CRT pointer from the
old to the new session structure.
- The remaining CRTs in the peer's chain are not affected by the triple
handshake attack protection, and for them we may employ the canonical
approach of freeing them before parsing the remainder of the new chain.
Note that this commit intends to not change any observable behavior
of the stack. In particular:
- The peer's CRT chain is still verified during renegotiation.
- The tail of the peer's CRT chain may change during renegotiation.
Context: During a handshake, the SSL/TLS handshake logic constructs
an instance of ::mbedtls_ssl_session representing the SSL session
being established. This structure contains information such as the
session's master secret, the peer certificate, or the session ticket
issues by the server (if applicable).
During a renegotiation, the new session is constructed aside the existing
one and destroys and replaces the latter only when the renegotiation is
complete. While conceptually clear, this means that during the renegotiation,
large pieces of information such as the peer's CRT or the session ticket
exist twice in memory, even though the original versions are removed
eventually.
This commit starts removing this memory inefficiency by freeing the old
session's SessionTicket before the one for the new session is allocated.
Context:
The existing API `mbedtls_x509_parse_crt_der()` for parsing DER
encoded X.509 CRTs unconditionally makes creates a copy of the
input buffer in RAM. While this comes at the benefit of easy use,
-- specifically: allowing the user to free or re-use the input
buffer right after the call -- it creates a significant memory
overhead, as the CRT is duplicated in memory (at least temporarily).
This might not be tolerable a resource constrained device.
As a remedy, this commit adds a new X.509 API call
`mbedtls_x509_parse_crt_der_nocopy()`
which has the same signature as `mbedtls_x509_parse_crt_der()`
and almost the same semantics, with one difference: The input
buffer must persist and be unmodified for the lifetime of the
established instance of `mbedtls_x509_crt`, that is, until
`mbedtls_x509_crt_free()` is called.
- Explain the use of explicit ASN.1 tagging for the extensions structuree
- Remove misleading comment which suggests that mbedtls_x509_get_ext()
also parsed the header of the first extension, which is not the case.
Some functions within the X.509 module return an ASN.1 low level
error code where instead this error code should be wrapped by a
high-level X.509 error code as in the bulk of the module.
Specifically, the following functions are affected:
- mbedtls_x509_get_ext()
- x509_get_version()
- x509_get_uid()
This commit modifies these functions to always return an
X.509 high level error code.
Care has to be taken when adapting `mbetls_x509_get_ext()`:
Currently, the callers `mbedtls_x509_crt_ext()` treat the
return code `MBEDTLS_ERR_ASN1_UNEXPECTED_TAG` specially to
gracefully detect and continue if the extension structure is not
present. Wrapping the ASN.1 error with
`MBEDTLS_ERR_X509_INVALID_EXTENSIONS` and adapting the check
accordingly would mean that an unexpected tag somewhere
down the extension parsing would be ignored by the caller.
The way out of this is the following: Luckily, the extension
structure is always the last field in the surrounding structure,
so if there is some data remaining, it must be an Extension
structure, so we don't need to deal with a tag mismatch gracefully
in the first place.
We may therefore wrap the return code from the initial call to
`mbedtls_asn1_get_tag()` in `mbedtls_x509_get_ext()` by
`MBEDTLS_ERR_X509_INVALID_EXTENSIONS` and simply remove
the special treatment of `MBEDTLS_ERR_ASN1_UNEXPECTED_TAG`
in the callers `x509_crl_get_ext()` and `x509_crt_get_ext()`.
This renders `mbedtls_x509_get_ext()` unsuitable if it ever
happened that an Extension structure is optional and does not
occur at the end of its surrounding structure, but for CRTs
and CRLs, it's fine.
The following tests need to be adapted:
- "TBSCertificate v3, issuerID wrong tag"
The issuerID is optional, so if we look for its presence
but find a different tag, we silently continue and try
parsing the subjectID, and then the extensions. The tag '00'
used in this test doesn't match either of these, and the
previous code would hence return LENGTH_MISMATCH after
unsucessfully trying issuerID, subjectID and Extensions.
With the new code, any data remaining after issuerID and
subjectID _must_ be Extension data, so we fail with
UNEXPECTED_TAG when trying to parse the Extension data.
- "TBSCertificate v3, UIDs, invalid length"
The test hardcodes the expectation of
MBEDTLS_ERR_ASN1_INVALID_LENGTH, which needs to be
wrapped in MBEDTLS_ERR_X509_INVALID_FORMAT now.
Fixes#2431.
When parsing a substructure of an ASN.1 structure, no field within
the substructure must exceed the bounds of the substructure.
Concretely, the `end` pointer passed to the ASN.1 parsing routines
must be updated to point to the end of the substructure while parsing
the latter.
This was previously not the case for the routines
- x509_get_attr_type_and_value(),
- mbedtls_x509_get_crt_ext(),
- mbedtls_x509_get_crl_ext().
These functions kept using the end of the parent structure as the
`end` pointer and would hence allow substructure fields to cross
the substructure boundary. This could lead to successful parsing
of ill-formed X.509 CRTs.
This commit fixes this.
Care has to be taken when adapting `mbedtls_x509_get_crt_ext()`
and `mbedtls_x509_get_crl_ext()`, as the underlying function
`mbedtls_x509_get_ext()` returns `0` if no extensions are present
but doesn't set the variable which holds the bounds of the Extensions
structure in case the latter is present. This commit addresses
this by returning early from `mbedtls_x509_get_crt_ext()` and
`mbedtls_x509_get_crl_ext()` if parsing has reached the end of
the input buffer.
The following X.509 parsing tests need to be adapted:
- "TBSCertificate, issuer two inner set datas"
This test exercises the X.509 CRT parser with a Subject name
which has two empty `AttributeTypeAndValue` structures.
This is supposed to fail with `MBEDTLS_ERR_ASN1_OUT_OF_DATA`
because the parser should attempt to parse the first structure
and fail because of a lack of data. Previously, it failed to
obey the (0-length) bounds of the first AttributeTypeAndValue
structure and would try to interpret the beginning of the second
AttributeTypeAndValue structure as the first field of the first
AttributeTypeAndValue structure, returning an UNEXPECTED_TAG error.
- "TBSCertificate, issuer, no full following string"
This test exercises the parser's behaviour on an AttributeTypeAndValue
structure which contains more data than expected; it should therefore
fail with MBEDTLS_ERR_ASN1_LENGTH_MISMATCH. Because of the missing bounds
check, it previously failed with UNEXPECTED_TAG because it interpreted
the remaining byte in the first AttributeTypeAndValue structure as the
first byte in the second AttributeTypeAndValue structure.
- "SubjectAltName repeated"
This test should exercise two SubjectAltNames extensions in succession,
but a wrong length values makes the second SubjectAltNames extension appear
outside of the Extensions structure. With the new bounds in place, this
therefore fails with a LENGTH_MISMATCH error. This commit adapts the test
data to put the 2nd SubjectAltNames extension inside the Extensions
structure, too.
This commit introduces a new SSL error code
`MBEDTLS_ERR_SSL_VERSION_MISMATCH`
which can be used to indicate operation failure due to a
mismatch of version or configuration.
It is put to use in the implementation of `mbedtls_ssl_session_load()`
to signal the attempt to de-serialize a session which has been serialized
in a build of Mbed TLS using a different version or configuration.