commit - f81a97b3569478a36e5cbe95229efd1b831b7a7b
commit + 86693a33abd5e8c31530adb3045c9f4664d4d6c9
blob - 6872ae738dce80e2acf9f027ced5244c93ddcf34
blob + 0b73090e43c8e6c5295f33323c4c803c1cbef5c2
--- Makefile
+++ Makefile
# all.
TESTS=
-GMID_SRCS = gmid.c config.c dirs.c fcgi.c iri.c log.c logger.c mime.c \
- proc.c proxy.c puny.c sandbox.c server.c utf8.c utils.c \
- y.tab.c
+GMID_SRCS = gmid.c config.c crypto.c dirs.c fcgi.c iri.c log.c \
+ logger.c mime.c proc.c proxy.c puny.c sandbox.c \
+ server.c utf8.c utils.c y.tab.c
GMID_OBJS = ${GMID_SRCS:.c=.o} ${COBJS}
-GE_SRCS = ge.c config.c dirs.c fcgi.c iri.c log.c mime.c proc.c \
- proxy.c puny.c sandbox.c server.c utf8.c utils.c
+GE_SRCS = ge.c config.c crypto.c dirs.c fcgi.c iri.c log.c mime.c \
+ proc.c proxy.c puny.c sandbox.c server.c utf8.c utils.c
GE_OBJS = ${GE_SRCS:.c=.o} ${COBJS}
DISTFILES = .cirrus.yml .dockerignore .gitignore ChangeLog LICENSE \
Makefile README.md config.c configure configure.local.example \
- dirs.c fcgi.c ge.1 ge.c gg.1 gg.c gmid.8 gmid.c gmid.conf.5 \
- gmid.h iri.c log.c log.h logger.c mime.c parse.y proxy.c \
- puny.c sandbox.c server.c utf8.c utils.c y.tab.c
+ crypto.c dirs.c fcgi.c ge.1 ge.c gg.1 gg.c gmid.8 gmid.c \
+ gmid.conf.5 gmid.h iri.c log.c log.h logger.c mime.c \
+ parse.y proxy.c puny.c sandbox.c server.c utf8.c utils.c \
+ y.tab.c
dist: ${DISTNAME}.sha256
blob - de266c42b8d60b2c4a929990edac3a357eb720ad
blob + 251095ccaad5a57252183e4a64fefaa6d140f5ee
--- config.c
+++ config.c
#include <limits.h>
#include <string.h>
+#include <openssl/pem.h>
+
#include "log.h"
#include "proc.h"
TAILQ_INIT(&conf->fcgi);
TAILQ_INIT(&conf->hosts);
+ TAILQ_INIT(&conf->pkis);
conf->port = 1965;
conf->ipv6 = 0;
struct proxy *p, *tp;
struct envlist *e, *te;
struct alist *a, *ta;
+ struct pki *pki, *tpki;
ps = conf->ps;
free(h);
}
+ TAILQ_FOREACH_SAFE(pki, &conf->pkis, pkis, tpki) {
+ TAILQ_REMOVE(&conf->pkis, pki, pkis);
+ free(pki->hash);
+ EVP_PKEY_free(pki->pkey);
+ free(pki);
+ }
+
memset(conf, 0, sizeof(*conf));
conf->ps = ps;
init_mime(&conf->mime);
TAILQ_INIT(&conf->fcgi);
TAILQ_INIT(&conf->hosts);
+ TAILQ_INIT(&conf->pkis);
}
static int
}
static int
+config_send_kp(struct privsep *ps, int cert_type, int key_type,
+ const char *cert, const char *key)
+{
+ int fd, d;
+
+ log_debug("sending %s", cert);
+ if ((fd = open(cert, O_RDONLY)) == -1)
+ fatal("can't open %s", cert);
+ if ((d = dup(fd)) == -1)
+ fatal("fd");
+
+ if (config_send_file(ps, PROC_SERVER, cert_type, fd, NULL, 0) == -1) {
+ close(d);
+ return -1;
+ }
+ if (config_send_file(ps, PROC_CRYPTO, cert_type, d, NULL, 0) == -1)
+ return -1;
+
+ log_debug("sending %s", key);
+ if ((fd = open(key, O_RDONLY)) == -1)
+ return -1;
+ if (config_send_file(ps, PROC_CRYPTO, key_type, fd, NULL, 0) == -1)
+ return -1;
+
+ if (proc_flush_imsg(ps, PROC_SERVER, -1) == -1)
+ return -1;
+ if (proc_flush_imsg(ps, PROC_CRYPTO, -1) == -1)
+ return -1;
+ return 0;
+}
+
+static int
make_socket(int port, int family)
{
int sock, v;
struct envlist *e;
struct alist *a;
size_t i;
- int fd;
for (i = 0; i < conf->mime.len; ++i) {
m = &conf->mime.t[i];
&vcopy, sizeof(vcopy)) == -1)
return -1;
- log_debug("sending certificate %s", h->cert_path);
- if ((fd = open(h->cert_path, O_RDONLY)) == -1)
- fatal("can't open %s", h->cert_path);
- if (config_send_file(ps, PROC_SERVER, IMSG_RECONF_CERT, fd,
- NULL, 0) == -1)
- return -1;
-
- log_debug("sending key %s", h->key_path);
- if ((fd = open(h->key_path, O_RDONLY)) == -1)
- fatal("can't open %s", h->key_path);
- if (config_send_file(ps, PROC_SERVER, IMSG_RECONF_KEY, fd,
- NULL, 0) == -1)
+ if (config_send_kp(ps, IMSG_RECONF_CERT, IMSG_RECONF_KEY,
+ h->cert_path, h->key_path) == -1)
return -1;
if (h->ocsp_path != NULL) {
fd, &pcopy, sizeof(pcopy)) == -1)
return -1;
- if (p->cert_path != NULL &&
- config_open_send(ps, PROC_SERVER,
- IMSG_RECONF_PROXY_CERT, p->cert_path) == -1)
+ if (proc_flush_imsg(ps, PROC_SERVER, -1) == -1)
return -1;
- if (p->key_path != NULL &&
+ if (p->cert_path == NULL || p->key_path == NULL)
+ continue;
+
+ if (config_open_send(ps, PROC_SERVER,
+ IMSG_RECONF_PROXY_CERT, p->cert_path) == -1 ||
config_open_send(ps, PROC_SERVER,
IMSG_RECONF_PROXY_KEY, p->key_path) == -1)
return -1;
return 0;
}
+static int
+config_crypto_recv_kp(struct conf *conf, struct imsg *imsg)
+{
+ static struct pki *pki;
+ uint8_t *d;
+ size_t len;
+
+ /* XXX: check for duplicates */
+
+ if (imsg->fd == -1)
+ fatalx("no fd for imsg %d", imsg->hdr.type);
+
+ switch (imsg->hdr.type) {
+ case IMSG_RECONF_CERT:
+ if (pki != NULL)
+ fatalx("imsg in wrong order; pki is not NULL");
+ if ((pki = calloc(1, sizeof(*pki))) == NULL)
+ fatal("calloc");
+ if (load_file(imsg->fd, &d, &len) == -1)
+ fatalx("can't load file");
+ if ((pki->hash = ssl_pubkey_hash(d, len)) == NULL)
+ fatalx("failed to compute cert hash");
+ free(d);
+ TAILQ_INSERT_TAIL(&conf->pkis, pki, pkis);
+ break;
+
+ case IMSG_RECONF_KEY:
+ if (pki == NULL)
+ fatalx("got key without cert beforehand %d",
+ imsg->hdr.type);
+ if (load_file(imsg->fd, &d, &len) == -1)
+ fatalx("failed to load private key");
+ if ((pki->pkey = ssl_load_pkey(d, len)) == NULL)
+ fatalx("failed load private key");
+ free(d);
+ pki = NULL;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
int
config_recv(struct conf *conf, struct imsg *imsg)
{
case IMSG_RECONF_CERT:
log_debug("receiving cert");
+ if (privsep_process == PROC_CRYPTO)
+ return config_crypto_recv_kp(conf, imsg);
if (h == NULL)
fatalx("recv'd cert without host");
if (h->cert != NULL)
case IMSG_RECONF_KEY:
log_debug("receiving key");
+ if (privsep_process == PROC_CRYPTO)
+ return config_crypto_recv_kp(conf, imsg);
if (h == NULL)
fatalx("recv'd key without host");
if (h->key != NULL)
blob - /dev/null
blob + 9b4f060e60aa901ccc1b3f0e35bd1185c67037d2 (mode 644)
--- /dev/null
+++ crypto.c
+/*
+ * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
+ * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "gmid.h"
+
+#include <string.h>
+
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/engine.h>
+
+#include "log.h"
+#include "proc.h"
+
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+static void crypto_init(struct privsep *, struct privsep_proc *, void *);
+static int crypto_dispatch_parent(int, struct privsep_proc *, struct imsg *);
+static int crypto_dispatch_server(int, struct privsep_proc *, struct imsg *);
+
+static struct privsep_proc procs[] = {
+ { "parent", PROC_PARENT, crypto_dispatch_parent },
+ { "server", PROC_SERVER, crypto_dispatch_server },
+};
+
+struct imsg_crypto_req {
+ uint64_t id;
+ char hash[TLS_CERT_HASH_SIZE];
+ size_t flen;
+ size_t tlen;
+ int padding;
+ /* followed by flen bytes of `from'. */
+};
+
+struct imsg_crypto_res {
+ uint64_t id;
+ int ret;
+ size_t len;
+ /* followed by len bytes of reply */
+};
+
+static uint64_t reqid;
+static struct conf *conf;
+
+void
+crypto(struct privsep *ps, struct privsep_proc *p)
+{
+ proc_run(ps, p, procs, nitems(procs), crypto_init, NULL);
+}
+
+static void
+crypto_init(struct privsep *ps, struct privsep_proc *p, void *arg)
+{
+#if 0
+ static volatile int attached;
+ while (!attached) sleep(1);
+#endif
+
+ conf = ps->ps_env;
+
+ sandbox_crypto_process();
+}
+
+static int
+crypto_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ switch (imsg->hdr.type) {
+ case IMSG_RECONF_START:
+ case IMSG_RECONF_CERT:
+ case IMSG_RECONF_KEY:
+ case IMSG_RECONF_END:
+ if (config_recv(conf, imsg) == -1)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static EVP_PKEY *
+get_pkey(const char *hash)
+{
+ struct pki *pki;
+
+ TAILQ_FOREACH(pki, &conf->pkis, pkis) {
+ if (!strcmp(pki->hash, hash))
+ return pki->pkey;
+ }
+
+ return NULL;
+}
+
+static int
+crypto_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ struct privsep *ps = p->p_ps;
+ RSA *rsa;
+ EC_KEY *ecdsa;
+ EVP_PKEY *pkey;
+ struct imsg_crypto_req req;
+ struct imsg_crypto_res res;
+ struct iovec iov[2];
+ const void *from;
+ unsigned char *to;
+ size_t datalen;
+ int n, len, ret;
+
+ datalen = IMSG_DATA_SIZE(imsg);
+
+ switch (imsg->hdr.type) {
+ case IMSG_CRYPTO_RSA_PRIVENC:
+ case IMSG_CRYPTO_RSA_PRIVDEC:
+ if (datalen < sizeof(req))
+ fatalx("size mismatch for imsg %d", imsg->hdr.type);
+ memcpy(&req, imsg->data, sizeof(req));
+ if (datalen != sizeof(req) + req.flen)
+ fatalx("size mismatch for imsg %d", imsg->hdr.type);
+ from = imsg->data + sizeof(req);
+
+ if ((pkey = get_pkey(req.hash)) == NULL ||
+ (rsa = EVP_PKEY_get1_RSA(pkey)) == NULL)
+ fatalx("invalid pkey hash");
+
+ if ((to = calloc(1, req.tlen)) == NULL)
+ fatal("calloc");
+
+ switch (imsg->hdr.type) {
+ case IMSG_CRYPTO_RSA_PRIVENC:
+ ret = RSA_private_encrypt(req.flen, from,
+ to, rsa, req.padding);
+ break;
+ case IMSG_CRYPTO_RSA_PRIVDEC:
+ ret = RSA_private_decrypt(req.flen, from,
+ to, rsa, req.padding);
+ break;
+ }
+
+ memset(&res, 0, sizeof(res));
+ res.id = req.id;
+ res.ret = ret;
+
+ memset(&iov, 0, sizeof(iov));
+ n = 0;
+ iov[n].iov_base = &res;
+ iov[n].iov_len = sizeof(res);
+ n++;
+
+ if (ret > 0) {
+ res.len = ret;
+ iov[n].iov_base = to;
+ iov[n].iov_len = ret;
+ n++;
+ }
+
+ log_debug("replying to server #%d", imsg->hdr.pid);
+ if (proc_composev_imsg(ps, PROC_SERVER, imsg->hdr.pid - 1,
+ imsg->hdr.type, 0, -1, iov, n) == -1)
+ fatal("proc_composev_imsg");
+
+ if (proc_flush_imsg(ps, PROC_SERVER, imsg->hdr.pid - 1) == -1)
+ fatal("proc_flush_imsg");
+
+ free(to);
+ RSA_free(rsa);
+ break;
+
+ case IMSG_CRYPTO_ECDSA_SIGN:
+ if (datalen < sizeof(req))
+ fatalx("size mismatch for imsg %d", imsg->hdr.type);
+ memcpy(&req, imsg->data, sizeof(req));
+ if (datalen != sizeof(req) + req.flen)
+ fatalx("size mismatch for imsg %d", imsg->hdr.type);
+ from = imsg->data + sizeof(req);
+
+ if ((pkey = get_pkey(req.hash)) == NULL ||
+ (ecdsa = EVP_PKEY_get1_EC_KEY(pkey)) == NULL)
+ fatalx("invalid pkey hash");
+
+ len = ECDSA_size(ecdsa);
+ if ((to = calloc(1, len)) == NULL)
+ fatal("calloc");
+ ret = ECDSA_sign(0, from, req.flen, to, &len, ecdsa);
+
+ memset(&res, 0, sizeof(res));
+ res.id = req.id;
+ res.ret = ret;
+
+ memset(&iov, 0, sizeof(iov));
+ n = 0;
+ iov[0].iov_base = &res;
+ iov[1].iov_len = sizeof(res);
+ n++;
+
+ if (ret > 0) {
+ res.len = len;
+ iov[n].iov_base = to;
+ iov[n].iov_len = len;
+ n++;
+ }
+
+ log_debug("replying to server #%d", imsg->hdr.pid);
+ if (proc_composev_imsg(ps, PROC_SERVER, imsg->hdr.pid - 1,
+ imsg->hdr.type, 0, -1, iov, n) == -1)
+ fatal("proc_composev_imsg");
+
+ if (proc_flush_imsg(ps, PROC_SERVER, imsg->hdr.pid - 1) == -1)
+ fatal("proc_flush_imsg");
+
+ free(to);
+ EC_KEY_free(ecdsa);
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * RSA privsep engine (called from unprivileged processes)
+ */
+
+static const RSA_METHOD *rsa_default;
+static RSA_METHOD *rsae_method;
+
+static int
+rsae_send_imsg(int flen, const unsigned char *from, unsigned char *to,
+ RSA *rsa, int padding, unsigned int cmd)
+{
+ struct imsg_crypto_req req;
+ struct iovec iov[2];
+ struct imsg_crypto_res res;
+ struct imsgev *iev;
+ struct privsep_proc *p;
+ struct privsep *ps = conf->ps;
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ int ret = 0;
+ int n, done = 0;
+ const void *toptr;
+ char *hash;
+ size_t datalen;
+
+ if ((hash = RSA_get_ex_data(rsa, 0)) == NULL)
+ return (0);
+
+ /*
+ * Send a synchronous imsg because we cannot defer the RSA
+ * operation in OpenSSL's engine layer.
+ */
+ memset(&req, 0, sizeof(req));
+ req.id = ++reqid;
+ if (strlcpy(req.hash, hash, sizeof(req.hash)) >= sizeof(req.hash))
+ fatalx("%s: hash too long (%zu)", __func__, strlen(hash));
+ req.flen = flen;
+ req.tlen = RSA_size(rsa);
+ req.padding = padding;
+
+ memset(&iov, 0, sizeof(iov));
+ iov[0].iov_base = &req;
+ iov[0].iov_len = sizeof(req);
+ iov[1].iov_base = (void *)from;
+ iov[1].iov_len = flen;
+
+ if (proc_composev(ps, PROC_CRYPTO, cmd, iov, 2) == -1)
+ fatal("proc_composev");
+
+ if (proc_flush_imsg(ps, PROC_CRYPTO, -1) == -1)
+ fatal("proc_flush_imsg");
+
+ iev = ps->ps_ievs[PROC_CRYPTO];
+ p = iev->proc;
+ ibuf = &iev->ibuf;
+
+ while (!done) {
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatalx("imsg_read");
+ if (n == 0)
+ fatalx("pipe closed");
+
+ while (!done) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatalx("imsg_get error");
+ if (n == 0)
+ break;
+
+#if DEBUG > 1
+ log_debug(
+ "%s: %s %d got imsg %d peerid %d from %s %d",
+ __func__, title, 1, imsg.hdr.type,
+ imsg.hdr.peerid, "crypto", imsg.hdr.pid);
+#endif
+
+ if ((p->p_cb)(ibuf->fd, p, &imsg) == 0) {
+ /* Message was handled by the callback */
+ imsg_free(&imsg);
+ continue;
+ }
+
+ switch (imsg.hdr.type) {
+ case IMSG_CRYPTO_RSA_PRIVENC:
+ case IMSG_CRYPTO_RSA_PRIVDEC:
+ break;
+ default:
+ fatalx("%s: %s %d got invalid imsg %d"
+ " peerid %d from %s %d",
+ __func__, "server", ps->ps_instance + 1,
+ imsg.hdr.type, imsg.hdr.peerid,
+ "crypto", imsg.hdr.pid);
+ }
+
+ datalen = IMSG_DATA_SIZE(&imsg);
+ if (datalen < sizeof(res))
+ fatalx("size mismatch for imsg %d",
+ imsg.hdr.type);
+ memcpy(&res, imsg.data, sizeof(res));
+ if (datalen != sizeof(res) + res.ret)
+ fatalx("size mismatch for imsg %d",
+ imsg.hdr.type);
+ ret = res.ret;
+ toptr = imsg.data + sizeof(res);
+
+ if (res.id != reqid)
+ fatalx("invalid response id"
+ " got %llu, want %llu", res.id, reqid);
+ if (res.ret > 0)
+ memcpy(to, toptr, res.len);
+
+ log_warnx("the return is %d", ret);
+
+ done = 1;
+
+ imsg_free(&imsg);
+ }
+ }
+ imsg_event_add(iev);
+
+ return (ret);
+}
+
+static int
+rsae_pub_enc(int flen,const unsigned char *from, unsigned char *to, RSA *rsa,
+ int padding)
+{
+ log_debug("debug: %s", __func__);
+ return (RSA_meth_get_pub_enc(rsa_default)(flen, from, to, rsa, padding));
+}
+
+static int
+rsae_pub_dec(int flen,const unsigned char *from, unsigned char *to, RSA *rsa,
+ int padding)
+{
+ log_debug("debug: %s", __func__);
+ return (RSA_meth_get_pub_dec(rsa_default)(flen, from, to, rsa, padding));
+}
+
+static int
+rsae_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa,
+ int padding)
+{
+ log_debug("debug: %s", __func__);
+ if (RSA_get_ex_data(rsa, 0) != NULL)
+ return (rsae_send_imsg(flen, from, to, rsa, padding,
+ IMSG_CRYPTO_RSA_PRIVENC));
+ return (RSA_meth_get_priv_enc(rsa_default)(flen, from, to, rsa, padding));
+}
+
+static int
+rsae_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa,
+ int padding)
+{
+ log_debug("debug: %s", __func__);
+ if (RSA_get_ex_data(rsa, 0) != NULL)
+ return (rsae_send_imsg(flen, from, to, rsa, padding,
+ IMSG_CRYPTO_RSA_PRIVDEC));
+
+ return (RSA_meth_get_priv_dec(rsa_default)(flen, from, to, rsa, padding));
+}
+
+static int
+rsae_mod_exp(BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx)
+{
+ log_debug("debug: %s", __func__);
+ return (RSA_meth_get_mod_exp(rsa_default)(r0, I, rsa, ctx));
+}
+
+static int
+rsae_bn_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+ const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx)
+{
+ log_debug("debug: %s", __func__);
+ return (RSA_meth_get_bn_mod_exp(rsa_default)(r, a, p, m, ctx, m_ctx));
+}
+
+static int
+rsae_init(RSA *rsa)
+{
+ log_debug("debug: %s", __func__);
+ if (RSA_meth_get_init(rsa_default) == NULL)
+ return (1);
+ return (RSA_meth_get_init(rsa_default)(rsa));
+}
+
+static int
+rsae_finish(RSA *rsa)
+{
+ log_debug("debug: %s", __func__);
+ if (RSA_meth_get_finish(rsa_default) == NULL)
+ return (1);
+ return (RSA_meth_get_finish(rsa_default)(rsa));
+}
+
+static int
+rsae_keygen(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb)
+{
+ log_debug("debug: %s", __func__);
+ return (RSA_meth_get_keygen(rsa_default)(rsa, bits, e, cb));
+}
+
+
+/*
+ * ECDSA privsep engine (called from unprivileged processes)
+ */
+
+static const EC_KEY_METHOD *ecdsa_default;
+static EC_KEY_METHOD *ecdsae_method;
+
+static ECDSA_SIG *
+ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len,
+ const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey)
+{
+ ECDSA_SIG *sig = NULL;
+ struct imsg_crypto_req req;
+ struct iovec iov[2];
+ struct imsg_crypto_res res;
+ struct imsgev *iev;
+ struct privsep_proc *p;
+ struct privsep *ps = conf->ps;
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ int n, done = 0;
+ const void *toptr;
+ char *hash;
+ size_t datalen;
+
+ if ((hash = EC_KEY_get_ex_data(eckey, 0)) == NULL)
+ return (0);
+
+ /*
+ * Send a synchronous imsg because we cannot defer the RSA
+ * operation in OpenSSL's engine layer.
+ */
+ memset(&req, 0, sizeof(req));
+ req.id = reqid++;
+ if (strlcpy(req.hash, hash, sizeof(req.hash)) >= sizeof(req.hash))
+ fatalx("%s: hash too long (%zu)", __func__, strlen(hash));
+ req.flen = dgst_len;
+
+ memset(&iov, 0, sizeof(iov));
+ iov[0].iov_base = &req;
+ iov[0].iov_len = sizeof(req);
+ iov[1].iov_base = (void *)dgst;
+ iov[1].iov_len = dgst_len;
+
+ if (proc_composev(ps, PROC_CRYPTO, IMSG_CRYPTO_ECDSA_SIGN, iov, 2) == -1)
+ fatal("proc_composev");
+
+ if (proc_flush_imsg(ps, PROC_CRYPTO, -1) == -1)
+ fatal("proc_flush_imsg");
+
+ iev = ps->ps_ievs[PROC_CRYPTO];
+ p = iev->proc;
+ ibuf = &iev->ibuf;
+
+ while (!done) {
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatalx("imsg_read");
+ if (n == 0)
+ fatalx("pipe closed");
+
+ while (!done) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatalx("imsg_get error");
+ if (n == 0)
+ break;
+
+#if DEBUG > 1
+ log_debug(
+ "%s: %s %d got imsg %d peerid %d from %s %d",
+ __func__, title, 1, imsg.hdr.type,
+ imsg.hdr.peerid, "crypto", imsg.hdr.pid);
+#endif
+
+ if (crypto_dispatch_server(ibuf->fd, p, &imsg) == 0) {
+ /* Message was handled by the callback */
+ imsg_free(&imsg);
+ continue;
+ }
+
+ if (imsg.hdr.type != IMSG_CRYPTO_ECDSA_SIGN)
+ fatalx("%s: %s %d got invalid imsg %d"
+ " peerid %d from %s %d",
+ __func__, "server", ps->ps_instance + 1,
+ imsg.hdr.type, imsg.hdr.peerid,
+ "crypto", imsg.hdr.pid);
+
+ datalen = IMSG_DATA_SIZE(&imsg);
+ if (datalen < sizeof(res))
+ fatalx("size mismatch for imsg %d",
+ imsg.hdr.type);
+ memcpy(&res, imsg.data, sizeof(res));
+ if (datalen != sizeof(res) + res.ret)
+ fatalx("size mismatch for imsg %d",
+ imsg.hdr.type);
+ toptr = imsg.data + sizeof(res);
+
+ if (res.id != reqid)
+ fatalx("invalid response id");
+ if (res.ret > 0) {
+ d2i_ECDSA_SIG(&sig,
+ (const unsigned char **)&toptr, res.len);
+ }
+
+ done = 1;
+
+ imsg_free(&imsg);
+ }
+ }
+ imsg_event_add(iev);
+
+ return (sig);
+}
+
+static int
+ecdsae_keygen(EC_KEY *eckey)
+{
+ int (*keygen)(EC_KEY *);
+
+ log_debug("debug: %s", __func__);
+ EC_KEY_METHOD_get_keygen(ecdsa_default, &keygen);
+ return (keygen(eckey));
+}
+
+static int
+ecdsae_compute_key(void *out, size_t outlen, const EC_POINT *pub_key,
+ EC_KEY *ecdh, void *(*kdf)(const void *, size_t, void *, size_t *))
+{
+ int (*ckey)(void *, size_t, const EC_POINT *, EC_KEY *,
+ void *(*)(const void *, size_t, void *, size_t *));
+
+ log_debug("debug: %s", __func__);
+ EC_KEY_METHOD_get_compute_key(ecdsa_default, &ckey);
+ return (ckey(out, outlen, pub_key, ecdh, kdf));
+}
+
+static int
+ecdsae_sign(int type, const unsigned char *dgst, int dlen, unsigned char *sig,
+ unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *eckey)
+{
+ int (*sign)(int, const unsigned char *, int, unsigned char *,
+ unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *);
+
+ log_debug("debug: %s", __func__);
+ EC_KEY_METHOD_get_sign(ecdsa_default, &sign, NULL, NULL);
+ return (sign(type, dgst, dlen, sig, siglen, kinv, r, eckey));
+}
+
+static ECDSA_SIG *
+ecdsae_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
+ const BIGNUM *rp, EC_KEY *eckey)
+{
+ ECDSA_SIG *(*psign_sig)(const unsigned char *, int, const BIGNUM *,
+ const BIGNUM *, EC_KEY *);
+
+ log_debug("debug: %s", __func__);
+ if (EC_KEY_get_ex_data(eckey, 0) != NULL)
+ return (ecdsae_send_enc_imsg(dgst, dgst_len, inv, rp, eckey));
+ EC_KEY_METHOD_get_sign(ecdsa_default, NULL, NULL, &psign_sig);
+ return (psign_sig(dgst, dgst_len, inv, rp, eckey));
+}
+
+static int
+ecdsae_sign_setup(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, BIGNUM **r)
+{
+ int (*psign_setup)(EC_KEY *, BN_CTX *, BIGNUM **, BIGNUM **);
+
+ log_debug("debug: %s", __func__);
+ EC_KEY_METHOD_get_sign(ecdsa_default, NULL, &psign_setup, NULL);
+ return (psign_setup(eckey, ctx, kinv, r));
+}
+
+static int
+ecdsae_verify(int type, const unsigned char *dgst, int dgst_len,
+ const unsigned char *sigbuf, int sig_len, EC_KEY *eckey)
+{
+ int (*verify)(int, const unsigned char *, int, const unsigned char *,
+ int, EC_KEY *);
+
+ log_debug("debug: %s", __func__);
+ EC_KEY_METHOD_get_verify(ecdsa_default, &verify, NULL);
+ return (verify(type, dgst, dgst_len, sigbuf, sig_len, eckey));
+}
+
+static int
+ecdsae_do_verify(const unsigned char *dgst, int dgst_len,
+ const ECDSA_SIG *sig, EC_KEY *eckey)
+{
+ int (*pverify_sig)(const unsigned char *, int, const ECDSA_SIG *,
+ EC_KEY *);
+
+ log_debug("debug: %s", __func__);
+ EC_KEY_METHOD_get_verify(ecdsa_default, NULL, &pverify_sig);
+ return (pverify_sig(dgst, dgst_len, sig, eckey));
+}
+
+
+/*
+ * Initialize the two engines.
+ */
+
+static void
+rsa_engine_init(void)
+{
+ ENGINE *e;
+ const char *errstr, *name;
+
+ if ((rsae_method = RSA_meth_new("RSA privsep engine", 0)) == NULL) {
+ errstr = "RSA_meth_new";
+ goto fail;
+ }
+
+ RSA_meth_set_pub_enc(rsae_method, rsae_pub_enc);
+ RSA_meth_set_pub_dec(rsae_method, rsae_pub_dec);
+ RSA_meth_set_priv_enc(rsae_method, rsae_priv_enc);
+ RSA_meth_set_priv_dec(rsae_method, rsae_priv_dec);
+ RSA_meth_set_mod_exp(rsae_method, rsae_mod_exp);
+ RSA_meth_set_bn_mod_exp(rsae_method, rsae_bn_mod_exp);
+ RSA_meth_set_init(rsae_method, rsae_init);
+ RSA_meth_set_finish(rsae_method, rsae_finish);
+ RSA_meth_set_keygen(rsae_method, rsae_keygen);
+
+ if ((e = ENGINE_get_default_RSA()) == NULL) {
+ if ((e = ENGINE_new()) == NULL) {
+ errstr = "ENGINE_new";
+ goto fail;
+ }
+ if (!ENGINE_set_name(e, RSA_meth_get0_name(rsae_method))) {
+ errstr = "ENGINE_set_name";
+ goto fail;
+ }
+ if ((rsa_default = RSA_get_default_method()) == NULL) {
+ errstr = "RSA_get_default_method";
+ goto fail;
+ }
+ } else if ((rsa_default = ENGINE_get_RSA(e)) == NULL) {
+ errstr = "ENGINE_get_RSA";
+ goto fail;
+ }
+
+ if ((name = ENGINE_get_name(e)) == NULL)
+ name = "unknown RSA engine";
+
+ log_debug("debug: %s: using %s", __func__, name);
+
+ if (RSA_meth_get_mod_exp(rsa_default) == NULL)
+ RSA_meth_set_mod_exp(rsae_method, NULL);
+ if (RSA_meth_get_bn_mod_exp(rsa_default) == NULL)
+ RSA_meth_set_bn_mod_exp(rsae_method, NULL);
+ if (RSA_meth_get_keygen(rsa_default) == NULL)
+ RSA_meth_set_keygen(rsae_method, NULL);
+ RSA_meth_set_flags(rsae_method,
+ RSA_meth_get_flags(rsa_default) | RSA_METHOD_FLAG_NO_CHECK);
+ RSA_meth_set0_app_data(rsae_method,
+ RSA_meth_get0_app_data(rsa_default));
+
+ if (!ENGINE_set_RSA(e, rsae_method)) {
+ errstr = "ENGINE_set_RSA";
+ goto fail;
+ }
+ if (!ENGINE_set_default_RSA(e)) {
+ errstr = "ENGINE_set_default_RSA";
+ goto fail;
+ }
+
+ return;
+
+ fail:
+ ssl_error(errstr);
+ fatalx("%s", errstr);
+}
+
+static void
+ecdsa_engine_init(void)
+{
+ ENGINE *e;
+ const char *errstr, *name;
+
+ if ((ecdsae_method = EC_KEY_METHOD_new(NULL)) == NULL) {
+ errstr = "EC_KEY_METHOD_new";
+ goto fail;
+ }
+
+ EC_KEY_METHOD_set_keygen(ecdsae_method, ecdsae_keygen);
+ EC_KEY_METHOD_set_compute_key(ecdsae_method, ecdsae_compute_key);
+ EC_KEY_METHOD_set_sign(ecdsae_method, ecdsae_sign, ecdsae_sign_setup,
+ ecdsae_do_sign);
+ EC_KEY_METHOD_set_verify(ecdsae_method, ecdsae_verify,
+ ecdsae_do_verify);
+
+ if ((e = ENGINE_get_default_EC()) == NULL) {
+ if ((e = ENGINE_new()) == NULL) {
+ errstr = "ENGINE_new";
+ goto fail;
+ }
+ if (!ENGINE_set_name(e, "ECDSA privsep engine")) {
+ errstr = "ENGINE_set_name";
+ goto fail;
+ }
+ if ((ecdsa_default = EC_KEY_get_default_method()) == NULL) {
+ errstr = "EC_KEY_get_default_method";
+ goto fail;
+ }
+ } else if ((ecdsa_default = ENGINE_get_EC(e)) == NULL) {
+ errstr = "ENGINE_get_EC";
+ goto fail;
+ }
+
+ if ((name = ENGINE_get_name(e)) == NULL)
+ name = "unknown ECDSA engine";
+
+ log_debug("debug: %s: using %s", __func__, name);
+
+ if (!ENGINE_set_EC(e, ecdsae_method)) {
+ errstr = "ENGINE_set_EC";
+ goto fail;
+ }
+ if (!ENGINE_set_default_EC(e)) {
+ errstr = "ENGINE_set_default_EC";
+ goto fail;
+ }
+
+ return;
+
+ fail:
+ ssl_error(errstr);
+ fatalx("%s", errstr);
+}
+
+void
+crypto_engine_init(struct conf *c)
+{
+ conf = c;
+
+ rsa_engine_init();
+ ecdsa_engine_init();
+}
+
blob - 463b7ae222bd2bcef0c091b702e42cb5c436dcb8
blob + 9ba75c76478d6836d8e81d5810ac9ff01f5541ba
--- gmid.c
+++ gmid.c
static void main_reload(struct conf *);
static void main_sig_handler(int, short, void *);
static int main_dispatch_server(int, struct privsep_proc *, struct imsg *);
+static int main_dispatch_crypto(int, struct privsep_proc *, struct imsg *);
static int main_dispatch_logger(int, struct privsep_proc *, struct imsg *);
static void __dead main_shutdown(struct conf *);
static void main_print_conf(struct conf *);
static struct privsep_proc procs[] = {
{ "server", PROC_SERVER, main_dispatch_server, server },
+ { "crypto", PROC_CRYPTO, main_dispatch_crypto, crypto },
{ "logger", PROC_LOGGER, main_dispatch_logger, logger },
};
{
struct privsep *ps = conf->ps;
- conf->reload = conf->prefork;
+ conf->reload = conf->prefork + 1; /* servers, crypto */
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_START, NULL, 0) == -1)
+ return -1;
+ if (proc_compose(ps, PROC_CRYPTO, IMSG_RECONF_START, NULL, 0) == -1)
return -1;
if (config_send(conf) == -1)
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_END, NULL, 0) == -1)
return -1;
+ if (proc_compose(ps, PROC_CRYPTO, IMSG_RECONF_END, NULL, 0) == -1)
+ return -1;
return 0;
}
}
static int
+main_dispatch_crypto(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ struct privsep *ps = p->p_ps;
+ struct conf *conf = ps->ps_env;
+
+ switch (imsg->hdr.type) {
+ case IMSG_RECONF_DONE:
+ main_configure_done(conf);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
main_dispatch_logger(int fd, struct privsep_proc *p, struct imsg *imsg)
{
struct privsep *ps = p->p_ps;
blob - 847af9e1cf68acbe1a5199b28428a8e036f588c7
blob + 353c31d7603e7ca52cc134d18fc646286ac7e513
--- gmid.h
+++ gmid.h
#define PROC_MAX_INSTANCES 16
+#define TLS_CERT_HASH_SIZE 128
+
/* forward declaration */
struct privsep;
struct privsep_proc;
size_t cap;
};
+TAILQ_HEAD(pkihead, pki);
+struct pki {
+ char *hash;
+ EVP_PKEY *pkey;
+ TAILQ_ENTRY(pki) pkis;
+};
+
struct conf {
struct privsep *ps;
int port;
struct fcgihead fcgi;
struct vhosthead hosts;
+ struct pkihead pkis;
};
extern const char *config_path;
IMSG_RECONF_PROXY_KEY,
IMSG_RECONF_END,
IMSG_RECONF_DONE,
+
+ IMSG_CRYPTO_RSA_PRIVENC,
+ IMSG_CRYPTO_RSA_PRIVDEC,
+ IMSG_CRYPTO_ECDSA_SIGN,
IMSG_CTL_PROCFD,
};
int config_send(struct conf *);
int config_recv(struct conf *, struct imsg *);
+/* crypto.c */
+void crypto(struct privsep *, struct privsep_proc *);
+void crypto_engine_init(struct conf *);
+
/* parse.y */
void yyerror(const char*, ...);
int parse_conf(struct conf *, const char*);
/* sandbox.c */
void sandbox_main_process(void);
void sandbox_server_process(void);
+void sandbox_crypto_process(void);
void sandbox_logger_process(void);
/* utf8.c */
void gen_certificate(const char*, const char*, const char*);
X509_STORE *load_ca(int);
int validate_against_ca(X509_STORE*, const uint8_t*, size_t);
+void ssl_error(const char *);
+char *ssl_pubkey_hash(const char *, size_t);
+EVP_PKEY *ssl_load_pkey(const char *, size_t);
struct vhost *new_vhost(void);
struct location *new_location(void);
struct proxy *new_proxy(void);
blob - 4ebacd18de758fca8b539a228d69181f2002a474
blob + f0def37e0aa277ee96bb187dcd22055aa343b7ff
--- proc.h
+++ proc.h
enum privsep_procid {
PROC_PARENT,
PROC_SERVER,
+ PROC_CRYPTO,
PROC_LOGGER,
PROC_MAX,
};
blob - 6d68b0f734994ca77ccdb0f8433265a1780cfdba
blob + c1894463b961560587ab30220cfe898d86ac5e98
--- sandbox.c
+++ sandbox.c
}
void
+sandbox_crypto_process(void)
+{
+ if (pledge("stdio recvfd", NULL) == -1)
+ fatal("pledge");
+}
+
+void
sandbox_logger_process(void)
{
if (pledge("stdio recvfd", NULL) == -1)
}
void
+sandbox_crypto_process(void)
+{
+ return;
+}
+
+void
sandbox_logger_process(void)
{
return;
blob - 483dd0b144ea9515d70728c7c5b9f4877acf545a
blob + b29249f26ffb61c551fad6b2bdcae9e9c7b192d3
--- server.c
+++ server.c
int connected_clients;
+/*
+ * This function is not publicy exported because it is a hack until libtls
+ * has a proper privsep setup.
+ */
+void tls_config_use_fake_private_key(struct tls_config *);
+
static inline int matches(const char*, const char*);
static int check_path(struct client*, const char*, int*);
static void handle_siginfo(int, short, void*);
static int server_dispatch_parent(int, struct privsep_proc *, struct imsg *);
+static int server_dispatch_crypto(int, struct privsep_proc *, struct imsg *);
static int server_dispatch_logger(int, struct privsep_proc *, struct imsg *);
static struct privsep_proc procs[] = {
{ "parent", PROC_PARENT, server_dispatch_parent },
+ { "crypto", PROC_CRYPTO, server_dispatch_crypto },
{ "logger", PROC_LOGGER, server_dispatch_logger },
};
if ((tlsconf = tls_config_new()) == NULL)
fatal("tls_config_new");
+ /*
+ * ge doesn't use the privsep crypto engine; it doesn't use
+ * privsep at all so `ps' is NULL.
+ */
+ if (conf->ps != NULL)
+ tls_config_use_fake_private_key(tlsconf);
+
/* optionally accept client certs, but don't try to verify them */
tls_config_verify_client_optional(tlsconf);
tls_config_insecure_noverifycert(tlsconf);
signal_add(&sigusr2, NULL);
sandbox_server_process();
+
+ /*
+ * ge doesn't use the privsep crypto engine; it doesn't use
+ * privsep at all so `ps' is NULL.
+ */
+ if (ps != NULL)
+ crypto_engine_init(ps->ps_env);
}
int
return 0;
}
+
static int
+server_dispatch_crypto(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ return -1;
+}
+
+static int
server_dispatch_logger(int fd, struct privsep_proc *p, struct imsg *imsg)
{
return -1;
blob - b8f09c78a7371abd6765f332139af72ee4e58c67
blob + c5f91a1bb89b4be42a239f0cb81b5d97e78e50e7
--- utils.c
+++ utils.c
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
+ * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
#include <string.h>
#include <openssl/bn.h>
+#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/x509_vfy.h>
#include <openssl/x509v3.h>
return ret;
}
+void
+ssl_error(const char *where)
+{
+ unsigned long code;
+ char errbuf[128];
+
+ while ((code = ERR_get_error()) != 0) {
+ ERR_error_string_n(code, errbuf, sizeof(errbuf));
+ log_debug("debug: SSL library error: %s: %s", where, errbuf);
+ }
+}
+
+char *
+ssl_pubkey_hash(const char *buf, size_t len)
+{
+ static const char hex[] = "0123456789abcdef";
+ BIO *in;
+ X509 *x509 = NULL;
+ char *hash = NULL;
+ size_t off;
+ char digest[EVP_MAX_MD_SIZE];
+ int dlen, i;
+
+ if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
+ log_warnx("%s: BIO_new_mem_buf failed", __func__);
+ return NULL;
+ }
+
+ if ((x509 = PEM_read_bio_X509(in, NULL, NULL, NULL)) == NULL) {
+ log_warnx("%s: PEM_read_bio_X509 failed", __func__);
+ ssl_error("PEM_read_bio_X509");
+ goto fail;
+ }
+
+ if ((hash = malloc(TLS_CERT_HASH_SIZE)) == NULL) {
+ log_warn("%s: malloc", __func__);
+ goto fail;
+ }
+
+ if (X509_pubkey_digest(x509, EVP_sha256(), digest, &dlen) != 1) {
+ log_warnx("%s: X509_pubkey_digest failed", __func__);
+ ssl_error("X509_pubkey_digest");
+ free(hash);
+ hash = NULL;
+ goto fail;
+ }
+
+ if (TLS_CERT_HASH_SIZE < 2 * dlen + sizeof("SHA256:"))
+ fatalx("%s: hash buffer too small", __func__);
+
+ off = strlcpy(hash, "SHA256:", TLS_CERT_HASH_SIZE);
+ for (i = 0; i < dlen; ++i) {
+ hash[off++] = hex[(digest[i] >> 4) & 0xf];
+ hash[off++] = hex[digest[i] & 0xf];
+ }
+ hash[off] = '\0';
+
+ fail:
+ BIO_free(in);
+ if (x509)
+ X509_free(x509);
+ return hash;
+}
+
+EVP_PKEY *
+ssl_load_pkey(const char *buf, size_t len)
+{
+ BIO *in;
+ EVP_PKEY *pkey;
+
+ if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
+ log_warnx("%s: BIO_new_mem_buf failed", __func__);
+ return NULL;
+ }
+
+ if ((pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL)) == NULL) {
+ log_warnx("%s: PEM_read_bio_PrivateKey failed", __func__);
+ ssl_error("PEM_read_bio_PrivateKey");
+ }
+
+ BIO_free(in);
+ return pkey;
+}
+
struct vhost *
new_vhost(void)
{