summary refs log tree commit diff stats
path: root/lib/wrappers/openssl.nim
diff options
context:
space:
mode:
authorFederico Ceratto <federico.ceratto@gmail.com>2017-12-13 19:07:57 +0000
committerFederico Ceratto <federico.ceratto@gmail.com>2017-12-28 21:57:43 +0000
commita78d7a31f780c6cf1e421f820d9ed19a5db64ca7 (patch)
treec978cd9fa5ce370c29cf59ee74d606999eea259c /lib/wrappers/openssl.nim
parent2b3ec0a7c66d2246371ed51348aaa87d4c3cf0f9 (diff)
downloadNim-a78d7a31f780c6cf1e421f820d9ed19a5db64ca7.tar.gz
Add OpenSSL 1.1.0 support #5000
Add a simple online test
Diffstat (limited to 'lib/wrappers/openssl.nim')
-rw-r--r--lib/wrappers/openssl.nim95
1 files changed, 75 insertions, 20 deletions
diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim
index 55b0bc3f8..1d0dc5897 100644
--- a/lib/wrappers/openssl.nim
+++ b/lib/wrappers/openssl.nim
@@ -8,6 +8,17 @@
 #
 
 ## OpenSSL support
+##
+## When OpenSSL is dynamically linked, the wrapper provides partial forward and backward
+## compatibility for OpenSSL versions above and below 1.1.0
+##
+## OpenSSL can be also statically linked using dynlibOverride:ssl for OpenSSL >= 1.1.0
+##
+## Build and test examples:
+##
+## .. code-block::
+##   ./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
 
 {.deadCodeElim: on.}
 
@@ -25,8 +36,8 @@ when useWinVersion:
 
   from winlean import SocketHandle
 else:
-  const
-    versions = "(|.38|.39|.41|.43|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8)"
+  const versions = "(.1.1|.38|.39|.41|.43|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)"
+
   when defined(macosx):
     const
       DLLSSLName = "libssl" & versions & ".dylib"
@@ -140,6 +151,7 @@ const
   SSL_OP_NO_SSLv2* = 0x01000000
   SSL_OP_NO_SSLv3* = 0x02000000
   SSL_OP_NO_TLSv1* = 0x04000000
+  SSL_OP_NO_TLSv1_1* = 0x08000000
   SSL_OP_ALL* = 0x000FFFFF
   SSL_VERIFY_NONE* = 0x00000000
   SSL_VERIFY_PEER* = 0x00000001
@@ -191,16 +203,39 @@ const
 
 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"):
-  proc SSL_library_init*(): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.}
-  proc SSL_load_error_strings*() {.cdecl, dynlib: DLLSSLName, importc.}
-  proc SSLv23_client_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
+  # Static linking
+  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 SSLv23_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
+  proc TLS_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
+  proc SSLv23_method*(): PSSL_METHOD =
+    TLS_method()
+
+  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.}
 
   template OpenSSL_add_all_algorithms*() = discard
+
+  proc OpenSSL_version_num(): culong {.cdecl, dynlib: DLLSSLName, 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
+
 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.
@@ -223,38 +258,58 @@ else:
       if not dl.isNil:
         result = symAddr(dl, name)
 
+  proc loadPSSLMethod(method1, method2: string): PSSL_METHOD =
+    ## Load <method1> from OpenSSL if available, otherwise <method2>
+    let m1 = cast[proc(): PSSL_METHOD {.cdecl, gcsafe.}](sslSym(method1))
+    if not m1.isNil:
+      return m1()
+    cast[proc(): PSSL_METHOD {.cdecl, gcsafe.}](sslSym(method2))()
+
   proc SSL_library_init*(): cint {.discardable.} =
-    let theProc = cast[proc(): cint {.cdecl.}](sslSym("SSL_library_init"))
-    if not theProc.isNil: result = theProc()
+    ## Initialize SSL using OPENSSL_init_ssl for OpenSSL >= 1.1.0 otherwise
+    ## SSL_library_init
+    let theProc = cast[proc(opts: uint64, settings: uint8): cint {.cdecl.}](sslSym("OPENSSL_init_ssl"))
+    if not theProc.isNil:
+      return theProc(0, 0)
+    let olderProc = cast[proc(): cint {.cdecl.}](sslSym("SSL_library_init"))
+    if not olderProc.isNil: result = olderProc()
 
   proc SSL_load_error_strings*() =
     let theProc = cast[proc() {.cdecl.}](sslSym("SSL_load_error_strings"))
     if not theProc.isNil: theProc()
 
   proc SSLv23_client_method*(): PSSL_METHOD =
-    let theProc = cast[proc(): PSSL_METHOD {.cdecl, gcsafe.}](sslSym("SSLv23_client_method"))
-    if not theProc.isNil: result = theProc()
-    else: result = TLSv1_method()
+    loadPSSLMethod("SSLv23_client_method", "TLS_client_method")
 
   proc SSLv23_method*(): PSSL_METHOD =
-    let theProc = cast[proc(): PSSL_METHOD {.cdecl, gcsafe.}](sslSym("SSLv23_method"))
-    if not theProc.isNil: result = theProc()
-    else: result = TLSv1_method()
+    loadPSSLMethod("SSLv23_method", "TLS_method")
 
   proc SSLv2_method*(): PSSL_METHOD =
-    let theProc = cast[proc(): PSSL_METHOD {.cdecl, gcsafe.}](sslSym("SSLv2_method"))
-    if not theProc.isNil: result = theProc()
-    else: result = TLSv1_method()
+    loadPSSLMethod("SSLv2_method", "TLS_method")
 
   proc SSLv3_method*(): PSSL_METHOD =
-    let theProc = cast[proc(): PSSL_METHOD {.cdecl, gcsafe.}](sslSym("SSLv3_method"))
-    if not theProc.isNil: result = theProc()
-    else: result = TLSv1_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*() =
     let theProc = cast[proc() {.cdecl.}](sslSym("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.}](sslSym("OpenSSL_version_num"))
+    result =
+      if theProc.isNil: 0.culong
+      else: theProc()
+
 proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.}
 
 proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.}