mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-03 10:24:44 +00:00
openssl 3 support (1.6) (#20669)
This commit is contained in:
@@ -544,18 +544,20 @@ proc fromSockAddr*(sa: Sockaddr_storage | SockAddr | Sockaddr_in | Sockaddr_in6,
|
||||
fromSockAddrAux(cast[ptr Sockaddr_storage](unsafeAddr sa), sl, address, port)
|
||||
|
||||
when defineSsl:
|
||||
CRYPTO_malloc_init()
|
||||
doAssert SslLibraryInit() == 1
|
||||
SSL_load_error_strings()
|
||||
ERR_load_BIO_strings()
|
||||
OpenSSL_add_all_algorithms()
|
||||
# OpenSSL >= 1.1.0 does not need explicit init.
|
||||
when not useOpenssl3:
|
||||
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`.
|
||||
## Useful for interfacing with `openssl`.
|
||||
self.sslHandle
|
||||
|
||||
proc raiseSSLError*(s = "") =
|
||||
proc raiseSSLError*(s = "") {.raises: [SslError].}=
|
||||
## Raises a new SSL error.
|
||||
if s != "":
|
||||
raise newException(SslError, s)
|
||||
@@ -620,9 +622,8 @@ when defineSsl:
|
||||
caDir = "", caFile = ""): SslContext =
|
||||
## Creates an SSL context.
|
||||
##
|
||||
## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1
|
||||
## are available with the addition of `protSSLv23` which allows for
|
||||
## compatibility with all of them.
|
||||
## Protocol version is currently ignored by default and TLS is used.
|
||||
## With `-d:openssl10`, only SSLv23 and TLSv1 may be used.
|
||||
##
|
||||
## There are three options for verify mode:
|
||||
## `CVerifyNone`: certificates are not verified;
|
||||
@@ -649,16 +650,24 @@ when defineSsl:
|
||||
## or using ECDSA:
|
||||
## - `openssl ecparam -out mykey.pem -name secp256k1 -genkey`
|
||||
## - `openssl req -new -key mykey.pem -x509 -nodes -days 365 -out mycert.pem`
|
||||
var newCTX: SslCtx
|
||||
case protVersion
|
||||
of protSSLv23:
|
||||
newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support.
|
||||
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())
|
||||
var mtd: PSSL_METHOD
|
||||
when defined(openssl10):
|
||||
case protVersion
|
||||
of protSSLv23:
|
||||
mtd = SSLv23_method()
|
||||
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:
|
||||
mtd = TLSv1_method()
|
||||
else:
|
||||
mtd = TLS_method()
|
||||
if mtd == nil:
|
||||
raiseSSLError("Failed to create TLS context")
|
||||
var newCTX = SSL_CTX_new(mtd)
|
||||
if newCTX == nil:
|
||||
raiseSSLError("Failed to create TLS context")
|
||||
|
||||
if newCTX.SSL_CTX_set_cipher_list(cipherList) != 1:
|
||||
raiseSSLError()
|
||||
@@ -812,24 +821,28 @@ when defineSsl:
|
||||
if SSL_set_fd(socket.sslHandle, socket.fd) != 1:
|
||||
raiseSSLError()
|
||||
|
||||
proc checkCertName(socket: Socket, hostname: string) =
|
||||
proc checkCertName(socket: Socket, hostname: string) {.raises: [SslError], tags:[RootEffect].} =
|
||||
## Check if the certificate Subject Alternative Name (SAN) or Subject CommonName (CN) matches hostname.
|
||||
## Wildcards match only in the left-most label.
|
||||
## When name starts with a dot it will be matched by a certificate valid for any subdomain
|
||||
when not defined(nimDisableCertificateValidation) and not defined(windows):
|
||||
assert socket.isSsl
|
||||
let certificate = socket.sslHandle.SSL_get_peer_certificate()
|
||||
if certificate.isNil:
|
||||
raiseSSLError("No SSL certificate found.")
|
||||
try:
|
||||
let certificate = socket.sslHandle.SSL_get_peer_certificate()
|
||||
if certificate.isNil:
|
||||
raiseSSLError("No SSL certificate found.")
|
||||
|
||||
const X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT = 0x1.cuint
|
||||
# https://www.openssl.org/docs/man1.1.1/man3/X509_check_host.html
|
||||
let match = certificate.X509_check_host(hostname.cstring, hostname.len.cint,
|
||||
X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, nil)
|
||||
# https://www.openssl.org/docs/man1.1.1/man3/SSL_get_peer_certificate.html
|
||||
X509_free(certificate)
|
||||
if match != 1:
|
||||
raiseSSLError("SSL Certificate check failed.")
|
||||
const X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT = 0x1.cuint
|
||||
# https://www.openssl.org/docs/man1.1.1/man3/X509_check_host.html
|
||||
let match = certificate.X509_check_host(hostname.cstring, hostname.len.cint,
|
||||
X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, nil)
|
||||
# https://www.openssl.org/docs/man1.1.1/man3/SSL_get_peer_certificate.html
|
||||
X509_free(certificate)
|
||||
if match != 1:
|
||||
raiseSSLError("SSL Certificate check failed.")
|
||||
|
||||
except LibraryError:
|
||||
raiseSSLError("SSL import failed")
|
||||
|
||||
proc wrapConnectedSocket*(ctx: SslContext, socket: Socket,
|
||||
handshake: SslHandshakeType,
|
||||
@@ -856,6 +869,7 @@ when defineSsl:
|
||||
let ret = SSL_connect(socket.sslHandle)
|
||||
socketError(socket, ret)
|
||||
when not defined(nimDisableCertificateValidation) and not defined(windows):
|
||||
# FIXME: this should be skipped on CVerifyNone
|
||||
if hostname.len > 0 and not isIpAddress(hostname):
|
||||
socket.checkCertName(hostname)
|
||||
of handshakeAsServer:
|
||||
@@ -1311,7 +1325,7 @@ when defined(nimdoc) or (defined(posix) and not useNimNetLite):
|
||||
(sizeof(socketAddr.sun_family) + path.len).SockLen) != 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
proc gotHandshake*(socket: Socket): bool =
|
||||
## Determines whether a handshake has occurred between a client (`socket`)
|
||||
## and the server that `socket` is connected to.
|
||||
@@ -2044,7 +2058,7 @@ proc dial*(address: string, port: Port,
|
||||
raise newException(IOError, "Couldn't resolve address: " & address)
|
||||
|
||||
proc connect*(socket: Socket, address: string,
|
||||
port = Port(0)) {.tags: [ReadIOEffect].} =
|
||||
port = Port(0)) {.tags: [ReadIOEffect, RootEffect].} =
|
||||
## Connects socket to `address`:`port`. `Address` can be an IP address or a
|
||||
## host name. If `address` is a host name, this function will try each IP
|
||||
## of that host name. `htons` is already performed on `port` so you must
|
||||
@@ -2119,7 +2133,7 @@ proc connectAsync(socket: Socket, name: string, port = Port(0),
|
||||
if not success: raiseOSError(lastError)
|
||||
|
||||
proc connect*(socket: Socket, address: string, port = Port(0),
|
||||
timeout: int) {.tags: [ReadIOEffect, WriteIOEffect].} =
|
||||
timeout: int) {.tags: [ReadIOEffect, WriteIOEffect, RootEffect].} =
|
||||
## Connects to server as specified by `address` on port specified by `port`.
|
||||
##
|
||||
## The `timeout` parameter specifies the time in milliseconds to allow for
|
||||
|
||||
@@ -7,33 +7,41 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## OpenSSL support
|
||||
## OpenSSL wrapper. Supports OpenSSL >= 1.1.0 dynamically (as default) or statically linked
|
||||
## using `--dynlibOverride:ssl`.
|
||||
##
|
||||
## When OpenSSL is dynamically linked, the wrapper provides partial forward and backward
|
||||
## compatibility for OpenSSL versions above and below 1.1.0
|
||||
##
|
||||
## 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`.
|
||||
## `-d:sslVersion=1.2.3` can be used to force an SSL version.
|
||||
## This version must be included in the library name.
|
||||
## `-d:useOpenssl3` may be set for OpenSSL 3 instead.
|
||||
##
|
||||
## There is also limited support for OpenSSL 1.0.x which may require `-d:openssl10`.
|
||||
##
|
||||
## Build and test examples:
|
||||
##
|
||||
## .. code-block::
|
||||
## ./bin/nim c -d:ssl -p:. -r tests/stdlib/tssl.nim
|
||||
## ./bin/nim c -d:ssl --threads:on -p:. -r tests/stdlib/thttpclient_ssl.nim
|
||||
## ./bin/nim c -d:ssl -p:. -r tests/untestable/tssl.nim
|
||||
## ./bin/nim c -d:ssl -p:. --dynlibOverride:ssl --passl:-lcrypto --passl:-lssl -r tests/untestable/tssl.nim
|
||||
## ./bin/nim r --putenv:NIM_TESTAMENT_REMOTE_NETWORKING:1 -d:ssl -p:testament/lib --threads:on tests/untestable/thttpclient_ssl_remotenetwork.nim
|
||||
|
||||
# https://www.feistyduck.com/library/openssl-cookbook/online/ch-testing-with-openssl.html
|
||||
#
|
||||
from strutils import startsWith
|
||||
|
||||
when defined(nimHasStyleChecks):
|
||||
{.push styleChecks: off.}
|
||||
|
||||
const useWinVersion = defined(windows) or defined(nimdoc)
|
||||
|
||||
# To force openSSL version use -d:sslVersion=1.0.0
|
||||
# To force openSSL version use -d:sslVersion=1.2.3
|
||||
# See: #10281, #10230
|
||||
# General issue:
|
||||
# Other dynamic libraries (like libpg) load different openSSL version then what nim loads.
|
||||
# Having two different openSSL loaded version causes a crash.
|
||||
# Use this compile time define to force the openSSL version that your other dynamic libraries want.
|
||||
const sslVersion {.strdefine.}: string = ""
|
||||
const useOpenssl3* {.booldefine.} = sslVersion.startsWith('3')
|
||||
when sslVersion != "":
|
||||
when defined(macosx):
|
||||
const
|
||||
@@ -72,10 +80,11 @@ elif useWinVersion:
|
||||
|
||||
from winlean import SocketHandle
|
||||
else:
|
||||
# same list of versions but ordered differently?
|
||||
when defined(osx):
|
||||
const versions = "(.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|)"
|
||||
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 = "(.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|.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|)"
|
||||
|
||||
when defined(macosx):
|
||||
const
|
||||
@@ -266,7 +275,9 @@ proc TLSv1_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.}
|
||||
# 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):
|
||||
const useStaticLink = compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks)
|
||||
|
||||
when useStaticLink:
|
||||
# Static linking
|
||||
|
||||
when defined(openssl10):
|
||||
@@ -277,6 +288,8 @@ when compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks):
|
||||
|
||||
proc getOpenSSLVersion*(): culong =
|
||||
SSLeay()
|
||||
|
||||
proc ERR_load_BIO_strings*() {.cdecl, dynlib: DLLUtilName, importc.}
|
||||
else:
|
||||
proc OPENSSL_init_ssl*(opts: uint64, settings: uint8): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.}
|
||||
proc SSL_library_init*(): cint {.discardable.} =
|
||||
@@ -299,10 +312,13 @@ when compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks):
|
||||
# Static linking against OpenSSL < 1.1.0 is not supported
|
||||
discard
|
||||
|
||||
proc ERR_load_BIO_strings*() =
|
||||
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
|
||||
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.}
|
||||
@@ -312,9 +328,10 @@ when compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks):
|
||||
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 CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, dynlib: DLLUtilName, importc.}
|
||||
|
||||
else:
|
||||
# Here we're trying to stay compatible with openssl 1.0.* and 1.1.*. Some
|
||||
# Here we're trying to stay compatible between openssl versions. Some
|
||||
# symbols are loaded dynamically and we don't use them if not found.
|
||||
proc thisModule(): LibHandle {.inline.} =
|
||||
var thisMod {.global.}: LibHandle
|
||||
@@ -322,9 +339,12 @@ else:
|
||||
|
||||
result = thisMod
|
||||
|
||||
proc sslModule(): LibHandle {.inline.} =
|
||||
proc sslModule(): LibHandle {.inline, raises: [LibraryError], tags:[RootEffect].} =
|
||||
var sslMod {.global.}: LibHandle
|
||||
if sslMod.isNil: sslMod = loadLibPattern(DLLSSLName)
|
||||
try:
|
||||
if sslMod.isNil: sslMod = loadLibPattern(DLLSSLName)
|
||||
except:
|
||||
raise newException(LibraryError, "Could not load SSL using " & DLLSSLName)
|
||||
|
||||
result = sslMod
|
||||
|
||||
@@ -349,26 +369,30 @@ else:
|
||||
if result.isNil and alternativeName.len > 0:
|
||||
result = symAddr(thisDynlib, alternativeName)
|
||||
|
||||
proc sslSymNullable(name: string, alternativeName = ""): pointer =
|
||||
proc sslSymNullable(name: string, alternativeName = ""): pointer {.raises: [LibraryError], tags:[RootEffect].} =
|
||||
sslModule().symNullable(name, alternativeName)
|
||||
|
||||
proc sslSymThrows(name: string, alternativeName = ""): pointer =
|
||||
proc sslSymThrows(name: string, alternativeName = ""): pointer {.raises: [LibraryError].} =
|
||||
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 =
|
||||
proc loadPSSLMethod(method1, method2: string): PSSL_METHOD {.raises: [LibraryError], tags:[RootEffect].} =
|
||||
## 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)
|
||||
let method2Proc = cast[proc(): PSSL_METHOD {.cdecl, gcsafe, raises: [].}](methodSym)
|
||||
return method2Proc()
|
||||
|
||||
proc CRYPTO_set_mem_functions(a,b,c: pointer) =
|
||||
let theProc = cast[proc(a,b,c: pointer) {.cdecl.}](utilModule().symNullable("CRYPTO_set_mem_functions"))
|
||||
if not theProc.isNil: theProc(a, b, c)
|
||||
|
||||
proc SSL_library_init*(): cint {.discardable.} =
|
||||
## Initialize SSL using OPENSSL_init_ssl for OpenSSL >= 1.1.0 otherwise
|
||||
## SSL_library_init
|
||||
@@ -385,6 +409,10 @@ else:
|
||||
let theProc = cast[proc() {.cdecl.}](sslSymNullable("SSL_load_error_strings"))
|
||||
if not theProc.isNil: theProc()
|
||||
|
||||
proc ERR_load_BIO_strings*() =
|
||||
let theProc = cast[proc() {.cdecl.}](utilModule().symNullable("ERR_load_BIO_strings"))
|
||||
if not theProc.isNil: theProc()
|
||||
|
||||
proc SSLv23_client_method*(): PSSL_METHOD =
|
||||
loadPSSLMethod("SSLv23_client_method", "TLS_client_method")
|
||||
|
||||
@@ -439,8 +467,6 @@ else:
|
||||
theProc = cast[typeof(theProc)](sslSymThrows("SSL_CTX_set_ciphersuites"))
|
||||
theProc(ctx, str)
|
||||
|
||||
proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.}
|
||||
|
||||
proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.}
|
||||
proc SSL_free*(ssl: SslPtr){.cdecl, dynlib: DLLSSLName, importc.}
|
||||
proc SSL_get_SSL_CTX*(ssl: SslPtr): SslCtx {.cdecl, dynlib: DLLSSLName, importc.}
|
||||
@@ -544,10 +570,10 @@ proc i2d_X509*(cert: PX509): string =
|
||||
if length.int <= 0:
|
||||
raise newException(Exception, "X.509 certificate encoding failed")
|
||||
|
||||
when not useWinVersion and not defined(macosx) and not defined(android) and not defined(nimNoAllocForSSL):
|
||||
proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl,
|
||||
dynlib: DLLUtilName, importc.}
|
||||
const
|
||||
useNimsAlloc = not defined(nimNoAllocForSSL) and not defined(gcDestructors)
|
||||
|
||||
when not useWinVersion and not defined(macosx) and not defined(android) and useNimsAlloc:
|
||||
proc allocWrapper(size: int): pointer {.cdecl.} = allocShared(size)
|
||||
proc reallocWrapper(p: pointer; newSize: int): pointer {.cdecl.} =
|
||||
if p == nil:
|
||||
@@ -557,9 +583,11 @@ when not useWinVersion and not defined(macosx) and not defined(android) and not
|
||||
proc deallocWrapper(p: pointer) {.cdecl.} =
|
||||
if p != nil: deallocShared(p)
|
||||
|
||||
proc CRYPTO_malloc_init*() =
|
||||
when not useWinVersion and not defined(macosx) and not defined(android) and not defined(nimNoAllocForSSL):
|
||||
proc CRYPTO_malloc_init*() =
|
||||
CRYPTO_set_mem_functions(allocWrapper, reallocWrapper, deallocWrapper)
|
||||
else:
|
||||
proc CRYPTO_malloc_init*() =
|
||||
discard
|
||||
|
||||
proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cint, larg: clong, parg: pointer): clong{.
|
||||
cdecl, dynlib: DLLSSLName, importc.}
|
||||
@@ -802,8 +830,21 @@ when defined(nimHasStyleChecks):
|
||||
# On old openSSL version some of these symbols are not available
|
||||
when not defined(nimDisableCertificateValidation) and not defined(windows):
|
||||
|
||||
proc SSL_get_peer_certificate*(ssl: SslCtx): PX509{.cdecl, dynlib: DLLSSLName,
|
||||
importc.}
|
||||
# SSL_get_peer_certificate removed in 3.0
|
||||
# SSL_get1_peer_certificate added in 3.0
|
||||
when useOpenssl3:
|
||||
proc SSL_get1_peer_certificate*(ssl: SslCtx): PX509 {.cdecl, dynlib: DLLSSLName, importc.}
|
||||
proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 =
|
||||
SSL_get1_peer_certificate(ssl)
|
||||
elif useStaticLink:
|
||||
proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 {.cdecl, dynlib: DLLSSLName, importc.}
|
||||
else:
|
||||
proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 =
|
||||
let methodSym = sslSymNullable("SSL_get_peer_certificate", "SSL_get1_peer_certificate")
|
||||
if methodSym.isNil:
|
||||
raise newException(LibraryError, "Could not load SSL_get_peer_certificate or SSL_get1_peer_certificate")
|
||||
let method2Proc = cast[proc(ssl: SslCtx): PX509 {.cdecl, gcsafe, raises: [].}](methodSym)
|
||||
return method2Proc(ssl)
|
||||
|
||||
proc X509_get_subject_name*(a: PX509): PX509_NAME{.cdecl, dynlib: DLLSSLName, importc.}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user