LibreSSL と Boost.ASIO.SSL を組み合わせて使う事になった場合に ::SSL_CTX_get_default_passwd_cb_userdata 等複数箇所で翻訳時エラーが生じてしまう場合の応急対処法


問題

LibreSSL と Boost.ASIO.SSL を組み合わせて使う事になった場合に ::SSL_CTX_get_default_passwd_cb_userdata 等複数箇所で翻訳時エラーが生じてしまう。

発生条件

以下の組み合わせにより発生する。

  1. LibreSSL を使用したい
    • 執筆時点の最新版は 2.5.0, 2.4.4 、あるいはそれ以降も対応されない可能性あり。
  2. Boost.ASIO.SSL を使用したい。
    • Boost-1.61以降執筆現在最新1.63まで、あるいはそれ以降も対応されない可能性あり。

現象

翻訳時エラーが発生する。Boost.ASIO.SSL

...include/boost/asio/ssl/impl/context.ipp: In destructor 'boost::asio::ssl::context::~context()':
.../include/boost/asio/ssl/impl/context.ipp:232:25: error: '::SSL_CTX_get_default_passwd_cb_userdata' has not been declared
     void* cb_userdata = ::SSL_CTX_get_default_passwd_cb_userdata(handle_);
                         ^~

類似のエラーが大量に発生する。

原因

Boost.ASIO.SSL の以下のコミットから asio/ssl/impl/context.hpp などのソースコード複数について OPENSSL_VERSION_NUMBER によるプリプロセッサーレベルの分岐処理が導入され、 OpenSSL-1.1 系の API への対応が追加された。

#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
    void* cb_userdata = ::SSL_CTX_get_default_passwd_cb_userdata(handle_);
#else // (OPENSSL_VERSION_NUMBER >= 0x10100000L)
    void* cb_userdata = handle_->default_passwd_callback_userdata;
#endif // (OPENSSL_VERSION_NUMBER >= 0x10100000L)
#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) \
    && (OPENSSL_VERSION_NUMBER < 0x10100000L)
    ::SSL_COMP_free_compression_methods();
#endif // (OPENSSL_VERSION_NUMBER >= 0x10002000L)

OpenSSL では OPENSSL_VERSION_NUMBER0x10100000L 未満か否かによって OpenSSL-1.1 系の API を使用可能か判別できる。

# define OPENSSL_VERSION_NUMBER  0x10100040L
# ifdef OPENSSL_FIPS
#  define OPENSSL_VERSION_TEXT    "OpenSSL 1.1.0d-fips-dev  xx XXX xxxx"
# else
#  define OPENSSL_VERSION_TEXT    "OpenSSL 1.1.0d-dev  xx XXX xxxx"
# endif

一方、 LibreSSL-2.4/2.5 系では openssl/opensslv.hOPENSSL_VERSION_NUMBER0x20000000L と LibreSSL または互換の OpenSSL の API とは連動せず固定値で定義している。

/* These will never change */
#define OPENSSL_VERSION_NUMBER  0x20000000L
#define OPENSSL_VERSION_TEXT    LIBRESSL_VERSION_TEXT
#define OPENSSL_VERSION_PTEXT   " part of " OPENSSL_VERSION_TEXT

また、 LibreSSL-2.4/2.5 は OpenSSL-1.1 系の API に対応していない。

よって、 LibreSSL-2.4/2.5 と Boost-1.61/1.62/1.63.ASIO.SSL を組み合わせると、 Boost.ASIO.SSL のソースコード中から OpenSSL-1.1 系の API を使用するコードがプリプロセッサーレベルで有効となり、 OpenSSL-1.1 系の API に未対応の LibreSSL-2.4/2.5 ではその API が未定義で使用できないため、翻訳時エラーとなる。

余談: LibreSSL のバージョン情報

原因と直接は関係しないが、 LibreSSL のバージョン情報をプリプロセッサーで取得したい場合は、 openssl/opensslv.h に次の LibreSSL 独自の定義が追加されているのでこちらを使用できる。

#define LIBRESSL_VERSION_NUMBER       0x2030200fL
#define LIBRESSL_VERSION_TEXT "LibreSSL 2.3.2"

応急対処法

Boost.ASIO.SSL のヘッダーインクルード前後で OPENSSL_VERSION_NUMBER を偽装する事で比較的簡単に応急対処できる。

/// @brief LibreSSL と Boost.ASIO.SSL の OpenSSL-1.1 API 対応判定の偽装処理
/// @ref http://qiita.com/usagi/items/acee410849ca8f4f9c11
/// @{
#if LIBRESSL_VERSION_NUMBER <= 0x2050100fL
  #include <openssl/opensslv.h>
  #undef OPENSSL_VERSION_NUMBER
  #define OPENSSL_VERSION_NUMBER 0x10001fffL
#endif
/// @}

#include <boost/asio/ssl.hpp>

注意

  • この記事で紹介する対処方法は「応急」である事を理解して施し、適切な保守を行う必要がある。