Fix the ceiling on how much encryption growth we can have (1.1.1)#19585
Fix the ceiling on how much encryption growth we can have (1.1.1)#19585mattcaswell wants to merge 1 commit intoopenssl:OpenSSL_1_1_1-stablefrom
Conversation
|
What are the steps to test this in 1.1.1? |
|
@mattcaswell Is this still relevant or is it superceded by your new fixes? |
It needs updating in light of the new fixes |
PR needs an update
Stitched ciphersuites can grow by more during encryption than the code allowed for. We fix the calculation and add an assert to check we go it right. Also if we are adding the MAC independently of the cipher algorithm then the encryption growth will not include that MAC so we should remove it from the amount of bytes that we reserve for that growth. Otherwise we might exceed our buffer size and the WPACKET_reserve operation will fail. Note that this is not a security issue. Even though we can overflow the amount of bytes reserved in the WPACKET for the encryption, the underlying buffer is still big enough.
6a289eb to
fb55d11
Compare
I've now updated this PR. Please take a look.
It's quite difficult to test this independently. If you remove the fix from this PR, but just add the assert (modified to match the original reserved amount), i.e. : Then that is sufficient to make the existing tests fail. |
|
Ah, now I see, here I have the necessary steps to reproduce the issue So it is easy to debug it, the assertion happens in s_client in the 4th time the WPACKET_reserve_bytes In the case where the assertion does not hold, I see that mac_size == 0. Therefore I would suggest something like the following: would look more conservative and more correct to me. |
bernd-edlinger
left a comment
There was a problem hiding this comment.
request changes as by my previous comment
|
Slightly better would indeed be adding |
I don't think that is the correct fix. We must be careful to only reserve the right amount. If we reserve too much then we could exceed the amount allocated in the underlying buffer and therefore the The right amount needs to be relative to the amount that we allocated in the original underlying buffer for encryption growth: openssl/ssl/record/ssl3_buffer.c Lines 89 to 119 in f868abc So, what we originally allocate is # define SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD \
(SSL_RT_MAX_CIPHER_BLOCK_SIZE + SSL3_RT_MAX_MD_SIZE)Here So given that we have allowed For some ciphersuites we do those 2 things separately, i.e. add the encrypted data and then add the MAC (for Encrypt-then-Mac), or sometimes add the MAC and then encrypt (Mac-then-Encrypt). Encrypt-then-MAC isn't that interesting because by the time we add the MAC we already know how much the encryption itself grew by. So we could just reserve The other case is where we have a stitched ciphersuite or an AEAD ciphersuite. In that case So, in all cases, |
|
I have only a problem with the assertion, because an engine-based aes-cbc-sha-x cipher might have a good reason to not use the minimal padding, for instance as a counter measure against the CRIME attack. test.conf:
|
Doing that is simply not safe - even before this PR. Libssl assumes minimal padding and that encryption will never grow by more than
Err no? The amount allocated for encryption growth is exactly |
Note: my example above is really working for small data size, and arbitrarily large ones as well. That is the case because the memory for the write buffer is allocated in ssl3_setup_write_buffer: len = ssl_get_max_send_fragment(s)
+ SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD + headerlen + align;
#ifndef OPENSSL_NO_COMP
if (ssl_allow_compression(s))
len += SSL3_RT_MAX_COMPRESSED_OVERHEAD;
#endif
if (!(s->options & SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS))
len += headerlen + align + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD;
}
wb = RECORD_LAYER_get_wbuf(&s->rlayer);
for (currpipe = 0; currpipe < numwpipes; currpipe++) {
SSL3_BUFFER *thiswb = &wb[currpipe];
if (thiswb->buf != NULL && thiswb->len != len) {
OPENSSL_free(thiswb->buf);
thiswb->buf = NULL; /* force reallocation */
}
if (thiswb->buf == NULL) {
p = OPENSSL_malloc(len);and ssl_get_max_send_fragment() is always greater than 512. As my working code |
So? This is the memory allocated for the plaintext record. If the length of the plaintext record is equal to Assuming compression is not allowed, then the remaining buffer is made up of allowances for the header and alignment bytes (which will also be consumed by the time you get to encryption)....which leaves
You would get away with it with small packets but only by accident - and as soon as you encrypt a full length packet you will get into trouble. Libssl assumes minimal padding. Anything other than that is definitely not supported. I would be very surprised if any existing engine is actually doing that. |
Yeah, that is clear, increasing a maximum size packet would be a rather silly idea, because it would add
All I can say, that I was very surprised when the 1.1.1r was withdrawn. |
There is a supported way to do this with TLSv1.3 (i.e. via SSL_CTX_set_record_padding_callback() and similar functions). IMO, in TLSv1.2 and below this is just not supported and never has been. You may have been able to subvert the ENGINE infrastructure to do this for smaller records, but this worked only by accident because we never validated the encryption growth amount. The fact that we didn't do this is a clear bug IMO and we need to fix it. |
|
Sorry, but if you want to do hardening, why don't you check the padding length against openssl/ssl/record/ssl3_record.c Lines 1062 to 1068 in f868abc before the overwrite happens? |
|
We could potentially do that, but there is no access to the WPACKET at that point in the code. It would require refactoring for that to happen. That's probably not appropriate for 1.1.1. |
|
It would just need one additional parameter to pass wr and pkt to the enc funtion, that does not look too hard. |
|
24 hours has passed since 'approval: done' was set, but as this PR has been updated in that time the label 'approval: ready to merge' is not being automatically set. Please review the updates and set the label manually. |
|
You'd need to do that for all enc functions and modify the |
Yeah probably, but adding an assertion at this place is neither appropriate. To make that clear, I see no problem with reserving the reserved amount of memory. |
|
So just for reference: #19710 is how I would like to fix it in 1.1.1 |
I just completely and strongly disagree with you. Any engine that deliberately exceeds the normal encryption growth expectations would have to be making assumptions about the size of the buffer that it has available which it has no business doing. This is very dangerous for such an engine to do. Consider the case where max_fragment_length has been negotiated, or the application has explicitly called Any engine doing that is dangerously broken IMO and having libssl fail in such a case would help to identify it and get it fixed.
It's not silent. In a production build the assert is still assessed and returns failure (as opposed to crashing as it does in a debug build). The ossl_assert is a valuable part of this change because it should cause the existing test suite to fail in the case that a cipher exceeds the growth expectations. |
|
Since we clearly have a disagreement here I am placing an OTC hold on this PR. OTC Question: Should we include the ossl_assert in this PR to confirm that the cipher had not exceeded the encryption growth expectations? |
I'd consider that approach to be orthogonal to this one. |
|
OTC: This is OK to go in 1.1.1 as it is. |
|
Pushed. Thanks. |
Stitched ciphersuites can grow by more during encryption than the code allowed for. We fix the calculation and add an assert to check we go it right. Also if we are adding the MAC independently of the cipher algorithm then the encryption growth will not include that MAC so we should remove it from the amount of bytes that we reserve for that growth. Otherwise we might exceed our buffer size and the WPACKET_reserve operation will fail. Note that this is not a security issue. Even though we can overflow the amount of bytes reserved in the WPACKET for the encryption, the underlying buffer is still big enough. Reviewed-by: Tomas Mraz <[email protected]> Reviewed-by: Hugo Landau <[email protected]> (Merged from #19585)
|
When applying this patch to my own environment, I was getting errors when calling the SSL_write function. The function's call stack fails at the WPACKET_reserve_bytes function. I took a closer look at the Perhaps |
|
@bangcheng - are you able to tell me the value of Also can you confirm what protocol version has been negotiated in this scenario? |
Stitched ciphersuites can grow by more during encryption than the code allowed for. We fix the calculation and add an assert to check we go it right. Also if we are adding the MAC independently of the cipher algorithm then the encryption growth will not include that MAC so we should remove it from the amount of bytes that we reserve for that growth. Otherwise we might exceed our buffer size and the WPACKET_reserve operation will fail. Note that this is not a security issue. Even though we can overflow the amount of bytes reserved in the WPACKET for the encryption, the underlying buffer is still big enough. Reviewed-by: Tomas Mraz <[email protected]> Reviewed-by: Hugo Landau <[email protected]> (Merged from openssl#19585)

This is a backport of #19517 to the 1.1.1 branch. Even though this is not a security defect per se, I would consider it hardening and therefore suitable for cherry-pick to 1.1.1.
Stitched ciphersuites can grow by more during encryption than the code allowed for. We fix the calculation and add an assert to check we go it right.
Note that this is not a security issue. Even though we can overflow the amount of bytes reserved in the WPACKET for the encryption, the underlying buffer is still big enough.
Checklist