Skip to content

Commit

Permalink
Add heartbeat extension bounds check.
Browse files Browse the repository at this point in the history
A missing bounds check in the handling of the TLS heartbeat extension
can be used to reveal up to 64k of memory to a connected client or
server.

Thanks for Neel Mehta of Google Security for discovering this bug and to
Adam Langley <agl@chromium.org> and Bodo Moeller <bmoeller@acm.org> for
preparing the fix (CVE-2014-0160)
(cherry picked from commit 96db902)
  • Loading branch information
snhenson committed Apr 7, 2014
1 parent 4e6c12f commit 731f431
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 13 deletions.
9 changes: 9 additions & 0 deletions CHANGES
Expand Up @@ -4,6 +4,15 @@

Changes between 1.0.2 and 1.1.0 [xx XXX xxxx]

*) A missing bounds check in the handling of the TLS heartbeat extension
can be used to reveal up to 64k of memory to a connected client or
server.

Thanks for Neel Mehta of Google Security for discovering this bug and to
Adam Langley <agl@chromium.org> and Bodo Moeller <bmoeller@acm.org> for
preparing the fix (CVE-2014-0160)
[Adam Langley, Bodo Moeller]

*) Fix for the attack described in the paper "Recovering OpenSSL
ECDSA Nonces Using the FLUSH+RELOAD Cache Side-channel Attack"
by Yuval Yarom and Naomi Benger. Details can be obtained from:
Expand Down
26 changes: 18 additions & 8 deletions ssl/d1_both.c
Expand Up @@ -1330,26 +1330,36 @@ dtls1_process_heartbeat(SSL *s)
unsigned int payload;
unsigned int padding = 16; /* Use minimum padding */

/* Read type and payload length first */
hbtype = *p++;
n2s(p, payload);
pl = p;

if (s->msg_callback)
s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
&s->s3->rrec.data[0], s->s3->rrec.length,
s, s->msg_callback_arg);

/* Read type and payload length first */

This comment has been minimized.

Copy link
@johanatan

johanatan Apr 9, 2014

The following block of code is way too large to be repeated in both d1_both.c and tl_lib.c. I'm sure there are many other common blocks of code between these files (and the project at large given its reputation) as well.

if (1 + 2 + 16 > s->s3->rrec.length)

This comment has been minimized.

Copy link
@mcvsama

mcvsama Apr 9, 2014

Wouldn't be better if you replaced those numbers with some named constants?

This comment has been minimized.

Copy link
@rtepowers

rtepowers Apr 9, 2014

It seems there is a bit of differing style on the '1+2+x' part as below you see '3+x'. I think named constants would help clarify what is actually happening here, otherwise why not just roll them up into 3?

This comment has been minimized.

Copy link
@jvz

jvz Apr 9, 2014

1, 2, and 16 are named constants for the binary numbers 1b, 10b, and 10000b.

This comment has been minimized.

Copy link
@babelshift

babelshift Apr 9, 2014

@jvz you know exactly what they meant. Your answer is counter-productive to improving the readability and documentation of the code.

This comment has been minimized.

Copy link
@jvz

jvz Apr 9, 2014

I know, the code as is barely makes sense as to why anything is done the way it is. Some named constants would go a long way toward improving readability.

This comment has been minimized.

Copy link
@rtepowers

rtepowers Apr 9, 2014

I think below on lines 1350 and 1351 specify the values. 1 = heartbeat type, and 2 = heartbeat length. It is difficult to know though as @jvz mentions.

This comment has been minimized.

Copy link
@borisvidolov

borisvidolov Apr 14, 2014

If 16 is the padding, the local variable above should be used. This makes the code more obvious and updatable.

return 0; /* silently discard */
hbtype = *p++;
n2s(p, payload);
if (1 + 2 + payload + 16 > s->s3->rrec.length)

This comment has been minimized.

Copy link
@soheil-zz

soheil-zz Apr 9, 2014

How about sticking this in an unsigned int for reuse below?

return 0; /* silently discard per RFC 6520 sec. 4 */
pl = p;

if (hbtype == TLS1_HB_REQUEST)
{
unsigned char *buffer, *bp;
unsigned int write_length = 1 /* heartbeat type */ +
2 /* heartbeat length */ +
payload + padding;

This comment has been minimized.

Copy link
@mozjag

mozjag Apr 11, 2014

Why not hoist this up to the first use at line 1343? Should make this code a bit easier to follow and gets rid of the magical 16. Maybe split out 1 + 2 + padding into overhead and also use that at line 1339?

This comment has been minimized.

Copy link
@rtepowers

rtepowers Apr 11, 2014

@mozjag, I agree. That would make this clearer.

int r;

if (write_length > SSL3_RT_MAX_PLAIN_LENGTH)
return 0;

This comment has been minimized.

Copy link
@soheil-zz

soheil-zz Apr 9, 2014

silently discard


/* Allocate memory for the response, size is 1 byte

This comment has been minimized.

Copy link
@ahmetb

ahmetb Apr 9, 2014

This comment probably does not belong here anymore since logic is moved above.

This comment has been minimized.

Copy link
@rtepowers

rtepowers Apr 9, 2014

@ahmetalpbalkan, you're right. The line 1350 describes this more succinctly.

* message type, plus 2 bytes payload length, plus
* payload, plus padding
*/
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
buffer = OPENSSL_malloc(write_length);
bp = buffer;

/* Enter response type, length and copy payload */
Expand All @@ -1360,11 +1370,11 @@ dtls1_process_heartbeat(SSL *s)
/* Random padding */
RAND_pseudo_bytes(bp, padding);

r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, write_length);

if (r >= 0 && s->msg_callback)
s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
buffer, 3 + payload + padding,
buffer, write_length,
s, s->msg_callback_arg);

OPENSSL_free(buffer);
Expand Down
14 changes: 9 additions & 5 deletions ssl/t1_lib.c
Expand Up @@ -3969,16 +3969,20 @@ tls1_process_heartbeat(SSL *s)
unsigned int payload;
unsigned int padding = 16; /* Use minimum padding */

/* Read type and payload length first */
hbtype = *p++;
n2s(p, payload);
pl = p;

if (s->msg_callback)
s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
&s->s3->rrec.data[0], s->s3->rrec.length,
s, s->msg_callback_arg);

/* Read type and payload length first */
if (1 + 2 + 16 > s->s3->rrec.length)
return 0; /* silently discard */
hbtype = *p++;
n2s(p, payload);
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0; /* silently discard per RFC 6520 sec. 4 */
pl = p;

if (hbtype == TLS1_HB_REQUEST)
{
unsigned char *buffer, *bp;
Expand Down

25 comments on commit 731f431

@daira
Copy link

@daira daira commented on 731f431 Apr 9, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice that the recommendation on http://heartbleed.com to disable support for the heartbeat extension has not been followed.

"Should heartbeat be removed to aid in detection of vulnerable services?

Recovery from this bug could benefit if the new version of the OpenSSL would both fix the bug and disable heartbeat temporarily until some future version. It appears that majority if not almost all TLS implementations that respond to the heartbeat request today are vulnerable versions of OpenSSL. If only vulnerable versions of OpenSSL would continue to respond to the heartbeat for next few months then large scale coordinated response to reach owners of vulnerable services would become more feasible."

@lwneal
Copy link

@lwneal lwneal commented on 731f431 Apr 9, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is simple to tell the difference between a TLS implementation that handles heartbeat requests correctly, and an implementation that is vulnerable to Heartbleed.

To do so, replace s2n(payload, p); on line 4086 with s2n(LARGE_NUMBER, p);. Replace the "silently discard" logic with any desired logging. Then compile and send heartbeat requests with:

./openssl s_client -connect www.serverundertest.com:443

@rdewolff
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has to be shared!

@lumaxis
Copy link

@lumaxis lumaxis commented on 731f431 Apr 9, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where are the braces?

@filinger
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They did it in hustle so no time to put braces!!11

@douglasheld
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lwneal, I've been using the simple python tester published here: https://github.com/musalbas/heartbleed-masstest
It's been one full day and I've found a handful of sites in my web history that are still vulnerable...

@jlmalone
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you douglasheld for giving hackers a tool to find all the exposed sites on the Internet.

http://vimeo.com/44182757

Perhaps if the code were a bit more readable in not in the format, 1 + 2 + payload + 16, this bug would have been caught earlier. It's ok though, it's not like the world's data security relies on this or anything.

@pierrevalade
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we imagine that writing tests could help make sure regressions are not introduced? Thanks.

@pcai
Copy link

@pcai pcai commented on 731f431 Apr 10, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jlmalone perhaps if you were making more pull requests instead of snarky comments, the code would be more readable. It's OK though, it's not like you're the only one to whine about FOSS while benefiting from it without contributing anything in return.

@mkotelba
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we imagine that writing tests could help make sure regressions are not introduced? Thanks.

Writing tests is for those mediocre developers who spend their lives writing Java (ugh). Its much better to just have a handful of printf-esque statements in the code (commented out, of course).

Also, defining constants, especially those with human-readable names, is a massive affront to performance! If another developer wants to know that "payload" actually describes the size of the buffer, they should go read the RFC.

@billymoon
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mkotelba if named constants are such a performance hit, maybe the code could be transpiled with constants replaced before compiling, so source is easier to understand, and performance is not hit.

It's late, I am going to go read some RFC specs to put myself to sleep.

@johanatan
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, you know, #define.

@jameswangz
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing tests is for those mediocre developers who spend their lives writing Java (ugh). Its much better to just have a handful of printf-esque statements in the code (commented out, of course).

@mkotelba You don't write test, so your users do the testing for you, they find a stupid bug and they ask : who wrote the stupid code?

@vczh
Copy link

@vczh vczh commented on 731f431 Apr 10, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mkotelba I cannot find any important softwares which don't need testing. If you managed to do that, please let me know. So that I can learn from you about how to write correct and efficient code in such a brand new way.

@johanatan
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read a sarcastic tone in @mkotelba's post. If it wasn't intended to be sarcastic, then this thread is becoming even more hilarious!

@brupm
Copy link

@brupm brupm commented on 731f431 Apr 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it only me or does anyone else agree that such a high profile lib could benefit for a robust test suite?

@rtepowers
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brupm, I agree wholeheartedly.

@douglasheld
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a functional/unit test would be really helpful, except that those typically don't identify security vulnerabilities (because they are not designed to).

Think about it however: some simple unit tests would have found the Apple "goto fail" -- which was in the wild for what, years? Pretty damning.

As for security assurance of infrastructure critical code, the path has to be:

  • declutter the code. Nobody can read OpenSSL, this has been documented.
  • Periodic security architecture reviews.
  • My own field is static analysis. The available static analysis tools should be coming up clean for the core library, plus sample code and reference implementations.

These steps are not impossible to achieve, but the list has correctly identified that the "many eyes" are not bringing this about automatically. "Many eyes" are really good for catching a broken build, but not design flaws such as this.

@vrpratt
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Higher level language" was my first reaction to this bug. But which one? There's a catch-22 here: the more universal the HLL the more clutter is likely to result in implementing any given algorithm. But the more targeted an HLL is to any domain, the more compilers you need, one for each HLL, each with its own opportunities for bugs in the compiler.

For lowering the incidence of heartbleed-type bugs, library coordination between servers might be a more effective and more immediately implementable solution than higher level languages. Why is it so hard to provide sendstring and receivestring functions that are provably unspoofable? That's not a rhetorical question.

@MetroWind
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait… No one noticed @mkotelba was just joking? @mkotelba, you were joking by saying things in the opposite way, right? Right? … I’m sure he was joking. Come on guys.

@mcvsama
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@corsair It was so obvious that no one needed to write about it.

@johanatan
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you read the thread? It wasn't obvious to the people who responded seriously.

@mkotelba
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh, I'm glad I forgot that I posted in this thread for a week.

Yes, of course I was being sarcastic :) I tend to work in the land of Java most of the time, and often on projects that are heavily security-focused (fancy S/MIME processing, etc).

Call me a fanboy if you must, but even the most complicated, massive, and poorly-documented Java libs/APIs out there (relevant ex., BounceyCastle) still manage to have a ~passable amount of test coverage. The fact that throwing a single JUnit or TestNG dependency into your project makes even complex testing a breeze certainly helps.

In regards to OpenSSL, @douglasheld is dead-on in his ideas: hell, even just deciding on a given code-style configuration and then letting a code formatter apply it to the entire codebase would help tremendously.

@MetroWind
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hamradiojava Haha that's a good one~~

@mbland
Copy link
Contributor

@mbland mbland commented on 731f431 Apr 26, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've posted #81 to add a unit test to prevent regression of the Heartbleed bug.

Please sign in to comment.