diff options
Diffstat (limited to 'web/server/h2o/libh2o/deps/neverbleed')
-rw-r--r-- | web/server/h2o/libh2o/deps/neverbleed/.clang-format | 7 | ||||
-rw-r--r-- | web/server/h2o/libh2o/deps/neverbleed/.gitignore | 32 | ||||
-rw-r--r-- | web/server/h2o/libh2o/deps/neverbleed/LICENSE | 22 | ||||
-rw-r--r-- | web/server/h2o/libh2o/deps/neverbleed/README.md | 70 | ||||
-rw-r--r-- | web/server/h2o/libh2o/deps/neverbleed/neverbleed.c | 1521 | ||||
-rw-r--r-- | web/server/h2o/libh2o/deps/neverbleed/neverbleed.h | 61 | ||||
-rw-r--r-- | web/server/h2o/libh2o/deps/neverbleed/test.c | 149 |
7 files changed, 0 insertions, 1862 deletions
diff --git a/web/server/h2o/libh2o/deps/neverbleed/.clang-format b/web/server/h2o/libh2o/deps/neverbleed/.clang-format deleted file mode 100644 index 9640123c7..000000000 --- a/web/server/h2o/libh2o/deps/neverbleed/.clang-format +++ /dev/null @@ -1,7 +0,0 @@ -# requires clang-format >= 3.6 -BasedOnStyle: "LLVM" -IndentWidth: 4 -ColumnLimit: 132 -BreakBeforeBraces: Linux -AllowShortFunctionsOnASingleLine: None -SortIncludes: false diff --git a/web/server/h2o/libh2o/deps/neverbleed/.gitignore b/web/server/h2o/libh2o/deps/neverbleed/.gitignore deleted file mode 100644 index bbf313b25..000000000 --- a/web/server/h2o/libh2o/deps/neverbleed/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -# Object files -*.o -*.ko -*.obj -*.elf - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ diff --git a/web/server/h2o/libh2o/deps/neverbleed/LICENSE b/web/server/h2o/libh2o/deps/neverbleed/LICENSE deleted file mode 100644 index 2bd3423e2..000000000 --- a/web/server/h2o/libh2o/deps/neverbleed/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Kazuho Oku, DeNA Co., Ltd. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/web/server/h2o/libh2o/deps/neverbleed/README.md b/web/server/h2o/libh2o/deps/neverbleed/README.md deleted file mode 100644 index 8357055ca..000000000 --- a/web/server/h2o/libh2o/deps/neverbleed/README.md +++ /dev/null @@ -1,70 +0,0 @@ -Neverbleed -=============== - -Neverbleed is an [OpenSSL engine](https://www.openssl.org/docs/manmaster/crypto/engine.html) that runs RSA private key operations in an isolated process, thereby minimizing the risk of private key leak in case of vulnerability such as [Heartbleed](http://heartbleed.com/). - -The engine is known to work together with existing versions of OpenSSL or LibreSSL, with minimal changes to the server source code. - -FAQ ---- - -### Q. How much is the overhead? - -Virtually none. - -Generally speaking, private key operations are much more heavier than the overhead of inter-process communication. -On my Linux VM running on Core i7 @ 2.4GHz (MacBook Pro 15" Late 2013), OpenSSL 1.0.2 without privilege separation processes 319.56 full TLS handshakes per second, whereas OpenSSL with privilege separation processes 316.72 handshakes per second (note: RSA key length: 2,048 bits, selected cipher-suite: ECDHE-RSA-AES128-GCM-SHA256). - -### Q. Why does the library only protect the private keys? - -Because private keys are the only _long-term_ secret being used for encrypting and/or digitally-signing the communication. - -Depending on how OpenSSL is used, it might be beneficial to separate symmetric cipher operations or TLS operations as a whole. -But even in such case, it would still be a good idea to isolate private key operations from them considering the impact of private key leaks. -In other words, separating private key operations only to an isolated process in always a good thing to do. - -### Q. Is there any HTTP server that uses Neverbleed? - -Neverbleed is used by [H2O](https://h2o.examp1e.net/) HTTP2 server since version [1.5.0-beta4](https://github.com/h2o/h2o/releases/tag/v1.5.0-beta4). - -How-to ------- - -The library exposes two functions: `neverbleed_init` and `neverbleed_load_private_key_file`. - -The first function spawns an external process dedicated to private key operations, and the second function assigns a RSA private key stored in the specified file to an existing SSL context (`SSL_CTX`). - -By - -1. adding call to `neverbleed_init` -2. replacing call to `SSL_CTX_use_PrivateKey_file` with `neverbleed_load_private_key_file` - -the privilege separation engine will be used for all the incoming TLS connections. - -``` - neverbleed_t nb; - char errbuf[NEVERBLEED_ERRBUF_SIZE]; - - /* initialize the OpenSSL library and the neverbleed engine */ - SSL_load_error_strings(); - SSL_library_init(); - OpenSSL_add_all_algorithms(); - if (neverbleed_init(&nb, errbuf) != 0) { - fprintf(stderr, "neverbleed_init failed: %s\n", errbuf); - ... - } - - ... - - /* load certificate chain and private key */ - if (SSL_CTX_use_certificate_chain_file(ssl_ctx, certchain_fn) != 1) { - fprintf(stderr, "failed to load certificate chain file:%s\n", certchain_fn); - ... - } - if (neverbleed_load_private_key_file(&nb, ctx, privkey_fn, errbuf) != 1) { - fprintf(stderr, "failed to load private key from file:%s:%s\n", privkey_fn, errbuf); - ... - } -``` - -Also, `neverbleed_setuidgid` function can be used to drop the privileges of the daemon process once it completes loading all the private keys. diff --git a/web/server/h2o/libh2o/deps/neverbleed/neverbleed.c b/web/server/h2o/libh2o/deps/neverbleed/neverbleed.c deleted file mode 100644 index 7b36d6cb6..000000000 --- a/web/server/h2o/libh2o/deps/neverbleed/neverbleed.c +++ /dev/null @@ -1,1521 +0,0 @@ -/* - * Copyright (c) 2015 Kazuho Oku, DeNA Co., Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#include <assert.h> -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <grp.h> -#include <limits.h> -#include <pthread.h> -#include <pwd.h> -#include <stdarg.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <unistd.h> -#include <openssl/rand.h> -#include <openssl/ssl.h> -#include <openssl/rsa.h> -#include <openssl/bn.h> -#ifdef __linux__ -#include <sys/prctl.h> -#endif -#include "neverbleed.h" - -#if (!defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x1010000fL) -#define OPENSSL_1_1_API 1 -#else -#define OPENSSL_1_1_API 0 -#endif - -enum neverbleed_type { NEVERBLEED_TYPE_ERROR, NEVERBLEED_TYPE_RSA, NEVERBLEED_TYPE_ECDSA }; - -struct expbuf_t { - char *buf; - char *start; - char *end; - size_t capacity; -}; - -struct st_neverbleed_rsa_exdata_t { - neverbleed_t *nb; - size_t key_index; -}; - -struct st_neverbleed_thread_data_t { - pid_t self_pid; - int fd; -}; - -static void warnvf(const char *fmt, va_list args) -{ - char errbuf[256]; - - if (errno != 0) { - strerror_r(errno, errbuf, sizeof(errbuf)); - } else { - errbuf[0] = '\0'; - } - - fprintf(stderr, "[openssl-privsep] "); - vfprintf(stderr, fmt, args); - if (errbuf[0] != '\0') - fputs(errbuf, stderr); - fputc('\n', stderr); -} - -__attribute__((format(printf, 1, 2))) static void warnf(const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - warnvf(fmt, args); - va_end(args); -} - -__attribute__((format(printf, 1, 2), noreturn)) static void dief(const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - warnvf(fmt, args); - va_end(args); - - abort(); -} - -static char *dirname(const char *path) -{ - const char *last_slash = strrchr(path, '/'); - char *ret; - - if (last_slash == NULL) { - errno = 0; - dief("dirname: no slash in given path:%s", path); - } - if ((ret = malloc(last_slash + 1 - path)) == NULL) - dief("no memory"); - memcpy(ret, path, last_slash - path); - ret[last_slash - path] = '\0'; - return ret; -} - -static void set_cloexec(int fd) -{ - if (fcntl(fd, F_SETFD, O_CLOEXEC) == -1) - dief("failed to set O_CLOEXEC to fd %d", fd); -} - -static int read_nbytes(int fd, void *p, size_t sz) -{ - while (sz != 0) { - ssize_t r; - while ((r = read(fd, p, sz)) == -1 && errno == EINTR) - ; - if (r == -1) { - return -1; - } else if (r == 0) { - errno = 0; - return -1; - } - p = (char *)p + r; - sz -= r; - } - return 0; -} - -static size_t expbuf_size(struct expbuf_t *buf) -{ - return buf->end - buf->start; -} - -static void expbuf_dispose(struct expbuf_t *buf) -{ - if (buf->capacity != 0) - OPENSSL_cleanse(buf->buf, buf->capacity); - free(buf->buf); - memset(buf, 0, sizeof(*buf)); -} - -static void expbuf_reserve(struct expbuf_t *buf, size_t extra) -{ - char *n; - - if (extra <= buf->buf + buf->capacity - buf->end) - return; - - if (buf->capacity == 0) - buf->capacity = 4096; - while (buf->buf + buf->capacity - buf->end < extra) - buf->capacity *= 2; - if ((n = realloc(buf->buf, buf->capacity)) == NULL) - dief("realloc failed"); - buf->start += n - buf->buf; - buf->end += n - buf->buf; - buf->buf = n; -} - -static void expbuf_push_num(struct expbuf_t *buf, size_t v) -{ - expbuf_reserve(buf, sizeof(v)); - memcpy(buf->end, &v, sizeof(v)); - buf->end += sizeof(v); -} - -static void expbuf_push_str(struct expbuf_t *buf, const char *s) -{ - size_t l = strlen(s) + 1; - expbuf_reserve(buf, l); - memcpy(buf->end, s, l); - buf->end += l; -} - -static void expbuf_push_bytes(struct expbuf_t *buf, const void *p, size_t l) -{ - expbuf_push_num(buf, l); - expbuf_reserve(buf, l); - memcpy(buf->end, p, l); - buf->end += l; -} - -static int expbuf_shift_num(struct expbuf_t *buf, size_t *v) -{ - if (expbuf_size(buf) < sizeof(*v)) - return -1; - memcpy(v, buf->start, sizeof(*v)); - buf->start += sizeof(*v); - return 0; -} - -static char *expbuf_shift_str(struct expbuf_t *buf) -{ - char *nul = memchr(buf->start, '\0', expbuf_size(buf)), *ret; - if (nul == NULL) - return NULL; - ret = buf->start; - buf->start = nul + 1; - return ret; -} - -static void *expbuf_shift_bytes(struct expbuf_t *buf, size_t *l) -{ - void *ret; - if (expbuf_shift_num(buf, l) != 0) - return NULL; - if (expbuf_size(buf) < *l) - return NULL; - ret = buf->start; - buf->start += *l; - return ret; -} - -static int expbuf_write(struct expbuf_t *buf, int fd) -{ - struct iovec vecs[2] = {{NULL}}; - size_t bufsz = expbuf_size(buf); - int vecindex; - ssize_t r; - - vecs[0].iov_base = &bufsz; - vecs[0].iov_len = sizeof(bufsz); - vecs[1].iov_base = buf->start; - vecs[1].iov_len = bufsz; - - for (vecindex = 0; vecindex != sizeof(vecs) / sizeof(vecs[0]);) { - while ((r = writev(fd, vecs + vecindex, sizeof(vecs) / sizeof(vecs[0]) - vecindex)) == -1 && errno == EINTR) - ; - if (r == -1) - return -1; - assert(r != 0); - while (r != 0 && r >= vecs[vecindex].iov_len) { - r -= vecs[vecindex].iov_len; - ++vecindex; - } - if (r != 0) { - vecs[vecindex].iov_base = (char *)vecs[vecindex].iov_base + r; - vecs[vecindex].iov_len -= r; - } - } - - return 0; -} - -static int expbuf_read(struct expbuf_t *buf, int fd) -{ - size_t sz; - - if (read_nbytes(fd, &sz, sizeof(sz)) != 0) - return -1; - expbuf_reserve(buf, sz); - if (read_nbytes(fd, buf->end, sz) != 0) - return -1; - buf->end += sz; - return 0; -} - -#if !defined(NAME_MAX) || defined(__linux__) -/* readdir(3) is known to be thread-safe on Linux and should be thread-safe on a platform that does not have a predefined value for - NAME_MAX */ -#define FOREACH_DIRENT(dp, dent) \ - struct dirent *dent; \ - while ((dent = readdir(dp)) != NULL) -#else -#define FOREACH_DIRENT(dp, dent) \ - struct { \ - struct dirent d; \ - char s[NAME_MAX + 1]; \ - } dent_; \ - struct dirent *dentp, *dent = &dent_.d; \ - int ret; \ - while ((ret = readdir_r(dp, dent, &dentp)) == 0 && dentp != NULL) -#endif /* FOREACH_DIRENT */ - -static void unlink_dir(const char *path) -{ - DIR *dp; - char buf[PATH_MAX]; - - if ((dp = opendir(path)) != NULL) { - FOREACH_DIRENT(dp, entp) - { - if (strcmp(entp->d_name, ".") == 0 || strcmp(entp->d_name, "..") == 0) - continue; - snprintf(buf, sizeof(buf), "%s/%s", path, entp->d_name); - unlink_dir(buf); - } - closedir(dp); - } - unlink(path); - rmdir(path); -} - -void dispose_thread_data(void *_thdata) -{ - struct st_neverbleed_thread_data_t *thdata = _thdata; - assert(thdata->fd >= 0); - close(thdata->fd); - thdata->fd = -1; -} - -struct st_neverbleed_thread_data_t *get_thread_data(neverbleed_t *nb) -{ - struct st_neverbleed_thread_data_t *thdata; - pid_t self_pid = getpid(); - ssize_t r; - - if ((thdata = pthread_getspecific(nb->thread_key)) != NULL) { - if (thdata->self_pid == self_pid) - return thdata; - /* we have been forked! */ - close(thdata->fd); - } else { - if ((thdata = malloc(sizeof(*thdata))) == NULL) - dief("malloc failed"); - } - - thdata->self_pid = self_pid; -#ifdef SOCK_CLOEXEC - if ((thdata->fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) - dief("socket(2) failed"); -#else - if ((thdata->fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) - dief("socket(2) failed"); - set_cloexec(thdata->fd); -#endif - while (connect(thdata->fd, (void *)&nb->sun_, sizeof(nb->sun_)) != 0) - if (errno != EINTR) - dief("failed to connect to privsep daemon"); - while ((r = write(thdata->fd, nb->auth_token, sizeof(nb->auth_token))) == -1 && errno == EINTR) - ; - if (r != sizeof(nb->auth_token)) - dief("failed to send authentication token"); - pthread_setspecific(nb->thread_key, thdata); - - return thdata; -} - -static void get_privsep_data(const RSA *rsa, struct st_neverbleed_rsa_exdata_t **exdata, - struct st_neverbleed_thread_data_t **thdata) -{ - *exdata = RSA_get_ex_data(rsa, 0); - if (*exdata == NULL) { - errno = 0; - dief("invalid internal ref"); - } - *thdata = get_thread_data((*exdata)->nb); -} - -static const size_t default_reserved_size = 8192; - -struct key_slots { - size_t size; - size_t reserved_size; - /* bit array slots: - * 1-bit slot available - * 0-bit slot unavailable - */ - uint8_t *bita_avail; -}; - -static struct { - struct { - pthread_mutex_t lock; - RSA **keys; - struct key_slots rsa_slots; - EC_KEY **ecdsa_keys; - struct key_slots ecdsa_slots; - } keys; - neverbleed_t *nb; -} daemon_vars = {{PTHREAD_MUTEX_INITIALIZER}}; - -static RSA *daemon_get_rsa(size_t key_index) -{ - RSA *rsa; - - pthread_mutex_lock(&daemon_vars.keys.lock); - rsa = daemon_vars.keys.keys[key_index]; - if (rsa) - RSA_up_ref(rsa); - pthread_mutex_unlock(&daemon_vars.keys.lock); - - return rsa; -} - -/* - * Returns an available slot in bit array B - * or if not found, returns SIZE_MAX - */ -static size_t bita_ffirst(const uint8_t *b, const size_t tot, size_t bits) -{ - if (bits >= tot) - return SIZE_MAX; - - uint64_t w = *((uint64_t *) b); - /* __builtin_ffsll returns one plus the index of the least significant 1-bit, or zero if not found */ - uint32_t r = __builtin_ffsll(w); - if (r) - return bits + r - 1; /* adjust result */ - - return bita_ffirst(&b[8], tot, bits + 64); -} - -/* - * bit operation helpers for the bit-array in key_slots - */ -#define BITMASK(b) (1 << ((b) % CHAR_BIT)) -#define BITBYTE(b) ((b) / CHAR_BIT) -#define BITSET(a, b) ((a)[BITBYTE(b)] |= BITMASK(b)) -#define BITUNSET(a, b) ((a)[BITBYTE(b)] &= ~BITMASK(b)) -#define BITBYTES(nb) ((nb + CHAR_BIT - 1) / CHAR_BIT) -#define BITCHECK(a, b) ((a)[BITBYTE(b)] & BITMASK(b)) - -static void adjust_slots_reserved_size(int type, struct key_slots *slots) -{ -#define ROUND2WORD(n) (n + 64 - 1 - (n + 64 - 1) % 64) - if (!slots->reserved_size || (slots->size >= slots->reserved_size)) { - size_t size = slots->reserved_size ? ROUND2WORD((size_t)(slots->reserved_size * 0.50) + slots->reserved_size) - : default_reserved_size; -#undef ROUND2WORD - - switch (type) { - case NEVERBLEED_TYPE_RSA: - if ((daemon_vars.keys.keys = realloc(daemon_vars.keys.keys, sizeof(*daemon_vars.keys.keys) * size)) == NULL) - dief("no memory"); - break; - case NEVERBLEED_TYPE_ECDSA: - if ((daemon_vars.keys.ecdsa_keys = realloc(daemon_vars.keys.ecdsa_keys, sizeof(*daemon_vars.keys.ecdsa_keys) * size)) == NULL) - dief("no memory"); - break; - default: - dief("invalid type adjusting reserved"); - } - - uint8_t *b; - if ((b = realloc(slots->bita_avail, BITBYTES(size))) == NULL) - dief("no memory"); - - /* set all bits to 1 making all slots available */ - memset(&b[BITBYTES(slots->reserved_size)], 0xff, BITBYTES(size - slots->reserved_size)); - - slots->bita_avail = b; - slots->reserved_size = size; - } -} - -static size_t daemon_set_rsa(RSA *rsa) -{ - pthread_mutex_lock(&daemon_vars.keys.lock); - - adjust_slots_reserved_size(NEVERBLEED_TYPE_RSA, &daemon_vars.keys.rsa_slots); - - size_t index = bita_ffirst(daemon_vars.keys.rsa_slots.bita_avail, daemon_vars.keys.rsa_slots.reserved_size, 0); - - if (index == SIZE_MAX) - dief("no available slot for key"); - - /* set slot as unavailable */ - BITUNSET(daemon_vars.keys.rsa_slots.bita_avail, index); - - daemon_vars.keys.rsa_slots.size++; - daemon_vars.keys.keys[index] = rsa; - RSA_up_ref(rsa); - pthread_mutex_unlock(&daemon_vars.keys.lock); - - return index; -} - -static int priv_encdec_proxy(const char *cmd, int flen, const unsigned char *from, unsigned char *_to, RSA *rsa, int padding) -{ - struct st_neverbleed_rsa_exdata_t *exdata; - struct st_neverbleed_thread_data_t *thdata; - struct expbuf_t buf = {NULL}; - size_t ret; - unsigned char *to; - size_t tolen; - - get_privsep_data(rsa, &exdata, &thdata); - - expbuf_push_str(&buf, cmd); - expbuf_push_bytes(&buf, from, flen); - expbuf_push_num(&buf, exdata->key_index); - expbuf_push_num(&buf, padding); - if (expbuf_write(&buf, thdata->fd) != 0) - dief(errno != 0 ? "write error" : "connection closed by daemon"); - expbuf_dispose(&buf); - - if (expbuf_read(&buf, thdata->fd) != 0) - dief(errno != 0 ? "read error" : "connection closed by daemon"); - if (expbuf_shift_num(&buf, &ret) != 0 || (to = expbuf_shift_bytes(&buf, &tolen)) == NULL) { - errno = 0; - dief("failed to parse response"); - } - memcpy(_to, to, tolen); - expbuf_dispose(&buf); - - return (int)ret; -} - -static int priv_encdec_stub(const char *name, - int (*func)(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding), - struct expbuf_t *buf) -{ - unsigned char *from, to[4096]; - size_t flen; - size_t key_index, padding; - RSA *rsa; - int ret; - - if ((from = expbuf_shift_bytes(buf, &flen)) == NULL || expbuf_shift_num(buf, &key_index) != 0 || - expbuf_shift_num(buf, &padding) != 0) { - errno = 0; - warnf("%s: failed to parse request", name); - return -1; - } - if ((rsa = daemon_get_rsa(key_index)) == NULL) { - errno = 0; - warnf("%s: invalid key index:%zu\n", name, key_index); - return -1; - } - ret = func((int)flen, from, to, rsa, (int)padding); - expbuf_dispose(buf); - RSA_free(rsa); - - expbuf_push_num(buf, ret); - expbuf_push_bytes(buf, to, ret > 0 ? ret : 0); - - return 0; -} - -static int priv_enc_proxy(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) -{ - return priv_encdec_proxy("priv_enc", flen, from, to, rsa, padding); -} - -static int priv_enc_stub(struct expbuf_t *buf) -{ - return priv_encdec_stub(__FUNCTION__, RSA_private_encrypt, buf); -} - -static int priv_dec_proxy(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) -{ - return priv_encdec_proxy("priv_dec", flen, from, to, rsa, padding); -} - -static int priv_dec_stub(struct expbuf_t *buf) -{ - return priv_encdec_stub(__FUNCTION__, RSA_private_decrypt, buf); -} - -static int sign_proxy(int type, const unsigned char *m, unsigned int m_len, unsigned char *_sigret, unsigned *_siglen, - const RSA *rsa) -{ - struct st_neverbleed_rsa_exdata_t *exdata; - struct st_neverbleed_thread_data_t *thdata; - struct expbuf_t buf = {NULL}; - size_t ret, siglen; - unsigned char *sigret; - - get_privsep_data(rsa, &exdata, &thdata); - - expbuf_push_str(&buf, "sign"); - expbuf_push_num(&buf, type); - expbuf_push_bytes(&buf, m, m_len); - expbuf_push_num(&buf, exdata->key_index); - if (expbuf_write(&buf, thdata->fd) != 0) - dief(errno != 0 ? "write error" : "connection closed by daemon"); - expbuf_dispose(&buf); - - if (expbuf_read(&buf, thdata->fd) != 0) - dief(errno != 0 ? "read error" : "connection closed by daemon"); - if (expbuf_shift_num(&buf, &ret) != 0 || (sigret = expbuf_shift_bytes(&buf, &siglen)) == NULL) { - errno = 0; - dief("failed to parse response"); - } - memcpy(_sigret, sigret, siglen); - *_siglen = (unsigned)siglen; - expbuf_dispose(&buf); - - return (int)ret; -} - -static int sign_stub(struct expbuf_t *buf) -{ - unsigned char *m, sigret[4096]; - size_t type, m_len, key_index; - RSA *rsa; - unsigned siglen = 0; - int ret; - - if (expbuf_shift_num(buf, &type) != 0 || (m = expbuf_shift_bytes(buf, &m_len)) == NULL || - expbuf_shift_num(buf, &key_index) != 0) { - errno = 0; - warnf("%s: failed to parse request", __FUNCTION__); - return -1; - } - if ((rsa = daemon_get_rsa(key_index)) == NULL) { - errno = 0; - warnf("%s: invalid key index:%zu", __FUNCTION__, key_index); - return -1; - } - ret = RSA_sign((int)type, m, (unsigned)m_len, sigret, &siglen, rsa); - expbuf_dispose(buf); - RSA_free(rsa); - - expbuf_push_num(buf, ret); - expbuf_push_bytes(buf, sigret, ret == 1 ? siglen : 0); - - return 0; -} - -#if !OPENSSL_1_1_API - -static void RSA_get0_key(const RSA *rsa, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) -{ - if (n) { - *n = rsa->n; - } - - if (e) { - *e = rsa->e; - } - - if (d) { - *d = rsa->d; - } -} - -static int RSA_set0_key(RSA *rsa, BIGNUM *n, BIGNUM *e, BIGNUM *d) -{ - if (n == NULL || e == NULL) { - return 0; - } - - BN_free(rsa->n); - BN_free(rsa->e); - BN_free(rsa->d); - rsa->n = n; - rsa->e = e; - rsa->d = d; - - return 1; -} - -static void RSA_set_flags(RSA *r, int flags) -{ - r->flags |= flags; -} -#endif - -static EVP_PKEY *create_pkey(neverbleed_t *nb, size_t key_index, const char *ebuf, const char *nbuf) -{ - struct st_neverbleed_rsa_exdata_t *exdata; - RSA *rsa; - EVP_PKEY *pkey; - BIGNUM *e = NULL, *n = NULL; - - if ((exdata = malloc(sizeof(*exdata))) == NULL) { - fprintf(stderr, "no memory\n"); - abort(); - } - exdata->nb = nb; - exdata->key_index = key_index; - - rsa = RSA_new_method(nb->engine); - RSA_set_ex_data(rsa, 0, exdata); - if (BN_hex2bn(&e, ebuf) == 0) { - fprintf(stderr, "failed to parse e:%s\n", ebuf); - abort(); - } - if (BN_hex2bn(&n, nbuf) == 0) { - fprintf(stderr, "failed to parse n:%s\n", nbuf); - abort(); - } - RSA_set0_key(rsa, n, e, NULL); - RSA_set_flags(rsa, RSA_FLAG_EXT_PKEY); - - pkey = EVP_PKEY_new(); - EVP_PKEY_set1_RSA(pkey, rsa); - RSA_free(rsa); - - return pkey; -} - -#if OPENSSL_1_1_API - -static EC_KEY *daemon_get_ecdsa(size_t key_index) -{ - EC_KEY *ec_key; - - pthread_mutex_lock(&daemon_vars.keys.lock); - ec_key = daemon_vars.keys.ecdsa_keys[key_index]; - if (ec_key) - EC_KEY_up_ref(ec_key); - pthread_mutex_unlock(&daemon_vars.keys.lock); - - return ec_key; -} - -static size_t daemon_set_ecdsa(EC_KEY *ec_key) -{ - pthread_mutex_lock(&daemon_vars.keys.lock); - - adjust_slots_reserved_size(NEVERBLEED_TYPE_ECDSA, &daemon_vars.keys.ecdsa_slots); - - size_t index = bita_ffirst(daemon_vars.keys.ecdsa_slots.bita_avail, daemon_vars.keys.ecdsa_slots.reserved_size, 0); - - if (index == SIZE_MAX) - dief("no available slot for key"); - - /* set slot as unavailable */ - BITUNSET(daemon_vars.keys.ecdsa_slots.bita_avail, index); - - daemon_vars.keys.ecdsa_slots.size++; - daemon_vars.keys.ecdsa_keys[index] = ec_key; - EC_KEY_up_ref(ec_key); - pthread_mutex_unlock(&daemon_vars.keys.lock); - - return index; -} - -static int ecdsa_sign_stub(struct expbuf_t *buf) -{ - unsigned char *m, sigret[4096]; - size_t type, m_len, key_index; - EC_KEY *ec_key; - unsigned siglen = 0; - int ret; - - if (expbuf_shift_num(buf, &type) != 0 || (m = expbuf_shift_bytes(buf, &m_len)) == NULL || - expbuf_shift_num(buf, &key_index) != 0) { - errno = 0; - warnf("%s: failed to parse request", __FUNCTION__); - return -1; - } - if ((ec_key = daemon_get_ecdsa(key_index)) == NULL) { - errno = 0; - warnf("%s: invalid key index:%zu", __FUNCTION__, key_index); - return -1; - } - - ret = ECDSA_sign((int)type, m, (unsigned)m_len, sigret, &siglen, ec_key); - expbuf_dispose(buf); - - EC_KEY_free(ec_key); - - expbuf_push_num(buf, ret); - expbuf_push_bytes(buf, sigret, ret == 1 ? siglen : 0); - - return 0; -} - -static void ecdsa_get_privsep_data(const EC_KEY *ec_key, struct st_neverbleed_rsa_exdata_t **exdata, - struct st_neverbleed_thread_data_t **thdata) -{ - *exdata = EC_KEY_get_ex_data(ec_key, 0); - if (*exdata == NULL) { - errno = 0; - dief("invalid internal ref"); - } - *thdata = get_thread_data((*exdata)->nb); -} - -static int ecdsa_sign_proxy(int type, const unsigned char *m, int m_len, unsigned char *_sigret, unsigned int *_siglen, - const BIGNUM *kinv, const BIGNUM *rp, EC_KEY *ec_key) -{ - struct st_neverbleed_rsa_exdata_t *exdata; - struct st_neverbleed_thread_data_t *thdata; - struct expbuf_t buf = {}; - size_t ret, siglen; - unsigned char *sigret; - - ecdsa_get_privsep_data(ec_key, &exdata, &thdata); - - /* as far as I've tested so far, kinv and rp are always NULL. - Looks like setup_sign will precompute this, but it is only - called sign_sig, and it seems to be not used in TLS ECDSA */ - if (kinv != NULL || rp != NULL) { - errno = 0; - dief("unexpected non-NULL kinv and rp"); - } - - expbuf_push_str(&buf, "ecdsa_sign"); - expbuf_push_num(&buf, type); - expbuf_push_bytes(&buf, m, m_len); - expbuf_push_num(&buf, exdata->key_index); - if (expbuf_write(&buf, thdata->fd) != 0) - dief(errno != 0 ? "write error" : "connection closed by daemon"); - expbuf_dispose(&buf); - - if (expbuf_read(&buf, thdata->fd) != 0) - dief(errno != 0 ? "read error" : "connection closed by daemon"); - if (expbuf_shift_num(&buf, &ret) != 0 || (sigret = expbuf_shift_bytes(&buf, &siglen)) == NULL) { - errno = 0; - dief("failed to parse response"); - } - memcpy(_sigret, sigret, siglen); - *_siglen = (unsigned)siglen; - expbuf_dispose(&buf); - - return (int)ret; -} - -static EVP_PKEY *ecdsa_create_pkey(neverbleed_t *nb, size_t key_index, int curve_name, const char *ec_pubkeybuf) -{ - struct st_neverbleed_rsa_exdata_t *exdata; - EC_KEY *ec_key; - EC_GROUP *ec_group; - BIGNUM *ec_pubkeybn = NULL; - EC_POINT *ec_pubkey; - EVP_PKEY *pkey; - - if ((exdata = malloc(sizeof(*exdata))) == NULL) { - fprintf(stderr, "no memory\n"); - abort(); - } - exdata->nb = nb; - exdata->key_index = key_index; - - ec_key = EC_KEY_new_method(nb->engine); - EC_KEY_set_ex_data(ec_key, 0, exdata); - - ec_group = EC_GROUP_new_by_curve_name(curve_name); - if (!ec_group) { - fprintf(stderr, "could not create EC_GROUP\n"); - abort(); - } - - EC_KEY_set_group(ec_key, ec_group); - - if (BN_hex2bn(&ec_pubkeybn, ec_pubkeybuf) == 0) { - fprintf(stderr, "failed to parse ECDSA ephemeral public key:%s\n", ec_pubkeybuf); - abort(); - } - - if ((ec_pubkey = EC_POINT_bn2point(ec_group, ec_pubkeybn, NULL, NULL)) == NULL) { - fprintf(stderr, "failed to get ECDSA ephemeral public key from BIGNUM\n"); - abort(); - } - - EC_KEY_set_public_key(ec_key, ec_pubkey); - - pkey = EVP_PKEY_new(); - EVP_PKEY_set1_EC_KEY(pkey, ec_key); - - EC_POINT_free(ec_pubkey); - BN_free(ec_pubkeybn); - EC_GROUP_free(ec_group); - EC_KEY_free(ec_key); - - return pkey; -} - -static void priv_ecdsa_finish(EC_KEY *key) -{ - struct st_neverbleed_rsa_exdata_t *exdata; - struct st_neverbleed_thread_data_t *thdata; - - ecdsa_get_privsep_data(key, &exdata, &thdata); - - struct expbuf_t buf = {NULL}; - size_t ret; - - expbuf_push_str(&buf, "del_ecdsa_key"); - expbuf_push_num(&buf, exdata->key_index); - if (expbuf_write(&buf, thdata->fd) != 0) - dief(errno != 0 ? "write error" : "connection closed by daemon"); - expbuf_dispose(&buf); - - if (expbuf_read(&buf, thdata->fd) != 0) - dief(errno != 0 ? "read error" : "connection closed by daemon"); - if (expbuf_shift_num(&buf, &ret) != 0) { - errno = 0; - dief("failed to parse response"); - } - expbuf_dispose(&buf); -} - -static int del_ecdsa_key_stub(struct expbuf_t *buf) -{ - size_t key_index; - int ret = 0; - - if (expbuf_shift_num(buf, &key_index) != 0) { - errno = 0; - warnf("%s: failed to parse request", __FUNCTION__); - return -1; - } - - if (!daemon_vars.keys.ecdsa_keys || key_index >= daemon_vars.keys.ecdsa_slots.reserved_size) { - errno = 0; - warnf("%s: invalid key index %zu", __FUNCTION__, key_index); - goto respond; - } - - if (BITCHECK(daemon_vars.keys.ecdsa_slots.bita_avail, key_index)) { - warnf("%s: index not in use %zu", __FUNCTION__, key_index); - goto respond; - } - - pthread_mutex_lock(&daemon_vars.keys.lock); - /* set slot as available */ - BITSET(daemon_vars.keys.ecdsa_slots.bita_avail, key_index); - daemon_vars.keys.ecdsa_slots.size--; - EC_KEY_free(daemon_vars.keys.ecdsa_keys[key_index]); - daemon_vars.keys.ecdsa_keys[key_index] = NULL; - pthread_mutex_unlock(&daemon_vars.keys.lock); - - ret = 1; - -respond: - expbuf_dispose(buf); - expbuf_push_num(buf, ret); - return 0; -} - -#endif - -int neverbleed_load_private_key_file(neverbleed_t *nb, SSL_CTX *ctx, const char *fn, char *errbuf) -{ - struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb); - struct expbuf_t buf = {NULL}; - int ret = 1; - size_t index, type; - EVP_PKEY *pkey; - - expbuf_push_str(&buf, "load_key"); - expbuf_push_str(&buf, fn); - if (expbuf_write(&buf, thdata->fd) != 0) - dief(errno != 0 ? "write error" : "connection closed by daemon"); - expbuf_dispose(&buf); - - if (expbuf_read(&buf, thdata->fd) != 0) - dief(errno != 0 ? "read error" : "connection closed by daemon"); - if (expbuf_shift_num(&buf, &type) != 0 || expbuf_shift_num(&buf, &index) != 0) { - errno = 0; - dief("failed to parse response"); - } - - switch (type) { - case NEVERBLEED_TYPE_RSA: { - char *estr, *nstr; - - if ((estr = expbuf_shift_str(&buf)) == NULL || (nstr = expbuf_shift_str(&buf)) == NULL) { - errno = 0; - dief("failed to parse response"); - } - pkey = create_pkey(nb, index, estr, nstr); - break; - } -#if OPENSSL_1_1_API - case NEVERBLEED_TYPE_ECDSA: { - char *ec_pubkeystr; - size_t curve_name; - - if (expbuf_shift_num(&buf, &curve_name) != 0 || (ec_pubkeystr = expbuf_shift_str(&buf)) == NULL) { - errno = 0; - dief("failed to parse response"); - } - pkey = ecdsa_create_pkey(nb, index, curve_name, ec_pubkeystr); - break; - } -#endif - default: { - char *errstr; - - if ((errstr = expbuf_shift_str(&buf)) == NULL) { - errno = 0; - dief("failed to parse response"); - } - - snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "%s", errstr); - return -1; - } - } - - expbuf_dispose(&buf); - - /* success */ - if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) { - snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "SSL_CTX_use_PrivateKey failed"); - ret = 0; - } - - EVP_PKEY_free(pkey); - return ret; -} - -static int load_key_stub(struct expbuf_t *buf) -{ - char *fn; - FILE *fp = NULL; - RSA *rsa = NULL; - size_t key_index = SIZE_MAX; - char *estr = NULL, *nstr = NULL, errbuf[NEVERBLEED_ERRBUF_SIZE] = ""; - size_t type = NEVERBLEED_TYPE_ERROR; - EVP_PKEY *pkey = NULL; - const EC_GROUP *ec_group; - BIGNUM *ec_pubkeybn = NULL; - char *ec_pubkeystr = NULL; - - if ((fn = expbuf_shift_str(buf)) == NULL) { - warnf("%s: failed to parse request", __FUNCTION__); - return -1; - } - - if ((fp = fopen(fn, "rt")) == NULL) { - strerror_r(errno, errbuf, sizeof(errbuf)); - goto Respond; - } - - if ((pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL) { - snprintf(errbuf, sizeof(errbuf), "failed to parse the private key"); - goto Respond; - } - - switch (EVP_PKEY_base_id(pkey)) { - case EVP_PKEY_RSA: { - const BIGNUM *e, *n; - - rsa = EVP_PKEY_get1_RSA(pkey); - type = NEVERBLEED_TYPE_RSA; - key_index = daemon_set_rsa(rsa); - RSA_get0_key(rsa, &n, &e, NULL); - estr = BN_bn2hex(e); - nstr = BN_bn2hex(n); - break; - } - case EVP_PKEY_EC: { -#if OPENSSL_1_1_API - const EC_POINT *ec_pubkey; - EC_KEY *ec_key; - - ec_key = EVP_PKEY_get0_EC_KEY(pkey); - type = NEVERBLEED_TYPE_ECDSA; - key_index = daemon_set_ecdsa(ec_key); - ec_group = EC_KEY_get0_group(ec_key); - ec_pubkey = EC_KEY_get0_public_key(ec_key); - ec_pubkeybn = BN_new(); - if (!EC_POINT_point2bn(ec_group, ec_pubkey, POINT_CONVERSION_COMPRESSED, ec_pubkeybn, NULL)) { - type = NEVERBLEED_TYPE_ERROR; - snprintf(errbuf, sizeof(errbuf), "failed to convert ECDSA public key to BIGNUM"); - goto Respond; - } - ec_pubkeystr = BN_bn2hex(ec_pubkeybn); - break; -#else - snprintf(errbuf, sizeof(errbuf), "ECDSA support requires OpenSSL >= 1.1.0"); - goto Respond; -#endif - } - default: - snprintf(errbuf, sizeof(errbuf), "unsupported private key: %d", EVP_PKEY_base_id(pkey)); - goto Respond; - } - -Respond: - expbuf_dispose(buf); - expbuf_push_num(buf, type); - expbuf_push_num(buf, key_index); - switch (type) { - case NEVERBLEED_TYPE_RSA: - expbuf_push_str(buf, estr != NULL ? estr : ""); - expbuf_push_str(buf, nstr != NULL ? nstr : ""); - break; - case NEVERBLEED_TYPE_ECDSA: - expbuf_push_num(buf, EC_GROUP_get_curve_name(ec_group)); - expbuf_push_str(buf, ec_pubkeystr); - break; - default: - expbuf_push_str(buf, errbuf); - } - if (rsa != NULL) - RSA_free(rsa); - if (pkey != NULL) - EVP_PKEY_free(pkey); - if (estr != NULL) - OPENSSL_free(estr); - if (nstr != NULL) - OPENSSL_free(nstr); - if (ec_pubkeystr != NULL) - OPENSSL_free(ec_pubkeystr); - if (ec_pubkeybn != NULL) - BN_free(ec_pubkeybn); - if (fp != NULL) - fclose(fp); - - return 0; -} - -int neverbleed_setuidgid(neverbleed_t *nb, const char *user, int change_socket_ownership) -{ - struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb); - struct expbuf_t buf = {NULL}; - size_t ret; - - expbuf_push_str(&buf, "setuidgid"); - expbuf_push_str(&buf, user); - expbuf_push_num(&buf, change_socket_ownership); - if (expbuf_write(&buf, thdata->fd) != 0) - dief(errno != 0 ? "write error" : "connection closed by daemon"); - expbuf_dispose(&buf); - - if (expbuf_read(&buf, thdata->fd) != 0) - dief(errno != 0 ? "read error" : "connection closed by daemon"); - if (expbuf_shift_num(&buf, &ret) != 0) { - errno = 0; - dief("failed to parse response"); - } - expbuf_dispose(&buf); - - return (int)ret; -} - -static int setuidgid_stub(struct expbuf_t *buf) -{ - const char *user; - size_t change_socket_ownership; - struct passwd pwbuf, *pw; - char pwstrbuf[65536]; /* should be large enough */ - int ret = -1; - - if ((user = expbuf_shift_str(buf)) == NULL || expbuf_shift_num(buf, &change_socket_ownership) != 0) { - errno = 0; - warnf("%s: failed to parse request", __FUNCTION__); - return -1; - } - - errno = 0; - if (getpwnam_r(user, &pwbuf, pwstrbuf, sizeof(pwstrbuf), &pw) != 0) { - warnf("%s: getpwnam_r failed", __FUNCTION__); - goto Respond; - } - if (pw == NULL) { - warnf("%s: failed to obtain information of user:%s", __FUNCTION__, user); - goto Respond; - } - - if (change_socket_ownership) { - char *dir; - if (chown(daemon_vars.nb->sun_.sun_path, pw->pw_uid, pw->pw_gid) != 0) - dief("chown failed for:%s", daemon_vars.nb->sun_.sun_path); - dir = dirname(daemon_vars.nb->sun_.sun_path); - if (chown(dir, pw->pw_uid, pw->pw_gid) != 0) - dief("chown failed for:%s", dir); - free(dir); - } - - /* setuidgid */ - if (setgid(pw->pw_gid) != 0) { - warnf("%s: setgid(%d) failed", __FUNCTION__, (int)pw->pw_gid); - goto Respond; - } - if (initgroups(pw->pw_name, pw->pw_gid) != 0) { - warnf("%s: initgroups(%s, %d) failed", __FUNCTION__, pw->pw_name, (int)pw->pw_gid); - goto Respond; - } - if (setuid(pw->pw_uid) != 0) { - warnf("%s: setuid(%d) failed\n", __FUNCTION__, (int)pw->pw_uid); - goto Respond; - } - ret = 0; - -Respond: - expbuf_dispose(buf); - expbuf_push_num(buf, ret); - return 0; -} - -__attribute__((noreturn)) static void *daemon_close_notify_thread(void *_close_notify_fd) -{ - int close_notify_fd = (int)((char *)_close_notify_fd - (char *)NULL); - char b; - ssize_t r; - -Redo: - r = read(close_notify_fd, &b, 1); - if (r == -1 && errno == EINTR) - goto Redo; - if (r > 0) - goto Redo; - /* close or error */ - - /* unlink the temporary directory and socket file */ - unlink_dir(dirname(daemon_vars.nb->sun_.sun_path)); - - _exit(0); -} - -static int priv_rsa_finish(RSA *rsa) -{ - struct st_neverbleed_rsa_exdata_t *exdata; - struct st_neverbleed_thread_data_t *thdata; - - get_privsep_data(rsa, &exdata, &thdata); - - struct expbuf_t buf = {NULL}; - size_t ret; - - expbuf_push_str(&buf, "del_rsa_key"); - expbuf_push_num(&buf, exdata->key_index); - if (expbuf_write(&buf, thdata->fd) != 0) - dief(errno != 0 ? "write error" : "connection closed by daemon"); - expbuf_dispose(&buf); - - if (expbuf_read(&buf, thdata->fd) != 0) - dief(errno != 0 ? "read error" : "connection closed by daemon"); - if (expbuf_shift_num(&buf, &ret) != 0) { - errno = 0; - dief("failed to parse response"); - } - expbuf_dispose(&buf); - - return (int)ret; -} - -static int del_rsa_key_stub(struct expbuf_t *buf) -{ - size_t key_index; - - int ret = 0; - - if (expbuf_shift_num(buf, &key_index) != 0) { - errno = 0; - warnf("%s: failed to parse request", __FUNCTION__); - return -1; - } - - if (!daemon_vars.keys.keys || key_index >= daemon_vars.keys.rsa_slots.reserved_size) { - errno = 0; - warnf("%s: invalid key index %zu", __FUNCTION__, key_index); - goto respond; - } - - if (BITCHECK(daemon_vars.keys.rsa_slots.bita_avail, key_index)) { - warnf("%s: index not in use %zu", __FUNCTION__, key_index); - goto respond; - } - - pthread_mutex_lock(&daemon_vars.keys.lock); - /* set slot as available */ - BITSET(daemon_vars.keys.rsa_slots.bita_avail, key_index); - daemon_vars.keys.rsa_slots.size--; - RSA_free(daemon_vars.keys.keys[key_index]); - daemon_vars.keys.keys[key_index] = NULL; - pthread_mutex_unlock(&daemon_vars.keys.lock); - - ret = 1; - -respond: - expbuf_dispose(buf); - expbuf_push_num(buf, ret); - return 0; -} - -static void *daemon_conn_thread(void *_sock_fd) -{ - int sock_fd = (int)((char *)_sock_fd - (char *)NULL); - struct expbuf_t buf = {NULL}; - unsigned char auth_token[NEVERBLEED_AUTH_TOKEN_SIZE]; - - /* authenticate */ - if (read_nbytes(sock_fd, &auth_token, sizeof(auth_token)) != 0) { - warnf("failed to receive authencication token from client"); - goto Exit; - } - if (memcmp(auth_token, daemon_vars.nb->auth_token, NEVERBLEED_AUTH_TOKEN_SIZE) != 0) { - warnf("client authentication failed"); - goto Exit; - } - - while (1) { - char *cmd; - if (expbuf_read(&buf, sock_fd) != 0) { - if (errno != 0) - warnf("read error"); - break; - } - if ((cmd = expbuf_shift_str(&buf)) == NULL) { - errno = 0; - warnf("failed to parse request"); - break; - } - if (strcmp(cmd, "priv_enc") == 0) { - if (priv_enc_stub(&buf) != 0) - break; - } else if (strcmp(cmd, "priv_dec") == 0) { - if (priv_dec_stub(&buf) != 0) - break; - } else if (strcmp(cmd, "sign") == 0) { - if (sign_stub(&buf) != 0) - break; -#if OPENSSL_1_1_API - } else if (strcmp(cmd, "ecdsa_sign") == 0) { - if (ecdsa_sign_stub(&buf) != 0) - break; - } else if (strcmp(cmd, "del_ecdsa_key") == 0) { - if (del_ecdsa_key_stub(&buf) != 0) - break; -#endif - } else if (strcmp(cmd, "load_key") == 0) { - if (load_key_stub(&buf) != 0) - break; - } else if (strcmp(cmd, "del_rsa_key") == 0) { - if (del_rsa_key_stub(&buf) != 0) - break; - } else if (strcmp(cmd, "setuidgid") == 0) { - if (setuidgid_stub(&buf) != 0) - break; - } else { - warnf("unknown command:%s", cmd); - break; - } - if (expbuf_write(&buf, sock_fd) != 0) { - warnf(errno != 0 ? "write error" : "connection closed by client"); - break; - } - expbuf_dispose(&buf); - } - -Exit: - expbuf_dispose(&buf); - close(sock_fd); - - return NULL; -} - -__attribute__((noreturn)) static void daemon_main(int listen_fd, int close_notify_fd, const char *tempdir) -{ - pthread_t tid; - pthread_attr_t thattr; - int sock_fd; - - { /* close all descriptors (except STDIN, STDOUT, STRERR, listen_fd, close_notify_fd) */ - int fd = (int)sysconf(_SC_OPEN_MAX) - 1; - for (; fd > 2; --fd) { - if (fd == listen_fd || fd == close_notify_fd) - continue; - close(fd); - } - } - - pthread_attr_init(&thattr); - pthread_attr_setdetachstate(&thattr, 1); - - if (pthread_create(&tid, &thattr, daemon_close_notify_thread, (char *)NULL + close_notify_fd) != 0) - dief("pthread_create failed"); - - while (1) { - while ((sock_fd = accept(listen_fd, NULL, NULL)) == -1) - ; - if (pthread_create(&tid, &thattr, daemon_conn_thread, (char *)NULL + sock_fd) != 0) - dief("pthread_create failed"); - } -} - -#if !OPENSSL_1_1_API - -static RSA_METHOD static_rsa_method = { - "privsep RSA method", /* name */ - NULL, /* rsa_pub_enc */ - NULL, /* rsa_pub_dec */ - priv_enc_proxy, /* rsa_priv_enc */ - priv_dec_proxy, /* rsa_priv_dec */ - NULL, /* rsa_mod_exp */ - NULL, /* bn_mod_exp */ - NULL, /* init */ - priv_rsa_finish, /* finish */ - RSA_FLAG_SIGN_VER, /* flags */ - NULL, /* app data */ - sign_proxy, /* rsa_sign */ - NULL, /* rsa_verify */ - NULL /* rsa_keygen */ -}; - -#endif - -int neverbleed_init(neverbleed_t *nb, char *errbuf) -{ - int pipe_fds[2] = {-1, -1}, listen_fd = -1; - char *tempdir = NULL; -#if OPENSSL_1_1_API - const RSA_METHOD *default_method = RSA_PKCS1_OpenSSL(); - EC_KEY_METHOD *ecdsa_method; - const EC_KEY_METHOD *ecdsa_default_method; - RSA_METHOD *rsa_method = RSA_meth_new("privsep RSA method", 0); - - RSA_meth_set_priv_enc(rsa_method, priv_enc_proxy); - RSA_meth_set_priv_dec(rsa_method, priv_dec_proxy); - RSA_meth_set_sign(rsa_method, sign_proxy); - - RSA_meth_set_pub_enc(rsa_method, RSA_meth_get_pub_enc(default_method)); - RSA_meth_set_pub_dec(rsa_method, RSA_meth_get_pub_dec(default_method)); - RSA_meth_set_verify(rsa_method, RSA_meth_get_verify(default_method)); - - RSA_meth_set_finish(rsa_method, priv_rsa_finish); - - /* setup EC_KEY_METHOD for ECDSA */ - ecdsa_default_method = EC_KEY_get_default_method(); - ecdsa_method = EC_KEY_METHOD_new(ecdsa_default_method); - - EC_KEY_METHOD_set_keygen(ecdsa_method, NULL); - EC_KEY_METHOD_set_compute_key(ecdsa_method, NULL); - /* it seems sign_sig and sign_setup is not used in TLS ECDSA. */ - EC_KEY_METHOD_set_sign(ecdsa_method, ecdsa_sign_proxy, NULL, NULL); - EC_KEY_METHOD_set_init(ecdsa_method, NULL, priv_ecdsa_finish, NULL, NULL, NULL, NULL); -#else - const RSA_METHOD *default_method = RSA_PKCS1_SSLeay(); - RSA_METHOD *rsa_method = &static_rsa_method; - - rsa_method->rsa_pub_enc = default_method->rsa_pub_enc; - rsa_method->rsa_pub_dec = default_method->rsa_pub_dec; - rsa_method->rsa_verify = default_method->rsa_verify; -#endif - - /* setup the daemon */ - if (pipe(pipe_fds) != 0) { - snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "pipe(2) failed:%s", strerror(errno)); - goto Fail; - } - set_cloexec(pipe_fds[1]); - if ((tempdir = strdup("/tmp/openssl-privsep.XXXXXX")) == NULL) { - snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "no memory"); - goto Fail; - } - if (mkdtemp(tempdir) == NULL) { - snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "failed to create temporary directory under /tmp:%s", strerror(errno)); - goto Fail; - } - memset(&nb->sun_, 0, sizeof(nb->sun_)); - nb->sun_.sun_family = AF_UNIX; - snprintf(nb->sun_.sun_path, sizeof(nb->sun_.sun_path), "%s/_", tempdir); - RAND_bytes(nb->auth_token, sizeof(nb->auth_token)); - if ((listen_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { - snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "socket(2) failed:%s", strerror(errno)); - goto Fail; - } - if (bind(listen_fd, (void *)&nb->sun_, sizeof(nb->sun_)) != 0) { - snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "failed to bind to %s:%s", nb->sun_.sun_path, strerror(errno)); - goto Fail; - } - if (listen(listen_fd, SOMAXCONN) != 0) { - snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "listen(2) failed:%s", strerror(errno)); - goto Fail; - } - nb->daemon_pid = fork(); - switch (nb->daemon_pid) { - case -1: - snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "fork(2) failed:%s", strerror(errno)); - goto Fail; - case 0: - close(pipe_fds[1]); -#ifdef __linux__ - prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); -#endif - daemon_vars.nb = nb; - daemon_main(listen_fd, pipe_fds[0], tempdir); - break; - default: - break; - } - close(listen_fd); - listen_fd = -1; - close(pipe_fds[0]); - pipe_fds[0] = -1; - - /* setup engine */ - if ((nb->engine = ENGINE_new()) == NULL || !ENGINE_set_id(nb->engine, "neverbleed") || - !ENGINE_set_name(nb->engine, "privilege separation software engine") || !ENGINE_set_RSA(nb->engine, rsa_method) -#if OPENSSL_1_1_API - || !ENGINE_set_EC(nb->engine, ecdsa_method) -#endif - ) { - snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "failed to initialize the OpenSSL engine"); - goto Fail; - } - ENGINE_add(nb->engine); - - /* setup thread key */ - pthread_key_create(&nb->thread_key, dispose_thread_data); - - free(tempdir); - return 0; -Fail: - if (pipe_fds[0] != -1) - close(pipe_fds[0]); - if (pipe_fds[1] != -1) - close(pipe_fds[1]); - if (tempdir != NULL) { - unlink_dir(tempdir); - free(tempdir); - } - if (listen_fd != -1) - close(listen_fd); - if (nb->engine != NULL) { - ENGINE_free(nb->engine); - nb->engine = NULL; - } - return -1; -} diff --git a/web/server/h2o/libh2o/deps/neverbleed/neverbleed.h b/web/server/h2o/libh2o/deps/neverbleed/neverbleed.h deleted file mode 100644 index 4abfad479..000000000 --- a/web/server/h2o/libh2o/deps/neverbleed/neverbleed.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2015 Kazuho Oku, DeNA Co., Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#ifndef NEVERBLEED_H -#define NEVERBLEED_H - -#include <pthread.h> -#include <sys/un.h> -#include <openssl/engine.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#define NEVERBLEED_ERRBUF_SIZE (256) -#define NEVERBLEED_AUTH_TOKEN_SIZE 32 - -typedef struct st_neverbleed_t { - ENGINE *engine; - pid_t daemon_pid; - struct sockaddr_un sun_; - pthread_key_t thread_key; - unsigned char auth_token[NEVERBLEED_AUTH_TOKEN_SIZE]; -} neverbleed_t; - -/** - * initializes the privilege separation engine (returns 0 if successful) - */ -int neverbleed_init(neverbleed_t *nb, char *errbuf); -/** - * loads a private key file (returns 1 if successful) - */ -int neverbleed_load_private_key_file(neverbleed_t *nb, SSL_CTX *ctx, const char *fn, char *errbuf); -/** - * setuidgid (also changes the file permissions so that `user` can connect to the daemon, if change_socket_ownership is non-zero) - */ -int neverbleed_setuidgid(neverbleed_t *nb, const char *user, int change_socket_ownership); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/web/server/h2o/libh2o/deps/neverbleed/test.c b/web/server/h2o/libh2o/deps/neverbleed/test.c deleted file mode 100644 index 494fc65f0..000000000 --- a/web/server/h2o/libh2o/deps/neverbleed/test.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2015 Kazuho Oku, DeNA Co., Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#include <netinet/in.h> -#include <stdio.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> -#include <openssl/crypto.h> -#include <openssl/ssl.h> -#include "neverbleed.h" - -static void setup_ecc_key(SSL_CTX *ssl_ctx) -{ - int nid = NID_X9_62_prime256v1; - EC_KEY *key = EC_KEY_new_by_curve_name(nid); - if (key == NULL) { - fprintf(stderr, "Failed to create curve \"%s\"\n", OBJ_nid2sn(nid)); - return; - } - SSL_CTX_set_tmp_ecdh(ssl_ctx, key); - EC_KEY_free(key); -} - -int dumb_https_server(unsigned short port, SSL_CTX *ctx) -{ - int listen_fd, reuse_flag; - struct sockaddr_in sin = {}; - - if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - fprintf(stderr, "failed to create socket:%s\n", strerror(errno)); - return 111; - } - reuse_flag = 1; - setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuse_flag, sizeof(reuse_flag)); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(0x7f000001); - sin.sin_port = htons(8888); - if (bind(listen_fd, (void *)&sin, sizeof(sin)) != 0) { - fprintf(stderr, "bind failed:%s\n", strerror(errno)); - return 111; - } - if (listen(listen_fd, SOMAXCONN) != 0) { - fprintf(stderr, "listen failed:%s\n", strerror(errno)); - return 111; - } - - while (1) { - int conn_fd; - SSL *ssl; - char buf[4096]; - /* accept connection */ - while ((conn_fd = accept(listen_fd, NULL, NULL)) == -1 && errno == EINTR) - ; - if (conn_fd == -1) { - fprintf(stderr, "accept(2) failed:%s\n", strerror(errno)); - return 111; - } - ssl = SSL_new(ctx); - SSL_set_fd(ssl, conn_fd); - if (SSL_accept(ssl) == 1) { - SSL_read(ssl, buf, sizeof(buf)); - const char *resp = - "HTTP/1.0 200 OK\r\nContent-Length: 6\r\nConnection: close\r\nContent-Type: text/plain\r\n\r\nhello\n"; - SSL_write(ssl, resp, strlen(resp)); - SSL_shutdown(ssl); - } else { - fprintf(stderr, "SSL_accept failed\n"); - } - SSL_free(ssl); - close(conn_fd); - } -} - -int main(int argc, char **argv) -{ - unsigned short port; - SSL_CTX *ctx; - neverbleed_t nb; - char errbuf[NEVERBLEED_ERRBUF_SIZE]; - int use_privsep; - - /* initialization */ - SSL_load_error_strings(); - SSL_library_init(); - OpenSSL_add_all_algorithms(); - if (neverbleed_init(&nb, errbuf) != 0) { - fprintf(stderr, "openssl_privsep_init: %s\n", errbuf); - return 111; - } - ctx = SSL_CTX_new(SSLv23_server_method()); - SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION); - setup_ecc_key(ctx); - - /* parse args */ - if (argc != 5) { - fprintf(stderr, "Usage: %s <internal|privsep> <port> <certificate-chain-file> <private-key-file>\n", argv[0]); - return 111; - } - if (strcmp(argv[1], "internal") == 0) { - use_privsep = 0; - } else if (strcmp(argv[1], "privsep") == 0) { - use_privsep = 1; - } else { - fprintf(stderr, "unknown mode:%s\n", argv[1]); - return 111; - } - if (sscanf(argv[2], "%hu", &port) != 1) { - fprintf(stderr, "failed to parse port:%s\n", argv[2]); - return 111; - } - if (SSL_CTX_use_certificate_chain_file(ctx, argv[3]) != 1) { - fprintf(stderr, "failed to load certificate chain file:%s\n", argv[3]); - return 111; - } - if (use_privsep) { - if (neverbleed_load_private_key_file(&nb, ctx, argv[4], errbuf) != 1) { - fprintf(stderr, "failed to load private key from file:%s:%s\n", argv[4], errbuf); - return 111; - } - } else { - if (SSL_CTX_use_PrivateKey_file(ctx, argv[4], SSL_FILETYPE_PEM) != 1) { - fprintf(stderr, "failed to load private key from file:%s\n", argv[4]); - return 111; - } - } - - /* start the httpd */ - return dumb_https_server(port, ctx); -} |