From d020254d0b3e5483d7fe0247050e3ca26e2d2e04 Mon Sep 17 00:00:00 2001 From: eabdullin Date: Wed, 20 Nov 2024 13:33:47 +0000 Subject: [PATCH] import RHEL 10 Beta tang-14-9.el10 --- .gitignore | 2 +- ...building-with-llhttp-instead-of-http.patch | 238 +++ ...uced-in-http-parser-llhttp-conversio.patch | 111 ++ .../0001-Move-key-generation-to-tang.patch | 1743 ----------------- ...s-unless-the-issue-was-with-with-tan.patch | 38 - .../0003-Fix-permissions-race-condition.patch | 31 - SOURCES/0004-Set-tang-owner-group.patch | 26 - SPECS/tang.spec | 163 -- sources | 1 + tang.spec | 265 +++ tang.sysusers | 1 + 11 files changed, 617 insertions(+), 2002 deletions(-) create mode 100644 0001-Add-support-for-building-with-llhttp-instead-of-http.patch create mode 100644 0002-Fix-issue-introduced-in-http-parser-llhttp-conversio.patch delete mode 100644 SOURCES/0001-Move-key-generation-to-tang.patch delete mode 100644 SOURCES/0002-Exit-with-success-unless-the-issue-was-with-with-tan.patch delete mode 100644 SOURCES/0003-Fix-permissions-race-condition.patch delete mode 100644 SOURCES/0004-Set-tang-owner-group.patch delete mode 100644 SPECS/tang.spec create mode 100644 sources create mode 100644 tang.spec create mode 100644 tang.sysusers diff --git a/.gitignore b/.gitignore index 1b068ef..45fcf4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/tang-7.tar.bz2 +tang-14.tar.xz diff --git a/0001-Add-support-for-building-with-llhttp-instead-of-http.patch b/0001-Add-support-for-building-with-llhttp-instead-of-http.patch new file mode 100644 index 0000000..7eae85e --- /dev/null +++ b/0001-Add-support-for-building-with-llhttp-instead-of-http.patch @@ -0,0 +1,238 @@ +From 6aebfd5499039b58b88eb15eba1aa719c117cfd4 Mon Sep 17 00:00:00 2001 +From: Sergio Correia +Date: Tue, 9 Jan 2024 08:56:59 +0000 +Subject: [PATCH] Add support for building with llhttp instead of http-parser + +As http-parser has been unmaintained for a while [1], let's add +support for its natural replacement, llhttp. + +However, as llhttp does not seem to be packaged in distros like +Debian [2], we will keep supporting building with http-parser for +time being, preferring llhttp, if it is present. + +[1] https://github.com/nodejs/http-parser/issues/522 +[2] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=977716 +--- + .github/workflows/install-dependencies | 2 +- + meson.build | 17 ++++++++++--- + src/http.c | 10 ++++---- + src/http.h | 35 +++++++++++++++++++++++--- + src/tangd.c | 16 ++++++------ + 5 files changed, 58 insertions(+), 22 deletions(-) + +diff --git a/.github/workflows/install-dependencies b/.github/workflows/install-dependencies +index 96852a8..a9bbab0 100755 +--- a/.github/workflows/install-dependencies ++++ b/.github/workflows/install-dependencies +@@ -13,7 +13,7 @@ debian:*|ubuntu:*) + echo 'max_parallel_downloads=10' >> /etc/dnf/dnf.conf + dnf -y clean all + dnf -y --setopt=deltarpm=0 update +- dnf -y install gcc meson pkgconfig libjose-devel jose http-parser-devel \ ++ dnf -y install gcc meson pkgconfig libjose-devel jose llhttp-devel \ + systemd gcovr curl socat iproute + ;; + +diff --git a/meson.build b/meson.build +index fd46cef..33c8aff 100644 +--- a/meson.build ++++ b/meson.build +@@ -55,13 +55,22 @@ add_project_arguments('-DVERSION="'+meson.project_version() + '"', language : 'c + jose = dependency('jose', version: '>=8') + a2x = find_program('a2x', required: false) + compiler = meson.get_compiler('c') +-if not compiler.has_header('http_parser.h',args : '-I/usr/local/include') +- error('http-parser devel files not found.') ++ ++http_lib = [] ++if compiler.has_header('llhttp.h', args: '-I/usr/local/include') ++ http_lib = 'llhttp' ++ add_project_arguments('-DUSE_LLHTTP', language: 'c') ++else ++ if not compiler.has_header('http_parser.h', args: '-I/usr/local/include') ++ error('neither llhttp nor http-parser devel files found.') ++ endif ++ http_lib = 'http_parser' + endif ++ + if host_machine.system() == 'freebsd' +- http_parser = compiler.find_library('http_parser',dirs : '/usr/local/lib') ++ http_parser = compiler.find_library(http_lib, dirs : '/usr/local/lib') + else +- http_parser = compiler.find_library('http_parser') ++ http_parser = compiler.find_library(http_lib) + endif + + licenses = ['COPYING'] +diff --git a/src/http.c b/src/http.c +index e9af37b..17b613f 100644 +--- a/src/http.c ++++ b/src/http.c +@@ -36,7 +36,7 @@ HTTP_METHOD_MAP(XX) + }; + + static int +-on_url(http_parser *parser, const char *at, size_t length) ++on_url(http_parser_t *parser, const char *at, size_t length) + { + struct http_state *state = parser->data; + +@@ -51,7 +51,7 @@ on_url(http_parser *parser, const char *at, size_t length) + } + + static int +-on_body(http_parser *parser, const char *at, size_t length) ++on_body(http_parser_t *parser, const char *at, size_t length) + { + struct http_state *state = parser->data; + +@@ -66,7 +66,7 @@ on_body(http_parser *parser, const char *at, size_t length) + } + + static int +-on_message_complete(http_parser *parser) ++on_message_complete(http_parser_t *parser) + { + struct http_state *state = parser->data; + const char *addr = NULL; +@@ -132,7 +132,7 @@ egress: + return 0; + } + +-const http_parser_settings http_settings = { ++const http_settings_t http_settings = { + .on_url = on_url, + .on_body = on_body, + .on_message_complete = on_message_complete, +@@ -140,7 +140,7 @@ const http_parser_settings http_settings = { + + int + http_reply(const char *file, int line, +- enum http_status code, const char *fmt, ...) ++ http_status_t code, const char *fmt, ...) + { + const char *msg = NULL; + va_list ap; +diff --git a/src/http.h b/src/http.h +index 8660a4f..2e35686 100644 +--- a/src/http.h ++++ b/src/http.h +@@ -19,12 +19,39 @@ + + #pragma once + +-#include + #include + #include + ++#ifdef USE_LLHTTP ++#include ++ ++typedef llhttp_method_t http_method_t; ++typedef llhttp_status_t http_status_t; ++typedef llhttp_settings_t http_settings_t; ++typedef llhttp_t http_parser_t; ++#define tang_http_parser_init(parser, settings) llhttp_init(parser, HTTP_REQUEST, settings) ++#define tang_http_parser_execute(parser, settings, req, rcvd) llhttp_execute(parser, req, rcvd) ++#define tang_http_parser_errno(parser) parser.error ++#define tang_http_errno_description(parser, errno) llhttp_get_error_reason(parser) ++ ++#else ++/* Legacy http-parser. */ ++#include ++ ++typedef enum http_method http_method_t; ++typedef enum http_status http_status_t; ++typedef http_parser_settings http_settings_t; ++typedef struct http_parser http_parser_t; ++ ++#define tang_http_parser_init(parser, settings) http_parser_init(parser, HTTP_REQUEST) ++#define tang_http_parser_execute(parser, settings, req, rcvd) http_parser_execute(parser, settings, req, rcvd) ++#define tang_http_parser_errno(parser) parser.http_errno ++#define tang_http_errno_description(parser, errno) http_errno_description(errno) ++ ++#endif /* USE_LLHTTP */ ++ + struct http_dispatch { +- int (*func)(enum http_method method, const char *path, ++ int (*func)(http_method_t method, const char *path, + const char *body, regmatch_t matches[], void *misc); + uint64_t methods; + size_t nmatches; +@@ -43,11 +70,11 @@ struct http_state { + void *misc; + }; + +-extern const http_parser_settings http_settings; ++extern const http_settings_t http_settings; + + int __attribute__ ((format(printf, 4, 5))) + http_reply(const char *file, int line, +- enum http_status code, const char *fmt, ...); ++ http_status_t code, const char *fmt, ...); + + #define http_reply(code, ...) \ + http_reply(__FILE__, __LINE__, code, __VA_ARGS__) +diff --git a/src/tangd.c b/src/tangd.c +index 1e3a6a3..7f197f6 100644 +--- a/src/tangd.c ++++ b/src/tangd.c +@@ -64,7 +64,7 @@ str_cleanup(char **str) + } + + static int +-adv(enum http_method method, const char *path, const char *body, ++adv(http_method_t method, const char *path, const char *body, + regmatch_t matches[], void *misc) + { + __attribute__((cleanup(str_cleanup))) char *adv = NULL; +@@ -101,7 +101,7 @@ adv(enum http_method method, const char *path, const char *body, + } + + static int +-rec(enum http_method method, const char *path, const char *body, ++rec(http_method_t method, const char *path, const char *body, + regmatch_t matches[], void *misc) + { + __attribute__((cleanup(str_cleanup))) char *enc = NULL; +@@ -197,13 +197,14 @@ static int + process_request(const char *jwkdir, int in_fileno) + { + struct http_state state = { .dispatch = dispatch, .misc = (char*)jwkdir }; +- struct http_parser parser = { .data = &state }; ++ http_parser_t parser; + struct stat st = {}; + char req[4096] = {}; + size_t rcvd = 0; + int r = 0; + +- http_parser_init(&parser, HTTP_REQUEST); ++ tang_http_parser_init(&parser, &http_settings); ++ parser.data = &state; + + if (stat(jwkdir, &st) != 0) { + fprintf(stderr, "Error calling stat() on path: %s: %m\n", jwkdir); +@@ -224,17 +225,16 @@ process_request(const char *jwkdir, int in_fileno) + + rcvd += r; + +- r = http_parser_execute(&parser, &http_settings, req, rcvd); +- if (parser.http_errno != 0) { ++ r = tang_http_parser_execute(&parser, &http_settings, req, rcvd); ++ if (tang_http_parser_errno(parser) != 0) { + fprintf(stderr, "HTTP Parsing Error: %s\n", +- http_errno_description(parser.http_errno)); ++ tang_http_errno_description(&parser, tang_http_parser_errno(parser))); + return EXIT_SUCCESS; + } + + memmove(req, &req[r], rcvd - r); + rcvd -= r; + } +- + return EXIT_SUCCESS; + } + +-- +2.41.0 + diff --git a/0002-Fix-issue-introduced-in-http-parser-llhttp-conversio.patch b/0002-Fix-issue-introduced-in-http-parser-llhttp-conversio.patch new file mode 100644 index 0000000..4583d92 --- /dev/null +++ b/0002-Fix-issue-introduced-in-http-parser-llhttp-conversio.patch @@ -0,0 +1,111 @@ +From 960b2036a97baded1b61b405e4fa99380f807ff9 Mon Sep 17 00:00:00 2001 +From: Sergio Correia +Date: Mon, 12 Feb 2024 13:07:45 +0000 +Subject: [PATCH 2/2] Fix issue introduced in http-parser -> llhttp conversion + +http_parser_execute() returns the number of parsed bytes, while +llhttp_execute() returns an error code. + +Signed-off-by: Sergio Correia +--- + src/http.h | 6 ++---- + src/tangd.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 48 insertions(+), 6 deletions(-) + +diff --git a/src/http.h b/src/http.h +index 2e35686..8d9de51 100644 +--- a/src/http.h ++++ b/src/http.h +@@ -30,10 +30,9 @@ typedef llhttp_status_t http_status_t; + typedef llhttp_settings_t http_settings_t; + typedef llhttp_t http_parser_t; + #define tang_http_parser_init(parser, settings) llhttp_init(parser, HTTP_REQUEST, settings) +-#define tang_http_parser_execute(parser, settings, req, rcvd) llhttp_execute(parser, req, rcvd) + #define tang_http_parser_errno(parser) parser.error + #define tang_http_errno_description(parser, errno) llhttp_get_error_reason(parser) +- ++#define tang_http_parser_resume(parser) llhttp_resume(parser) + #else + /* Legacy http-parser. */ + #include +@@ -44,10 +43,9 @@ typedef http_parser_settings http_settings_t; + typedef struct http_parser http_parser_t; + + #define tang_http_parser_init(parser, settings) http_parser_init(parser, HTTP_REQUEST) +-#define tang_http_parser_execute(parser, settings, req, rcvd) http_parser_execute(parser, settings, req, rcvd) + #define tang_http_parser_errno(parser) parser.http_errno + #define tang_http_errno_description(parser, errno) http_errno_description(errno) +- ++#define tang_http_parser_resume(parser) http_parser_pause(parser, 0) + #endif /* USE_LLHTTP */ + + struct http_dispatch { +diff --git a/src/tangd.c b/src/tangd.c +index 7f197f6..ab7f0cf 100644 +--- a/src/tangd.c ++++ b/src/tangd.c +@@ -193,6 +193,44 @@ static struct http_dispatch dispatch[] = { + + #define DEFAULT_PORT 9090 + ++static size_t ++tang_http_parser_execute(http_parser_t *parser, const char* data, size_t len) ++{ ++#ifdef USE_LLHTTP ++ llhttp_errno_t error; ++ size_t parsed_len; ++ ++ /* ++ * Unlike http_parser, which returns the number of parsed ++ * bytes in the _execute() call, llhttp returns an error ++ * code. ++ */ ++ ++ if (data == NULL || len == 0) { ++ error = llhttp_finish(parser); ++ } else { ++ error = llhttp_execute(parser, data, len); ++ } ++ ++ parsed_len = len; ++ /* ++ * Adjust number of parsed bytes in case of error. ++ */ ++ if (error != HPE_OK) { ++ parsed_len = llhttp_get_error_pos(parser) - data; ++ ++ /* This isn't a real pause, just a way to stop parsing early. */ ++ if (error == HPE_PAUSED_UPGRADE) { ++ llhttp_resume_after_upgrade(parser); ++ } ++ } ++ ++ return parsed_len; ++#else ++ return http_parser_execute(parser, &http_settings, data, len); ++#endif ++} ++ + static int + process_request(const char *jwkdir, int in_fileno) + { +@@ -225,8 +263,14 @@ process_request(const char *jwkdir, int in_fileno) + + rcvd += r; + +- r = tang_http_parser_execute(&parser, &http_settings, req, rcvd); +- if (tang_http_parser_errno(parser) != 0) { ++ r = tang_http_parser_execute(&parser, req, rcvd); ++ switch (tang_http_parser_errno(parser)) { ++ case HPE_OK: ++ break; ++ case HPE_PAUSED: ++ tang_http_parser_resume(&parser); ++ break; ++ default: + fprintf(stderr, "HTTP Parsing Error: %s\n", + tang_http_errno_description(&parser, tang_http_parser_errno(parser))); + return EXIT_SUCCESS; +-- +2.43.0 + diff --git a/SOURCES/0001-Move-key-generation-to-tang.patch b/SOURCES/0001-Move-key-generation-to-tang.patch deleted file mode 100644 index 74fa4cc..0000000 --- a/SOURCES/0001-Move-key-generation-to-tang.patch +++ /dev/null @@ -1,1743 +0,0 @@ -From a0aa280cfe7a756bc9f965129c847dc9bfd2e84d Mon Sep 17 00:00:00 2001 -From: Sergio Correia -Date: Sun, 29 Sep 2019 10:05:05 -0300 -Subject: [PATCH] Move key generation to tang - -Until now, tang has relied the following two scripts to handle keys: -- tangd-keygen, which generates the keys in a specific JWK dir -- tangd-update, which reads the directory with the keys and updates a - cache directory with files that will be used by tang during its - operation - -We also rely on systemd to watch the JWK dir and automatically run -tangd-update when it is modified, so that the cache directory would be -kept in a consistent state with regard to the available keys. - -However, this has shown to be unreliable and to cause issues in some -situations. For instance, in #23 we see that sometimes the keys are not -ready when tang starts operating, and in #24 we see that we have issues -if somehow the cache directory is removed while tang is running. - -To improve reliability, the handling of keys is now being moved to tang -itself. In this commit we implement routines to perform the operations -that the aforementioned scripts did; tang can now create keys and also -read them directly to perform its required operations. - -Changes after this commit: -1) there is no cache directory anymore; tang will read the keys directly - from JWK dir instead of reading files from the cache directory -2) as a consequence of 1), we now pass JWK dir as the argument to tang -3) tangd-update is gone, since there is no need for a cache directory -4) when reading JWK dir, tang will create keys if there are none -- this - was already the case on startup before this commit, but now we do not - rely on systemd units to do this any longer -5) many of the previously used systemd units are not required anymore - -Resolves #23 -Resolves #24 ---- - Makefile.am | 11 +- - src/keys.c | 1043 +++++++++++++++++++++++++++++++++ - src/keys.h | 84 +++ - src/tangd-update | 83 --- - src/tangd.c | 60 +- - src/util.c | 141 +++++ - src/util.h | 33 ++ - tests/adv | 4 +- - tests/rec | 4 +- - units/tangd-keygen.service.in | 8 - - units/tangd-update.path.in | 4 - - units/tangd-update.service.in | 6 - - units/tangd.socket.in | 5 - - units/tangd@.service.in | 2 +- - 14 files changed, 1342 insertions(+), 146 deletions(-) - create mode 100644 src/keys.c - create mode 100644 src/keys.h - delete mode 100755 src/tangd-update - create mode 100644 src/util.c - create mode 100644 src/util.h - delete mode 100644 units/tangd-keygen.service.in - delete mode 100644 units/tangd-update.path.in - delete mode 100644 units/tangd-update.service.in - -diff --git a/Makefile.am b/Makefile.am -index af30d2f..f855dcd 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -7,17 +7,13 @@ man8_MANS= - AM_CFLAGS = @TANG_CFLAGS@ @jose_CFLAGS@ - LDADD = @jose_LIBS@ @http_parser_LIBS@ - --cachedir = $(localstatedir)/cache/$(PACKAGE_NAME) - jwkdir = $(localstatedir)/db/$(PACKAGE_NAME) - - nodist_systemdsystemunit_DATA = \ - units/tangd@.service \ -- units/tangd.socket \ -- units/tangd-update.path \ -- units/tangd-update.service \ -- units/tangd-keygen.service -+ units/tangd.socket - --dist_libexec_SCRIPTS = src/tangd-update src/tangd-keygen -+dist_libexec_SCRIPTS = src/tangd-keygen - dist_bin_SCRIPTS = src/tang-show-keys - libexec_PROGRAMS = src/tangd - -@@ -39,14 +35,13 @@ man1_MANS += doc/tang-show-keys.1 - man8_MANS += doc/tang.8 - endif - --src_tangd_SOURCES = src/http.c src/http.h src/tangd.c -+src_tangd_SOURCES = src/util.c src/util.h src/keys.c src/keys.h src/http.c src/http.h src/tangd.c - - %: %.in - $(AM_V_GEN)mkdir -p "`dirname "$@"`" - $(AM_V_GEN)$(SED) \ - -e 's,@libexecdir\@,$(libexecdir),g' \ - -e 's,@jwkdir\@,$(jwkdir),g' \ -- -e 's,@cachedir\@,$(cachedir),g' \ - $(srcdir)/$@.in > $@ - - AM_TESTS_ENVIRONMENT = SD_ACTIVATE="@SD_ACTIVATE@" PATH=$(srcdir)/src:$(builddir)/src:$(PATH) -diff --git a/src/keys.c b/src/keys.c -new file mode 100644 -index 0000000..77f5d3c ---- /dev/null -+++ b/src/keys.c -@@ -0,0 +1,1043 @@ -+/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */ -+/* -+ * Copyright (c) 2019 Red Hat, Inc. -+ * Author: Sergio Correia -+ * -+ * 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 . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "util.h" -+#include "keys.h" -+ -+#ifndef PATH_MAX -+#define PATH_MAX 4096 -+#endif -+ -+#define TANG_MAXBUFLEN (1024 * 1024) -+ -+#define DEFAULT_HASH_ALG "S1" -+ -+/* TODO: check if jose has a way to export the hash algorithms it supports. */ -+const char *hash_alg[] = {"S1", "S224", "S256", "S384", "S512", NULL}; -+ -+size_t -+hash_alg_size(void) -+{ -+ size_t count = 0; -+ for (size_t i = 0; hash_alg[i]; i++) { -+ count++; -+ } -+ return count; -+} -+ -+int -+is_hash(const char *alg) -+{ -+ for (size_t a = 0, size = hash_alg_size(); a < size; a++) { -+ if (strcmp(alg, hash_alg[a]) == 0) { -+ return 1; -+ } -+ } -+ return 0; -+} -+ -+/* -+ * Generates a JWK and returns a json_t*, which must be released with -+ * json_decref(). -+ */ -+static json_t* -+jwk_generate(const char* alg) -+{ -+ json_t *jalg = json_pack("{s:s}", "alg", alg); -+ if (!jalg) { -+ fprintf(stderr, "Error packing JSON with alg %s\n", alg); -+ return NULL; -+ } -+ -+ if (!jose_jwk_gen(NULL, jalg)) { -+ fprintf(stderr, "Error generating JWK with alg %s\n", alg); -+ json_decref(jalg); -+ return NULL; -+ } -+ -+ return jalg; -+} -+ -+/* -+ * Returns a thumbprint from a JWK and a given algorithm, which must be -+ * released with free(). -+ */ -+char* -+jwk_thumbprint(const json_t* jwk, const char* alg) -+{ -+ size_t elen = 0; -+ size_t dlen = 0; -+ -+ const char* hash = alg; -+ if (!is_hash(alg)) { -+ hash = DEFAULT_HASH_ALG; -+ } -+ -+ dlen = jose_jwk_thp_buf(NULL, NULL, hash, NULL, 0); -+ if (dlen == SIZE_MAX) { -+ fprintf(stderr, "Error determining hash size for %s\n", hash); -+ return NULL; -+ } -+ -+ elen = jose_b64_enc_buf(NULL, dlen, NULL, 0); -+ if (elen == SIZE_MAX) { -+ fprintf(stderr, "Error determining encoded size for %s\n", hash); -+ return NULL; -+ } -+ -+ uint8_t dec[dlen]; -+ char enc[elen]; -+ -+ if (!jose_jwk_thp_buf(NULL, jwk, hash, dec, sizeof(dec))) { -+ fprintf(stderr, "Error making thumbprint\n"); -+ return NULL; -+ } -+ -+ if (jose_b64_enc_buf(dec, dlen, enc, sizeof(enc)) != elen) { -+ fprintf(stderr, "Error encoding data Base64\n"); -+ return NULL; -+ } -+ -+ char *thp = malloc(elen + 1); -+ if (!thp) { -+ fprintf(stderr, "Error allocating string for thumbprint\n"); -+ return NULL; -+ } -+ -+ if (!strncpy(thp, enc, elen)) { -+ fprintf(stderr, "Error copying thumbprint to string\n"); -+ free(thp); -+ return NULL; -+ } -+ -+ thp[elen] = '\0'; -+ return thp; -+} -+ -+char* -+jwk_thumbprint_from_file(const char *file, const char *alg) -+{ -+ json_auto_t *jwk = json_load_file(file, 0, NULL); -+ if (!jwk) { -+ return 0; -+ } -+ return jwk_thumbprint(jwk, alg); -+} -+ -+/* -+ * Releases the allocated memory by struct tang_jwk*. -+ */ -+void -+free_tang_jwk(struct tang_jwk* tjwk) -+{ -+ if (!tjwk) { -+ return; -+ } -+ -+ if (tjwk->m_json) { -+ json_decref(tjwk->m_json); -+ } -+ free(tjwk->m_from_file); -+ free(tjwk->m_thp); -+ free(tjwk->m_alg); -+ free(tjwk->m_str); -+ free(tjwk); -+} -+ -+void -+cleanup_tang_jwk(struct tang_jwk **jwk) -+{ -+ if (!jwk || !*jwk) { -+ return; -+ } -+ free_tang_jwk(*jwk); -+} -+ -+struct tang_jwk* -+new_tang_jwk_from_args(json_t *jwk, const char* thp, const char* alg) -+{ -+ if (!jwk) { -+ fprintf(stderr, "Invalid JWK (%p) \n", jwk); -+ return NULL; -+ } -+ -+ struct tang_jwk *tjwk = calloc(1, sizeof(*tjwk)); -+ if (!tjwk) { -+ fprintf(stderr, "Error allocating new struct tang_jwk.\n"); -+ return NULL; -+ } -+ -+ tjwk->m_json = json_incref(jwk); -+ tjwk->m_from_file = NULL; -+ -+ if (alg) { -+ tjwk->m_alg = strdup(alg); -+ if (!tjwk->m_alg) { -+ fprintf(stderr, "Unable to copy algorithm (%s) to tang_jwk.\n", alg); -+ free_tang_jwk(tjwk); -+ return NULL; -+ } -+ } -+ -+ if (thp) { -+ tjwk->m_thp = strdup(thp); -+ if (!tjwk->m_thp) { -+ fprintf(stderr, "Unable to copy thumbprint (%s).\n", thp); -+ free_tang_jwk(tjwk); -+ return NULL; -+ } -+ } -+ -+ tjwk->m_str = json_dumps(tjwk->m_json, JSON_SORT_KEYS | JSON_COMPACT); -+ if (!tjwk->m_str) { -+ fprintf(stderr, "Unable to get string version from JWK.\n"); -+ free_tang_jwk(tjwk); -+ return NULL; -+ } -+ -+ return tjwk; -+} -+ -+struct tang_jwk* -+tang_jwk_dup(const struct tang_jwk *jwk) -+{ -+ if (!jwk) { -+ return NULL; -+ } -+ -+ struct tang_jwk *new_jwk = new_tang_jwk_from_args(jwk->m_json, jwk->m_thp, jwk->m_alg); -+ if (!new_jwk) { -+ return NULL; -+ } -+ -+ if (jwk->m_from_file) { -+ new_jwk->m_from_file = strdup(jwk->m_from_file); -+ if (!new_jwk->m_from_file) { -+ free_tang_jwk(new_jwk); -+ return NULL; -+ } -+ } -+ -+ return new_jwk; -+} -+ -+struct tang_jwk* -+new_tang_jwk(const char* file, const char* alg) -+{ -+ if (!file || !alg) { -+ fprintf(stderr, "Invalid file (%s) or algorithm (%s).\n", file, alg); -+ return NULL; -+ } -+ -+ json_auto_t *jwk = json_load_file(file, 0, NULL); -+ if (!jwk) { -+ fprintf(stderr, "Unable to parse JSON from %s.\n", file); -+ return NULL; -+ } -+ -+ struct tang_jwk *tjwk = calloc(1, sizeof(*tjwk)); -+ if (!tjwk) { -+ fprintf(stderr, "Error allocating new struct tang_jwk.\n"); -+ return NULL; -+ } -+ -+ tjwk->m_json = json_incref(jwk); -+ tjwk->m_from_file = strdup(file); -+ if (!tjwk->m_from_file) { -+ fprintf(stderr, "Unable to copy file name (%s) to m_from_file.\n", file); -+ free_tang_jwk(tjwk); -+ return NULL; -+ } -+ tjwk->m_alg = strdup(alg); -+ if (!tjwk->m_alg) { -+ fprintf(stderr, "Unable to copy algorithm (%s) to tang_jwk.\n", alg); -+ free_tang_jwk(tjwk); -+ return NULL; -+ } -+ -+ tjwk->m_thp = jwk_thumbprint(tjwk->m_json, tjwk->m_alg); -+ if (!tjwk->m_thp) { -+ fprintf(stderr, "Unable to get thumbprint using alg (%s).\n", alg); -+ free_tang_jwk(tjwk); -+ return NULL; -+ } -+ -+ tjwk->m_str = json_dumps(tjwk->m_json, JSON_SORT_KEYS | JSON_COMPACT); -+ if (!tjwk->m_str) { -+ fprintf(stderr, "Unable to get string version from JWK.\n"); -+ free_tang_jwk(tjwk); -+ return NULL; -+ } -+ -+ return tjwk; -+} -+ -+/* -+ * Builds a new struct tang_jwk*, which should be destructed by calling -+ * free_tang_jwk(). -+ */ -+struct tang_jwk* -+generate_new_tang_jwk(const char* alg) -+{ -+ if (!alg) { -+ fprintf(stderr, "Invalid algorithm.\n"); -+ return NULL; -+ } -+ -+ struct tang_jwk *tjwk = calloc(1, sizeof(*tjwk)); -+ if (!tjwk) { -+ fprintf(stderr, "Error allocating new struct tang_jwk.\n"); -+ return NULL; -+ } -+ -+ tjwk->m_alg = strdup(alg); -+ if (!tjwk->m_alg) { -+ fprintf(stderr, "Unable to copy algorithm (%s) to tang_jwk.\n", alg); -+ free_tang_jwk(tjwk); -+ return NULL; -+ } -+ -+ tjwk->m_json = jwk_generate(alg); -+ if (!tjwk->m_json) { -+ fprintf(stderr, "Unable to generate new JWK using alg (%s).\n", alg); -+ free_tang_jwk(tjwk); -+ return NULL; -+ } -+ tjwk->m_thp = jwk_thumbprint(tjwk->m_json, tjwk->m_alg); -+ if (!tjwk->m_thp) { -+ fprintf(stderr, "Unable to get thumbprint using alg (%s).\n", alg); -+ free_tang_jwk(tjwk); -+ return NULL; -+ } -+ -+ tjwk->m_str = json_dumps(tjwk->m_json, JSON_SORT_KEYS | JSON_COMPACT); -+ if (!tjwk->m_str) { -+ fprintf(stderr, "Unable to get string version from JWK.\n"); -+ free_tang_jwk(tjwk); -+ return NULL; -+ } -+ return tjwk; -+} -+ -+static int -+file_valid_for(const char *file, const char *use) -+{ -+ json_auto_t *jwk = json_load_file(file, 0, NULL); -+ if (!jwk) { -+ return 0; -+ } -+ -+ return jose_jwk_prm(NULL, jwk, false, use); -+} -+ -+static int -+jwk_valid_for(const json_t *jwk, const char *use) -+{ -+ return jose_jwk_prm(NULL, jwk, false, use); -+} -+ -+ -+int valid_for_signing_and_verifying(const char *file) -+{ -+ json_auto_t *jwk = json_load_file(file, 0, NULL); -+ if (!jwk) { -+ return 0; -+ } -+ -+ const char *use[] = {"sign", "verify", NULL}; -+ int ret = 1; -+ for (int i = 0; use[i] != NULL; i++) { -+ if (!jwk_valid_for(jwk, use[i])) { -+ ret = 0; -+ break; -+ } -+ } -+ -+ return ret; -+} -+ -+int valid_for_signing(const char *file) -+{ -+ return file_valid_for(file, "sign"); -+} -+ -+int valid_for_deriving_keys(const char *file) -+{ -+ return file_valid_for(file, "deriveKey"); -+} -+ -+struct tang_jwk_list* -+new_tang_jwk_list(void) -+{ -+ struct tang_jwk_list *tjl = malloc(sizeof(*tjl)); -+ if (!tjl) { -+ return NULL; -+ } -+ tjl->m_jwk = NULL; -+ tjl->m_size = 0; -+ return tjl; -+} -+ -+void -+free_tang_jwk_list(struct tang_jwk_list *tjl) -+{ -+ if (!tjl) { -+ return; -+ } -+ -+ for (size_t i = 0, size = tjl->m_size; i < size; i++) { -+ free_tang_jwk(tjl->m_jwk[i]); -+ } -+ free(tjl->m_jwk); -+ free(tjl); -+} -+ -+int -+tang_jwk_list_add(struct tang_jwk_list *tjl, struct tang_jwk *jwk_to_add) -+{ -+ if (!tjl || !jwk_to_add) { -+ return 0; -+ } -+ -+ struct tang_jwk *jwk = tang_jwk_dup(jwk_to_add); -+ if (!jwk) { -+ return 0; -+ } -+ -+ struct tang_jwk **new_jwk = realloc(tjl->m_jwk, sizeof(struct tang_jwk*) * (tjl->m_size + 1)); -+ if (!new_jwk) { -+ fprintf(stderr, "Error reallocating memory for the new JWK.\n"); -+ free_tang_jwk(jwk); -+ return 0; -+ } -+ -+ tjl->m_jwk = new_jwk; -+ tjl->m_jwk[tjl->m_size++] = jwk; -+ return 1; -+} -+ -+static int -+tang_jwk_thp_bsearch_cmp_func(const void *a, const void *b) -+{ -+ const char *key = (const char*)a; -+ const struct tang_jwk *jwk = *(const struct tang_jwk**)b; -+ return strcmp(key, jwk->m_thp); -+} -+ -+struct tang_jwk* -+tang_jwk_list_find_thp(const struct tang_jwk_list *tjl, const char *thp) -+{ -+ if (!tjl || !thp) { -+ return NULL; -+ } -+ -+ if (tjl->m_size == 0) { -+ return NULL; -+ } -+ -+ struct tang_jwk **item = bsearch(thp, tjl->m_jwk, tjl->m_size, sizeof(struct tang_jwk*), tang_jwk_thp_bsearch_cmp_func); -+ if (!item) { -+ return NULL; -+ } -+ return *item; -+} -+ -+void -+free_tang_keys_info(struct tang_keys_info *tki) -+{ -+ if (!tki) { -+ return; -+ } -+ -+ free(tki->m_jwkdir); -+ free_file_list(tki->m_payload_keys); -+ free_file_list(tki->m_sign_keys); -+ free_tang_jwk_list(tki->m_derive); -+ free_tang_jwk_list(tki->m_adv); -+ free_tang_jwk(tki->m_default_adv); -+ free(tki); -+} -+ -+struct tang_keys_info* -+new_tang_keys_info(const char* jwkdir) -+{ -+ if (!jwkdir) { -+ fprintf(stderr, "Invalid JWK dir.\n"); -+ return NULL; -+ } -+ -+ struct tang_keys_info *tki = calloc(1, sizeof(struct tang_keys_info)); -+ if (!tki) { -+ fprintf(stderr, "Error allocating tang_keys_info struct.\n"); -+ return NULL; -+ } -+ -+ tki->m_jwkdir = strdup(jwkdir); -+ if (!tki->m_jwkdir) { -+ fprintf(stderr, "Error copying JWK dir to tang_keys_info struct.\n"); -+ free_tang_keys_info(tki); -+ return NULL; -+ } -+ -+ tki->m_payload_keys = new_file_list(); -+ if (!tki->m_payload_keys) { -+ fprintf(stderr, "Error allocating payload keys.\n"); -+ free_tang_keys_info(tki); -+ return NULL; -+ } -+ -+ tki->m_sign_keys = new_file_list(); -+ if (!tki->m_sign_keys) { -+ fprintf(stderr, "Error allocating signing keys.\n"); -+ free_tang_keys_info(tki); -+ return NULL; -+ } -+ -+ tki->m_derive = new_tang_jwk_list(); -+ if (!tki->m_derive) { -+ fprintf(stderr, "Error allocating list of deriving keys.\n"); -+ free_tang_keys_info(tki); -+ return NULL; -+ } -+ -+ tki->m_adv = new_tang_jwk_list(); -+ if (!tki->m_adv) { -+ fprintf(stderr, "Error allocating list adv.\n"); -+ free_tang_keys_info(tki); -+ return NULL; -+ } -+ -+ tki->m_default_adv = NULL; -+ -+ return tki; -+} -+ -+int -+check_keys(const char* jwkdir) -+{ -+ if (!jwkdir) { -+ return 0; -+ } -+ -+ /* We ignore hidden files in here because we only care about -+ * advertised keys. */ -+ struct file_list *fl __attribute__ ((__cleanup__(cleanup_file_list))) = list_files(jwkdir, ".jwk", 1 /* ignore hidden */); -+ if (!fl) { -+ return 0; -+ } -+ -+ if (fl->m_size > 0) { -+ /* There are already keys in the JWKdir, so let's leave it as is. */ -+ return 1; -+ } -+ -+ /* At this point, there are no keys, so let's create them. */ -+ const char *alg[] = {"ES512", "ECMR", NULL}; -+ char path[PATH_MAX]; -+ for (int i = 0; alg[i] != NULL; i++) { -+ struct tang_jwk *jwk __attribute__((cleanup(cleanup_tang_jwk))) = generate_new_tang_jwk(alg[i]); -+ if (!jwk) { -+ fprintf(stderr, "Error generating JWK using %s\n", alg[i]); -+ return 0; -+ } -+ -+ snprintf(path, PATH_MAX, "%s/%s.jwk", jwkdir, jwk->m_thp); -+ path[sizeof(path) - 1] = '\0'; -+ -+ FILE *fp = fopen(path, "w+"); -+ if (!fp) { -+ fprintf(stderr, "Error creating JWK file to %s\n", path); -+ return 0; -+ } -+ fprintf(fp, "%s", jwk->m_str); -+ fclose(fp); -+ } -+ return 1; -+} -+ -+static int -+tang_jwk_thp_cmp_func(const void *a, const void *b) -+{ -+ const struct tang_jwk *ta = *(const struct tang_jwk**)a; -+ const struct tang_jwk *tb = *(const struct tang_jwk**)b; -+ return strcmp(ta->m_thp, tb->m_thp); -+} -+ -+void -+cleanup_tang_keys_info(struct tang_keys_info **tki) -+{ -+ if (!tki || !*tki) { -+ return; -+ } -+ free_tang_keys_info(*tki); -+} -+ -+static void -+cleanup_buffer(char **buffer) -+{ -+ if (!buffer || !*buffer) { -+ return; -+ } -+ free(*buffer); -+} -+ -+static void -+cleanup_jose_io_t(jose_io_t ***iosp) -+{ -+ jose_io_t **ios = *iosp; -+ for (size_t i = 0; ios && ios[i]; i++) { -+ jose_io_auto(&ios[i]); -+ } -+} -+ -+static json_t* -+build_json_array(const char **files, size_t total_files) -+{ -+ if (!files || total_files == 0) { -+ return NULL; -+ } -+ -+ json_t *arr = json_array(); -+ if (!arr) { -+ fprintf(stderr, "Unable to create json array\n"); -+ return NULL; -+ } -+ -+ for (size_t i = 0; i < total_files; i++) { -+ json_t *jwk = json_load_file(files[i], 0, NULL); -+ if (!jwk) { -+ fprintf(stderr, "Unable to load JSON from %s; skipping\n", files[i]); -+ continue; -+ } -+ -+ if (json_array_append_new(arr, jwk) != 0) { -+ fprintf(stderr, "Unable to append JSON %s to array; skipping\n", files[i]); -+ continue; -+ } -+ } -+ return arr; -+} -+ -+static json_t* -+remove_private_keys(const struct file_list *fl) -+{ -+ if (!fl) { -+ fprintf(stderr, "Invalid file list for cleaning private keys.\n"); -+ return NULL; -+ } -+ -+ json_auto_t *array = build_json_array((const char**)fl->m_files, fl->m_size); -+ if (!array || json_array_size(array) == 0) { -+ fprintf(stderr, "Empty array %p.\n", array); -+ return NULL; -+ } -+ -+ for (size_t i = 0, size = json_array_size(array); i < size; i++) { -+ if (!jose_jwk_pub(NULL, json_array_get(array, i))) { -+ fprintf(stderr, "Error removing private keys.\n"); -+ return NULL; -+ } -+ } -+ -+ return json_pack("{s:O}", "keys", array); -+} -+ -+static json_t* -+prepare_template_sigs(size_t total) -+{ -+ json_t *arr = json_array(); -+ if (!arr) { -+ fprintf(stderr, "Unable to create JSON sigs array\n"); -+ return NULL; -+ } -+ -+ for (size_t i = 0; i < total; i++) { -+ json_t *cty = json_pack("{s:{s:s}}", "protected", "cty", "jwk-set+json"); -+ if (!cty) { -+ fprintf(stderr, "Unable to create item %zu/%zu; skipping\n", i, total); -+ continue; -+ } -+ -+ if (json_array_append_new(arr, cty) != 0) { -+ fprintf(stderr, "Unable to append item %zu/%zu to array; skipping\n", i, total); -+ continue; -+ } -+ } -+ return arr; -+} -+ -+static jose_io_t* -+tang_prep_io(jose_io_t *io, uint8_t *buffer, size_t *buflen) -+{ -+ if (!io || !buffer || !buflen) { -+ fprintf(stderr, "Either io (%p) the buffer (%p) or the buffer len (%p) are NULL\n", io, buffer, buflen); -+ } -+ -+ jose_io_t **ios __attribute__((cleanup(cleanup_jose_io_t))) = NULL; -+ size_t i = 0; -+ -+ ios = alloca(sizeof(*ios) * 3); -+ memset(ios, 0, sizeof(*ios) * 3); -+ -+ if (io) { -+ ios[i++] = io; -+ } -+ -+ ios[i] = jose_io_buffer(NULL, buffer, buflen); -+ if (!ios[i]) { -+ return NULL; -+ } -+ -+ for (i = 0; ios[i]; i++) { -+ jose_io_auto_t *b64 = NULL; -+ -+ b64 = jose_b64_enc_io(ios[i]); -+ if (!b64) { -+ return NULL; -+ } -+ -+ jose_io_decref(ios[i]); -+ ios[i] = jose_io_incref(b64); -+ } -+ -+ return jose_io_multiplex(NULL, ios, true); -+} -+ -+static int -+prepare_deriving_jwk(struct tang_keys_info *tki, const struct file_list *fl) -+{ -+ const size_t hash_alg_count = hash_alg_size(); -+ for (size_t a = 0; a < hash_alg_count; a++) { -+ for (size_t i = 0; i < fl->m_size; i++) { -+ struct tang_jwk *jwk __attribute__((cleanup(cleanup_tang_jwk))) = new_tang_jwk(fl->m_files[i], hash_alg[a]); -+ if (!jwk) { -+ fprintf(stderr, "Unable to create tang_jwk from %s with alg %s; skipping.\n", fl->m_files[i], hash_alg[a]); -+ continue; -+ } -+ if (!tang_jwk_list_add(tki->m_derive, jwk)) { -+ fprintf(stderr, "Unable to add JWK from %s with alg %s to list of deriving keys; skipping.\n", fl->m_files[i], hash_alg[a]); -+ continue; -+ } -+ } -+ } -+ -+ if (tki->m_derive->m_size > 1) { -+ qsort(tki->m_derive->m_jwk, tki->m_derive->m_size, sizeof(struct tang_jwk*), tang_jwk_thp_cmp_func); -+ } -+ return 1; -+} -+ -+static int -+prepare_adv_jwk(struct tang_keys_info *tki, const struct file_list *fl) -+{ -+ const size_t hash_alg_count = hash_alg_size(); -+ json_auto_t *keys = build_json_array((const char**)tki->m_sign_keys->m_files, tki->m_sign_keys->m_size); -+ json_auto_t *pub = remove_private_keys(tki->m_payload_keys); -+ -+ /* -+ * Adding dummy element in the first position. We will be be replacing -+ * it with the actual ones and then creating the JWS data. -+ */ -+ json_auto_t *dummy = json_object(); -+ if (json_array_insert(keys, 0, dummy) != 0) { -+ fprintf(stderr, "Error preparing adv JWKs.\n"); -+ return 0; -+ } -+ -+ json_auto_t *sigs = prepare_template_sigs(json_array_size(keys)); -+ -+ for (size_t i = 0; i < fl->m_size; i++) { -+ json_auto_t *jwk = json_load_file(fl->m_files[i], 0, NULL); -+ if (!jwk) { -+ fprintf(stderr, "Unable to load JWK from %s; skipping.\n", fl->m_files[i]); -+ continue; -+ } -+ if (json_array_set(keys, 0, jwk) != 0) { -+ fprintf(stderr, "Unable to add JWK from file (%s) to array; skipping.\n", fl->m_files[i]); -+ continue; -+ } -+ -+ char *jws_data __attribute__((cleanup(cleanup_buffer))) = process_adv(keys, sigs, pub); -+ if (!jws_data) { -+ fprintf(stderr, "Unable to obtain JWS from %s; skipping.\n", fl->m_files[i]); -+ continue; -+ } -+ json_auto_t *jws_json = json_loads(jws_data, 0, NULL); -+ if (!jws_json) { -+ fprintf(stderr, "Unable to convert string to JSON; skipping.\n"); -+ continue; -+ } -+ for (size_t a = 0; a < hash_alg_count; a++) { -+ char *thp __attribute__((cleanup(cleanup_buffer))) = jwk_thumbprint(jwk, hash_alg[a]); -+ if (!thp) { -+ fprintf(stderr, "Unable to obtain thumbprint from file (%s) and alg (%s); skipping.\n", fl->m_files[i], hash_alg[a]); -+ continue; -+ } -+ struct tang_jwk *jws __attribute__((cleanup(cleanup_tang_jwk)))= new_tang_jwk_from_args(jws_json, thp, hash_alg[a]); -+ if (!jws) { -+ fprintf(stderr, "Error creating tang_jwk with JWS data from file (%s) and alg (%s); skipping.\n", fl->m_files[i], hash_alg[a]); -+ continue; -+ } -+ if (!tang_jwk_list_add(tki->m_adv, jws)) { -+ fprintf(stderr, "Error adding JWS data from file (%s) and alg (%s) to adv list; skipping.\n", fl->m_files[i], hash_alg[a]); -+ continue; -+ } -+ } -+ } -+ -+ if (tki->m_adv->m_size > 1) { -+ qsort(tki->m_adv->m_jwk, tki->m_adv->m_size, sizeof(struct tang_jwk*), tang_jwk_thp_cmp_func); -+ } -+ return 1; -+} -+ -+static int -+prepare_deriving_and_adv(struct tang_keys_info *tki) -+{ -+ if (!tki) { -+ return 0; -+ } -+ -+ struct file_list *fl __attribute__ ((__cleanup__(cleanup_file_list))) = list_files(tki->m_jwkdir, ".jwk", 0 /* ignore hidden */); -+ if (!fl || fl->m_size == 0) { -+ fprintf(stderr, "No JWK keys available.\n"); -+ return 0; -+ } -+ -+ struct file_list *derive_key __attribute__((__cleanup__(cleanup_file_list))) = new_file_list(); -+ struct file_list *jws __attribute__((__cleanup__(cleanup_file_list))) = new_file_list(); -+ -+ char filepath[PATH_MAX] = {}; -+ for (size_t i = 0, size = fl->m_size; i < size; i++) { -+ snprintf(filepath, sizeof(filepath), "%s/%s", tki->m_jwkdir, fl->m_files[i]); -+ filepath[sizeof(filepath) - 1] = '\0'; -+ if (valid_for_deriving_keys(filepath)) { -+ if (!file_list_add(derive_key, filepath)) { -+ fprintf(stderr, "Error adding %s to file list of keys valid for deriving keys; skipping.\n", filepath); -+ } -+ } else if (valid_for_signing(filepath)) { -+ if (!file_list_add(jws, filepath)) { -+ fprintf(stderr, "Error adding %s to file list of keys valid for signing; skipping.\n", filepath); -+ } -+ } -+ } -+ -+ if (derive_key->m_size == 0 || jws->m_size == 0) { -+ fprintf(stderr, "Either the number of keys able to derive keys (%zu) or to sign keys (%zu) is zero.\n", derive_key->m_size, jws->m_size); -+ return 0; -+ } -+ -+ if (!prepare_deriving_jwk(tki, derive_key)) { -+ fprintf(stderr, "Error preparing deriving keys JWK.\n"); -+ return 0; -+ } -+ -+ if (!prepare_adv_jwk(tki, jws)) { -+ fprintf(stderr, "Error preparing advertising JWK.\n"); -+ return 0; -+ } -+ -+ return 1; -+} -+ -+struct tang_jwk* -+find_adv(const struct tang_keys_info *tki, const char* thp) -+{ -+ if (!tki) { -+ fprintf(stderr, "Invalid tang_keys_info (%p).\n", tki); -+ return NULL; -+ } -+ return tang_jwk_list_find_thp(tki->m_adv, thp); -+} -+ -+struct tang_jwk* -+find_deriving_key(const struct tang_keys_info *tki, const char* thp) -+{ -+ if (!tki) { -+ fprintf(stderr, "Invalid tang_keys_info (%p).\n", tki); -+ return NULL; -+ } -+ return tang_jwk_list_find_thp(tki->m_derive, thp); -+} -+ -+struct tang_keys_info* -+read_keys(const char *jwkdir) -+{ -+ struct tang_keys_info *tki = new_tang_keys_info(jwkdir); -+ if (!tki) { -+ fprintf(stderr, "Unable to create tang_keys_info\n"); -+ return NULL; -+ } -+ -+ struct file_list *fl __attribute__ ((__cleanup__(cleanup_file_list))) = list_files(jwkdir, ".jwk", 1 /* ignore hidden */); -+ if (!fl || fl->m_size == 0) { -+ fprintf(stderr, "No JWK keys available.\n"); -+ free_tang_keys_info(tki); -+ return NULL; -+ } -+ -+ char filepath[PATH_MAX] = {}; -+ for (size_t i = 0, size = fl->m_size; i < size; i++) { -+ snprintf(filepath, sizeof(filepath), "%s/%s", jwkdir, fl->m_files[i]); -+ filepath[sizeof(filepath) - 1] = '\0'; -+ if (valid_for_signing_and_verifying(filepath)) { -+ if (!file_list_add(tki->m_sign_keys, filepath)) { -+ fprintf(stderr, "Error adding %s to the list of signing keys\n", filepath); -+ free_tang_keys_info(tki); -+ return NULL; -+ } -+ if (!file_list_add(tki->m_payload_keys, filepath)) { -+ fprintf(stderr, "Error adding %s to the list of payload keys\n", filepath); -+ free_tang_keys_info(tki); -+ return NULL; -+ } -+ } else if (valid_for_deriving_keys(filepath)) { -+ if (!file_list_add(tki->m_payload_keys, filepath)) { -+ fprintf(stderr, "Error adding %s to the list of payload keys\n", filepath); -+ free_tang_keys_info(tki); -+ return NULL; -+ } -+ } -+ } -+ -+ if (!prepare_deriving_and_adv(tki)) { -+ fprintf(stderr, "Unable to prepare deriving and advcertising JWKs.\n"); -+ free_tang_keys_info(tki); -+ return NULL; -+ } -+ -+ if (!prepare_default_adv(tki)) { -+ fprintf(stderr, "Unable to prepare the default adv.\n"); -+ free_tang_keys_info(tki); -+ return NULL; -+ } -+ -+ return tki; -+} -+ -+int -+process_payload(const json_t *jwk, jose_io_t *io) -+{ -+ char *payload __attribute__((__cleanup__(cleanup_buffer))) = json_dumps(jwk, JSON_SORT_KEYS | JSON_COMPACT); -+ if (!payload) { -+ fprintf(stderr, "Error converting JSON to char*.\n"); -+ return 0; -+ } -+ -+ for (size_t i = 0, size = strlen(payload); i < size; i++) { -+ uint8_t b = payload[i]; -+ if (!io->feed(io, &b, sizeof(b))) { -+ fprintf(stderr, "Error calling io-feed with b = [%c].\n", b); -+ return 0; -+ } -+ } -+ -+ if (!io->done(io)) { -+ fprintf(stderr, "Error calling io-done.\n"); -+ return 0; -+ } -+ return 1; -+} -+ -+char* -+process_adv(const json_t *keys, json_t* sigs, const json_t *pub) -+{ -+ /* For the IO data, we need to have an own buffer. */ -+ uint8_t buffer[TANG_MAXBUFLEN] = {}; -+ size_t buflen = sizeof(buffer); -+ json_auto_t *io_data = json_object(); -+ -+ jose_io_auto_t *io = jose_jws_sig_io(NULL, io_data, sigs, keys); -+ if (!io) { -+ fprintf(stderr, "jose_jws_sig_io() failed.\n"); -+ return NULL; -+ } -+ -+ io = tang_prep_io(io, buffer, &buflen); -+ if (!io) { -+ fprintf(stderr, "tang_prep_io() failed.\n"); -+ return NULL; -+ } -+ -+ if (!process_payload(pub, io)) { -+ fprintf(stderr, "Error processing payload.\n"); -+ return NULL; -+ } -+ -+ const char *preamble = "{\"payload\":\""; -+ const char *separator = "\","; -+ const char *postamble = "}"; -+ char *data __attribute__ ((__cleanup__(cleanup_buffer))) = json_dumps(io_data, JSON_EMBED | JSON_COMPACT | JSON_SORT_KEYS); -+ if (!data) { -+ fprintf(stderr, "Error obtaining signing data.\n"); -+ return NULL; -+ } -+ -+ size_t adv_len = strlen(preamble) + buflen + strlen(separator) + strlen(data) + strlen(postamble) + 1; -+ char *adv_data = malloc(adv_len); -+ snprintf(adv_data, adv_len, "%s%s%s%s%s", preamble, buffer, separator, data, postamble); -+ adv_data[adv_len - 1] = '\0'; -+ return adv_data; -+} -+ -+int -+prepare_default_adv(struct tang_keys_info *tki) -+{ -+ if (!tki) { -+ fprintf(stderr, "Invalid tang_keys_info\n"); -+ return 0; -+ } -+ -+ if (!tki->m_sign_keys || tki->m_sign_keys->m_size == 0) { -+ fprintf(stderr, "No valid signing keys.\n"); -+ return 0; -+ } -+ -+ if (!tki->m_payload_keys || tki->m_payload_keys->m_size <= tki->m_sign_keys->m_size) { -+ fprintf(stderr, "Invalid payload keys.\n"); -+ return 0; -+ } -+ -+ json_auto_t *keys = build_json_array((const char**)tki->m_sign_keys->m_files, tki->m_sign_keys->m_size); -+ json_auto_t *sigs = prepare_template_sigs(tki->m_sign_keys->m_size); -+ json_auto_t *pub = remove_private_keys(tki->m_payload_keys); -+ -+ char *adv_str __attribute__((cleanup(cleanup_buffer))) = process_adv(keys, sigs, pub); -+ if (!adv_str) { -+ return 0; -+ } -+ json_auto_t *json = json_loads(adv_str, 0, NULL); -+ if (!json) { -+ return 0; -+ } -+ -+ struct tang_jwk *jwk = new_tang_jwk_from_args(json, NULL, NULL); -+ if (!jwk) { -+ return 0; -+ } -+ tki->m_default_adv = jwk; -+ return 1; -+} -diff --git a/src/keys.h b/src/keys.h -new file mode 100644 -index 0000000..150b881 ---- /dev/null -+++ b/src/keys.h -@@ -0,0 +1,84 @@ -+/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */ -+/* -+ * Copyright (c) 2019 Red Hat, Inc. -+ * Author: Sergio Correia -+ * -+ * 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 . -+ */ -+ -+#pragma once -+ -+#include -+#include -+ -+struct tang_jwk { -+ json_t* m_json; -+ char* m_from_file; -+ char* m_str; -+ char* m_thp; -+ char* m_alg; -+}; -+ -+struct tang_jwk_list { -+ struct tang_jwk **m_jwk; -+ size_t m_size; -+}; -+ -+struct tang_keys_info { -+ char *m_jwkdir; -+ struct file_list *m_payload_keys; -+ struct file_list *m_sign_keys; -+ -+ struct tang_jwk_list *m_derive; -+ struct tang_jwk_list *m_adv; -+ struct tang_jwk *m_default_adv; -+}; -+ -+/* struct tang_jwk. */ -+struct tang_jwk *new_tang_jwk(const char* /* file */, const char* /* alg */); -+struct tang_jwk* new_tang_jwk_from_args(json_t*, const char* /* thp */, const char* /* alg */); -+struct tang_jwk* tang_jwk_dup(const struct tang_jwk*); -+struct tang_jwk *generate_new_tang_jwk(const char* /* alg */); -+void free_tang_jwk(struct tang_jwk*); -+void cleanup_tang_jwk(struct tang_jwk **jwk); -+ -+/* struct tang_jwk_list. */ -+struct tang_jwk_list* new_tang_jwk_list(void); -+void free_tang_jwk_list(struct tang_jwk_list*); -+int tang_jwk_list_add(struct tang_jwk_list*, struct tang_jwk*); -+struct tang_jwk* tang_jwk_list_find_thp(const struct tang_jwk_list*, const char*); -+ -+char *jwk_thumbprint(const json_t* /* jwk */, const char* /* alg */); -+char *jwk_thumbprint_from_file(const char* /* file */, const char* /* alg */); -+int valid_for_signing(const char* /* file */); -+int valid_for_signing_and_verifying(const char* /* file */); -+int valid_for_deriving_keys(const char* /* file */); -+ -+struct tang_keys_info* new_tang_keys_info(const char*); -+void free_tang_keys_info(struct tang_keys_info*); -+void cleanup_tang_keys_info(struct tang_keys_info**); -+ -+ -+struct tang_keys_info* read_keys(const char*); -+ -+int process_payload(const json_t*, jose_io_t*); -+char* process_adv(const json_t*, json_t*, const json_t*); -+int prepare_default_adv(struct tang_keys_info*); -+ -+size_t hash_alg_size(void); -+int check_keys(const char*); -+struct tang_jwk* find_adv(const struct tang_keys_info*, const char*); -+struct tang_jwk* find_deriving_key(const struct tang_keys_info*, const char*); -+int is_hash(const char*); -+ -diff --git a/src/tangd-update b/src/tangd-update -deleted file mode 100755 -index 652dbef..0000000 ---- 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 --# --# 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 . --# -- --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 " >&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 -diff --git a/src/tangd.c b/src/tangd.c -index dc45a90..b569f38 100644 ---- a/src/tangd.c -+++ b/src/tangd.c -@@ -28,6 +28,7 @@ - #include - - #include -+#include "keys.h" - - static void - str_cleanup(char **str) -@@ -50,9 +51,8 @@ adv(enum http_method method, const char *path, const char *body, - __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; -+ const char *jwkdir = misc; - - if (matches[1].rm_so < matches[1].rm_eo) { - size_t size = matches[1].rm_eo - matches[1].rm_so; -@@ -61,23 +61,25 @@ adv(enum http_method method, const char *path, const char *body, - 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) -- return http_reply(HTTP_STATUS_NOT_FOUND, NULL); -- -- if (fstat(fileno(file), &st) != 0) -+ if (!check_keys(jwkdir)) { - return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); -+ } - -- adv = calloc(st.st_size + 1, 1); -- if (!adv) -+ tki = read_keys(jwkdir); -+ if (!tki) { - 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); -+ if (thp) { -+ const struct tang_jwk *jwk = find_adv(tki, thp); -+ if (!jwk) { -+ return http_reply(HTTP_STATUS_NOT_FOUND, NULL); -+ } -+ adv = strdup(jwk->m_str); -+ } else { -+ /* Default adv. */ -+ adv = strdup(tki->m_default_adv->m_str); -+ } - - return http_reply(HTTP_STATUS_OK, - "Content-Type: application/jose+json\r\n" -@@ -91,10 +93,11 @@ rec(enum http_method method, const char *path, const char *body, - { - __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; -+ const struct tang_jwk *jwk = NULL; -+ - size_t size = matches[1].rm_eo - matches[1].rm_so; -- char filename[PATH_MAX] = {}; -- const char *cachedir = misc; -- json_auto_t *jwk = NULL; -+ const char *jwkdir = misc; - json_auto_t *req = NULL; - json_auto_t *rep = NULL; - const char *alg = NULL; -@@ -129,17 +132,24 @@ rec(enum http_method method, const char *path, const char *body, - if (!thp) - return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); - -- if (snprintf(filename, sizeof(filename), "%s/%s.jwk", cachedir, thp) < 0) -+ if (!check_keys(jwkdir)) { -+ return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); -+ } -+ -+ tki = read_keys(jwkdir); -+ if (!tki) { - return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); -+ } - -- jwk = json_load_file(filename, 0, NULL); -- if (!jwk) -+ jwk = find_deriving_key(tki, thp); -+ if (!jwk) { - return http_reply(HTTP_STATUS_NOT_FOUND, NULL); -+ } - -- if (!jose_jwk_prm(NULL, jwk, true, "deriveKey")) -+ if (!jose_jwk_prm(NULL, jwk->m_json, true, "deriveKey")) - return http_reply(HTTP_STATUS_FORBIDDEN, NULL); - -- if (json_unpack(jwk, "{s:s,s?s}", "d", &d, "alg", &alg) < 0) -+ if (json_unpack(jwk->m_json, "{s:s,s?s}", "d", &d, "alg", &alg) < 0) - return http_reply(HTTP_STATUS_FORBIDDEN, NULL); - - if (alg && strcmp(alg, "ECMR") != 0) -@@ -148,7 +158,7 @@ rec(enum http_method method, const char *path, const char *body, - /* - * Perform the exchange and return - */ -- rep = jose_jwk_exc(NULL, jwk, req); -+ rep = jose_jwk_exc(NULL, jwk->m_json, req); - if (!rep) - return http_reply(HTTP_STATUS_BAD_REQUEST, NULL); - -diff --git a/src/util.c b/src/util.c -new file mode 100644 -index 0000000..b8e3756 ---- /dev/null -+++ b/src/util.c -@@ -0,0 +1,141 @@ -+/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */ -+/* -+ * Copyright (c) 2019 Red Hat, Inc. -+ * Author: Sergio Correia -+ * -+ * 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 . -+ */ -+ -+ -+#include -+#include -+#include -+#include -+ -+#include "util.h" -+ -+struct file_list* -+new_file_list(void) -+{ -+ struct file_list *fl = malloc(sizeof(*fl)); -+ if (!fl) { -+ return NULL; -+ } -+ fl->m_files = NULL; -+ fl->m_size = 0; -+ -+ return fl; -+} -+ -+void -+free_file_list(struct file_list *fl) -+{ -+ if (!fl) { -+ return; -+ } -+ -+ for (size_t i = 0, size = fl->m_size; i < size; i++) { -+ free(fl->m_files[i]); -+ } -+ free(fl->m_files); -+ fl->m_size = 0; -+ free(fl); -+} -+ -+void -+cleanup_file_list(struct file_list **fl) -+{ -+ if (!fl || !*fl) { -+ return; -+ } -+ free_file_list(*fl); -+} -+ -+int -+file_list_add(struct file_list *fl, const char *filepath) -+{ -+ if (!fl || !filepath) { -+ return 0; -+ } -+ -+ char **new_files = realloc(fl->m_files, sizeof(char *) * (fl->m_size + 1)); -+ if (!new_files) { -+ fprintf(stderr, "Error reallocating memory for the new file\n"); -+ return 0; -+ } -+ -+ fl->m_files = new_files; -+ fl->m_files[fl->m_size++] = strdup(filepath); -+ return 1; -+} -+ -+int -+match_file(const char *file, const char *pattern) -+{ -+ if (!file) { -+ return 0; -+ } -+ -+ if (!pattern) { -+ return 1; -+ } -+ -+ return strstr(file, pattern) != NULL; -+} -+ -+int -+list_files_cmp_func(const void *a, const void *b) -+{ -+ const char *sa = *(const char**)a; -+ const char *sb = *(const char**)b; -+ return strcmp(sa, sb); -+} -+ -+struct file_list* -+list_files(const char *path, const char *pattern, int ignore_hidden) -+{ -+ struct file_list *fl = new_file_list(); -+ struct dirent *d; -+ DIR *dir = opendir(path); -+ -+ if (dir == NULL) { -+ return fl; -+ } -+ -+ while ((d = readdir(dir)) != NULL) { -+ if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) { -+ continue; -+ } -+ -+ if (ignore_hidden && d->d_name[0] == '.') { -+ continue; -+ } -+ -+ if (match_file(d->d_name, pattern)) { -+ if (!file_list_add(fl, d->d_name)) { -+ fprintf(stderr, "Unable to add file %s to file list.\n", d->d_name); -+ continue; -+ } -+ } -+ } -+ -+ if (fl->m_size > 1) { -+ qsort(fl->m_files, fl->m_size, sizeof(char*), list_files_cmp_func); -+ } -+ -+ closedir(dir); -+ return fl; -+} -+ -+ -diff --git a/src/util.h b/src/util.h -new file mode 100644 -index 0000000..c3af014 ---- /dev/null -+++ b/src/util.h -@@ -0,0 +1,33 @@ -+/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */ -+/* -+ * Copyright (c) 2019 Red Hat, Inc. -+ * Author: Sergio Correia -+ * -+ * 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 . -+ */ -+ -+#pragma once -+ -+struct file_list { -+ char **m_files; -+ size_t m_size; -+}; -+ -+struct file_list* new_file_list(void); -+void free_file_list(struct file_list*); -+void cleanup_file_list(struct file_list**); -+int file_list_add(struct file_list* /* fl */, const char* /* filepath */); -+int match_file(const char* /* file */, const char* /* pattern */); -+int list_files_cmp_func(const void*, const void*); -+struct file_list* list_files(const char* /* path */, const char* /* match */, int /* ignore_hidden */); -diff --git a/tests/adv b/tests/adv -index 357d097..986e632 100755 ---- a/tests/adv -+++ b/tests/adv -@@ -36,15 +36,13 @@ trap 'exit' ERR - - 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 - -diff --git a/tests/rec b/tests/rec -index d1dfe97..3316565 100755 ---- a/tests/rec -+++ b/tests/rec -@@ -28,11 +28,9 @@ trap 'exit' ERR - - 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 @@ jose jwk pub -i $TMP/exc.jwk -o $TMP/exc.pub.jwk - - # 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 - -diff --git a/units/tangd-keygen.service.in b/units/tangd-keygen.service.in -deleted file mode 100644 -index 2f80cd8..0000000 ---- 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@ -diff --git a/units/tangd-update.path.in b/units/tangd-update.path.in -deleted file mode 100644 -index ee9005d..0000000 ---- a/units/tangd-update.path.in -+++ /dev/null -@@ -1,4 +0,0 @@ --[Path] --PathChanged=@jwkdir@ --MakeDirectory=true --DirectoryMode=0700 -diff --git a/units/tangd-update.service.in b/units/tangd-update.service.in -deleted file mode 100644 -index 11a4cb2..0000000 ---- 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@ -diff --git a/units/tangd.socket.in b/units/tangd.socket.in -index 22474ea..0a3e239 100644 ---- a/units/tangd.socket.in -+++ b/units/tangd.socket.in -@@ -1,10 +1,5 @@ - [Unit] - Description=Tang Server socket --Requires=tangd-keygen.service --Requires=tangd-update.service --Requires=tangd-update.path --After=tangd-keygen.service --After=tangd-update.service - - [Socket] - ListenStream=80 -diff --git a/units/tangd@.service.in b/units/tangd@.service.in -index c4b9597..f1db261 100644 ---- a/units/tangd@.service.in -+++ b/units/tangd@.service.in -@@ -5,4 +5,4 @@ Description=Tang Server - StandardInput=socket - StandardOutput=socket - StandardError=journal --ExecStart=@libexecdir@/tangd @cachedir@ -+ExecStart=@libexecdir@/tangd @jwkdir@ --- -2.18.1 - diff --git a/SOURCES/0002-Exit-with-success-unless-the-issue-was-with-with-tan.patch b/SOURCES/0002-Exit-with-success-unless-the-issue-was-with-with-tan.patch deleted file mode 100644 index f8c27a3..0000000 --- a/SOURCES/0002-Exit-with-success-unless-the-issue-was-with-with-tan.patch +++ /dev/null @@ -1,38 +0,0 @@ -From ea43ca02cf52d0455c6949683692a95e38ccdf70 Mon Sep 17 00:00:00 2001 -From: Sergio Correia -Date: Fri, 4 Dec 2020 09:05:19 -0300 -Subject: [PATCH 2/2] Exit with success unless the issue was with with tangd - itself - -When an HTTP parser error happens, tangd is currently exiting with an -error status, which may cause trouble in some scenarios [1]. - -However, we don't exit with an error in situations where we try requests -that do not exist, for instance. It makes sense to only exit with an -error when the error was with tangd itself, e.g.: when we are unable to -read the directory with the keys, not when the actual HTTP operation -does not succeed for some reason. - -Upstream: https://github.com/latchset/tang/pull/55 - -[1] https://bugzilla.redhat.com/show_bug.cgi?id=1828558 ---- - src/tangd.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/tangd.c b/src/tangd.c -index b569f38..d40201f 100644 ---- a/src/tangd.c -+++ b/src/tangd.c -@@ -225,7 +225,7 @@ main(int argc, char *argv[]) - if (parser.http_errno != 0) { - fprintf(stderr, "HTTP Parsing Error: %s\n", - http_errno_description(parser.http_errno)); -- return EXIT_FAILURE; -+ return EXIT_SUCCESS; - } - - memmove(req, &req[r], rcvd - r); --- -2.27.0 - diff --git a/SOURCES/0003-Fix-permissions-race-condition.patch b/SOURCES/0003-Fix-permissions-race-condition.patch deleted file mode 100644 index c24ac55..0000000 --- a/SOURCES/0003-Fix-permissions-race-condition.patch +++ /dev/null @@ -1,31 +0,0 @@ ---- tang-7.ori/src/tangd-keygen 2017-06-10 15:29:39.000000000 +0200 -+++ tang-7/src/tangd-keygen 2023-06-28 11:40:01.700819479 +0200 -@@ -27,6 +27,8 @@ - - [ $# -eq 3 ] && sig=$2 && exc=$3 - -+# Set default umask for file creation. -+umask 0337 - jwe=`jose jwk gen -i '{"alg":"ES512"}'` - [ -z "$sig" ] && sig=`echo "$jwe" | jose jwk thp -i-` - echo "$jwe" > $1/$sig.jwk ---- tang-7.ori/src/keys.c 2023-06-28 09:57:08.706712410 +0200 -+++ tang-7/src/keys.c 2023-06-28 11:43:41.742247417 +0200 -@@ -23,6 +23,7 @@ - #include - #include - #include -+#include - - #include "util.h" - #include "keys.h" -@@ -557,6 +558,9 @@ - /* At this point, there are no keys, so let's create them. */ - const char *alg[] = {"ES512", "ECMR", NULL}; - char path[PATH_MAX]; -+ -+ /* Set default umask for file creation. */ -+ umask(0337); - for (int i = 0; alg[i] != NULL; i++) { - struct tang_jwk *jwk __attribute__((cleanup(cleanup_tang_jwk))) = generate_new_tang_jwk(alg[i]); - if (!jwk) { diff --git a/SOURCES/0004-Set-tang-owner-group.patch b/SOURCES/0004-Set-tang-owner-group.patch deleted file mode 100644 index 7cc9bc5..0000000 --- a/SOURCES/0004-Set-tang-owner-group.patch +++ /dev/null @@ -1,26 +0,0 @@ ---- tang-7.ori/src/tangd-keygen 2023-07-21 11:45:39.091100369 +0200 -+++ tang-7/src/tangd-keygen 2023-07-21 11:47:58.813612221 +0200 -@@ -20,6 +20,13 @@ - - trap 'exit' ERR - -+set_perms() { -+ chmod -- 0440 "${1}" -+ if ! chown -- "tang:tang" "${1}" 2>/dev/null; then -+ echo "Unable to change owner/group for ${1} to tang:tang" >&2 -+ fi -+} -+ - if [ $# -ne 1 -a $# -ne 3 ] || [ ! -d "$1" ]; then - echo "Usage: $0 [ ]" >&2 - exit 1 -@@ -32,7 +39,9 @@ - jwe=`jose jwk gen -i '{"alg":"ES512"}'` - [ -z "$sig" ] && sig=`echo "$jwe" | jose jwk thp -i-` - echo "$jwe" > $1/$sig.jwk -+set_perms "$1/$sig.jwk" - - jwe=`jose jwk gen -i '{"alg":"ECMR"}'` - [ -z "$exc" ] && exc=`echo "$jwe" | jose jwk thp -i-` - echo "$jwe" > $1/$exc.jwk -+set_perms "$1/$exc.jwk" diff --git a/SPECS/tang.spec b/SPECS/tang.spec deleted file mode 100644 index 8e0bd89..0000000 --- a/SPECS/tang.spec +++ /dev/null @@ -1,163 +0,0 @@ -Name: tang -Version: 7 -Release: 8%{?dist} -Summary: Network Presence Binding Daemon - -License: GPLv3+ -URL: https://github.com/latchset/%{name} -Source0: https://github.com/latchset/%{name}/releases/download/v%{version}/%{name}-%{version}.tar.bz2 -Patch1: 0001-Move-key-generation-to-tang.patch -Patch2: 0002-Exit-with-success-unless-the-issue-was-with-with-tan.patch -Patch3: 0003-Fix-permissions-race-condition.patch -Patch4: 0004-Set-tang-owner-group.patch - -BuildRequires: gcc -BuildRequires: autoconf -BuildRequires: automake -BuildRequires: jose >= 8 -BuildRequires: libjose-devel >= 8 -BuildRequires: libjose-zlib-devel >= 8 -BuildRequires: libjose-openssl-devel >= 8 - -BuildRequires: http-parser-devel >= 2.7.1-3 -BuildRequires: systemd-devel -BuildRequires: pkgconfig - -BuildRequires: systemd -BuildRequires: curl - -BuildRequires: asciidoc -BuildRequires: coreutils -BuildRequires: grep -BuildRequires: sed -BuildRequires: git-core - -%{?systemd_requires} -Requires: coreutils -Requires: jose >= 8 -Requires: grep -Requires: sed - -Requires(pre): shadow-utils - -%description -Tang is a small daemon for binding data to the presence of a third party. - -%prep -%autosetup -S git - -%build -autoreconf -i -%configure -make %{?_smp_mflags} V=1 - -%install -rm -rf $RPM_BUILD_ROOT -%make_install -echo "User=%{name}" >> $RPM_BUILD_ROOT/%{_unitdir}/%{name}d@.service -%{__mkdir_p} $RPM_BUILD_ROOT/%{_localstatedir}/db/%{name} - -%check -if ! make %{?_smp_mflags} check; then - cat test-suite.log - false -fi - -%pre -getent group %{name} >/dev/null || groupadd -r %{name} -getent passwd %{name} >/dev/null || \ - useradd -r -g %{name} -d %{_localstatedir}/cache/%{name} -s /sbin/nologin \ - -c "Tang Network Presence Daemon user" %{name} -exit 0 - -%post -%systemd_post %{name}d.socket - -%preun -%systemd_preun %{name}d.socket - -%postun -%systemd_postun_with_restart %{name}d.socket - -%files -%license COPYING -%attr(0700, %{name}, %{name}) %{_localstatedir}/db/%{name} -%{_unitdir}/%{name}d@.service -%{_unitdir}/%{name}d.socket -%{_libexecdir}/%{name}d-keygen -%{_libexecdir}/%{name}d -%{_mandir}/man8/tang.8* -%{_bindir}/%{name}-show-keys -%{_mandir}/man1/tang-show-keys.1* - -%changelog -* Fri Jul 21 2023 Sergio Arroutbi - 7-8 -- Set correct user/group (tang/tang) in tangd-keygen - Resolves: rhbz#2188743 - -* Wed Jun 28 2023 Sergio Arroutbi - 7-7 -- Fix race condition when creating/rotating keys - Resolves: rhbz#2182410 - Resolves: CVE-2023-1672 - -* Wed Jan 13 2021 Sergio Correia - 7-6 -- Exit with success unless the issue was with with tangd itself - Resolves: rhbz#1828558 - -* Sun Dec 01 2019 Sergio Correia - 7-5 -- Permissions of /var/db/tang set to 0700 -- Home dir of user tang is /var/cache/tang - -* Fri Nov 29 2019 Sergio Correia - 7-4 -- Fix permissions of /var/db/tang - -* Tue Oct 15 2019 Sergio Correia - 7-3 -- Rebuild to ensure correct dist tag - -* Sun Sep 29 2019 Sergio Correia - 7-2 -- Move key generation to tang -- Resolves rhbz#1745177, rhbz#1679186 - -* Fri Aug 10 2018 Nathaniel McCallum - 7-1 -- New upstream release -- Retire tang-nagios package (now separate upstream) - -* Sat Jul 14 2018 Fedora Release Engineering - 6-5 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild - -* Fri Feb 09 2018 Fedora Release Engineering - 6-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild - -* Thu Aug 03 2017 Fedora Release Engineering - 6-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild - -* Thu Jul 27 2017 Fedora Release Engineering - 6-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild - -* Wed Jun 14 2017 Nathaniel McCallum - 6-1 -- New upstream release - -* Wed Jun 14 2017 Nathaniel McCallum - 5-2 -- Fix incorrect dependencies - -* Wed Jun 14 2017 Nathaniel McCallum - 5-1 -- New upstream release - -* Sat Feb 11 2017 Fedora Release Engineering - 4-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild - -* Mon Nov 14 2016 Nathaniel McCallum - 4-2 -- Fix a race condition in one of the tests - -* Thu Nov 10 2016 Nathaniel McCallum - 4-1 -- New upstream release -- Add nagios subpackage - -* Wed Oct 26 2016 Nathaniel McCallum - 3-1 -- New upstream release - -* Wed Oct 19 2016 Nathaniel McCallum - 2-1 -- New upstream release - -* Tue Aug 23 2016 Nathaniel McCallum - 1-1 -- First release diff --git a/sources b/sources new file mode 100644 index 0000000..9f4f7cd --- /dev/null +++ b/sources @@ -0,0 +1 @@ +SHA512 (tang-14.tar.xz) = 1f41542116c27cd4c05f683d0b03a51fca37f07abc13f9a8301602fff0b8681383875f5c4fa7fe5f1c7e216790ff84f2271432836b126c825e6b1a55e1cf44fc diff --git a/tang.spec b/tang.spec new file mode 100644 index 0000000..cc1b6d9 --- /dev/null +++ b/tang.spec @@ -0,0 +1,265 @@ +## START: Set by rpmautospec +## (rpmautospec version 0.6.1) +## RPMAUTOSPEC: autorelease, autochangelog +%define autorelease(e:s:pb:n) %{?-p:0.}%{lua: + release_number = 9; + base_release_number = tonumber(rpm.expand("%{?-b*}%{!?-b:1}")); + print(release_number + base_release_number - 1); +}%{?-e:.%{-e*}}%{?-s:.%{-s*}}%{!?-n:%{?dist}} +## END: Set by rpmautospec + +Name: tang +Version: 14 +Release: %autorelease +Summary: Network Presence Binding Daemon + +License: GPL-3.0-or-later +URL: https://github.com/latchset/%{name} +Source0: https://github.com/latchset/%{name}/releases/download/v%{version}/%{name}-%{version}.tar.xz +Source1: tang.sysusers +Patch: 0001-Add-support-for-building-with-llhttp-instead-of-http.patch +Patch: 0002-Fix-issue-introduced-in-http-parser-llhttp-conversio.patch + +BuildRequires: gcc +BuildRequires: meson +BuildRequires: git-core +BuildRequires: jose >= 8 +BuildRequires: libjose-devel >= 8 +BuildRequires: libjose-zlib-devel >= 8 +BuildRequires: libjose-openssl-devel >= 8 + +BuildRequires: llhttp-devel +BuildRequires: systemd-devel +BuildRequires: pkgconfig + +BuildRequires: systemd +BuildRequires: systemd-rpm-macros +BuildRequires: curl + +BuildRequires: asciidoc +BuildRequires: coreutils +BuildRequires: grep +BuildRequires: socat +BuildRequires: sed +BuildRequires: iproute + +%{?systemd_requires} +Requires: coreutils +Requires: jose >= 8 +Requires: llhttp +Requires: grep +Requires: sed + +Requires(pre): shadow-utils + +%description +Tang is a small daemon for binding data to the presence of a third party. + +%prep +%autosetup -S git + +%build +%meson +%meson_build + +%install +%meson_install +install -p -D -m 0644 %{SOURCE1} %{buildroot}%{_sysusersdir}/tang.conf +%{__mkdir_p} $RPM_BUILD_ROOT/%{_localstatedir}/db/%{name} + +%check +%meson_test \ +%ifarch riscv64 + --timeout-multiplier 10 \ +%endif + %{nil} + +%pre +%sysusers_create_compat %{SOURCE1} +exit 0 + +%post +%systemd_post %{name}d.socket + +# Let's make sure any existing keys are readable only +# by the owner/group. +if [ -d /var/db/tang ]; then + for k in /var/db/tang/*.jwk; do + test -e "${k}" || continue + chmod 0440 -- "${k}" + done + for k in /var/db/tang/.*.jwk; do + test -e "${k}" || continue + chmod 0440 -- "${k}" + done + chown tang:tang -R /var/db/tang +fi + +%preun +%systemd_preun %{name}d.socket + +%postun +%systemd_postun_with_restart %{name}d.socket + +%files +%license COPYING +%attr(0700, %{name}, %{name}) %{_localstatedir}/db/%{name} +%{_unitdir}/%{name}d@.service +%{_unitdir}/%{name}d.socket +%{_libexecdir}/%{name}d-keygen +%{_libexecdir}/%{name}d-rotate-keys +%{_libexecdir}/%{name}d +%{_mandir}/man8/tang.8* +%{_bindir}/%{name}-show-keys +%{_mandir}/man1/tang-show-keys.1* +%{_mandir}/man1/tangd-rotate-keys.1.* +%{_sysusersdir}/tang.conf + +%changelog +## START: Generated by rpmautospec +* Mon Jun 24 2024 Troy Dawson - 14-9 +- Bump release for June 2024 mass rebuild + +* Mon Jun 10 2024 David Abdurachmanov - 14-8 +- riscv64: Give more time for tests to finish + +* Mon May 27 2024 koncpa - 14-7 +- Enable RHEL gating for tang + +* Mon Feb 12 2024 Sergio Correia - 14-6 +- Backport follow-up fix for llhttp conversion + +* Sat Jan 27 2024 Fedora Release Engineering - 14-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Thu Jan 11 2024 Sergio Correia - 14.3 +- Use llhttp instead of http-parser + +* Sat Jul 22 2023 Fedora Release Engineering - 14-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + +* Wed Jun 14 2023 Sergio Arroutbi - 14-1 +- New upstream release - v14 + Resolves: rhbz#2180990 + +* Fri Feb 10 2023 Sergio Arroutbi - 13-1 +- New upstream release - v13 + +* Sat Jan 21 2023 Fedora Release Engineering - 11-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + +* Wed Dec 07 2022 Sergio Correia - 11-5 +- Report error details when json_load_file() fails + +* Wed Aug 17 2022 Sergio Arroutbi - 11-4 +- Adopt systemd-sysusers format + +* Sat Jul 23 2022 Fedora Release Engineering - 11-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Sat Jan 22 2022 Fedora Release Engineering - 11-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Tue Dec 14 2021 Sergio Correia - 11-1 +- New upstream release - v11. + Resolves: CVE-2021-4076 + +* Mon Oct 04 2021 Sergio Arroutbi - 10-5 +- Fix scriptlet from previous commit + +* Mon Oct 04 2021 Sergio Correia - 10-4 +- Keys are created with 0440 mode + Resolves rhbz#2008204 + +* Fri Jul 23 2021 Fedora Release Engineering - 10-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Thu May 20 2021 Sergio Correia - 10-2 +- Fix issues reported by shellcheck and a possible NULL pointer + dereference reported by gcc static analyzer (3d770c6, 262d98f) + +* Wed May 05 2021 Sergio Correia - 10-1 +- New upstream release - v10. + +* Tue Mar 02 2021 Zbigniew Jędrzejewski-Szmek - 8-3 +- Rebuilt for updated systemd-rpm-macros + See https://pagure.io/fesco/issue/2583. + +* Tue Feb 09 2021 Sergio Correia - 8-2 +- Remove extra patches as they are already included in v8 release + +* Mon Feb 08 2021 Sergio Correia - 8-1 +- New upstream release - v8. + +* Wed Jan 27 2021 Fedora Release Engineering - 7-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Tue Dec 1 2020 Sergio Correia - 7.8 +- Move build system to meson + Upstream commits (fed9020, 590de27) +- Move key handling to tang itself + Upstream commits (6090505, c71df1d, 7119454) + +* Wed Jul 29 2020 Fedora Release Engineering - 7-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Wed Apr 15 2020 Igor Raits - 7-6 +- Rebuild for http-parser 2.9.4 + +* Tue Feb 25 2020 Sergio Correia - 7-5 +- Rebuilt after http-parser update + +* Fri Jan 31 2020 Fedora Release Engineering - 7-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Sat Jul 27 2019 Fedora Release Engineering - 7-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Sun Feb 03 2019 Fedora Release Engineering - 7-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Fri Aug 10 2018 Nathaniel McCallum - 7-1 +- New upstream release +- Retire tang-nagios package (now separate upstream) + +* Sat Jul 14 2018 Fedora Release Engineering - 6-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Fri Feb 09 2018 Fedora Release Engineering - 6-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Thu Aug 03 2017 Fedora Release Engineering - 6-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Thu Jul 27 2017 Fedora Release Engineering - 6-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Wed Jun 14 2017 Nathaniel McCallum - 6-1 +- New upstream release + +* Wed Jun 14 2017 Nathaniel McCallum - 5-2 +- Fix incorrect dependencies + +* Wed Jun 14 2017 Nathaniel McCallum - 5-1 +- New upstream release + +* Sat Feb 11 2017 Fedora Release Engineering - 4-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Mon Nov 14 2016 Nathaniel McCallum - 4-2 +- Fix a race condition in one of the tests + +* Thu Nov 10 2016 Nathaniel McCallum - 4-1 +- New upstream release +- Add nagios subpackage + +* Wed Oct 26 2016 Nathaniel McCallum - 3-1 +- New upstream release + +* Wed Oct 19 2016 Nathaniel McCallum - 2-1 +- New upstream release + +* Tue Aug 23 2016 Nathaniel McCallum - 1-1 +- First release + +## END: Generated by rpmautospec diff --git a/tang.sysusers b/tang.sysusers new file mode 100644 index 0000000..98e12f5 --- /dev/null +++ b/tang.sysusers @@ -0,0 +1 @@ +u tang - "Tang Network Presence Daemon user" /var/cache/tang -