Qualys Security Advisory – LibreSSL
seclists.org
seclists.org
1. OpenSSL (or this time LibreSSL) has a vulnerability discovered.
2. Everyone gets in an uproar about how we need a clean implementation in a language that prevents stupid errors.
3. Someone suggests unit testing. tptacek tells them to go away.
4. Someone actually points out a Haskell project (https://news.ycombinator.com/item?id=7557089).
5. People poo poo any alternative to OpenSSL because it's not 100% feature complete and besides the best crypto people are already working on OpenSSL so what do these schmucks know?
6. Someone points out that in higher level languages it's hard to write code that is resistant against timing attacks. The discussion dies.
I see only two alternatives. Either as a community we actually stop bike shedding and throw our weight behind a project that might actually become a good alternative to OpenSSL, or we accept that the canonical TLS library will be written in C and have bugs that are periodically discovered and quickly patched.
1. OCaml-TLS (https://github.com/mirleft/ocaml-tls) [1]
2. miTLS (http://www.mitls.org/wsgi/home)
3. s2n (https://github.com/awslabs/s2n)
[1] Home of the epic Bitcoin Pinata! http://ownme.ipredator.se/
Here's a strategy:
Take OpenSSL, remove a bunch of stuff that almost nobody needs, rearrange the code to make it clearer. You will get BoringSSL.
Take the BoringSSL code, remove more stuff that people outside of Google generally don't need. Temporarily remove the X.509 code and the TLS code so that you can remove even more stuff.
Next, write bindings for the remaining code for a safe language like Rust, so that the replacement X.509 and TLS code can be written in the safer language, while still using the OpenSSL crypto code.
Then, write the new X.509 code, focusing on what really is needed and ignoring all the extra cruft that was added to X.509 but which doesn't actually improve security.
Then, over time, refactor the the language bindings so more code is written in the safe language and less code is written in C, with the goal of almost all the code that isn't implementing directly crypto algorithms being in the safe language.
Then, add a good, clean TLS implementation in the safe language, that focuses on the good parts of TLS, and that avoids all the legacy cruft that makes TLS seem complicated.
I've been doing an experiment along these lines. I've open sourced two parts so far:
1. https://github.com/briansmith/ring 2. https://github.com/briansmith/webpki
It is a big project, but I am pretty sure it is going to succeed.
This doesn't matter, because the code that should be resistant to timing attacks should be written in assembly language or a language specially designed for that purpose (that doesn't exist yet) anyway. High-level languages have ways of accessing code written in assembly language.
Anyway, my point is that all these things are solvable.
This would put you right back into buffer overflow territory...
In any case, the code in question for this post doesn't seem to be written to be timing sensitive.
> Patch: http://ftp.openbsd.org/pub/OpenBSD/patches/5.8/common/007_ob...
> Releases: http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/
I am glad that bugs are found. It shows that the audit is thorough, since bugs can still be expected in LibreSSL (still quite new) and OpenSMTPD (audit was explicitly requested).
https://en.wikipedia.org/wiki/LibreSSL#Security_and_vulnerab...
The only one I know of that has written almost bug-free C code is djb, and that code is a fucking pain to look at regardless of what Aaron Schwartz said. It is good, but it is not how anyone in their right mind writes C. If that is what is required to write safe C then I'm all for using another language.
Edit: I do like how Zed Shaw writes C, but I have no idea if the software he wrote is safe...
Yes and no.
No in that LibreSSL aims to be mostly drop-in compatible with OpenSSL (modulo deprecations/removals) and OpenSSL has a very complex API exposing lots of internal gunk to the user, so moving to a non-C implementation may not be possible at all.
Yes because there's a related `libtls`[0] project which aims to define more abstracted API (with the reference implementation being done on top of libressl). As POC, tedu has/had an implementation of the API using golang's TLS stack[1], and noted that you could build the same on e.g. the OCaml/Mirage TLS stack[2]. If `libtls` fulfils its promises, it would become much easier to swap TLS stack and indeed move to non-C implementations.
[0] http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/... HN discussion: https://news.ycombinator.com/item?id=8572029 (some comments may not apply anymore IIRC the discussion was the initial API which has been extended since)
Lua is built on c, and is much more (than C) memory safe. It retains all the advantages of C (ie, can go anywhere), and we limit the C to 17.3k (as of 5.2.3) lines of c, which is relatively static (won't change, won't bloat in the same way maintaining/building on current C SSL implementations), and could allow us to handle memory safely.
I'm curious why this approach is not used. Portability? Proficiency? Lua is quite possibly the most beautiful language to read. I can't see any downsides, but I'll gladly be enlightened!
As for the space considerations, I have two ways to reason this:
* Wouldn't it only take up 1x414K? If you create luaSSL as a drop in replacement to OpenSSL, you'd only need one copy in your filesystem, just as you only have one OpenSSL.
* Even if you bloat your binary sizes by 414K per executable, isn't it worth it to go from "yes, it could be unsafe, we'll never know... let's wait for the next CVE" to 100% guaranteed no memory faults EVER? Nothing is free, and this could be a cheap price to pay for the guarantee of memory safety, and the implications that come with it.
Edit: wording
I really think that everyone serious about programming should learn C, to the level where they can read parts of the Linux kernel or something, but never actually deploy C code to a production environment. It's not a "must" -- not a requirement to be a good programmer or developer at all -- but you'll understand something much closer to the hardware than you will in a more modern language. Just don't expose that program to an adversary if you can avoid it, because that proximity to the hardware makes a single mistake catastrophic.
Plus, if you ever need to do embedded work, you often don't have a choice other than C.
Rust can't mature fast enough.
Misunderstand me correctly, he writes solid software. He's not just a guy I would like to work with :)
Nothing against the programmer; these things happen. But I wouldn't say because of C in this case.
Sure, it's not generally possible for a language to perfectly manage every resource all the time, but this doesn't mean we should just give up and throw away computer assistance in cases when we can have it.
C is an important language, but there's no need to look at it through rose-coloured glasses.
If I were going to place blame (I think a better approach is to skip that and just solve the problem), then I would blame developers, not the language.
To me, the code you are criticizing is very "regular" -- it follows predictable patterns. I find it easier to follow than most other C code I read.
More importantly I believe it is worth following. Obviously he is doing something right as reliability, performance and paucity of bugs shows.
All this despite not being in the "right mind", whatever that means.
I wish all the programs I am forced to use were written with such care.
Compare that to a basic unencrypted HTTP server, which you can write in ~10KiB of code. Complexity breeds these kinds of problems as a natural consequence.
I do know that verifiability is significantly easier than all of this (signatures; hashing), but it's quite possible there is no simpler solution for the secrecy aspect of TLS. I certainly wish there was more support for HTTP+integrity, for those who didn't need the secrecy component. These constant CVEs in every TLS library put our servers at risk in cases where secrecy simply isn't necessary.
But the bugs described are not complex - they are local problems that can be understood by just reading one function. The problem here is not complexity, but size (not enough people spend enough time to review all of the code) and the fact that the library is written in a language with manual memory management and unsafe memory access.
> I do know that verifiability is significantly easier than all of this (signatures; hashing), but it's quite possible there is no simpler solution for the secrecy aspect of TLS.
In this case, the affected component was related to the identity part (certificates), which are needed for verifiability. It was not directly related to secrecy.
C's simplicity is certainly a blessing and a curse. Right now, I'm of the opinion that C++ is the best we've got, even if not perfectly ideal. RAII, shared pointers and smart containers with bounds checking allows you to keep nearly all of the performance without the drastic downsides of tracing garbage collectors (obscene amounts of memory usage, nasty stalls, etc.)
And when you get into the really high-level languages (Python, Ruby), they simply don't scale.
Everyone's darling right now is Rust. I still think it's too early in its life to know how it'll play out (hot new languages follow a predictable path of gaining negative baggage and shortcomings as they increase in real-world usage), but I guess we'll see. Maybe it will indeed be a great panacea to these issues.
> In this case, the affected component was related to the identity part (certificates), which are needed for verifiability. It was not directly related to secrecy.
Sure, but I'm generalizing here.
I can (and did) write a SHA256 hashing algorithm in 3KiB of code. Combined with a package gist updater (these use signing with local keys for updates), that's enough to secure binary package downloads on the BSDs over plain-text HTTP.
Surely we can come up with a less complex system for verification alone. Something akin to signify, perhaps. I know that doesn't solve how to universally handle anonymously verifying public key authenticity, but surely we can make something less complex than the clusterfuck that is TLS and the CAs. Yet it doesn't seem there's much, if any, interest in even trying. Everyone has hitched their wagons to HTTP/2 and mandatory TLS; which to me feels like a huge step backward by means of making tech vastly more complex.
I think you need to qualify what "scale" means in this context. Otherwise java is, for all its warts, more or less as high level as ruby and python, and it's probably been proven to scale up (to big machines, or many developers) reasonably well.
> The system is built on the JVM platform ... in fact they ended up by doing all the business logic for their platform: all trades, from all customers, in all markets - on a single thread. A thread that will process 6 million orders per second using commodity hardware
https://www.reddit.com/r/programming/comments/3p6r2v/i_wrote...
I've constantly had similar experiences. I tried to write the GUI portion of my programs with Python and PyGTK. I needed to write a simple function that scaled up a bitmap from 256x224 to 512x448. A nearest-neighbor pixel doubler with no interpolation, color conversion, or anything else. It ended up running at around 30fps with nothing but the scaling, on a Core i7-2600k processor. And when I asked for help in optimizing it, nobody could really improve the Python, and they recommended I write "the slow parts" in C as a shared library, and invoke that from the Python GUI.
It's okay to be 10%, 30% or even 50% slower than C. But to be 2500% slower or more is completely unacceptable for production code. That level of performance only works on websites with small amounts of traffic, or on the simplest of applications.
The root problem here is of course dynamic typing. That makes scaling both performance and code size much more challenging.
Java is certainly vastly better, but the garbage collection and its stalls, wasteful memory usage (I've seen our production servers bog down with Tomcat eating up all the RAM on the server for simple programs), and overall extreme negatives of the language itself (no unsigned integers, limited to single inheritance, terrible support for generics [maybe this has changed recently?], etc), plus the legal issues that Oracle is saddling the language with, make it an unacceptable alternative.
C# might have been better if Microsoft ever cared about any platform other than Windows.
Both dynamic typing and garbage collection are the wrong ways to solve the problems of C. Rust is the closest I've seen to addressing the real problems, but it coming from Mozilla (with their terrible record with Firefox), and the syntax being so radically different from C, I think will chase a lot of people off.
signify also looks really promising. Get the key down to the size of a QR code, and you could separate the issue of signing from signature validation entirely ... post it on your HTTPS Twitter feed, put it on your business card, whatever.
I think it would be a positive step to get users to think about how they are verifying integrity themselves and the sources they trust.
You could even have a dozen sources and let there be a little button in the URL bar to show you which sources agree on a public key provided by a server.
I don't trust any implementation of it that uses unmanaged code. (yes, pretty much assuming everything based on openssl will be broken time and time again)
You need to use some sort of bignum function if you want to display large arc numbers in decimal. Which is what OpenSSL and LibreSSL do, and that involves creating and destroying BIGNUM objects. The problem is that someone in LibreSSL moved the free() to outside the loop body handling the arc numbers while they were cleaning up. So only the last BIGNUM used gets freed.
Oh, did I tell you that arc numbers are encoded in base 128, with the first bit of every octet used to signal whether it's the last octet of that arc number? Because they are.
It still seems questionable that they'd let any length be set, especially since you can just chain numbers together into a longer OID.
Base128 encoding seems fair though; the other varint formats are more exotic and only work with many ints at once.
I assume that's because OID nodes are essentially namespaces, giving an arbitrary upper bound to node ids would artificially and "unnecessarily" limit the size of the corresponding namespace. That looks like a "correct versus realistic" debate which was won by the "correct" side.
While Rust can export functions the same way C does it you are in unmanaged land again then.
With http://www.ponylang.org/ its much better, it can export functions like C does it, but its fully managed. The only limitation are the Data Types, you are restricted to those C knows.
Also, FWIW, I don't think Rust should be considered unmanaged for these purposes: it bridges the divide between "managed" and "unmanaged" by bringing the memory safety of the former to a language that doesn't require a complicated runtime. So strictly speaking, yes, it's unmanaged, but it doesn't suffer the worst of the problems of existing unmanaged languages like C or C++.
You can do that with most "managed" languages. You can call Python, Haskell, Go or OCaml from C, pony isn't exactly special here.
One issue in all these cases is you're carrying a whole runtime with you, possibly one per library you're using, praying they don't conflict with one another[0] and limiting the portability of the whole[1]. You're also significantly increasing the cost of using the library in question as it now brings in megabytes of runtime along for the ride.
[0] with conflicting signal handling for instance
[1] if only because all those runtimes you're bringing in might be doing stuff like spawning their own threads and the like, so that needs to be available on the target platform
Possibly less as there's a pretty high chance to get the ownership rules wrong on the callee side in a way which'll fault (because the callee will have freed the structure) rather than "just" leak memory (because the caller will not send the structure back for deallocation).
Example: https://blog.filippo.io/building-python-modules-with-go-1-5/ in the third snippet (AddDot) the author returns a Go string which may (will?) be freed next time the GC runs[0] regardless of the caller still using it.
[0] https://www.reddit.com/r/golang/comments/3ieiiu/building_pyt...
Think pass-by-value structs with only fixed size fields. Or JSON, protobufs etc. (Ok, you need the one pointer for the JSON/PB data, but it's not kept around after null check and decode)
There are (at least) three issues with that:
1. not all FFIs can handle structs, according to the Rust FFI omnibus GHC's doesn't[0]
2. the callee side must be able to express on-stack by-value structs, many (most?) managed languages don't, either not supporting them or having them only as optimisation (based on escape analysis)
3. and finally the layout (and size) of the struct must be deterministic, which further restricts (2)
> Or JSON, protobufs etc. (Ok, you need the one pointer for the JSON/PB data, but it's not kept around after null check and decode)
Depending on the callee's implementation that may be too late already, there is absolutely no guarantee the data will even go through the FFI without being released, so you'd be relying on garbage data not being zeroed/reused/moved in the interval, which would be an implementation detail.
[0] http://jakegoulding.com/rust-ffi-omnibus/tuples/#haskell
I think the FFI could well express this. Just generate a C thunk that converts the value from the stack to a suitable representation.
> 3. and finally the layout (and size) of the struct must be deterministic
The layout and padding of structs is deterministic in all C environments I know of since they end up as part of ABIs.
> Depending on the callee's implementation that may be too late already, there is absolutely no guarantee the data will even go through the FFI without being released
So you make a C FFI call from a safe language to libfoo.so function void foo(char *jsondata) and before the callee returns your safe runtime yanks the data from under it in another thread?
Yeah, you would also need the caller's FFI to guarantee against this to get safety. In absence of such guarantees you should stick to only passing pass-by-value structures!
Oh definitely, I'm just saying not all of them do (if you follow the link, the Ruby/MRI, CPython and Node FFIs all provide for that at least on the caller side)
> So you make a C FFI call from a safe language to libfoo.so function void foo(char *jsondata) and before the callee returns your safe runtime yanks the data from under it in another thread?
Yup, could happen with a concurrent GC kicking it at an inconvenient time, or with a refcounting GC.
> Yeah, you would also need the FFI to guarantee against this to get safety.
I'm not sure it could (without caller cooperation), best I can see is offer side-channels to "leak" memory (allocate non-managed memory and copy stuff into it) or recommend that you keep an internal strong reference of some sort (e.g. a global list/set).
As a general answer though: static analysis will give you better answers if you write your code in a clean and well structured way. If you goto from a branch from a branch from a (potentially infinite) for-loop from a (not counting) while-loop - good luck :) OpenSSL's OBJ_/BN_/... functions seem to strive for complexity.
I think Apple never discloses information about security vulnerabilities until a fix is out.
See http://eli.thegreenplace.net/2009/04/27/using-goto-for-error... for a short rundown, or http://blog.regehr.org/archives/894 where regehr — who works specifically in correctness and verifiability — states:
> many people, including me, prefer the goto version
Also, a common use of goto in C is when you're doing something in nested for loops and want to break out of all of them. A goto is actually much cleaner here than using sentinel values or boolean flags, or naughty things like setting your loop variables to be outside of the conditional bounds.
Every conditional is a goto (jump) at the machine level.
Would you really disregard all that because of a 1-byte buffer overflow and a memory leak?
CVE-2015-0204 affected LibreSSL, but they thought it was a low priority vulnerability, when it actually is a high priority. They fixed it, didn't notify upstream afaict and just issued a new release.
LibreSSL isn't a panacea, and based on that, they can't even classify vulnerabilities correctly.
Most of the vulnerabilities in OpenSSL are in parts (e.g. DTLS) which are disabled in lots of builds.