summary refs log tree commit diff stats
path: root/lib/pure
diff options
context:
space:
mode:
authorChristian Ulrich <christian@ulrich.earth>2020-03-22 21:00:37 +0100
committerGitHub <noreply@github.com>2020-03-22 21:00:37 +0100
commit0ac9c7bb642aefc18a9ca1a7b58f22aea6cf61ce (patch)
tree4095d436e1d61c088119463380ca54d65ba73a05 /lib/pure
parentef2566218e701e6294608c6c30a367624786dcd6 (diff)
downloadNim-0ac9c7bb642aefc18a9ca1a7b58f22aea6cf61ce.tar.gz
introduce getPeerCertificates, fixes #13299 (#13650)
* make i2d_X509 and d2i_X509 always available

i2d_X509 and d2i_X509 have been available in all versions of OpenSSL, so
make them available even if nimDisableCertificateValidation is set.

* introduce getPeerCertificates, fixes #13299

getPeerCertificates retrieves the verified certificate chain of the peer
we are connected to through an SSL-wrapped Socket/AsyncSocket. This
introduces the new type Certificate which stores a DER-encoded X509 certificate.
Diffstat (limited to 'lib/pure')
-rw-r--r--lib/pure/asyncnet.nim13
-rw-r--r--lib/pure/net.nim34
2 files changed, 47 insertions, 0 deletions
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index 88852fb84..8bdab88b1 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -95,6 +95,8 @@
 ##   runForever()
 ##
 
+include "system/inclrtl"
+
 import asyncdispatch
 import nativesockets
 import net
@@ -743,6 +745,17 @@ when defineSsl:
     of handshakeAsServer:
       sslSetAcceptState(socket.sslHandle)
 
+  proc getPeerCertificates*(socket: AsyncSocket): seq[Certificate] {.since: (1, 1).} =
+    ## Returns the certificate chain received by the peer we are connected to
+    ## through the given socket.
+    ## The handshake must have been completed and the certificate chain must
+    ## have been verified successfully or else an empty sequence is returned.
+    ## The chain is ordered from leaf certificate to root certificate.
+    if not socket.isSsl:
+      result = newSeq[Certificate]()
+    else:
+      result = getPeerCertificates(socket.sslHandle)
+
 proc getSockOpt*(socket: AsyncSocket, opt: SOBool, level = SOL_SOCKET): bool {.
   tags: [ReadIOEffect].} =
   ## Retrieves option ``opt`` as a boolean value.
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index ca3d37259..2f43573ac 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -64,6 +64,8 @@
 ##     socket.acceptAddr(client, address)
 ##     echo("Client connected from: ", address)
 
+include "system/inclrtl"
+
 {.deadCodeElim: on.} # dce option deprecated
 import nativesockets, os, strutils, parseutils, times, sets, options,
   std/monotimes
@@ -82,6 +84,8 @@ when defineSsl:
 
 when defineSsl:
   type
+    Certificate* = string ## DER encoded certificate
+
     SslError* = object of Exception
 
     SslCVerifyMode* = enum
@@ -747,6 +751,36 @@ when defineSsl:
       let ret = SSL_accept(socket.sslHandle)
       socketError(socket, ret)
 
+  proc getPeerCertificates*(sslHandle: SslPtr): seq[Certificate] {.since: (1, 1).} =
+    ## Returns the certificate chain received by the peer we are connected to
+    ## through the OpenSSL connection represented by ``sslHandle``.
+    ## The handshake must have been completed and the certificate chain must
+    ## have been verified successfully or else an empty sequence is returned.
+    ## The chain is ordered from leaf certificate to root certificate.
+    result = newSeq[Certificate]()
+    if SSL_get_verify_result(sslHandle) != X509_V_OK:
+      return
+    let stack = SSL_get0_verified_chain(sslHandle)
+    if stack == nil:
+      return
+    let length = OPENSSL_sk_num(stack)
+    if length == 0:
+      return
+    for i in 0 .. length - 1:
+      let x509 = cast[PX509](OPENSSL_sk_value(stack, i))
+      result.add(i2d_X509(x509))
+
+  proc getPeerCertificates*(socket: Socket): seq[Certificate] {.since: (1, 1).} =
+    ## Returns the certificate chain received by the peer we are connected to
+    ## through the given socket.
+    ## The handshake must have been completed and the certificate chain must
+    ## have been verified successfully or else an empty sequence is returned.
+    ## The chain is ordered from leaf certificate to root certificate.
+    if not socket.isSsl:
+      result = newSeq[Certificate]()
+    else:
+      result = getPeerCertificates(socket.sslHandle)
+
 proc getSocketError*(socket: Socket): OSErrorCode =
   ## Checks ``osLastError`` for a valid error. If it has been reset it uses
   ## the last error stored in the socket object.