Saturday, December 24, 2016

Updating Your App to Support OpenSSL 1.1.0

Introduction

OpenSSL 1.1.0 introduced several API-breaking changes, and with distributions starting to use it, I found myself scrambling to get my software working. I found various docs stating that 1.1.0 changed things, but nothing that clearly stated: "if you have this now, then change it to this..." So, that's what I'll do here, and hopefully it will help you if you find your app in the same situation.

Deprecated "method" Functions

Lots of "method" functions like TLSv1_server_method() and SSLv23_client_method() and TLSv1_2_method() are deprecated in OpenSSL 1.1.0, and if you compile your software with -Werror, then that presents a problem.

All you can call now is one of:

  • TLS_method()
  • TLS_client_method()
  • TLS_server_method()

If your code allowed the user to select a SSL/TLS mode then it can't do it by calling a mode-specific function any more. It'll have to select the mode some other way that I'm not aware of. The new methods negotiate the most modern mode supported by the client and server. It's not immediately clear how to override that, or if you can. If someone knows a way, please leave a comment below.

It's easy enough to replace function calls, but if you want to support older versions, then you have to add tests for the old and new functions to your configure script and add all kinds of #ifdefs, and so forth. Actually, since various methods like these have been added over the years, you may already have such tests and #ifdefs (I did) and it won't be so bad.

Deprecated ASN1_STRING_data
unsigned char *ASN1_STRING_data(...);

has been replaced with

const unsigned char *ASN1_STRING_get0_data(...);

This is a straightforward replacement, but note the const'ness of the return value of the new function. If you're using C++ then you may have to change the type of the variable it's being assigned to (or un-const it).

Again, this is a brand new function, so, configure tests and #ifdefs.

Opaque EVP_PKEY

The EVP_PKEY struct (public key struct) is opaque now. The header files declare that an EVP_PKEY struct exists, but the definition is hidden up in the library. The error you get (with g++ at least) is fairly obtuse though, complaining about a forward declaration and an invalid use of an incomplete type. The error just means that the struct is opaque.

Fortunately there are a bunch of functions that return the various components of the struct.

I had to replace code to get the key type like:


EVP_PKEY *pubkey = X509_get_pubkey(c);
...
int t = pubkey->type;

with code like:


EVP_PKEY *pubkey = X509_get_pubkey(c);
...
int t = EVP_PKEY_base_id(pubkey);

Note that there IS an EVP_PKEY_type() function, but it does NOT return the "type" member of the struct. Ha! Not super intuitive, but the change is still straightforward.

Similarly, I had to replace code to get the key itself like:


EVP_PKEY *pubkey = X509_get_pubkey(c);
...
const unsigned char *key = pubkey->pkey.ptr;

with code like:


EVP_PKEY *pubkey = X509_get_pubkey(c);
...
const unsigned char *key = EVP_PKEY_get0(pubkey);

I'm not sure when those functions were introduced, but they appear to be available in OpenSSL 1.0.x but not in OpenSSL 0.9.x, so again, if you want to support really old versions, then you'll have to add configure tests and #ifdefs.

Opaque X509

The X509 struct is also opaque now. Like the EVP_PKEY struct, you'll have to use functions to access its members.

The one case I needed was slightly trickier though.

In the past, I was getting the signature algorithm name like:


X509 *c = SSL_get_peer_certificate(ssl);
...
char sigalg[256];
OBJ_obj2txt(pvt->_sigalg,256,c->sig_alg->algorithm,0);

But I had to change that code to:


X509 *c = SSL_get_peer_certificate(ssl);
...
char sigalg[256];
OBJ_obj2txt(pvt->_sigalg,256,OBJ_nid2obj(X509_get_signature_nid(c)),0);

Which is a little contrived. In the old code, c->sig_alg->algorithm is an "obj", and I could pass it directly into OBJ_obj2txt. In the new code, I have to get the "nid", convert it to an object, and pass that in. Ideally I'd just get the object to begin with, but there doesn't appear to be an obvious way to do that.

As a side note, the function name X509_get_signature_nid() was a little confusing at first, because it appears to return the "nid" of the signature _algorithm_ as opposed to the signature itself. But I guess the signature itself wouldn't have a "nid", so it makes sense, but it wasn't immediately intuitive to me.

Again, X509_get_signature_nid() was introduced sometime in the 1.0.x series, so you'll have to add a test for it and #ifdefs if you want to support older versions.

Conclusion

The opacity of the structs ought to help OpenSSL be more maintainable in the future. They ought to be able to change the code at will now, without breaking the API or ABI, and that's always good. I did the same kind of thing with my Rudiments library way back, and recently to SQL Relay for that exact reason. Hopefully it will work out well for both of us.

I'm not sure about the TLS_*_method() thing though. It seems like there really ought to be a way to choose a specific SSL/TLS mode. Perhaps there is though, and I just didn't see it. Somebody let me know if they figure that one out.