Hacker News new | past | comments | ask | show | jobs | submit login
GnuTLS certificate verification vulnerability announced (CVE-2014-0092)
65 points by mpyne on March 4, 2014 | hide | past | favorite | 40 comments
The GnuTLS team has their announcement at http://www.gnutls.org/security.html#GNUTLS-SA-2014-2 but the announcement with details seems to be Red Hat's at https://rhn.redhat.com/errata/RHSA-2014-0247.html

"It was discovered that GnuTLS did not correctly handle certain errors that could occur during the verification of an X.509 certificate, causing it to incorrectly report a successful verification. An attacker could use this flaw to create a specially crafted certificate that could be accepted by GnuTLS as valid for a site chosen by the attacker."

Here's the diff:


Uninitialized "result" variable? Any time the code hits one of those "cleanup" gotos, it's probably returning nonzero unexpectedly?

Later: @filcab on Twitter points out the much dumber issue, which is that if issuer_version is < 0, the function returns issuer_version and not zero. Ow.

(Who uses GnuTLS?)

  emacs + email/nntp
That is just the highlights for libgnutls26.

I had forgotten about libcurl3-gnutls. There are a lot of things that depend on the libcurl3-gnutls, that list is long and distinguished. But I feel a little icky listing things without actually verifying they are affected. Just because they depend on the libs does not mean they use the bad code in the lib.

If you want to see what depends on libgnutls26 or libcurl3-gnutls in debian:

  $ apt-cache rdepends library-name
The --installed filter selects only those things that you have installed that depend on X:

  $ apt-cache --installed rdepends library-name

  dfc@ronin:~$ apt-cache  rdepends libgnutls26 libgnutls28 libcurl3-gnutls |wc
      629     632   10661
  dfc@ronin:~$ apt-cache  rdepends libssl1.0.0|wc
      751     752   11642

git on Debian, for example. In general, GPL programs need special exception to link to OpenSSL, and git is licensed under GPL without the exception.

There's a standing debate regarding whether that actually matters in the case of a distribution that included OpenSSL as a standard component, due to the GPL's system libraries exception. (And, of special relevance to Debian, there is debate even among those who agree that it's probably legal as to the ethics of doing it absent some fairly explicit indication of intent from the author(s) of the GPL'd software.)

Distributions can't take advantage of the system libraries exception, it only works for software that isn't shipped together with OpenSSL. The whole GPLv2 clause:

"However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable."

That is one reasonable interpretation of "accompanies", but it is not the only one. Hence the existence of debate. (Consider, for example, a CDN that happens to distribute both a Linux distribution that includes OpenSSL, and an unrelated GPL project not included in the distribution and written by people who don't contribute to the distribution. You surely would not believe this meets the definition of "accompanies" as used in the GPL? Now, figure out where the line is crossed.)

Off topic but can you explain the exception? I had a look at OpenSSL's license and it seems pretty open.


tl;dr: A conjunction of two old licenses, resulting in the advertising requirement that "every occurrence of the license with a different name required a separate acknowledgment", which is incompatible to GNU GPL.

Exim can be compiled against OpenSSL or GnuTLS. The official Debian package comes compiled against GnuTLS, although it is trivial to build a Debian package compiled against OpenSSL instead.

Hmmm...and no one has complained about that?

A similar thing happened around 20 years ago, with a program called RIPEM. RIPEM was an implementation of S/MIME. It was distributed in source form, and to build it you needed two libraries (well, you probably needed more than two, but only two are relevant for this discussion).

1. You needed a library that implemented RSA cryptographic algorithms. RIPEM used the RSAREF library for this. RSAREF was a library distributed by RSA (the company) that was free to use, but was not free software. I believe that a copy of RSAREF was distributed with RIPEM, although I don't recall for sure.

2. You needed an arbitrary precision integer arithmetic library. No such library was included with RIPEM. You were expected to supply your own (or build RIPEM on a system that included one as part of the system).

The two most common arbitrary precision integer arithmetic libraries on Unix and Unix-like systems at the time were the Berkeley MP library and GNU MP. I'll call these BMP and GMP.

RIPEM could be compiled to work with with BMP or GMP. There were #ifdef statements in the code that selected the appropriate code for the library you wanted to compile for.

RSAREF was not under a GPL compatible license, so if you built a binary of RIPEM linked with GMP, you ended up with a binary that you could not distribute. RIPEM, however, was distributed in source form, not binary form, and did not include GMP in the distribution.

RMS, and someone else from the FSF, objected and told the RIPEM people that they were violating GPL. Their argument was that when you ship source code that uses an interface provided by GMP (even if that is only on option), some people will download GMP in order to build the program, and so your distribution of your source has induced the distribution of GPLed code. That induced distribution, they believe, made your source subject to GPL [1].

What finally happened was that to put an end to this nonsense the RIPEM people wrote a new arbitrary precision integer arithmetic library that had the same interface as GMP but was actually just a wrapper for BMP. That satisfied RMS/FSF, since now there was a non-GPL implementation of the GMP interface, and so now those who downloaded the RIPEM source and built with the "use GMP" option could be linking with either GMP or the new library, so this no longer counted as an induced GMP distribution.

[1] It is actually possible for one party to be liable for the copyright infringement of another party, and so if that infringement involved GPL code, then the first party could be subject to GPL. There are a couple ways this can happen.

One way is called "induced infringement", which happens when a party that is under your control infringes under your direction. This would not apply here because there is not a sufficient control relationship.

The other way is called "contributory infringement", and happens when one party helps another party infringe. This would not apply here because in order for party A to be a contributory infringer to party B's infringement, there has to be a direct infringement by party B. Since it does NOT violate the GPL for me to download GPL code and link it with non-GPL code to produce a binary that I run on my computer (it only becomes a violation if I distribute that binary to others), nothing you do to aid me in this can be contributory infringement.

> Who uses GnuTLS

According to this https://en.wikipedia.org/wiki/GnuTLS

Apache httpd (configurable), GNOME, wireshark

It isn't the default in Apache, and I don't think anyone uses GNOME any more.

> (Who uses GnuTLS?)

People who want IPv6, DTLS, decent cli tools (gnutls-cli), pkcs11.


I wonder if there is any TLS library that actually does certificate verification properly...

First, define "properly."

Not if the NSA can help it ...

This is similar to the Apple SSL bug.

Good on them for looking for similar errors in their own code. Thanks Redhat, for the audit, and for making the internet safer!

This is a win for Free software (and OSS), since the history of the bug can be traced, and outside developers can audit it. We still don't know how the bug got in there with the Apple bug - since the repository is not open.

So Apple has an admittedly serious bug that is in the wild on iOS for ~17 months, and desktops for ~ 4 1/2 months, and everyone shits on Apple for it.

GNUTLS has a (using your words) "similar" bug in the wild for more than 13 YEARS, and its nothing but praise because "they audited the code"?

This is definitely a moment of shame, not praise. Good on RedHat for the audit, but a dark moment for GnuTLS. That was some dumb code in a very important place. Nobody reviewed that at all, clearly.

"EVERY version of GnuTLS EVER is vulnerable to certificate verification bypass"


For all C++ haters and C defenders: the cleanup/fail labels set result to 0 and perform a bunch of _gnutls_free_datum calls. This would not have happened in a properly designed C++ program because you could have just written "return 0;" and let the compiler release the resources (RAII, destructors).

IMO, C is no longer suitable as a systems programming language. It has stopped being that a long time ago, and its only value is ABI stability. (COM and similar technologies work with C++ and other languages, so C is unnecessary even for that purpose.)

What do you think of C++ and timing attacks? What do you think of the surface area of the C++ runtime itself? How do you find auditors for C++ code, when the standard is so big no one person could possibly understand it all?

These are genuine questions, it would be great to hear your answers about these things :)

> What do you think of C++ and timing attacks?

Same underlying machine model => same mitigation techniques apply as in C.

> surface area of the C++ runtime itself

No RTTI and no exceptions => no runtime. (By "runtime" I mean code necessary to support language features, not the C++ standard library. E.g., without RTTI and exceptions C++ is as suitable for building an OS as C is.)

Still, RTTI and exceptions are table-driven and I'd worry about their integrity if somebody manages to change the RTII and exception tables embedded in the executable. Largely prevented by signing executables. (Oh, the irony :-))

> How do you find auditors for C++ code [..cut]

More than half of the standard text is dedicated to standard library. I've heard it been said (I've not checked myself) that the description of the core language is only slightly longer than that of Java or C#.

But standard size is not that relevant. Reasonable C++ code is easy to write (for an experienced developer), easy to understand, and auditors can always "fail" the code if they don't know what's going on.

Auditing is expensive, so you have a lot of incentives to write reasonable code from the start.

> I've heard it been said (I've not checked myself) that the description of the core language is only slightly longer than that of Java or C#.

Here is a comment which links to a talk in which Herb Sutter says it:


> IMO, C is no longer suitable as a systems programming language.

Neither is C++. People writing new systems level code should seriously consider safer languages. ATS, Rust, Mercury, OCaml, SML, and many others.

Of the languages you listed, only Ocaml is remotely ready for production use and it's half-dead.

Mercury is used in production with PrinceXML [1] and ODASE [2]. ATS is used in production in the implementation of a bitcoin mining pool [3]. OCaml is heavily used by Jane St [4]. SML (via the MLton implementation) is used in industry [5]. Rust is not ready for production, I agree, but is being used to develop Servo by Mozilla and Samsung [6].

That said I'd hope that systems like ATS, Mercury, MLTon and OCaml being open source make it easier to contribute to the implementation for issues that come up and this would offset any 'not enough real world' problems that they have. If you don't like those languages, pick another (eg. Haskell).

[1] http://en.wikipedia.org/wiki/Prince_XML

[2] http://www.missioncriticalit.com/technology.html

[3] http://mmpool.bitparking.com/pool

[4] https://blogs.janestreet.com/category/ocaml/

[5] http://mlton.org/Users

[6] https://blog.mozilla.org/blog/2013/04/03/mozilla-and-samsung...

Actually, it's being "ressurected" through the work of ocamlpro & ocamllabs. The tooling in particular has improved a lot in the last 2 years.

What if you have a bug in your destructor then? Bugs happen, in any language.

Maybe C++ is better suited, maybe it isn't. I don't care. It's not the problem here.

The problem is poor test coverage and code auditing. You could rewrite it in prolog for all I care, it doesn't address this core issue. Don't miss the forest for the trees.

> What if you have a bug in your destructor then?

This is a straw-man. A bug in the destructor would lead to resource leaks and to eventual program failure. "Slightly" different from a complete and silent failure to perform a critical security task.

> Bugs happen, in any language.

Another straw-man. Some languages are more conducive to certain types of bugs. See PHP for example.

> You could rewrite it in prolog for all I care, it doesn't address this core issue.

The core issue is bad software engineering. If you look at the code more closely, it doesn't even use a consistent convention for signaling failure. The code looks something like

    if (ret < 0) { ... goto fail; }
and at 'fail' label you find `ret = 0` followed by freeing resources and returning ret to the caller.

The bug wouldn't be there either if they just settled on using the POSIX convention of negative return value meaning failure. But no.., they have to translate it to a pseudo-boolean stored in an int.

I wasn't planting a straw man, I was just pointing out that it's possible to write subtly broken C++ (or prolog, or ADA, or whatever) code as well.

Maybe it would make it harder to introduce this kind of bug (after all, goto error handling could be considered a hack because C lacks proper exceptions) but no language is bug-proof by design. If you're writing security-sensitive code you should have complete code coverage with tests. If you have that you could implement the library in assembly for all I care.

I'm not attempting to bash C++, it's a language I use from time to time. And I agree that RAII is a convenient patter that I often yearn for in C. It's just that in my opinion in this case (and in the "goto fail" bug of late) that's not the core of the issue.

OK, we agree then.

For those like me, wondering what GnuTLS is :

"GnuTLS is a secure communications library implementing the SSL, TLS and DTLS protocols and technologies around them. It provides a simple C language application programming interface (API) to access the secure communications protocols as well as APIs to parse and write X.509, PKCS #12, OpenPGP and other required structures. It is aimed to be portable and efficient with focus on security and interoperability."

from : http://www.gnutls.org/

An easier way to put that is "OpenSSL replacement".

What do other people think about the "goto" pattern for handling errors? I've never been a huge fan of it due to the unclear control flow you get, a point of view which this seems to back up very well. I'd much rather set a return/error code appropriately, encase each chunk of code in a 'if (OK == ec) {...}' and then clean up based on the ec at the bottom. That way what code is executed in any given circumstance is blindingly obvious and so is the clean up.

The "goto fail" pattern seems pretty common though - I was wondering what it had in its favour apart from being a easier to type (which would also be true of "not checking for errors" for example).

It's actually not horrific IMHO in the context of C when you use it explicitly in this fashion, as a "poor man's exceptions with resource cleanup". And it's not simply a method to avoid typing, it helps with the very common design pattern of:

    acquire A
    acquire B
    acquire C
    release C
    release B
    release A
It has all the benefit of "common return path" (since it is a common return path) without the 8 levels of indentation and random subsidiary conditional checks that obscure the code logic and add their own confusion. Instead you fail as fast as possible.

Like any other design pattern (C or otherwise) it's easy to misuse or abuse and it's not suited to every combination of library usage.

For the Apple code we did not have the full commit history. Do we know the history of this bug yet?

Edit: See https://www.gitorious.org/gnutls/gnutls/blame/6aa26f78150ccb...

Again, I find myself wondering "Why not just use OpenSSL instead?"

perhaps read any of the other comments? tldr, license and it's also terrible.

OpenSSL seems to have a less restrictive licence to me (although I suppose it is an issue if you want to directly integrate it with GPL code, but that's why it's a library), and I somehow wouldn't expect a bug of this magnitude to make it past their review.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact