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 b27b29f
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 227 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
220 changes: 12 additions & 208 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 All @@ -87,8 +73,6 @@ else:
DLLUtilName* = "libcrypto.so" & versions
from std/posix import SocketHandle

import std/dynlib

type
SslStruct {.final, pure.} = object
SslPtr* = ptr SslStruct
Expand Down Expand Up @@ -256,186 +240,15 @@ 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
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.}
proc TLS_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
proc OpenSSL_version_num*(): culong {.cdecl, dynlib: DLLUtilName, 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.}

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()

result = thisMod

proc sslModule(): LibHandle {.inline.} =
var sslMod {.global.}: LibHandle
if sslMod.isNil: sslMod = loadLibPattern(DLLSSLName)

result = sslMod

proc utilModule(): LibHandle {.inline.} =
var utilMod {.global.}: LibHandle
if utilMod.isNil: utilMod = loadLibPattern(DLLUtilName)

result = utilMod

proc symNullable(dll: LibHandle, name: string, alternativeName = ""): pointer =
# Load from DLL.
if not dll.isNil:
result = symAddr(dll, name)
if result.isNil and alternativeName.len > 0:
result = symAddr(dll, alternativeName)

# Attempt to load from current exe.
if result.isNil:
let thisDynlib = thisModule()
if thisDynlib.isNil: return nil
result = symAddr(thisDynlib, name)
if result.isNil and alternativeName.len > 0:
result = symAddr(thisDynlib, alternativeName)

proc sslSymNullable(name: string, alternativeName = ""): pointer =
sslModule().symNullable(name, alternativeName)

proc sslSymThrows(name: string, alternativeName = ""): pointer =
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.}
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 +426,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

0 comments on commit b27b29f

Please sign in to comment.