LibreSSL users currently build with -Dopenssl_0.9.0, which forces LibreSSL through a code path designed for ancient OpenSSL. This silently disables functionality that LibreSSL actually supports (ALPN, modern init, modern EVP API) and runs unnecessary legacy code (the CRYPTO_set_locking_callback threading ceremony, which is a no-op in LibreSSL).
LibreSSL's actual API compatibility
LibreSSL's API is a hybrid — mostly OpenSSL 1.1.x compatible, with a few 0.9.x/1.0.x holdovers. It does not fit cleanly into any of the three existing ifdef paths (openssl_0.9.0, openssl_1.1.x, openssl_3.0.x).
Functions LibreSSL has (1.1.x compatible)
| Function |
Status |
TLS_method() |
Available (since LibreSSL 2.2.2) |
OPENSSL_init_ssl() |
Available (with pthread_once semantics) |
EVP_MD_CTX_new() / EVP_MD_CTX_free() |
Available (old names also still work) |
SSL_CTX_set_alpn_select_cb() |
Available |
SSL_CTX_set_alpn_protos() |
Available |
SSL_get0_alpn_selected() |
Available |
Functions LibreSSL does NOT have
| Function |
LibreSSL alternative |
Notes |
OPENSSL_sk_pop() / OPENSSL_sk_free() |
sk_pop() / sk_free() |
Old naming convention only |
SSL_get1_peer_certificate() |
SSL_get_peer_certificate() |
3.0.x rename not adopted |
SSL_has_pending() |
Not available |
SSL_pending() exists but SSL_has_pending() (added in OpenSSL 1.1.0) is not |
SSL_CTX_set_options() as function |
Macro calling SSL_CTX_ctrl() |
OpenSSL 1.1.x made this a real function; LibreSSL kept the macro |
Threading
CRYPTO_set_locking_callback() exists in LibreSSL for API compatibility but is a no-op. LibreSSL handles threading internally using pthread mutexes. The current 0.9.0 code path calls @ponyint_ssl_multithreading and @CRYPTO_set_locking_callback unnecessarily.
Impact of the current misidentification
Because LibreSSL is forced through the openssl_0.9.0 path:
- ALPN is disabled — the 0.9.0 path returns
false from alpn_set_resolver() and alpn_set_client_protocols(), even though LibreSSL fully supports ALPN.
- Legacy init ceremony runs —
SSL_library_init(), SSL_load_error_strings(), CRYPTO_num_locks(), @ponyint_ssl_multithreading(), and CRYPTO_set_locking_callback() all run. LibreSSL doesn't need any of this; OPENSSL_init_ssl() works fine.
- Legacy API names used —
SSLv23_method(), EVP_MD_CTX_create()/EVP_MD_CTX_destroy(), SSL_CTX_ctrl() for options. LibreSSL supports the modern equivalents.
SSL_has_pending() not called — this one is actually correct since LibreSSL doesn't have it, but for the wrong reason (the 0.9.0 path skips it because 0.9.0 doesn't have it, not because LibreSSL doesn't).
Proposed fix
Add a new define (-Dlibressl or similar) that gives LibreSSL its own code path. This path should be based on the openssl_1.1.x path with targeted overrides for the four differences:
- Use
sk_pop()/sk_free() instead of OPENSSL_sk_pop()/OPENSSL_sk_free()
- Use
SSL_get_peer_certificate() instead of SSL_get1_peer_certificate()
- Handle
SSL_CTX_set_options() being a macro (this may already work transparently)
- Skip
SSL_has_pending() (use the SSL_pending() fallback)
Once LibreSSL has its own path, the actual OpenSSL 0.9.0 code path can be removed — OpenSSL 0.9.x has been EOL since 2015.
Downstream projects that have Makefiles with ssl= options (e.g., lori) will need to add the new define as a build option so users can build against LibreSSL properly.
LibreSSL users currently build with
-Dopenssl_0.9.0, which forces LibreSSL through a code path designed for ancient OpenSSL. This silently disables functionality that LibreSSL actually supports (ALPN, modern init, modern EVP API) and runs unnecessary legacy code (theCRYPTO_set_locking_callbackthreading ceremony, which is a no-op in LibreSSL).LibreSSL's actual API compatibility
LibreSSL's API is a hybrid — mostly OpenSSL 1.1.x compatible, with a few 0.9.x/1.0.x holdovers. It does not fit cleanly into any of the three existing ifdef paths (
openssl_0.9.0,openssl_1.1.x,openssl_3.0.x).Functions LibreSSL has (1.1.x compatible)
TLS_method()OPENSSL_init_ssl()pthread_oncesemantics)EVP_MD_CTX_new()/EVP_MD_CTX_free()SSL_CTX_set_alpn_select_cb()SSL_CTX_set_alpn_protos()SSL_get0_alpn_selected()Functions LibreSSL does NOT have
OPENSSL_sk_pop()/OPENSSL_sk_free()sk_pop()/sk_free()SSL_get1_peer_certificate()SSL_get_peer_certificate()SSL_has_pending()SSL_pending()exists butSSL_has_pending()(added in OpenSSL 1.1.0) is notSSL_CTX_set_options()as functionSSL_CTX_ctrl()Threading
CRYPTO_set_locking_callback()exists in LibreSSL for API compatibility but is a no-op. LibreSSL handles threading internally usingpthreadmutexes. The current 0.9.0 code path calls@ponyint_ssl_multithreadingand@CRYPTO_set_locking_callbackunnecessarily.Impact of the current misidentification
Because LibreSSL is forced through the
openssl_0.9.0path:falsefromalpn_set_resolver()andalpn_set_client_protocols(), even though LibreSSL fully supports ALPN.SSL_library_init(),SSL_load_error_strings(),CRYPTO_num_locks(),@ponyint_ssl_multithreading(), andCRYPTO_set_locking_callback()all run. LibreSSL doesn't need any of this;OPENSSL_init_ssl()works fine.SSLv23_method(),EVP_MD_CTX_create()/EVP_MD_CTX_destroy(),SSL_CTX_ctrl()for options. LibreSSL supports the modern equivalents.SSL_has_pending()not called — this one is actually correct since LibreSSL doesn't have it, but for the wrong reason (the 0.9.0 path skips it because 0.9.0 doesn't have it, not because LibreSSL doesn't).Proposed fix
Add a new define (
-Dlibresslor similar) that gives LibreSSL its own code path. This path should be based on theopenssl_1.1.xpath with targeted overrides for the four differences:sk_pop()/sk_free()instead ofOPENSSL_sk_pop()/OPENSSL_sk_free()SSL_get_peer_certificate()instead ofSSL_get1_peer_certificate()SSL_CTX_set_options()being a macro (this may already work transparently)SSL_has_pending()(use theSSL_pending()fallback)Once LibreSSL has its own path, the actual OpenSSL 0.9.0 code path can be removed — OpenSSL 0.9.x has been EOL since 2015.
Downstream projects that have Makefiles with
ssl=options (e.g., lori) will need to add the new define as a build option so users can build against LibreSSL properly.