Implement the functions needed by the crl-persist logic when openssl
is enabled. Such functions are used in the ssl_verify module.

Note that the CRL file is stored in an adhoc data structure and no
openssl specific object is used. The data structure being used is a
sorted array or serials that can later be looked up in log(N) with
a binary search, thus guaranteeing a fast lookup operation.

Such data structure may be changed in the future with an optimized
openssl specific object.

Tests have been performed by using a CRL file having size 143MB.
Original delay upon client connection was around 5-8 seconds.
With this patch the delay gets close to 0.

Signed-off-by: Antonio Quartulli <a...@unstable.cc>
---
 src/openvpn/ssl_verify_openssl.c | 183 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 183 insertions(+)

diff --git a/src/openvpn/ssl_verify_openssl.c b/src/openvpn/ssl_verify_openssl.c
index a4b9432..c5649ca 100644
--- a/src/openvpn/ssl_verify_openssl.c
+++ b/src/openvpn/ssl_verify_openssl.c
@@ -47,6 +47,9 @@
 #include <openssl/x509v3.h>
 #include <openssl/err.h>
 
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
 int
 verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
 {
@@ -625,6 +628,186 @@ x509_write_pem(FILE *peercert_file, X509 *peercert)
   return SUCCESS;
 }
 
+static int
+x509_serial_cmp(const void *a, const void *b)
+{
+  const ASN1_INTEGER **serial1 = (const ASN1_INTEGER **)a;
+  const ASN1_INTEGER **serial2 = (const ASN1_INTEGER **)b;
+
+  ASSERT(serial1 && *serial1 && serial2 && *serial2);
+
+  return ASN1_INTEGER_cmp(*serial1, *serial2);
+}
+
+result_t
+x509_verify_crl_mem(const openvpn_x509_crl_t *crl, X509 *cert,
+                   const char *subject)
+{
+  result_t ret = FAILURE;
+  struct gc_arena gc = gc_new();
+  ASN1_INTEGER *serial;
+
+  ASSERT(crl && cert);
+
+  /* authorize the client if the CRL list is empty */
+  if (crl->num == 0)
+    {
+      ret = SUCCESS;
+      goto end;
+    }
+
+  /*
+   * make sure the issuer of the CRL and the certificate are the same,
+   * otherwise the check cannot be performed
+   */
+  if (X509_NAME_cmp(crl->issuer, X509_get_issuer_name(cert)) != 0)
+  {
+    msg(M_WARN, "CRL: CRL [in-memory] is from a different issuer than the 
issuer of certificate %s",
+       subject);
+    ret = SUCCESS;
+    goto end;
+  }
+
+  serial = X509_get_serialNumber(cert);
+  if (!serial)
+    {
+      msg(M_WARN, "CRL: can't extract serial from cert");
+      goto end;
+    }
+
+  /*
+   * perform binary search on sorted CRL array. The cmp function assumes that
+   * the arguments are all pointers to pointers, therefore the address of the
+   * "key" object has to be passed instead of the object itself.
+   */
+  if (bsearch(&serial, crl->buff, crl->num, sizeof(ASN1_INTEGER *),
+             x509_serial_cmp))
+    {
+           fprintf(stderr, "!\n");
+      msg(D_HANDSHAKE, "CRL CHECK FAILED: %s (serial %s) is REVOKED", subject,
+         format_hex_ex(serial->data, serial->length, 0, 1, ":", &gc));
+      goto end;
+    }
+
+  /* serial not found in CRL: certificate is valid */
+  ret = SUCCESS;
+  msg(D_HANDSHAKE, "CRL CHECK OK: %s", subject);
+
+end:
+  gc_free(&gc);
+
+  return ret;
+}
+
+void
+x509_crl_free(openvpn_x509_crl_t *crl)
+{
+  int i;
+
+  ASSERT(crl);
+
+  for (i = 0; i < crl->num; i++)
+    ASN1_INTEGER_free(crl->buff[i]);
+
+  free(crl->buff);
+  X509_NAME_free(crl->issuer);
+  crl->buff = NULL;
+  crl->issuer = NULL;
+  crl->num = 0;
+}
+
+result_t
+x509_load_crl_mem(openvpn_x509_crl_t *crl, const char *crl_file)
+{
+  result_t ret = FAILURE;
+  X509_CRL *x509_crl = NULL;
+  X509_REVOKED *revoked;
+  X509_NAME *issuer;
+  BIO *in = NULL;
+  uint64_t num, i;
+
+  ASSERT(crl && crl_file);
+
+  msg(D_TLS_DEBUG_LOW, "CRL-persist: loading file: %s", crl_file);
+
+  /* free previously allocated data - useful in case or context update */
+  if (crl->buff)
+    x509_crl_free(crl);
+
+  in = BIO_new_file(crl_file, "r");
+  if (!in)
+    {
+      msg(M_WARN, "CRL: cannot read: %s", crl_file);
+      goto end;
+    }
+
+  x509_crl = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL);
+  if (!x509_crl)
+    {
+      msg(M_WARN, "CRL: cannot read CRL from file %s", crl_file);
+      goto end;
+    }
+
+  crl->num = 0;
+  crl->buff = NULL;
+  crl->issuer = NULL;
+
+  issuer = X509_CRL_get_issuer(x509_crl);
+  if (issuer)
+    crl->issuer = X509_NAME_dup(issuer);
+
+  num = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(x509_crl));
+  /* CRL is empty */
+  if (num == (uint64_t)-1)
+    {
+      ret = SUCCESS;
+      goto end;
+    }
+
+  crl->num = num;
+  crl->buff = malloc(sizeof(ASN1_INTEGER **) * num);
+  if (!crl->buff)
+    {
+      msg(M_WARN, "CRL: cannot allocate CRL buffer for file %s", crl_file);
+      goto end;
+    }
+
+  /* store each serial number in the buffer array as ASN1_INTEGER objects */
+  for (i = 0; i < num; i++)
+    {
+      revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(x509_crl), i);
+      crl->buff[i] = ASN1_INTEGER_dup(revoked->serialNumber);
+      if (!crl->buff[i])
+        {
+          msg(M_WARN, "CRL: cannot store CRL serial for file %s", crl_file);
+          goto err;
+        }
+    }
+
+  /* sort the array for fast lookup later */
+  qsort(crl->buff, crl->num, sizeof(ASN1_INTEGER *), x509_serial_cmp);
+  msg(D_TLS_DEBUG_LOW, "CRL-persist: loaded and sorted %" PRIu64 " serials",
+      crl->num);
+
+  ret = SUCCESS;
+  goto end;
+err:
+  /* rollback the for-loop and release all the allocated memory */
+  for (;i > 0; i--)
+    ASN1_INTEGER_free(crl->buff[i - 1]);
+
+  free(crl->buff);
+  X509_NAME_free(crl->issuer);
+
+end:
+  if (in)
+    BIO_free(in);
+  if (x509_crl)
+    X509_CRL_free(x509_crl);
+
+  return ret;
+}
+
 /*
  * check peer cert against CRL
  */
-- 
2.10.1


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most 
engaging tech sites, SlashDot.org! http://sdm.link/slashdot
_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to