1606525324.v7-8-g7119454.move-key-handling-to-tang-itself.patch 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. Subject: Move key handling to tang itself
  2. Origin: v7-8-g7119454 <https://github.com/latchset/tang/commit/v7-8-g7119454>
  3. Upstream-Author: Sergio Correia <scorreia@redhat.com>
  4. Date: Fri Nov 27 22:02:04 2020 -0300
  5. Use the key manipulation functions added in src/keys.{c|h} in tangd.
  6. This effectively removes the need for a cache directory -- usually
  7. /var/cache/tang --, which contained pre-computed files with signed
  8. advertisements and JWK with keys for deriving new keys.
  9. This computation was done by the tangd-update script, which has also
  10. been removed in this commit.
  11. We relied on systemd to run this script whenever the JWK dir -- usually
  12. /var/db/tang, which is where the actual keys are located -- changed, to
  13. keep the cache directory updated, but this is sometimes unreliable,
  14. causing issues like the ones reported in #23 and #24.
  15. As of now, tang performs these computations itself and does not depend
  16. on external scripts to make sure it has reliable information regarding
  17. its keys.
  18. Additionally, tang also creates a new pair of keys if none exist.
  19. --- a/meson.build
  20. +++ b/meson.build
  21. @@ -16,14 +16,12 @@
  22. bindir = join_paths(get_option('prefix'), get_option('bindir'))
  23. systemunitdir = join_paths(get_option('prefix'), '../lib/systemd/system')
  24. licensedir = join_paths(get_option('prefix'), 'share', 'licenses', meson.project_name())
  25. -cachedir = join_paths(get_option('localstatedir'), 'cache', meson.project_name())
  26. jwkdir = join_paths(get_option('localstatedir'), 'db', meson.project_name())
  27. data = configuration_data()
  28. data.set('libexecdir', libexecdir)
  29. data.set('sysconfdir', sysconfdir)
  30. data.set('systemunitdir', systemunitdir)
  31. -data.set('cachedir', cachedir)
  32. data.set('jwkdir', jwkdir)
  33. add_project_arguments(
  34. --- a/src/meson.build
  35. +++ b/src/meson.build
  36. @@ -1,6 +1,6 @@
  37. tangd = executable('tangd',
  38. - 'http.h',
  39. 'http.c',
  40. + 'keys.c',
  41. 'tangd.c',
  42. dependencies: [jose, http_parser],
  43. install: true,
  44. @@ -9,6 +9,5 @@
  45. bins += join_paths(meson.current_source_dir(), 'tang-show-keys')
  46. libexecbins += join_paths(meson.current_source_dir(), 'tangd-keygen')
  47. -libexecbins += join_paths(meson.current_source_dir(), 'tangd-update')
  48. # vim:set ts=2 sw=2 et:
  49. --- a/src/tangd-update
  50. +++ /dev/null
  51. @@ -1,83 +0,0 @@
  52. -#!/bin/bash
  53. -# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
  54. -#
  55. -# Copyright (c) 2016 Red Hat, Inc.
  56. -# Author: Nathaniel McCallum <npmccallum@redhat.com>
  57. -#
  58. -# This program is free software: you can redistribute it and/or modify
  59. -# it under the terms of the GNU General Public License as published by
  60. -# the Free Software Foundation, either version 3 of the License, or
  61. -# (at your option) any later version.
  62. -#
  63. -# This program is distributed in the hope that it will be useful,
  64. -# but WITHOUT ANY WARRANTY; without even the implied warranty of
  65. -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  66. -# GNU General Public License for more details.
  67. -#
  68. -# You should have received a copy of the GNU General Public License
  69. -# along with this program. If not, see <http://www.gnu.org/licenses/>.
  70. -#
  71. -
  72. -TMP='{"protected":{"cty":"jwk-set+json"}}'
  73. -
  74. -trap 'exit' ERR
  75. -
  76. -shopt -s nullglob
  77. -
  78. -HASHES=`jose alg -k hash`
  79. -
  80. -if [ $# -ne 2 ] || [ ! -d "$1" ]; then
  81. - echo "Usage: $0 <jwkdir> <cachedir>" >&2
  82. - exit 1
  83. -fi
  84. -
  85. -[ ! -d "$2" ] && mkdir -p -m 0700 "$2"
  86. -
  87. -src=`realpath "$1"`
  88. -dst=`realpath "$2"`
  89. -
  90. -payl=()
  91. -sign=()
  92. -
  93. -for jwk in $src/*.jwk; do
  94. - if jose jwk use -i "$jwk" -r -u sign -u verify; then
  95. - sign+=("-s" "$TMP" "-k" "$jwk")
  96. - payl+=("-i" "$jwk")
  97. - elif jose jwk use -i "$jwk" -r -u deriveKey; then
  98. - payl+=("-i" "$jwk")
  99. - else
  100. - echo "Skipping invalid key: $jwk" >&2
  101. - fi
  102. -done
  103. -
  104. -if [ ${#sign[@]} -gt 0 ]; then
  105. - jose jwk pub -s "${payl[@]}" \
  106. - | jose jws sig -I- "${sign[@]}" -o "$dst/.default.jws"
  107. - mv -f "$dst/.default.jws" "$dst/default.jws"
  108. - new=default.jws
  109. -fi
  110. -
  111. -shopt -s dotglob
  112. -
  113. -for jwk in $src/*.jwk; do
  114. - for hsh in $HASHES; do
  115. - thp=`jose jwk thp -i "$jwk" -a $hsh`
  116. -
  117. - if jose jwk use -i "$jwk" -r -u deriveKey; then
  118. - ln -sf "$jwk" "$dst/.$thp.jwk"
  119. - mv -f "$dst/.$thp.jwk" "$dst/$thp.jwk"
  120. - new="$new\n$thp.jwk"
  121. - elif jose jwk use -i "$jwk" -r -u sign; then
  122. - keys=("${sign[@]}" -s "$TMP" -k "$jwk")
  123. - jose jwk pub -s "${payl[@]}" \
  124. - | jose jws sig -I- "${keys[@]}" -o "$dst/.$thp.jws"
  125. - mv -f "$dst/.$thp.jws" "$dst/$thp.jws"
  126. - new="$new\n$thp.jws"
  127. - fi
  128. - done
  129. -done
  130. -
  131. -for f in "$dst"/*; do
  132. - b=`basename "$f"`
  133. - echo -e "$new" | grep -q "^$b\$" || rm -f "$f"
  134. -done
  135. --- a/src/tangd.c
  136. +++ b/src/tangd.c
  137. @@ -28,6 +28,7 @@
  138. #include <unistd.h>
  139. #include <jose/jose.h>
  140. +#include "keys.h"
  141. static void
  142. str_cleanup(char **str)
  143. @@ -36,23 +37,20 @@
  144. free(*str);
  145. }
  146. -static void
  147. -FILE_cleanup(FILE **file)
  148. -{
  149. - if (file && *file)
  150. - fclose(*file);
  151. -}
  152. -
  153. static int
  154. adv(enum http_method method, const char *path, const char *body,
  155. regmatch_t matches[], void *misc)
  156. {
  157. - __attribute__((cleanup(FILE_cleanup))) FILE *file = NULL;
  158. __attribute__((cleanup(str_cleanup))) char *adv = NULL;
  159. __attribute__((cleanup(str_cleanup))) char *thp = NULL;
  160. - char filename[PATH_MAX] = {};
  161. - const char *cachedir = misc;
  162. - struct stat st = {};
  163. + __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info *tki = NULL;
  164. + json_auto_t *jws = NULL;
  165. + const char *jwkdir = misc;
  166. +
  167. + tki = read_keys(jwkdir);
  168. + if (!tki || tki->m_keys_count == 0) {
  169. + return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
  170. + }
  171. if (matches[1].rm_so < matches[1].rm_eo) {
  172. size_t size = matches[1].rm_eo - matches[1].rm_so;
  173. @@ -61,24 +59,15 @@
  174. return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
  175. }
  176. - if (snprintf(filename, sizeof(filename),
  177. - "%s/%s.jws", cachedir, thp ? thp : "default") < 0)
  178. - return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
  179. -
  180. - file = fopen(filename, "r");
  181. - if (!file)
  182. + jws = find_jws(tki, thp);
  183. + if (!jws) {
  184. return http_reply(HTTP_STATUS_NOT_FOUND, NULL);
  185. + }
  186. - if (fstat(fileno(file), &st) != 0)
  187. - return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
  188. -
  189. - adv = calloc(st.st_size + 1, 1);
  190. + adv = json_dumps(jws, 0);
  191. if (!adv)
  192. return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
  193. - if (fread(adv, st.st_size, 1, file) != 1)
  194. - return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
  195. -
  196. return http_reply(HTTP_STATUS_OK,
  197. "Content-Type: application/jose+json\r\n"
  198. "Content-Length: %zu\r\n"
  199. @@ -91,9 +80,9 @@
  200. {
  201. __attribute__((cleanup(str_cleanup))) char *enc = NULL;
  202. __attribute__((cleanup(str_cleanup))) char *thp = NULL;
  203. + __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info *tki = NULL;
  204. size_t size = matches[1].rm_eo - matches[1].rm_so;
  205. - char filename[PATH_MAX] = {};
  206. - const char *cachedir = misc;
  207. + const char *jwkdir = misc;
  208. json_auto_t *jwk = NULL;
  209. json_auto_t *req = NULL;
  210. json_auto_t *rep = NULL;
  211. @@ -124,15 +113,16 @@
  212. /*
  213. * Parse and validate the server-side JWK
  214. */
  215. + tki = read_keys(jwkdir);
  216. + if (!tki || tki->m_keys_count == 0) {
  217. + return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
  218. + }
  219. thp = strndup(&path[matches[1].rm_so], size);
  220. if (!thp)
  221. return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
  222. - if (snprintf(filename, sizeof(filename), "%s/%s.jwk", cachedir, thp) < 0)
  223. - return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
  224. -
  225. - jwk = json_load_file(filename, 0, NULL);
  226. + jwk = find_jwk(tki, thp);
  227. if (!jwk)
  228. return http_reply(HTTP_STATUS_NOT_FOUND, NULL);
  229. @@ -188,7 +178,7 @@
  230. http_parser_init(&parser, HTTP_REQUEST);
  231. if (argc != 2) {
  232. - fprintf(stderr, "Usage: %s <cachedir>\n", argv[0]);
  233. + fprintf(stderr, "Usage: %s <jwkdir>\n", argv[0]);
  234. return EXIT_FAILURE;
  235. }
  236. --- a/tests/adv
  237. +++ b/tests/adv
  238. @@ -36,15 +36,13 @@
  239. export TMP=`mktemp -d`
  240. mkdir -p $TMP/db
  241. -mkdir -p $TMP/cache
  242. tangd-keygen $TMP/db sig exc
  243. jose jwk gen -i '{"alg": "ES512"}' -o $TMP/db/.sig.jwk
  244. jose jwk gen -i '{"alg": "ES512"}' -o $TMP/db/.oth.jwk
  245. -tangd-update $TMP/db $TMP/cache
  246. export PORT=`shuf -i 1024-65536 -n 1`
  247. -$SD_ACTIVATE -l "127.0.0.1:$PORT" -a $VALGRIND tangd $TMP/cache &
  248. +$SD_ACTIVATE -l "127.0.0.1:$PORT" -a $VALGRIND tangd $TMP/db &
  249. export PID=$!
  250. sleep 0.5
  251. @@ -84,4 +82,4 @@
  252. -g 0 -Og protected -SyOg cty -Sq "jwk-set+json" -EUUUUU \
  253. -g 1 -Og protected -SyOg cty -Sq "jwk-set+json" -EUUUUU
  254. -test $(tang-show-keys $PORT) == $(jose jwk thp -i $TMP/db/sig.jwk)
  255. +test "$(tang-show-keys $PORT)" == "$(jose jwk thp -i $TMP/db/sig.jwk)"
  256. --- a/tests/rec
  257. +++ b/tests/rec
  258. @@ -28,11 +28,9 @@
  259. export TMP=`mktemp -d`
  260. mkdir -p $TMP/db
  261. -mkdir -p $TMP/cache
  262. # Generate the server keys
  263. tangd-keygen $TMP/db sig exc
  264. -tangd-update $TMP/db $TMP/cache
  265. # Generate the client keys
  266. exc_kid=`jose jwk thp -i $TMP/db/exc.jwk`
  267. @@ -42,7 +40,7 @@
  268. # Start the server
  269. port=`shuf -i 1024-65536 -n 1`
  270. -$SD_ACTIVATE -l 127.0.0.1:$port -a $VALGRIND tangd $TMP/cache &
  271. +$SD_ACTIVATE -l 127.0.0.1:$port -a $VALGRIND tangd $TMP/db &
  272. export PID=$!
  273. sleep 0.5
  274. --- a/units/meson.build
  275. +++ b/units/meson.build
  276. @@ -1,31 +1,10 @@
  277. -tangd_keygen_service = configure_file(
  278. - input: 'tangd-keygen.service.in',
  279. - output: 'tangd-keygen.service',
  280. - configuration: data
  281. -)
  282. -
  283. tangd_service = configure_file(
  284. input: 'tangd@.service.in',
  285. output: 'tangd@.service',
  286. configuration: data
  287. )
  288. -tangd_update_path = configure_file(
  289. - input: 'tangd-update.path.in',
  290. - output: 'tangd-update.path',
  291. - configuration: data
  292. -)
  293. -
  294. -tangd_update_service = configure_file(
  295. - input: 'tangd-update.service.in',
  296. - output: 'tangd-update.service',
  297. - configuration: data
  298. -)
  299. -
  300. units += join_paths(meson.current_source_dir(), 'tangd.socket')
  301. -units += tangd_keygen_service
  302. units += tangd_service
  303. -units += tangd_update_path
  304. -units += tangd_update_service
  305. # vim:set ts=2 sw=2 et:
  306. --- a/units/tangd-keygen.service.in
  307. +++ /dev/null
  308. @@ -1,8 +0,0 @@
  309. -[Unit]
  310. -Description=Tang Server key generation script
  311. -ConditionDirectoryNotEmpty=|!@jwkdir@
  312. -Requires=tangd-update.path
  313. -
  314. -[Service]
  315. -Type=oneshot
  316. -ExecStart=@libexecdir@/tangd-keygen @jwkdir@
  317. --- a/units/tangd-update.path.in
  318. +++ /dev/null
  319. @@ -1,4 +0,0 @@
  320. -[Path]
  321. -PathChanged=@jwkdir@
  322. -MakeDirectory=true
  323. -DirectoryMode=0700
  324. --- a/units/tangd-update.service.in
  325. +++ /dev/null
  326. @@ -1,6 +0,0 @@
  327. -[Unit]
  328. -Description=Tang Server key update script
  329. -
  330. -[Service]
  331. -Type=oneshot
  332. -ExecStart=@libexecdir@/tangd-update @jwkdir@ @cachedir@
  333. --- a/units/tangd@.service.in
  334. +++ b/units/tangd@.service.in
  335. @@ -1,10 +1,8 @@
  336. [Unit]
  337. Description=Tang Server
  338. -Requires=tangd-keygen.service
  339. -After=tangd-keygen.service
  340. [Service]
  341. StandardInput=socket
  342. StandardOutput=socket
  343. StandardError=journal
  344. -ExecStart=@libexecdir@/tangd @cachedir@
  345. +ExecStart=@libexecdir@/tangd @jwkdir@