Skip to content

Commit

Permalink
Make importing CNIOOpenSSL safer. (#41)
Browse files Browse the repository at this point in the history
Motivation:

There are a number of dangerous portions of CNIOOpenSSL that make it
tricky to use. This mostly doesn't manifest in debug mode, or in this
module, but when compiling in release mode or when others use this module
it can all go subtly wrong.

We should make this module easier to use correctly.

Modifications:

- Remove the constant defined in the header file, replace with #define.
- Move all static inline declarations to regular function declarations.

Result:

Fewer compile errors, easier to use this module.
  • Loading branch information
Lukasa authored and weissi committed Sep 18, 2018
1 parent 9b03e08 commit 8380fa2
Show file tree
Hide file tree
Showing 3 changed files with 291 additions and 227 deletions.
33 changes: 23 additions & 10 deletions Sources/CNIOOpenSSL/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,7 @@
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// Note that we don't include our umbrella header because it declares a bunch of
// static inline functions that will cause havoc if they end up in multiple
// compilation units.
//
// At some point we should probably just make them all live in this .o file, as the
// cost of the function call is in practice very small.
#include <openssl/bio.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <c_nio_openssl.h>
#include <stdio.h>
#include <pthread.h>
#include <assert.h>
Expand Down Expand Up @@ -158,3 +149,25 @@ void CNIOOpenSSL_InitializeOpenSSL(void) {
CNIOOpenSSL_InitializeLockingCallbacks();
}

/// A no-op verify callback, for use from Swift.
int CNIOOpenSSL_noop_verify_callback(int preverify_ok, X509_STORE_CTX *context) {
return preverify_ok;
}

/// A small helper to allow querying whether we were build against LibreSSL or not.
///
/// Returns 1 if we built against LibreSSL, 0 if we did not.
int CNIOOpenSSL_is_libressl(void) {
#if defined(LIBRESSL_VERSION_NUMBER)
return 1;
#else
return 0;
#endif
}

// This wrapper is used to erase the types required for this function. It's a bad
// thing to have to do, but until OpaquePointer gets better this is the only way to make
// this function work.
int CNIOOpenSSL_PKCS12_parse(void *p12, const char *pass, void **pkey, void **cert, void **ca) {
return PKCS12_parse((PKCS12 *)p12, pass, (EVP_PKEY **)pkey, (X509 **)cert, (STACK_OF(X509) **)ca);
}
272 changes: 55 additions & 217 deletions Sources/CNIOOpenSSL/include/c_nio_openssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,134 +26,56 @@
#include <openssl/pkcs12.h>
#include <openssl/x509v3.h>

// MARK: OpenSSL version shims
#if defined(SSL_OP_NO_TLSv1_3)
int CNIOOpenSSL_SSL_OP_NO_TLSv1_3 = SSL_OP_NO_TLSv1_3;
#else
int CNIOOpenSSL_SSL_OP_NO_TLSv1_3 = 0;
#endif

// These are functions that shim over differences in different OpenSSL versions,
// which are best handled by using the C preprocessor.
static inline void CNIOOpenSSL_SSL_CTX_setAutoECDH(SSL_CTX *ctx) {

#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL && OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, 1, NULL);
#endif
}

static inline int CNIOOpenSSL_SSL_set_tlsext_host_name(SSL *ssl, const char *name) {
return SSL_set_tlsext_host_name(ssl, name);
}

/// Initialize OpenSSL.
///
/// This method is NOT THREAD SAFE. Please only call it from inside a lock or a pthread_once.
void CNIOOpenSSL_InitializeOpenSSL(void);

static inline const unsigned char *CNIOOpenSSL_ASN1_STRING_get0_data(ASN1_STRING *x) {
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
return ASN1_STRING_data(x);
#else
return ASN1_STRING_get0_data(x);
#endif
}

static inline const SSL_METHOD *CNIOOpenSSL_TLS_Method(void) {
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
return SSLv23_method();
#else
return TLS_method();
#endif
}


/// A no-op verify callback, for use from Swift.
static inline int CNIOOpenSSL_noop_verify_callback(int preverify_ok, X509_STORE_CTX *context) {
return preverify_ok;
}
int CNIOOpenSSL_noop_verify_callback(int preverify_ok, X509_STORE_CTX *context);

/// A small helper to allow querying whether we were build against LibreSSL or not.
///
/// Returns 1 if we built against LibreSSL, 0 if we did not.
static inline int CNIOOpenSSL_is_libressl(void) {
#if defined(LIBRESSL_VERSION_NUMBER)
return 1;
int CNIOOpenSSL_is_libressl(void);

// This wrapper is used to erase the types required for this function. It's a bad
// thing to have to do, but until OpaquePointer gets better this is the only way to make
// this function work.
int CNIOOpenSSL_PKCS12_parse(void *p12, const char *pass, void **pkey, void **cert, void **ca);

// MARK: OpenSSL version shims
#if defined(SSL_OP_NO_TLSv1_3)
#define CNIOOpenSSL_SSL_OP_NO_TLSv1_3 SSL_OP_NO_TLSv1_3
#else
return 0;
#define CNIOOpenSSL_SSL_OP_NO_TLSv1_3 0
#endif
}

// These are functions that shim over differences in different OpenSSL versions,
// which are best handled by using the C preprocessor.
void CNIOOpenSSL_SSL_CTX_setAutoECDH(SSL_CTX *ctx);
int CNIOOpenSSL_SSL_set_tlsext_host_name(SSL *ssl, const char *name);
const unsigned char *CNIOOpenSSL_ASN1_STRING_get0_data(ASN1_STRING *x);
const SSL_METHOD *CNIOOpenSSL_TLS_Method(void);

// MARK: Macro wrappers
// These are functions that rely on things declared in macros in OpenSSL, at least in
// some versions. The Swift compiler cannot expand C macros, so we need a file that
// can.
static inline int CNIOOpenSSL_sk_GENERAL_NAME_num(STACK_OF(GENERAL_NAME) *x) {
return sk_GENERAL_NAME_num(x);
}

static inline const GENERAL_NAME *CNIOOpenSSL_sk_GENERAL_NAME_value(STACK_OF(GENERAL_NAME) *x, int idx) {
return sk_GENERAL_NAME_value(x, idx);
}

static inline int CNIOOpenSSL_sk_X509_num(STACK_OF(X509) *x) {
return sk_X509_num(x);
}

static inline const X509 *CNIOOpenSSL_sk_X509_value(STACK_OF(X509) *x, int idx) {
return sk_X509_value(x, idx);
}

static inline void CNIOOpenSSL_sk_X509_free(STACK_OF(X509) *x) {
return sk_X509_free(x);
}

static inline int CNIOOpenSSL_SSL_CTX_set_app_data(SSL_CTX *ctx, void *arg) {
return SSL_CTX_set_app_data(ctx, arg);
}

static inline void *CNIOOpenSSL_SSL_CTX_get_app_data(SSL_CTX *ctx) {
return SSL_CTX_get_app_data(ctx);
}

static inline long CNIOOpenSSL_SSL_CTX_set_mode(SSL_CTX *ctx, long mode) {
return SSL_CTX_set_mode(ctx, mode);
}

static inline long CNIOOpenSSL_SSL_CTX_set_options(SSL_CTX *ctx, long options) {
return SSL_CTX_set_options(ctx, options);
}

static inline int CNIOOpenSSL_SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str) {
#if (OPENSSL_VERSION_NUMBER < 0x10101000L) || defined(LIBRESSL_VERSION_NUMBER)
return 1;
#else
return SSL_CTX_set_ciphersuites(ctx, str);
#endif
}

static inline void CNIOOpenSSL_OPENSSL_free(void *addr) {
OPENSSL_free(addr);
}

static inline int CNIOOpenSSL_X509_set_notBefore(X509 *x, const ASN1_TIME *tm) {
return X509_set_notBefore(x, tm);
}

static inline int CNIOOpenSSL_X509_set_notAfter(X509 *x, const ASN1_TIME *tm) {
return X509_set_notAfter(x, tm);
}

static inline unsigned long CNIOOpenSSL_OpenSSL_version_num(void) {
return SSLeay();
}

// This wrapper is used to erase the types required for this function. It's a bad
// thing to have to do, but until OpaquePointer gets better this is the only way to make
// this function work.
static inline int CNIOOpenSSL_PKCS12_parse(void *p12, const char *pass, void **pkey, void **cert, void **ca) {
return PKCS12_parse((PKCS12 *)p12, pass, (EVP_PKEY **)pkey, (X509 **)cert, (STACK_OF(X509) **)ca);
}
int CNIOOpenSSL_sk_GENERAL_NAME_num(STACK_OF(GENERAL_NAME) *x);
const GENERAL_NAME *CNIOOpenSSL_sk_GENERAL_NAME_value(STACK_OF(GENERAL_NAME) *x, int idx);
int CNIOOpenSSL_sk_X509_num(STACK_OF(X509) *x);
const X509 *CNIOOpenSSL_sk_X509_value(STACK_OF(X509) *x, int idx);
void CNIOOpenSSL_sk_X509_free(STACK_OF(X509) *x);
int CNIOOpenSSL_SSL_CTX_set_app_data(SSL_CTX *ctx, void *arg);
void *CNIOOpenSSL_SSL_CTX_get_app_data(SSL_CTX *ctx);
long CNIOOpenSSL_SSL_CTX_set_mode(SSL_CTX *ctx, long mode);
long CNIOOpenSSL_SSL_CTX_set_options(SSL_CTX *ctx, long options);
int CNIOOpenSSL_SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str);
void CNIOOpenSSL_OPENSSL_free(void *addr);
int CNIOOpenSSL_X509_set_notBefore(X509 *x, const ASN1_TIME *tm);
int CNIOOpenSSL_X509_set_notAfter(X509 *x, const ASN1_TIME *tm);
unsigned long CNIOOpenSSL_OpenSSL_version_num(void);

// We bring this typedef forward in case it's not present in the version of OpenSSL
// we have.
Expand All @@ -164,112 +86,28 @@ typedef int (*CNIOOpenSSL_SSL_CTX_alpn_select_cb_func)(SSL *ssl,
unsigned int inlen,
void *arg);

static inline int CNIOOpenSSL_SSL_CTX_set_alpn_protos(SSL_CTX *ctx,
const unsigned char *protos,
unsigned int protos_len) {
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
return SSL_CTX_set_alpn_protos(ctx, protos, protos_len);
#else
return 1;
#endif
}

static inline void CNIOOpenSSL_SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
CNIOOpenSSL_SSL_CTX_alpn_select_cb_func cb,
void *arg) {
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
SSL_CTX_set_alpn_select_cb(ctx, cb, arg);
#endif
}

static inline void CNIOOpenSSL_SSL_get0_alpn_selected(const SSL *ssl,
const unsigned char **data,
unsigned int *len) {
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
SSL_get0_alpn_selected(ssl, data, len);
#endif
}

static inline int CNIOOpenSSL_BIO_get_init(BIO *bio) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
return bio->init;
#else
return BIO_get_init(bio);
#endif
}

static inline void CNIOOpenSSL_BIO_set_init(BIO *bio, int init) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
bio->init = init;
#else
BIO_set_init(bio, init);
#endif
}

static inline void *CNIOOpenSSL_BIO_get_data(BIO *bio) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
return bio->ptr;
#else
return BIO_get_data(bio);
#endif
}

static inline void CNIOOpenSSL_BIO_set_data(BIO *bio, void *ptr) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
bio->ptr = ptr;
#else
BIO_set_data(bio, ptr);
#endif
}

static inline int CNIOOpenSSL_BIO_get_shutdown(BIO *bio) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
return bio->shutdown;
#else
return BIO_get_shutdown(bio);
#endif
}

static inline void CNIOOpenSSL_BIO_set_shutdown(BIO *bio, int shut) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
bio->shutdown = shut;
#else
BIO_set_shutdown(bio, shut);
#endif
}

static inline void CNIOOpenSSL_BIO_clear_retry_flags(BIO *bio) {
BIO_clear_retry_flags(bio);
}

static inline void CNIOOpenSSL_BIO_set_retry_read(BIO *bio) {
BIO_set_retry_read(bio);
}

static inline int CNIOOpenSSL_BIO_up_ref(BIO *bio) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
CRYPTO_add(&bio->references, 1, CRYPTO_LOCK_BIO);
return 1;
#else
return BIO_up_ref(bio);
#endif
}

static inline int CNIOOpenSSL_BIO_should_retry(BIO *bio) {
return BIO_should_retry(bio);
}

static inline int CNIOOpenSSL_BIO_should_read(BIO *bio) {
return BIO_should_read(bio);
}

static inline int CNIOOpenSSL_BIO_get_close(BIO *bio) {
return BIO_get_close(bio);
}

static inline int CNIOOpenSSL_BIO_set_close(BIO *bio, long flag) {
return BIO_set_close(bio, flag);
}
int CNIOOpenSSL_SSL_CTX_set_alpn_protos(SSL_CTX *ctx,
const unsigned char *protos,
unsigned int protos_len);
void CNIOOpenSSL_SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
CNIOOpenSSL_SSL_CTX_alpn_select_cb_func cb,
void *arg);
void CNIOOpenSSL_SSL_get0_alpn_selected(const SSL *ssl,
const unsigned char **data,
unsigned int *len);
int CNIOOpenSSL_BIO_get_init(BIO *bio);
void CNIOOpenSSL_BIO_set_init(BIO *bio, int init);
void *CNIOOpenSSL_BIO_get_data(BIO *bio);
void CNIOOpenSSL_BIO_set_data(BIO *bio, void *ptr);
int CNIOOpenSSL_BIO_get_shutdown(BIO *bio);
void CNIOOpenSSL_BIO_set_shutdown(BIO *bio, int shut);
void CNIOOpenSSL_BIO_clear_retry_flags(BIO *bio);
void CNIOOpenSSL_BIO_set_retry_read(BIO *bio);
int CNIOOpenSSL_BIO_up_ref(BIO *bio);
int CNIOOpenSSL_BIO_should_retry(BIO *bio);
int CNIOOpenSSL_BIO_should_read(BIO *bio);
int CNIOOpenSSL_BIO_get_close(BIO *bio);
int CNIOOpenSSL_BIO_set_close(BIO *bio, long flag);

// MARK: BIO helpers
/// This is a pointer to the BIO_METHOD structure for NIO's ByteBufferBIO.
Expand Down
Loading

0 comments on commit 8380fa2

Please sign in to comment.