Skip to content

Commit

Permalink
openssl, net: remove openssl pre-1.1.1 and libressl support
Browse files Browse the repository at this point in the history
OpenSSL older than 3.0 has reached EoL and should no longer be used in
production. However, since version 1.1.1 is still widely used in LTS
distributions such as RHEL 8/Ubuntu 20.04, support for 1.1.1 is still
retained.

LibreSSL support is also removed from the wrapper, as it has not been
tested and likely no longer work with the latest versions of LibreSSL.

This commit also removes various helper procedures that bridges 1.1 APIs
and 1.0/0.9 APIs as well as "hacks" within the wrapper.

Coming with this, support for `protTLSv1` in `net` has been removed, as
this TLS version is very old and is no longer safe. Direct usage of it
has already been deprecated within OpenSSL.
  • Loading branch information
alaviss committed Aug 28, 2024
1 parent 17f96a1 commit b9051ec
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 195 deletions.
23 changes: 4 additions & 19 deletions lib/pure/net.nim
Original file line number Diff line number Diff line change
Expand Up @@ -518,10 +518,6 @@ proc fromSockAddr*(sa: Sockaddr_storage | SockAddr | Sockaddr_in | Sockaddr_in6,

when defineSsl:
CRYPTO_malloc_init()
doAssert SslLibraryInit() == 1
SSL_load_error_strings()
ERR_load_BIO_strings()
OpenSSL_add_all_algorithms()

proc sslHandle*(self: Socket): SslPtr =
## Retrieve the ssl pointer of `socket`.
Expand Down Expand Up @@ -625,29 +621,18 @@ when defineSsl:
var newCTX: SslCtx
case protVersion
of protSSLv23:
newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support.
newCTX = SSL_CTX_new(TLS_method()) # SSLv3, TLS 1.0 and above
of protSSLv2:
raiseSSLError("SSLv2 is no longer secure and has been deprecated, use protSSLv23")
of protSSLv3:
raiseSSLError("SSLv3 is no longer secure and has been deprecated, use protSSLv23")
of protTLSv1:
newCTX = SSL_CTX_new(TLSv1_method())
raiseSSLError("TLSv1 is no longer secure and has been deprecated, use protSSLv23")

if newCTX.SSL_CTX_set_cipher_list(cipherList) != 1:
raiseSSLError()
when not defined(openssl10) and not defined(libressl):
let sslVersion = getOpenSSLVersion()
if sslVersion >= 0x010101000 and not sslVersion == 0x020000000:
# In OpenSSL >= 1.1.1, TLSv1.3 cipher suites can only be configured via
# this API.
if newCTX.SSL_CTX_set_ciphersuites(cipherList) != 1:
raiseSSLError()
# Automatically the best ECDH curve for client exchange. Without this, ECDH
# ciphers will be ignored by the server.
#
# From OpenSSL >= 1.1.0, this setting is set by default and can't be
# overriden.
if newCTX.SSL_CTX_set_ecdh_auto(1) != 1:
# TLSv1.3 cipher suites can only be configured via this API.
if newCTX.SSL_CTX_set_ciphersuites(cipherList) != 1:
raiseSSLError()

when defined(nimDisableCertificateValidation):
Expand Down
200 changes: 24 additions & 176 deletions lib/wrappers/openssl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@
## OpenSSL support
##
## When OpenSSL is dynamically linked, the wrapper provides partial forward and backward
## compatibility for OpenSSL versions above and below 1.1.0
## compatibility for OpenSSL versions 1.1.1 and above.
##
## OpenSSL can also be statically linked using `--dynlibOverride:ssl` for OpenSSL >= 1.1.0.
## If you want to statically link against OpenSSL 1.0.x, you now have to
## define the `openssl10` symbol via `-d:openssl10`.
## OpenSSL can also be statically linked using `--dynlibOverride:ssl` for OpenSSL >= 1.1.1.
##
## Build and test examples:
##
Expand Down Expand Up @@ -52,30 +50,18 @@ when sslVersion != "":
from std/posix import SocketHandle

elif useWinVersion:
when defined(openssl10) or defined(nimOldDlls):
when defined(cpu64):
const
DLLSSLName* = "(ssleay32|ssleay64).dll"
DLLUtilName* = "(libeay32|libeay64).dll"
else:
const
DLLSSLName* = "ssleay32.dll"
DLLUtilName* = "libeay32.dll"
elif defined(cpu64):
when defined(cpu64):
const
DLLSSLName* = "(libssl-3-x64|libssl-1_1-x64|ssleay64|libssl64).dll"
DLLUtilName* = "(libcrypto-3-x64|libcrypto-1_1-x64|libeay64).dll"
DLLSSLName* = "(libssl-3-x64|libssl-1_1-x64).dll"
DLLUtilName* = "(libcrypto-3-x64|libcrypto-1_1-x64).dll"
else:
const
DLLSSLName* = "(libssl-3|libssl-1_1|ssleay32|libssl32).dll"
DLLUtilName* = "(libssl-3|libcrypto-1_1|libeay32).dll"
DLLSSLName* = "(libssl-3|libssl-1_1).dll"
DLLUtilName* = "(libssl-3|libcrypto-1_1).dll"

from std/winlean import SocketHandle
else:
when defined(osx):
const versions = "(.3|.1.1|.38|.39|.41|.43|.44|.45|.46|.47|.48|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)"
else:
const versions = "(.3|.1.1|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|.48|.47|.46|.45|.44|.43|.41|.39|.38|.10|)"
const versions = "(.3|.1.1)"

when defined(macosx):
const
Expand Down Expand Up @@ -256,62 +242,12 @@ const
BIO_C_DO_STATE_MACHINE = 101
BIO_C_GET_SSL = 110

proc TLSv1_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.}

# TLS_method(), TLS_server_method(), TLS_client_method() are introduced in 1.1.0
# and support SSLv3, TLSv1, TLSv1.1 and TLSv1.2
# SSLv23_method(), SSLv23_server_method(), SSLv23_client_method() are removed in 1.1.0

when compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks):
# Static linking

when defined(openssl10):
proc SSL_library_init*(): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.}
proc SSL_load_error_strings*() {.cdecl, dynlib: DLLSSLName, importc.}
proc SSLv23_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
proc SSLeay(): culong {.cdecl, dynlib: DLLUtilName, importc.}

proc getOpenSSLVersion*(): culong =
SSLeay()
else:
proc OPENSSL_init_ssl*(opts: uint64, settings: uint8): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.}
proc SSL_library_init*(): cint {.discardable.} =
## Initialize SSL using OPENSSL_init_ssl for OpenSSL >= 1.1.0
return OPENSSL_init_ssl(0.uint64, 0.uint8)

proc TLS_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
proc SSLv23_method*(): PSSL_METHOD =
TLS_method()

proc OpenSSL_version_num(): culong {.cdecl, dynlib: DLLUtilName, importc.}

proc getOpenSSLVersion*(): culong =
## Return OpenSSL version as unsigned long
OpenSSL_version_num()

proc SSL_load_error_strings*() =
## Removed from OpenSSL 1.1.0
# This proc prevents breaking existing code calling SslLoadErrorStrings
# Static linking against OpenSSL < 1.1.0 is not supported
discard

when defined(libressl) or defined(openssl10):
proc SSL_state(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc.}
proc SSL_in_init*(ssl: SslPtr): cint {.inline.} =
SSl_state(ssl) and SSL_ST_INIT
when not defined(openssl111):
proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 {.cdecl, dynlib: DLLSSLName, importc: "SSL_get1_peer_certificate".}
else:
proc SSL_in_init*(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc.}
proc SSL_CTX_set_ciphersuites*(ctx: SslCtx, str: cstring): cint {.cdecl, dynlib: DLLSSLName, importc.}

template OpenSSL_add_all_algorithms*() = discard

proc SSLv23_client_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
proc SSLv2_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
proc SSLv3_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}

proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 {.cdecl, dynlib: DLLSSLName, importc: "SSL_get_peer_certificate".}
else:
# Here we're trying to stay compatible with openssl 1.0.* and 1.1.*. Some
# symbols are loaded dynamically and we don't use them if not found.
proc thisModule(): LibHandle {.inline.} =
var thisMod {.global.}: LibHandle
if thisMod.isNil: thisMod = loadLib()
Expand Down Expand Up @@ -352,90 +288,19 @@ else:
result = sslSymNullable(name, alternativeName)
if result.isNil: raiseInvalidLibrary(name)

proc utilSymNullable(name: string, alternativeName = ""): pointer =
utilModule().symNullable(name, alternativeName)

proc loadPSSLMethod(method1, method2: string): PSSL_METHOD =
## Load <method1> from OpenSSL if available, otherwise <method2>
##
let methodSym = sslSymNullable(method1, method2)
if methodSym.isNil:
raise newException(LibraryError, "Could not load " & method1 & " nor " & method2)

let method2Proc = cast[proc(): PSSL_METHOD {.cdecl, gcsafe.}](methodSym)
return method2Proc()

proc SSL_library_init*(): cint {.discardable.} =
## Initialize SSL using OPENSSL_init_ssl for OpenSSL >= 1.1.0 otherwise
## SSL_library_init
let newInitSym = sslSymNullable("OPENSSL_init_ssl")
if not newInitSym.isNil:
let newInitProc =
cast[proc(opts: uint64, settings: uint8): cint {.cdecl.}](newInitSym)
return newInitProc(0, 0)
let olderProc = cast[proc(): cint {.cdecl.}](sslSymThrows("SSL_library_init"))
if not olderProc.isNil: result = olderProc()

proc SSL_load_error_strings*() =
# TODO: Are we ignoring this on purpose? SSL GitHub CI fails otherwise.
let theProc = cast[proc() {.cdecl.}](sslSymNullable("SSL_load_error_strings"))
if not theProc.isNil: theProc()

proc SSLv23_client_method*(): PSSL_METHOD =
loadPSSLMethod("SSLv23_client_method", "TLS_client_method")

proc SSLv23_method*(): PSSL_METHOD =
loadPSSLMethod("SSLv23_method", "TLS_method")

proc SSLv2_method*(): PSSL_METHOD =
loadPSSLMethod("SSLv2_method", "TLS_method")

proc SSLv3_method*(): PSSL_METHOD =
loadPSSLMethod("SSLv3_method", "TLS_method")

proc TLS_method*(): PSSL_METHOD =
loadPSSLMethod("TLS_method", "SSLv23_method")

proc TLS_client_method*(): PSSL_METHOD =
loadPSSLMethod("TLS_client_method", "SSLv23_client_method")

proc TLS_server_method*(): PSSL_METHOD =
loadPSSLMethod("TLS_server_method", "SSLv23_server_method")

proc OpenSSL_add_all_algorithms*() =
# TODO: Are we ignoring this on purpose? SSL GitHub CI fails otherwise.
let theProc = cast[proc() {.cdecl.}](sslSymNullable("OPENSSL_add_all_algorithms_conf"))
if not theProc.isNil: theProc()

proc getOpenSSLVersion*(): culong =
## Return OpenSSL version as unsigned long or 0 if not available
let theProc = cast[proc(): culong {.cdecl, gcsafe.}](utilSymNullable("OpenSSL_version_num", "SSLeay"))
result =
if theProc.isNil: 0.culong
else: theProc()

proc SSL_in_init*(ssl: SslPtr): cint =
# A compatibility wrapper for `SSL_in_init()` for OpenSSL 1.0, 1.1 and LibreSSL
const MainProc = "SSL_in_init"
let
theProc {.global.} = cast[proc(ssl: SslPtr): cint {.cdecl, gcsafe.}](sslSymNullable(MainProc))
# Fallback
sslState {.global.} = cast[proc(ssl: SslPtr): cint {.cdecl, gcsafe.}](sslSymNullable("SSL_state"))

if not theProc.isNil:
result = theProc(ssl)
elif not sslState.isNil:
result = sslState(ssl) and SSL_ST_INIT
else:
raiseInvalidLibrary MainProc

proc SSL_CTX_set_ciphersuites*(ctx: SslCtx, str: cstring): cint =
var theProc {.global.}: proc(ctx: SslCtx, str: cstring) {.cdecl, gcsafe.}
if theProc.isNil:
theProc = cast[typeof(theProc)](sslSymThrows("SSL_CTX_set_ciphersuites"))
theProc(ctx, str)

proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.}
when not defined(nimDisableCertificateValidation):
proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 {.gcsafe, tags: [].} =
{.cast(tags: []), cast(gcsafe).}:
let thisProc {.global.} = cast[proc (ssl: SslCtx): PX509 {.cdecl.}](
sslSymThrows("SSL_get1_peer_certificate", "SSL_get_peer_certificate")
)
if not thisProc.isNil: result = thisProc(ssl)

proc TLS_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
proc OpenSSL_version_num*(): culong {.cdecl, dynlib: DLLUtilName, importc.}

proc SSL_in_init*(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc.}
proc SSL_CTX_set_ciphersuites*(ctx: SslCtx, str: cstring): cint {.cdecl, dynlib: DLLSSLName, importc.}

proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.}
proc SSL_free*(ssl: SslPtr){.cdecl, dynlib: DLLSSLName, importc.}
Expand Down Expand Up @@ -613,15 +478,6 @@ proc SSL_CTX_use_psk_identity_hint*(ctx: SslCtx; hint: cstring): cint {.cdecl, d
proc SSL_get_psk_identity*(ssl: SslPtr): cstring {.cdecl, dynlib: DLLSSLName, importc.}
## Get PSK identity.

proc SSL_CTX_set_ecdh_auto*(ctx: SslCtx, onoff: cint): cint {.inline.} =
## Set automatic curve selection.
##
## On OpenSSL >= 1.1.0 this is on by default and cannot be disabled.
if getOpenSSLVersion() < 0x010100000 or getOpenSSLVersion() == 0x020000000:
result = cint SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, nil)
else:
result = 1

proc bioNew*(b: PBIO_METHOD): BIO{.cdecl, dynlib: DLLUtilName, importc: "BIO_new".}
proc bioFreeAll*(b: BIO){.cdecl, dynlib: DLLUtilName, importc: "BIO_free_all".}
proc bioSMem*(): PBIO_METHOD{.cdecl, dynlib: DLLUtilName, importc: "BIO_s_mem".}
Expand Down Expand Up @@ -797,14 +653,6 @@ when defined(nimHasStyleChecks):
# Certificate validation
# On old openSSL version some of these symbols are not available
when not defined(nimDisableCertificateValidation):

proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 {.gcsafe, tags: [].} =
{.cast(tags: []), cast(gcsafe).}:
let thisProc {.global.} = cast[proc (ssl: SslCtx): PX509 {.cdecl.}](
sslSymThrows("SSL_get1_peer_certificate", "SSL_get_peer_certificate")
)
if not thisProc.isNil: result = thisProc(ssl)

proc X509_get_subject_name*(a: PX509): PX509_NAME{.cdecl, dynlib: DLLSSLName, importc.}

proc X509_get_issuer_name*(a: PX509): PX509_NAME{.cdecl, dynlib: DLLUtilName, importc.}
Expand Down

0 comments on commit b9051ec

Please sign in to comment.