libdoltlite-sys 0.38.12

Native bindings to the DoltLite SQLite-compatible library
Documentation

#include "doltlite_tls.h"

#include "mbedtls/ctr_drbg.h"
#include "mbedtls/entropy.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/pk.h"
#include "mbedtls/ssl.h"
#include "mbedtls/x509_crt.h"

#include "doltlite_net.h"
#ifdef _WIN32
#include <wincrypt.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct DoltliteConn {
  int useTls;
  int ownsConf;
  mbedtls_net_context net;

  mbedtls_ssl_context ssl;
  mbedtls_ssl_config conf;
  mbedtls_x509_crt cacert;
  mbedtls_ctr_drbg_context drbg;
  mbedtls_entropy_context entropy;
};

struct DoltliteTlsServer {
  mbedtls_x509_crt cert;
  mbedtls_pk_context key;
  mbedtls_ssl_config conf;
  mbedtls_ctr_drbg_context drbg;
  mbedtls_entropy_context entropy;
};

#ifdef _WIN32
static int loadSystemRoots(mbedtls_x509_crt *ca) {
  HCERTSTORE store = CertOpenSystemStoreW(0, L"ROOT");
  PCCERT_CONTEXT ctx = NULL;
  int loaded = 0;
  if (!store) return 1;
  while ((ctx = CertEnumCertificatesInStore(store, ctx)) != NULL) {
    if (mbedtls_x509_crt_parse_der(ca, ctx->pbCertEncoded, ctx->cbCertEncoded) == 0) {
      loaded++;
    }
  }
  CertCloseStore(store, 0);
  return loaded > 0 ? 0 : 1;
}
#else
static const char *const CA_PATHS[] = {
    "/etc/ssl/cert.pem",
    "/etc/ssl/certs/ca-certificates.crt",
    "/etc/pki/tls/certs/ca-bundle.crt",
    "/etc/ssl/ca-bundle.pem",
    NULL,
};
#endif

static int loadTrustRoots(mbedtls_x509_crt *ca) {
  const char *env = getenv("DOLTLITE_CA_FILE");
  if (env && *env) {
    return (mbedtls_x509_crt_parse_file(ca, env) >= 0 && ca->version != 0) ? 0 : 1;
  }
#ifdef _WIN32
  return loadSystemRoots(ca);
#else
  {
    int i;
    for (i = 0; CA_PATHS[i] != NULL; i++) {
      if (mbedtls_x509_crt_parse_file(ca, CA_PATHS[i]) >= 0 && ca->version != 0) {
        return 0;
      }
    }
    return 1;
  }
#endif
}

DoltliteConn *doltliteConnOpen(const char *host, int port, int useTls) {
  DoltliteConn *c = (DoltliteConn *)calloc(1, sizeof(*c));
  char portstr[16];
  int ret;

  if (!c) return NULL;
  c->useTls = useTls;
  c->ownsConf = 1;
  mbedtls_net_init(&c->net);

  snprintf(portstr, sizeof(portstr), "%d", port);
  if (mbedtls_net_connect(&c->net, host, portstr, MBEDTLS_NET_PROTO_TCP) != 0) {
    goto fail_net;
  }
  if (!useTls) {
    return c;
  }

  mbedtls_ssl_init(&c->ssl);
  mbedtls_ssl_config_init(&c->conf);
  mbedtls_x509_crt_init(&c->cacert);
  mbedtls_ctr_drbg_init(&c->drbg);
  mbedtls_entropy_init(&c->entropy);

  if (mbedtls_ctr_drbg_seed(&c->drbg, mbedtls_entropy_func, &c->entropy,
                            (const unsigned char *)"doltlite-tls", 12) != 0) {
    goto fail_tls;
  }
  if (loadTrustRoots(&c->cacert) != 0) {
    goto fail_tls;
  }
  if (mbedtls_ssl_config_defaults(&c->conf, MBEDTLS_SSL_IS_CLIENT,
                                  MBEDTLS_SSL_TRANSPORT_STREAM,
                                  MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
    goto fail_tls;
  }
  mbedtls_ssl_conf_min_tls_version(&c->conf, MBEDTLS_SSL_VERSION_TLS1_3);
  mbedtls_ssl_conf_authmode(&c->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
  mbedtls_ssl_conf_ca_chain(&c->conf, &c->cacert, NULL);
  mbedtls_ssl_conf_rng(&c->conf, mbedtls_ctr_drbg_random, &c->drbg);

  if (mbedtls_ssl_setup(&c->ssl, &c->conf) != 0) goto fail_tls;

  if (mbedtls_ssl_set_hostname(&c->ssl, host) != 0) goto fail_tls;
  mbedtls_ssl_set_bio(&c->ssl, &c->net, mbedtls_net_send, mbedtls_net_recv, NULL);

  do {
    ret = mbedtls_ssl_handshake(&c->ssl);
  } while (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE);
  if (ret != 0) goto fail_tls;
  if (mbedtls_ssl_get_verify_result(&c->ssl) != 0) goto fail_tls;

  return c;

fail_tls:
  mbedtls_ssl_free(&c->ssl);
  mbedtls_ssl_config_free(&c->conf);
  mbedtls_x509_crt_free(&c->cacert);
  mbedtls_ctr_drbg_free(&c->drbg);
  mbedtls_entropy_free(&c->entropy);
fail_net:
  mbedtls_net_free(&c->net);
  free(c);
  return NULL;
}

int doltliteConnWriteAll(DoltliteConn *c, const void *buf, int nbuf) {
  const unsigned char *p = (const unsigned char *)buf;
  int off = 0;
  while (off < nbuf) {
    int n;
    if (c->useTls) {
      n = mbedtls_ssl_write(&c->ssl, p + off, (size_t)(nbuf - off));
    } else {
      n = (int)mbedtls_net_send(&c->net, p + off, (size_t)(nbuf - off));
    }
    if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) continue;
    if (n <= 0) return 1;
    off += n;
  }
  return 0;
}

int doltliteConnRead(DoltliteConn *c, void *buf, int nbuf) {
  int n;
  for (;;) {
    if (c->useTls) {
      n = mbedtls_ssl_read(&c->ssl, (unsigned char *)buf, (size_t)nbuf);
    } else {
      n = (int)mbedtls_net_recv(&c->net, (unsigned char *)buf, (size_t)nbuf);
    }
    if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) continue;

    if (c->useTls && n == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) return 0;
    if (n < 0) return -1;
    return n;
  }
}

void doltliteConnClose(DoltliteConn *c) {
  if (!c) return;
  if (c->useTls) {
    mbedtls_ssl_close_notify(&c->ssl);
    mbedtls_ssl_free(&c->ssl);

    if (c->ownsConf) {
      mbedtls_ssl_config_free(&c->conf);
      mbedtls_x509_crt_free(&c->cacert);
      mbedtls_ctr_drbg_free(&c->drbg);
      mbedtls_entropy_free(&c->entropy);
    }
  }
  mbedtls_net_free(&c->net);
  free(c);
}

DoltliteTlsServer *doltliteTlsServerNew(const char *certFile, const char *keyFile) {
  DoltliteTlsServer *s = (DoltliteTlsServer *)calloc(1, sizeof(*s));
  if (!s) return NULL;

  mbedtls_x509_crt_init(&s->cert);
  mbedtls_pk_init(&s->key);
  mbedtls_ssl_config_init(&s->conf);
  mbedtls_ctr_drbg_init(&s->drbg);
  mbedtls_entropy_init(&s->entropy);

  if (mbedtls_ctr_drbg_seed(&s->drbg, mbedtls_entropy_func, &s->entropy,
                            (const unsigned char *)"doltlite-tls-srv", 16) != 0) {
    goto fail;
  }
  if (mbedtls_x509_crt_parse_file(&s->cert, certFile) != 0) goto fail;
  if (mbedtls_pk_parse_keyfile(&s->key, keyFile, NULL,
                               mbedtls_ctr_drbg_random, &s->drbg) != 0) {
    goto fail;
  }
  if (mbedtls_ssl_config_defaults(&s->conf, MBEDTLS_SSL_IS_SERVER,
                                  MBEDTLS_SSL_TRANSPORT_STREAM,
                                  MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
    goto fail;
  }
  mbedtls_ssl_conf_min_tls_version(&s->conf, MBEDTLS_SSL_VERSION_TLS1_3);
  mbedtls_ssl_conf_rng(&s->conf, mbedtls_ctr_drbg_random, &s->drbg);

  mbedtls_ssl_conf_authmode(&s->conf, MBEDTLS_SSL_VERIFY_NONE);
  if (mbedtls_ssl_conf_own_cert(&s->conf, &s->cert, &s->key) != 0) goto fail;

  return s;

fail:
  doltliteTlsServerFree(s);
  return NULL;
}

void doltliteTlsServerFree(DoltliteTlsServer *s) {
  if (!s) return;
  mbedtls_ssl_config_free(&s->conf);
  mbedtls_x509_crt_free(&s->cert);
  mbedtls_pk_free(&s->key);
  mbedtls_ctr_drbg_free(&s->drbg);
  mbedtls_entropy_free(&s->entropy);
  free(s);
}

DoltliteConn *doltliteConnServerAccept(DoltliteTlsServer *s, int clientFd) {
  DoltliteConn *c = (DoltliteConn *)calloc(1, sizeof(*c));
  int ret;
  if (!c) {
    doltliteCloseSocket(clientFd);
    return NULL;
  }
  c->useTls = 1;
  c->ownsConf = 0;
  mbedtls_net_init(&c->net);
  c->net.fd = clientFd;
  mbedtls_ssl_init(&c->ssl);

  if (mbedtls_ssl_setup(&c->ssl, &s->conf) != 0) goto fail;
  mbedtls_ssl_set_bio(&c->ssl, &c->net, mbedtls_net_send, mbedtls_net_recv, NULL);
  do {
    ret = mbedtls_ssl_handshake(&c->ssl);
  } while (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE);
  if (ret != 0) goto fail;
  return c;

fail:
  mbedtls_ssl_free(&c->ssl);
  mbedtls_net_free(&c->net);
  free(c);
  return NULL;
}

DoltliteConn *doltliteConnFromFd(int clientFd) {
  DoltliteConn *c = (DoltliteConn *)calloc(1, sizeof(*c));
  if (!c) {
    doltliteCloseSocket(clientFd);
    return NULL;
  }
  c->useTls = 0;
  c->ownsConf = 0;
  mbedtls_net_init(&c->net);
  c->net.fd = clientFd;
  return c;
}