|Benefits for LWN subscribers|
The primary benefit from subscribing to LWN is helping to keep us publishing, but, beyond that, subscribers get immediate access to all site content and access to a number of extra site features. Please sign up today!
A serious flaw in the way Apple's iOS and OS X verify the keys in an HTTPS connection has been a major black eye for the company. The problem is in some of the open-source code that the company releases, so we can actually see the problem—it is eye-opening to be sure. The bug should have been fairly obvious from code inspection/review or could have been found with some intensive testing, so the fact that it went undetected—at least publicly—for so long is rather amazing.
The problem exists in Apple's Secure Transport API that provides access to SSL/TLS services for both OS X and iOS. It was first introduced in iOS 6, which was released in September 2012, and in OS X 10.9, which was released in October 2013. Updates to iOS 6 and 7, as well as to OS X 10.9, have been released, though the OS X problem went unfixed for several days after the problem was disclosed—which was deemed irresponsible by several observers.
Looking at the code in question should make it quickly apparent to those with even limited knowledge of C that something is amiss. In a function called SSLVerifySignedServerKeyExchange() is the following code:
if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail; goto fail; if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) goto fail;The second "goto fail;" after the SSLHashSHA1.update() call for &signedParams is a bug. While it is indented to seem like it depends on the preceding if statement, that is not the case. There are no curly braces to turn it into a multi-statement if, so the second goto is unconditionally executed, which skips the rest of the signature verification.
But "fail" isn't quite accurate here. If it had been, the problem would presumably have been noticed quickly as many HTTPS servers would not have passed muster. Instead, the code at the fail label just cleans up a few things and returns err, which is likely to be zero, since the update() probably did not fail. That means that instead of verifying the signed key that the server sent over, the function will just succeed—for any key offered.
The key in question here is the ephemeral session key that is exchanged using the Diffie-Hellman and Elliptic-curve Diffie-Hellman ephemeral (DHE and ECDHE) key exchange protocols. DHE and ECDHE are used to provide forward secrecy. That key should be signed by the private key corresponding to the public key in the server's certificate. The signature is the proof that the server is actually in possession of that private key—without it, the link between certificate and identity (loosely defined) is broken. The bug allows any signature (thus any key) to validate. That means that a malicious server could use any certificate to spoof that site with impunity—it doesn't need to sign the ephemeral key with the private key it does not possess.
Google's Adam Langley has a nice analysis of the bug. As he noted, servers get to choose what cipher suites they support, so an attacker can force clients to use DHE or ECDHE to trigger the bug (if the client refuses to use one of those, it can't connect at all). The most recent revision of Transport Layer Security (TLS), 1.2, is not affected because the API uses a different function to verify those keys. But earlier versions of TLS and all versions of its predecessor, Secure Sockets Layer (SSL), are affected. Clients could work around the bug by requiring TLS 1.2 (or, less preferably, by disabling the DHE/ECDHE cipher suites)—that would mean they couldn't connect to some servers, perhaps, but they wouldn't run afoul of this problem either.
Evidently, code inspection/review did not turn up this bug (there is some speculation by John Gruber that it is the result of a botched merge). What is perhaps more surprising is that no testing with invalid signatures on the ephemeral keys was done. Langley, who works on the Chrome/Chromium browser, noted that the condition is kind of difficult to test for, because that exchange happens well into TLS/SSL handshake. On the other hand, Gruber also speculated that the NSA may well have known about the flaw from its testing, given that it added Apple to the list of companies participating in the PRISM surveillance program shortly after iOS 6 was released.
It is tempting to recite "Linus's Law" ("given enough eyeballs, all bugs are shallow") and believe that this kind of thing could never happen in free software. Tempting, but wrong. The truth of the matter is that plenty of free software only gets cursory (or no) code review, so something like this could slip through. In this case, Apple's code was available and no one ever publicly complained about it.
As Langley noted, compilers don't generally complain about unreachable code, which is unfortunate, for sure, but warnings tend to have a high false-positive rate, so they are ignored—or suppressed. Code that implements security protocols clearly needs a higher level of scrutiny, though, so one would hope warnings are actually being used by Apple (and OpenSSL, OpenSSH, ...). An incident like this is clear evidence that delivering bug-free code is a never-ending battle.
Copyright © 2014, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds