Commit b5d01d9c authored by Geoff Simmons's avatar Geoff Simmons

Add the set_key() function, and the KEY_* interface.

We are no longer using hard-wired keys.
parent 7ec2a695
......@@ -14,7 +14,8 @@ libvmod_ece_la_SOURCES = \
vfp.h \
vfp_set_salt.c \
foreign/vend.h \
keys.h
keys.h \
keys.c
nodist_libvmod_ece_la_SOURCES = \
vcc_if.c \
......
/*-
* Copyright (c) 2019 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "config.h"
// for pthread_rwlock_* and posix_memalign()
#define _POSIX_C_SOURCE 200112L
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include "cache/cache.h"
#include "verrno.h"
/* XXX grr */
#undef ZERO_OBJ
#define ZERO_OBJ(to, sz) (void)memset(to, 0, sz)
#include "keys.h"
#include "rfc8188.h"
/* XXX add VCL_TIME fields for time added and updated */
struct key {
unsigned magic;
#define KEY_MAGIC 0xb4f7d1eb
VRBT_ENTRY(key) entry;
uint8_t *key;
uint8_t *id;
uint8_t idlen;
};
static inline int
key_cmp(const struct key *k1, const struct key *k2)
{
assert(k1->idlen == k2->idlen);
return (memcmp(k1->id, k2->id, k1->idlen));
}
VRBT_HEAD(key_tree, key);
VRBT_PROTOTYPE_STATIC(key_tree, key, entry, key_cmp);
VRBT_GENERATE_STATIC(key_tree, key, entry, key_cmp);
struct key_ent {
struct key_tree tree;
pthread_rwlock_t lock;
};
static struct key_ent key_tbl[UINT8_MAX + 1];
static long pagesz = 0;
static struct VSC_lck *lck_page;
static struct lock page_mtx;
/* List of allocated key pages */
struct page_ent {
unsigned magic;
#define PAGE_ENTRY_MAGIC 0xde66bce4
VTAILQ_ENTRY(page_ent) list;
void *addr;
};
VTAILQ_HEAD(page_head_s, page_ent) page_head
= VTAILQ_HEAD_INITIALIZER(page_head);
/* Free list of key addresses */
struct addr_ent {
unsigned magic;
#define ADDR_ENTRY_MAGIC 0xde66bce4
VSTAILQ_ENTRY(addr_ent) list;
uint8_t *addr;
};
VSTAILQ_HEAD(addr_head_s, addr_ent) addr_head
= VSTAILQ_HEAD_INITIALIZER(addr_head);
/* allocate a page, mlock it, fill the free list with addresses */
/* XXX the number of pages in use only increases, never decreases */
static int
key_alloc_page(VRT_CTX)
{
void *page, *p;
struct page_ent *page_ent;
struct addr_ent *addr_ent;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
AN(pagesz);
Lck_Lock(&page_mtx);
errno = 0;
if (posix_memalign(&page, pagesz, pagesz) != 0) {
VRT_fail(ctx, "allocating space for keys: %s",
vstrerror(errno));
goto fail;
}
errno = 0;
if (mlock(page, pagesz) != 0) {
VRT_fail(ctx, "cannot lock memory for keys (mlock(2)): %s",
vstrerror(errno));
goto fail;
}
errno = 0;
ALLOC_OBJ(page_ent, PAGE_ENTRY_MAGIC);
if (page_ent == NULL) {
VRT_fail(ctx, "allocating page entry: %s", vstrerror(errno));
goto fail;
}
page_ent->addr = page;
VTAILQ_INSERT_HEAD(&page_head, page_ent, list);
for (p = page; p < page + pagesz; p += AES128_KEYLEN) {
errno = 0;
ALLOC_OBJ(addr_ent, ADDR_ENTRY_MAGIC);
if (addr_ent == NULL) {
VRT_fail(ctx, "allocating address entry: %s",
vstrerror(errno));
goto fail;
}
addr_ent->addr = p;
VSTAILQ_INSERT_TAIL(&addr_head, addr_ent, list);
}
Lck_Unlock(&page_mtx);
return (0);
fail:
Lck_Unlock(&page_mtx);
return (-1);
}
int
KEY_Init(VRT_CTX)
{
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
errno = 0;
pagesz = sysconf(_SC_PAGESIZE);
if (pagesz < 1) {
VRT_fail(ctx, "cannot determine page size: %s",
vstrerror(errno));
return (-1);
}
/* requirements for posix_memalign() */
if ((pagesz & (pagesz - 1)) != 0) {
VRT_fail(ctx, "page size %ld is not a power of two", pagesz);
return (-1);
}
if ((pagesz % sizeof(void *)) != 0) {
VRT_fail(ctx, "page size %ld is not a multiple of address size "
"%zu", pagesz, sizeof(void *));
return (-1);
}
lck_page = Lck_CreateClass(NULL, "ece.key_mem");
AN(lck_page);
Lck_New(&page_mtx, lck_page);
if (key_alloc_page(ctx) != 0)
return (-1);
for (unsigned i = 0; i < UINT8_MAX; i++) {
VRBT_INIT(&key_tbl[i].tree);
AZ(pthread_rwlock_init(&key_tbl[i].lock, NULL));
}
return (0);
}
static inline void
wipe(void * const dst, size_t len, uint8_t val)
{
volatile uint8_t *p = (volatile uint8_t *)dst;
while (((uintptr_t)p & (sizeof(uint64_t)-1)) && len) {
*p++ = val;
len--;
}
if (len >= sizeof(uint64_t)) {
volatile uint64_t *p64 = (volatile void *)p;
uint64_t val64 = (uint64_t)0x0101010101010101 * val;
do {
*p64++ = val64;
len -= sizeof(uint64_t);
} while (len >= sizeof(uint64_t));
p = (volatile void *)p64;
}
while (len) {
*p++ = val;
len--;
}
}
/*
* wipe all the keys, destroy the rwlocks, de-allocate the free list,
* de-allocate the key pages
*/
void
KEY_Fini(void)
{
struct key_tree *tree_h;
struct key *key, *nxt_k;
struct addr_ent *addr, *nxt_addr;
struct page_ent *page, *nxt_page;
for (unsigned i = 0; i < UINT8_MAX; i++) {
AZ(pthread_rwlock_destroy(&key_tbl[i].lock));
tree_h = &key_tbl[i].tree;
if (!VRBT_EMPTY(tree_h)) {
key = VRBT_ROOT(tree_h);
while (key != NULL) {
CHECK_OBJ(key, KEY_MAGIC);
wipe(key->key, 16, 0xff);
wipe(key->key, 16, 0xaa);
wipe(key->key, 16, 0x55);
wipe(key->key, 16, 0x00);
nxt_k = VRBT_NEXT(key_tree, tree_h, key);
VRBT_REMOVE(key_tree, tree_h, key);
FREE_OBJ(key);
key = nxt_k;
}
}
}
if (!VSTAILQ_EMPTY(&addr_head)) {
addr = VSTAILQ_FIRST(&addr_head);
while (addr != NULL) {
CHECK_OBJ(addr, ADDR_ENTRY_MAGIC);
nxt_addr = VSTAILQ_NEXT(addr, list);
VSTAILQ_REMOVE_HEAD(&addr_head, list);
FREE_OBJ(addr);
addr = nxt_addr;
}
}
if (!VTAILQ_EMPTY(&page_head)) {
page = VTAILQ_FIRST(&page_head);
while (page != NULL) {
CHECK_OBJ(page, PAGE_ENTRY_MAGIC);
AN(page->addr);
free(page->addr);
nxt_page = VTAILQ_NEXT(page, list);
VTAILQ_REMOVE(&page_head, page, list);
FREE_OBJ(page);
page = nxt_page;
}
}
Lck_Delete(&page_mtx);
}
void
KEY_Rdlock(uint8_t idlen)
{
AZ(pthread_rwlock_rdlock(&key_tbl[idlen].lock));
}
void
KEY_Unlock(uint8_t idlen)
{
AZ(pthread_rwlock_unlock(&key_tbl[idlen].lock));
}
static inline void
key_wrlock(uint8_t idlen)
{
AZ(pthread_rwlock_wrlock(&key_tbl[idlen].lock));
}
static inline struct key *
key_find(struct key_tree *tree_h, uint8_t *id, uint8_t idlen)
{
struct key *key;
struct key keycmp;
AN(tree_h);
if (VRBT_EMPTY(tree_h))
return NULL;
if (idlen != 0) {
keycmp.id = id;
keycmp.idlen = idlen;
key = VRBT_FIND(key_tree, tree_h, &keycmp);
}
else
key = VRBT_MIN(key_tree, tree_h);
CHECK_OBJ_ORNULL(key, KEY_MAGIC);
return (key);
}
uint8_t *
KEY_Get(uint8_t *id, uint8_t idlen)
{
struct key_tree *tree_h;
struct key *key;
AN(id);
tree_h = &key_tbl[idlen].tree;
key = key_find(tree_h, id, idlen);
if (key == NULL)
return (NULL);
CHECK_OBJ(key, KEY_MAGIC);
AN(key->key);
return (key->key);
}
int
KEY_Set(VRT_CTX, uint8_t *id, uint8_t idlen, const uint8_t *key)
{
struct key_tree *tree_h;
struct key *k;
struct addr_ent *addr_ent;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
AN(id);
AN(key);
key_wrlock(idlen);
tree_h = &key_tbl[idlen].tree;
k = key_find(tree_h, id, idlen);
if (k == NULL) {
errno = 0;
ALLOC_OBJ(k, KEY_MAGIC);
if (k == NULL) {
VRT_fail(ctx, "cannot allocate key entry: %s",
vstrerror(errno));
goto fail;
}
errno = 0;
k->id = malloc(idlen);
if (k->id == NULL) {
VRT_fail(ctx, "cannot allocate key id: %s",
vstrerror(errno));
goto fail;
}
if (VSTAILQ_EMPTY(&addr_head))
if (key_alloc_page(ctx) != 0)
goto fail;
assert(!VSTAILQ_EMPTY(&addr_head));
addr_ent = VSTAILQ_FIRST(&addr_head);
AN(addr_ent);
AN(addr_ent->addr);
VSTAILQ_REMOVE_HEAD(&addr_head, list);
k->key = addr_ent->addr;
memcpy(k->id, id, idlen);
k->idlen = idlen;
VRBT_INSERT(key_tree, tree_h, k);
}
CHECK_OBJ_NOTNULL(k, KEY_MAGIC);
memcpy(k->key, key, AES128_KEYLEN);
KEY_Unlock(idlen);
return (0);
fail:
KEY_Unlock(idlen);
return (-1);
}
......@@ -27,50 +27,14 @@
*/
#include <stdint.h>
#include <stddef.h>
#include <string.h>
/*
* XXX mockup for testing
* These are the keys in the two examples in chs 3.1 & 3.2 of RFC 8188.
*/
static struct key {
char *id;
const uint8_t idlen;
uint8_t key[16];
} key[] = {
{
"",
0,
{
0xca, 0xa7, 0x65, 0x67, 0xeb, 0x58, 0x7a, 0x67,
0xe8, 0x81, 0x29, 0xaf, 0xed, 0x6b, 0x39, 0x3d,
},
},
{
"a1",
2,
{
0x04, 0xed, 0xd9, 0x54, 0xfc, 0x54, 0x96, 0x72,
0xce, 0x45, 0xb5, 0x46, 0x32, 0x96, 0xd3, 0xd5,
},
},
{
NULL,
0,
{ 0 }
},
};
#ifndef VRT_H_INCLUDED
#include "vrt.h"
#endif
static inline uint8_t *
get_key(uint8_t *id, uint8_t idlen)
{
for (int i = 0; key[i].id != NULL; i++)
if (idlen != key[i].idlen)
continue;
else if (idlen == 0 && key[i].idlen == 0)
return key[i].key;
else if (memcmp(id, key[i].id, idlen) == 0)
return key[i].key;
return (NULL);
}
int KEY_Init(VRT_CTX);
void KEY_Fini(void);
void KEY_Rdlock(uint8_t idlen);
void KEY_Unlock(uint8_t idlen);
uint8_t *KEY_Get(uint8_t *id, uint8_t idlen);
int KEY_Set(VRT_CTX, uint8_t *id, uint8_t idlen, const uint8_t *key);
......@@ -7,6 +7,10 @@ varnish v1 -vcl {
backend b { .host = "${bad_ip}"; }
} -start
varnish v1 -vsc LCK.ece.key_mem.*
varnish v1 -expect LCK.ece.key_mem.creat == 1
varnish v1 -expect LCK.ece.key_mem.locks == 1
varnish v1 -vcl {backend b { .host = "${bad_ip}"; }}
varnish v1 -cli "vcl.list"
......@@ -20,6 +24,12 @@ varnish v1 -cli "vcl.use vcl2"
varnish v1 -cli "vcl.discard vcl1"
varnish v1 -cli "vcl.list"
# Lock destroyed on last discard
varnish v1 -vsc LCK.ece.key_mem.*
varnish v1 -expect LCK.ece.key_mem.creat == 1
varnish v1 -expect LCK.ece.key_mem.destroy == 1
varnish v1 -expect LCK.ece.key_mem.locks == 1
varnish v1 -vcl {
import ${vmod_ece};
backend b { .host = "${bad_ip}"; }
......
......@@ -25,6 +25,14 @@ server s1 {
varnish v1 -arg "-p vsl_mask=+VfpAcct" -vcl+backend {
import ${vmod_ece};
import blob;
sub vcl_init {
ece.set_key("", blob.decode(BASE64,
encoded="yqdlZ+tYemfogSmv7Ws5PQ=="));
ece.set_key("a1", blob.decode(BASE64,
encoded="BO3ZVPxUlnLORbVGMpbT1Q=="));
}
sub vcl_backend_response {
set beresp.filters = "ece_decrypt";
......
......@@ -19,6 +19,12 @@ server s1 {
varnish v1 -arg "-p vsl_mask=+VfpAcct" -vcl+backend {
import ${vmod_ece};
import blob;
sub vcl_init {
ece.set_key("", blob.decode(BASE64,
encoded="yqdlZ+tYemfogSmv7Ws5PQ=="));
}
# When set-salt is enabled, the base64-encoded salt can be
# read from bereq header XYZZY-ECE-Salt.
......
......@@ -46,6 +46,12 @@ Enim per accumsan, augue id maecenas bibendum ullamcorper in fermentum, platea f
varnish v1 -arg "-p vsl_mask=+VfpAcct" -vcl+backend {
import ${vmod_ece};
import blob;
sub vcl_init {
ece.set_key("a1", blob.decode(BASE64,
encoded="BO3ZVPxUlnLORbVGMpbT1Q=="));
}
sub vcl_backend_response {
set bereq.http.X-ECE-Key-ID = "a1";
......
......@@ -28,6 +28,8 @@
#include "config.h"
#include <string.h>
#include "cache/cache.h"
/* XXX grr */
......@@ -256,13 +258,13 @@ crypto_init(struct vfp_ctx *ctx, struct ece_crypto *crypto, uint8_t *salt,
AZ(crypto->seq_hi);
AZ(crypto->seq_lo);
/* XXX rdlock key table */
key = get_key(id, idlen);
KEY_Rdlock(idlen);
key = KEY_Get(id, idlen);
if (key == NULL)
vp = VERR_DEC(ctx, "unknown key %.*s", idlen, id);
else if (derive_prk(salt, key, prk, errmsg) != 0)
vp = VERR_DEC(ctx, "%s", errmsg);
/* XXX rdunlock key table */
KEY_Unlock(idlen);
if (vp == VFP_ERROR)
return (vp);
......
......@@ -28,6 +28,8 @@
#include "config.h"
#include <string.h>
#include <openssl/crypto.h>
#include "cache/cache.h"
......@@ -35,9 +37,9 @@
#include "vcc_if.h"
#include "vfp.h"
#include "keys.h"
/* Event function */
int
VPFX(event)(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
{
......@@ -45,14 +47,23 @@ VPFX(event)(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
AN(priv);
static int loaded = 0;
switch(e) {
case VCL_EVENT_LOAD:
VRT_AddVFP(ctx, &vfp_encrypt);
VRT_AddVFP(ctx, &vfp_decrypt);
assert(loaded >= 0);
if (loaded++ == 0)
if (KEY_Init(ctx) != 0)
return (-1);
return (0);
case VCL_EVENT_DISCARD:
VRT_RemoveVFP(ctx, &vfp_encrypt);
VRT_RemoveVFP(ctx, &vfp_decrypt);
AN(loaded);
if (--loaded == 0)
KEY_Fini();
return (0);
case VCL_EVENT_WARM:
case VCL_EVENT_COLD:
......@@ -63,6 +74,33 @@ VPFX(event)(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
NEEDLESS(return (0));
}
VCL_VOID
vmod_set_key(VRT_CTX, VCL_STRING id, VCL_BLOB key)
{
size_t len;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
AN(id);
AN(key);
if (key->blob == NULL) {
VRT_fail(ctx, "key contents are empty");
return;
}
if (key->len != 16) {
VRT_fail(ctx, "illegal key length %zu (must be 16)", key->len);
return;
}
len = strlen(id);
if (len > 255) {
VRT_fail(ctx, "id too long (length %zu > 255)", len);
return;
}
/* KEY_Set calls VRT_fail() on error. */
(void)KEY_Set(ctx, (uint8_t *)id, (uint8_t)len, key->blob);
}
VCL_STRING
vmod_libcrypto_version(VRT_CTX)
{
......
......@@ -56,6 +56,13 @@ Encryption and HTTP
XXX ...
$Function VOID set_key(STRING id, BLOB key)
Set the keying material identified by ``id`` to the contents of the
blob ``key``.
XXX ...
$Function STRING libcrypto_version()
Return the libcrypto version string.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment