123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- Subject: Move key handling to tang itself
- Origin: v7-8-g7119454 <https://github.com/latchset/tang/commit/v7-8-g7119454>
- Upstream-Author: Sergio Correia <scorreia@redhat.com>
- Date: Fri Nov 27 22:02:04 2020 -0300
- Use the key manipulation functions added in src/keys.{c|h} in tangd.
- This effectively removes the need for a cache directory -- usually
- /var/cache/tang --, which contained pre-computed files with signed
- advertisements and JWK with keys for deriving new keys.
- This computation was done by the tangd-update script, which has also
- been removed in this commit.
- We relied on systemd to run this script whenever the JWK dir -- usually
- /var/db/tang, which is where the actual keys are located -- changed, to
- keep the cache directory updated, but this is sometimes unreliable,
- causing issues like the ones reported in #23 and #24.
- As of now, tang performs these computations itself and does not depend
- on external scripts to make sure it has reliable information regarding
- its keys.
- Additionally, tang also creates a new pair of keys if none exist.
- --- a/meson.build
- +++ b/meson.build
- @@ -16,14 +16,12 @@
- bindir = join_paths(get_option('prefix'), get_option('bindir'))
- systemunitdir = join_paths(get_option('prefix'), '../lib/systemd/system')
- licensedir = join_paths(get_option('prefix'), 'share', 'licenses', meson.project_name())
- -cachedir = join_paths(get_option('localstatedir'), 'cache', meson.project_name())
- jwkdir = join_paths(get_option('localstatedir'), 'db', meson.project_name())
-
- data = configuration_data()
- data.set('libexecdir', libexecdir)
- data.set('sysconfdir', sysconfdir)
- data.set('systemunitdir', systemunitdir)
- -data.set('cachedir', cachedir)
- data.set('jwkdir', jwkdir)
-
- add_project_arguments(
- --- a/src/meson.build
- +++ b/src/meson.build
- @@ -1,6 +1,6 @@
- tangd = executable('tangd',
- - 'http.h',
- 'http.c',
- + 'keys.c',
- 'tangd.c',
- dependencies: [jose, http_parser],
- install: true,
- @@ -9,6 +9,5 @@
-
- bins += join_paths(meson.current_source_dir(), 'tang-show-keys')
- libexecbins += join_paths(meson.current_source_dir(), 'tangd-keygen')
- -libexecbins += join_paths(meson.current_source_dir(), 'tangd-update')
-
- # vim:set ts=2 sw=2 et:
- --- a/src/tangd-update
- +++ /dev/null
- @@ -1,83 +0,0 @@
- -#!/bin/bash
- -# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
- -#
- -# Copyright (c) 2016 Red Hat, Inc.
- -# Author: Nathaniel McCallum <npmccallum@redhat.com>
- -#
- -# This program is free software: you can redistribute it and/or modify
- -# it under the terms of the GNU General Public License as published by
- -# the Free Software Foundation, either version 3 of the License, or
- -# (at your option) any later version.
- -#
- -# This program is distributed in the hope that it will be useful,
- -# but WITHOUT ANY WARRANTY; without even the implied warranty of
- -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- -# GNU General Public License for more details.
- -#
- -# You should have received a copy of the GNU General Public License
- -# along with this program. If not, see <http://www.gnu.org/licenses/>.
- -#
- -
- -TMP='{"protected":{"cty":"jwk-set+json"}}'
- -
- -trap 'exit' ERR
- -
- -shopt -s nullglob
- -
- -HASHES=`jose alg -k hash`
- -
- -if [ $# -ne 2 ] || [ ! -d "$1" ]; then
- - echo "Usage: $0 <jwkdir> <cachedir>" >&2
- - exit 1
- -fi
- -
- -[ ! -d "$2" ] && mkdir -p -m 0700 "$2"
- -
- -src=`realpath "$1"`
- -dst=`realpath "$2"`
- -
- -payl=()
- -sign=()
- -
- -for jwk in $src/*.jwk; do
- - if jose jwk use -i "$jwk" -r -u sign -u verify; then
- - sign+=("-s" "$TMP" "-k" "$jwk")
- - payl+=("-i" "$jwk")
- - elif jose jwk use -i "$jwk" -r -u deriveKey; then
- - payl+=("-i" "$jwk")
- - else
- - echo "Skipping invalid key: $jwk" >&2
- - fi
- -done
- -
- -if [ ${#sign[@]} -gt 0 ]; then
- - jose jwk pub -s "${payl[@]}" \
- - | jose jws sig -I- "${sign[@]}" -o "$dst/.default.jws"
- - mv -f "$dst/.default.jws" "$dst/default.jws"
- - new=default.jws
- -fi
- -
- -shopt -s dotglob
- -
- -for jwk in $src/*.jwk; do
- - for hsh in $HASHES; do
- - thp=`jose jwk thp -i "$jwk" -a $hsh`
- -
- - if jose jwk use -i "$jwk" -r -u deriveKey; then
- - ln -sf "$jwk" "$dst/.$thp.jwk"
- - mv -f "$dst/.$thp.jwk" "$dst/$thp.jwk"
- - new="$new\n$thp.jwk"
- - elif jose jwk use -i "$jwk" -r -u sign; then
- - keys=("${sign[@]}" -s "$TMP" -k "$jwk")
- - jose jwk pub -s "${payl[@]}" \
- - | jose jws sig -I- "${keys[@]}" -o "$dst/.$thp.jws"
- - mv -f "$dst/.$thp.jws" "$dst/$thp.jws"
- - new="$new\n$thp.jws"
- - fi
- - done
- -done
- -
- -for f in "$dst"/*; do
- - b=`basename "$f"`
- - echo -e "$new" | grep -q "^$b\$" || rm -f "$f"
- -done
- --- a/src/tangd.c
- +++ b/src/tangd.c
- @@ -28,6 +28,7 @@
- #include <unistd.h>
-
- #include <jose/jose.h>
- +#include "keys.h"
-
- static void
- str_cleanup(char **str)
- @@ -36,23 +37,20 @@
- free(*str);
- }
-
- -static void
- -FILE_cleanup(FILE **file)
- -{
- - if (file && *file)
- - fclose(*file);
- -}
- -
- static int
- adv(enum http_method method, const char *path, const char *body,
- regmatch_t matches[], void *misc)
- {
- - __attribute__((cleanup(FILE_cleanup))) FILE *file = NULL;
- __attribute__((cleanup(str_cleanup))) char *adv = NULL;
- __attribute__((cleanup(str_cleanup))) char *thp = NULL;
- - char filename[PATH_MAX] = {};
- - const char *cachedir = misc;
- - struct stat st = {};
- + __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info *tki = NULL;
- + json_auto_t *jws = NULL;
- + const char *jwkdir = misc;
- +
- + tki = read_keys(jwkdir);
- + if (!tki || tki->m_keys_count == 0) {
- + return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
- + }
-
- if (matches[1].rm_so < matches[1].rm_eo) {
- size_t size = matches[1].rm_eo - matches[1].rm_so;
- @@ -61,24 +59,15 @@
- return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
- }
-
- - if (snprintf(filename, sizeof(filename),
- - "%s/%s.jws", cachedir, thp ? thp : "default") < 0)
- - return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
- -
- - file = fopen(filename, "r");
- - if (!file)
- + jws = find_jws(tki, thp);
- + if (!jws) {
- return http_reply(HTTP_STATUS_NOT_FOUND, NULL);
- + }
-
- - if (fstat(fileno(file), &st) != 0)
- - return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
- -
- - adv = calloc(st.st_size + 1, 1);
- + adv = json_dumps(jws, 0);
- if (!adv)
- return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
-
- - if (fread(adv, st.st_size, 1, file) != 1)
- - return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
- -
- return http_reply(HTTP_STATUS_OK,
- "Content-Type: application/jose+json\r\n"
- "Content-Length: %zu\r\n"
- @@ -91,9 +80,9 @@
- {
- __attribute__((cleanup(str_cleanup))) char *enc = NULL;
- __attribute__((cleanup(str_cleanup))) char *thp = NULL;
- + __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info *tki = NULL;
- size_t size = matches[1].rm_eo - matches[1].rm_so;
- - char filename[PATH_MAX] = {};
- - const char *cachedir = misc;
- + const char *jwkdir = misc;
- json_auto_t *jwk = NULL;
- json_auto_t *req = NULL;
- json_auto_t *rep = NULL;
- @@ -124,15 +113,16 @@
- /*
- * Parse and validate the server-side JWK
- */
- + tki = read_keys(jwkdir);
- + if (!tki || tki->m_keys_count == 0) {
- + return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
- + }
-
- thp = strndup(&path[matches[1].rm_so], size);
- if (!thp)
- return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
-
- - if (snprintf(filename, sizeof(filename), "%s/%s.jwk", cachedir, thp) < 0)
- - return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
- -
- - jwk = json_load_file(filename, 0, NULL);
- + jwk = find_jwk(tki, thp);
- if (!jwk)
- return http_reply(HTTP_STATUS_NOT_FOUND, NULL);
-
- @@ -188,7 +178,7 @@
- http_parser_init(&parser, HTTP_REQUEST);
-
- if (argc != 2) {
- - fprintf(stderr, "Usage: %s <cachedir>\n", argv[0]);
- + fprintf(stderr, "Usage: %s <jwkdir>\n", argv[0]);
- return EXIT_FAILURE;
- }
-
- --- a/tests/adv
- +++ b/tests/adv
- @@ -36,15 +36,13 @@
-
- export TMP=`mktemp -d`
- mkdir -p $TMP/db
- -mkdir -p $TMP/cache
-
- tangd-keygen $TMP/db sig exc
- jose jwk gen -i '{"alg": "ES512"}' -o $TMP/db/.sig.jwk
- jose jwk gen -i '{"alg": "ES512"}' -o $TMP/db/.oth.jwk
- -tangd-update $TMP/db $TMP/cache
-
- export PORT=`shuf -i 1024-65536 -n 1`
- -$SD_ACTIVATE -l "127.0.0.1:$PORT" -a $VALGRIND tangd $TMP/cache &
- +$SD_ACTIVATE -l "127.0.0.1:$PORT" -a $VALGRIND tangd $TMP/db &
- export PID=$!
- sleep 0.5
-
- @@ -84,4 +82,4 @@
- -g 0 -Og protected -SyOg cty -Sq "jwk-set+json" -EUUUUU \
- -g 1 -Og protected -SyOg cty -Sq "jwk-set+json" -EUUUUU
-
- -test $(tang-show-keys $PORT) == $(jose jwk thp -i $TMP/db/sig.jwk)
- +test "$(tang-show-keys $PORT)" == "$(jose jwk thp -i $TMP/db/sig.jwk)"
- --- a/tests/rec
- +++ b/tests/rec
- @@ -28,11 +28,9 @@
-
- export TMP=`mktemp -d`
- mkdir -p $TMP/db
- -mkdir -p $TMP/cache
-
- # Generate the server keys
- tangd-keygen $TMP/db sig exc
- -tangd-update $TMP/db $TMP/cache
-
- # Generate the client keys
- exc_kid=`jose jwk thp -i $TMP/db/exc.jwk`
- @@ -42,7 +40,7 @@
-
- # Start the server
- port=`shuf -i 1024-65536 -n 1`
- -$SD_ACTIVATE -l 127.0.0.1:$port -a $VALGRIND tangd $TMP/cache &
- +$SD_ACTIVATE -l 127.0.0.1:$port -a $VALGRIND tangd $TMP/db &
- export PID=$!
- sleep 0.5
-
- --- a/units/meson.build
- +++ b/units/meson.build
- @@ -1,31 +1,10 @@
- -tangd_keygen_service = configure_file(
- - input: 'tangd-keygen.service.in',
- - output: 'tangd-keygen.service',
- - configuration: data
- -)
- -
- tangd_service = configure_file(
- input: 'tangd@.service.in',
- output: 'tangd@.service',
- configuration: data
- )
-
- -tangd_update_path = configure_file(
- - input: 'tangd-update.path.in',
- - output: 'tangd-update.path',
- - configuration: data
- -)
- -
- -tangd_update_service = configure_file(
- - input: 'tangd-update.service.in',
- - output: 'tangd-update.service',
- - configuration: data
- -)
- -
- units += join_paths(meson.current_source_dir(), 'tangd.socket')
- -units += tangd_keygen_service
- units += tangd_service
- -units += tangd_update_path
- -units += tangd_update_service
-
- # vim:set ts=2 sw=2 et:
- --- a/units/tangd-keygen.service.in
- +++ /dev/null
- @@ -1,8 +0,0 @@
- -[Unit]
- -Description=Tang Server key generation script
- -ConditionDirectoryNotEmpty=|!@jwkdir@
- -Requires=tangd-update.path
- -
- -[Service]
- -Type=oneshot
- -ExecStart=@libexecdir@/tangd-keygen @jwkdir@
- --- a/units/tangd-update.path.in
- +++ /dev/null
- @@ -1,4 +0,0 @@
- -[Path]
- -PathChanged=@jwkdir@
- -MakeDirectory=true
- -DirectoryMode=0700
- --- a/units/tangd-update.service.in
- +++ /dev/null
- @@ -1,6 +0,0 @@
- -[Unit]
- -Description=Tang Server key update script
- -
- -[Service]
- -Type=oneshot
- -ExecStart=@libexecdir@/tangd-update @jwkdir@ @cachedir@
- --- a/units/tangd@.service.in
- +++ b/units/tangd@.service.in
- @@ -1,10 +1,8 @@
- [Unit]
- Description=Tang Server
- -Requires=tangd-keygen.service
- -After=tangd-keygen.service
-
- [Service]
- StandardInput=socket
- StandardOutput=socket
- StandardError=journal
- -ExecStart=@libexecdir@/tangd @cachedir@
- +ExecStart=@libexecdir@/tangd @jwkdir@
|