* 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.
This commit is contained in:
parent
ef2566218e
commit
0ac9c7bb64
4 changed files with 82 additions and 22 deletions
|
|
@ -119,6 +119,9 @@ echo f
|
||||||
- Added a new module, `std / compilesettings` for querying the compiler about
|
- Added a new module, `std / compilesettings` for querying the compiler about
|
||||||
diverse configuration settings.
|
diverse configuration settings.
|
||||||
- `base64` adds URL-Safe Base64, implements RFC-4648 Section-7.
|
- `base64` adds URL-Safe Base64, implements RFC-4648 Section-7.
|
||||||
|
- Added `net.getPeerCertificates` and `asyncnet.getPeerCertificates` for
|
||||||
|
retrieving the verified certificate chain of the peer we are connected to
|
||||||
|
through an SSL-wrapped `Socket`/`AsyncSocket`.
|
||||||
|
|
||||||
|
|
||||||
## Library changes
|
## Library changes
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,8 @@
|
||||||
## runForever()
|
## runForever()
|
||||||
##
|
##
|
||||||
|
|
||||||
|
include "system/inclrtl"
|
||||||
|
|
||||||
import asyncdispatch
|
import asyncdispatch
|
||||||
import nativesockets
|
import nativesockets
|
||||||
import net
|
import net
|
||||||
|
|
@ -743,6 +745,17 @@ when defineSsl:
|
||||||
of handshakeAsServer:
|
of handshakeAsServer:
|
||||||
sslSetAcceptState(socket.sslHandle)
|
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 {.
|
proc getSockOpt*(socket: AsyncSocket, opt: SOBool, level = SOL_SOCKET): bool {.
|
||||||
tags: [ReadIOEffect].} =
|
tags: [ReadIOEffect].} =
|
||||||
## Retrieves option ``opt`` as a boolean value.
|
## Retrieves option ``opt`` as a boolean value.
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,8 @@
|
||||||
## socket.acceptAddr(client, address)
|
## socket.acceptAddr(client, address)
|
||||||
## echo("Client connected from: ", address)
|
## echo("Client connected from: ", address)
|
||||||
|
|
||||||
|
include "system/inclrtl"
|
||||||
|
|
||||||
{.deadCodeElim: on.} # dce option deprecated
|
{.deadCodeElim: on.} # dce option deprecated
|
||||||
import nativesockets, os, strutils, parseutils, times, sets, options,
|
import nativesockets, os, strutils, parseutils, times, sets, options,
|
||||||
std/monotimes
|
std/monotimes
|
||||||
|
|
@ -82,6 +84,8 @@ when defineSsl:
|
||||||
|
|
||||||
when defineSsl:
|
when defineSsl:
|
||||||
type
|
type
|
||||||
|
Certificate* = string ## DER encoded certificate
|
||||||
|
|
||||||
SslError* = object of Exception
|
SslError* = object of Exception
|
||||||
|
|
||||||
SslCVerifyMode* = enum
|
SslCVerifyMode* = enum
|
||||||
|
|
@ -747,6 +751,36 @@ when defineSsl:
|
||||||
let ret = SSL_accept(socket.sslHandle)
|
let ret = SSL_accept(socket.sslHandle)
|
||||||
socketError(socket, ret)
|
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 =
|
proc getSocketError*(socket: Socket): OSErrorCode =
|
||||||
## Checks ``osLastError`` for a valid error. If it has been reset it uses
|
## Checks ``osLastError`` for a valid error. If it has been reset it uses
|
||||||
## the last error stored in the socket object.
|
## the last error stored in the socket object.
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ type
|
||||||
PSslPtr* = ptr SslPtr
|
PSslPtr* = ptr SslPtr
|
||||||
SslCtx* = SslPtr
|
SslCtx* = SslPtr
|
||||||
PSSL_METHOD* = SslPtr
|
PSSL_METHOD* = SslPtr
|
||||||
|
PSTACK* = SslPtr
|
||||||
PX509* = SslPtr
|
PX509* = SslPtr
|
||||||
PX509_NAME* = SslPtr
|
PX509_NAME* = SslPtr
|
||||||
PEVP_MD* = SslPtr
|
PEVP_MD* = SslPtr
|
||||||
|
|
@ -359,6 +360,8 @@ proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.}
|
||||||
proc SSL_free*(ssl: 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.}
|
proc SSL_get_SSL_CTX*(ssl: SslPtr): SslCtx {.cdecl, dynlib: DLLSSLName, importc.}
|
||||||
proc SSL_set_SSL_CTX*(ssl: SslPtr, ctx: SslCtx): SslCtx {.cdecl, dynlib: DLLSSLName, importc.}
|
proc SSL_set_SSL_CTX*(ssl: SslPtr, ctx: SslCtx): SslCtx {.cdecl, dynlib: DLLSSLName, importc.}
|
||||||
|
proc SSL_get0_verified_chain*(ssl: SslPtr): PSTACK {.cdecl, dynlib: DLLSSLName,
|
||||||
|
importc.}
|
||||||
proc SSL_CTX_new*(meth: PSSL_METHOD): SslCtx{.cdecl,
|
proc SSL_CTX_new*(meth: PSSL_METHOD): SslCtx{.cdecl,
|
||||||
dynlib: DLLSSLName, importc.}
|
dynlib: DLLSSLName, importc.}
|
||||||
proc SSL_CTX_load_verify_locations*(ctx: SslCtx, CAfile: cstring,
|
proc SSL_CTX_load_verify_locations*(ctx: SslCtx, CAfile: cstring,
|
||||||
|
|
@ -426,6 +429,35 @@ proc ERR_peek_last_error*(): cint{.cdecl, dynlib: DLLUtilName, importc.}
|
||||||
|
|
||||||
proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.}
|
proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.}
|
||||||
|
|
||||||
|
proc OPENSSL_sk_num*(stack: PSTACK): int {.cdecl, dynlib: DLLSSLName, importc.}
|
||||||
|
|
||||||
|
proc OPENSSL_sk_value*(stack: PSTACK, index: int): pointer {.cdecl,
|
||||||
|
dynlib: DLLSSLName, importc.}
|
||||||
|
|
||||||
|
proc d2i_X509*(px: ptr PX509, i: ptr ptr cuchar, len: cint): PX509 {.cdecl,
|
||||||
|
dynlib: DLLSSLName, importc.}
|
||||||
|
|
||||||
|
proc i2d_X509*(cert: PX509; o: ptr ptr cuchar): cint {.cdecl,
|
||||||
|
dynlib: DLLSSLName, importc.}
|
||||||
|
|
||||||
|
proc d2i_X509*(b: string): PX509 =
|
||||||
|
## decode DER/BER bytestring into X.509 certificate struct
|
||||||
|
var bb = b.cstring
|
||||||
|
let i = cast[ptr ptr cuchar](addr bb)
|
||||||
|
let ret = d2i_X509(addr result, i, b.len.cint)
|
||||||
|
if ret.isNil:
|
||||||
|
raise newException(Exception, "X.509 certificate decoding failed")
|
||||||
|
|
||||||
|
proc i2d_X509*(cert: PX509): string =
|
||||||
|
## encode `cert` to DER string
|
||||||
|
let encoded_length = i2d_X509(cert, nil)
|
||||||
|
result = newString(encoded_length)
|
||||||
|
var q = result.cstring
|
||||||
|
let o = cast[ptr ptr cuchar](addr q)
|
||||||
|
let length = i2d_X509(cert, o)
|
||||||
|
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):
|
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,
|
proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl,
|
||||||
dynlib: DLLUtilName, importc.}
|
dynlib: DLLUtilName, importc.}
|
||||||
|
|
@ -688,30 +720,8 @@ when not defined(nimDisableCertificateValidation) and not defined(windows):
|
||||||
proc X509_STORE_set_trust*(ctx: PX509_STORE; trust: cint): cint
|
proc X509_STORE_set_trust*(ctx: PX509_STORE; trust: cint): cint
|
||||||
proc X509_STORE_add_cert*(ctx: PX509_STORE; x: PX509): cint
|
proc X509_STORE_add_cert*(ctx: PX509_STORE; x: PX509): cint
|
||||||
|
|
||||||
proc d2i_X509*(px: ptr PX509, i: ptr ptr cuchar, len: cint): PX509
|
|
||||||
|
|
||||||
proc i2d_X509*(cert: PX509; o: ptr ptr cuchar): cint
|
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
proc d2i_X509*(b: string): PX509 =
|
|
||||||
## decode DER/BER bytestring into X.509 certificate struct
|
|
||||||
var bb = b.cstring
|
|
||||||
let i = cast[ptr ptr cuchar](addr bb)
|
|
||||||
let ret = d2i_X509(addr result, i, b.len.cint)
|
|
||||||
if ret.isNil:
|
|
||||||
raise newException(Exception, "X.509 certificate decoding failed")
|
|
||||||
|
|
||||||
proc i2d_X509*(cert: PX509): string =
|
|
||||||
## encode `cert` to DER string
|
|
||||||
let encoded_length = i2d_X509(cert, nil)
|
|
||||||
result = newString(encoded_length)
|
|
||||||
var q = result.cstring
|
|
||||||
let o = cast[ptr ptr cuchar](addr q)
|
|
||||||
let length = i2d_X509(cert, o)
|
|
||||||
if length.int <= 0:
|
|
||||||
raise newException(Exception, "X.509 certificate encoding failed")
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
# A simple certificate test
|
# A simple certificate test
|
||||||
let certbytes = readFile("certificate.der")
|
let certbytes = readFile("certificate.der")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue