clevis-decrypt-sss.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
  2. /*
  3. * Copyright (c) 2015 Red Hat, Inc.
  4. * Author: Nathaniel McCallum <npmccallum@redhat.com>
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. * Additional permission under GPLv3 section 7:
  20. *
  21. * In the following paragraph, "GPL" means the GNU General Public
  22. * License, version 3 or any later version, and "Non-GPL Code" means
  23. * code that is governed neither by the GPL nor a license
  24. * compatible with the GPL.
  25. *
  26. * You may link the code of this Program with Non-GPL Code and convey
  27. * linked combinations including the two, provided that such Non-GPL
  28. * Code only links to the code of this Program through those well
  29. * defined interfaces identified in the file named EXCEPTION found in
  30. * the source code files (the "Approved Interfaces"). The files of
  31. * Non-GPL Code may instantiate templates or use macros or inline
  32. * functions from the Approved Interfaces without causing the resulting
  33. * work to be covered by the GPL. Only the copyright holders of this
  34. * Program may make changes or additions to the list of Approved
  35. * Interfaces.
  36. */
  37. #define _GNU_SOURCE
  38. #include "sss.h"
  39. #include <jose/b64.h>
  40. #include <jose/jwe.h>
  41. #include <sys/epoll.h>
  42. #include <sys/types.h>
  43. #include <sys/wait.h>
  44. #include <libgen.h>
  45. #include <errno.h>
  46. #include <fcntl.h>
  47. #include <limits.h>
  48. #include <unistd.h>
  49. #include <signal.h>
  50. #include <stdbool.h>
  51. #include <stdint.h>
  52. #include <string.h>
  53. #include <ctype.h>
  54. struct pin {
  55. struct pin *prev;
  56. struct pin *next;
  57. uint8_t *pt;
  58. size_t ptl;
  59. FILE *file;
  60. pid_t pid;
  61. };
  62. static size_t
  63. nchldrn(const struct pin *pins, bool response)
  64. {
  65. size_t n = 0;
  66. for (const struct pin *p = pins->next; p != pins; p = p->next) {
  67. if (response && p->pt)
  68. n++;
  69. else if (!response)
  70. n++;
  71. }
  72. return n;
  73. }
  74. static json_t *
  75. compact_field(FILE *file)
  76. {
  77. json_t *str = NULL;
  78. char *buf = NULL;
  79. size_t used = 0;
  80. size_t size = 0;
  81. for (int c = fgetc(file); c != EOF && c != '.' && !isspace(c); c = fgetc(file)) {
  82. if (used >= size) {
  83. char *tmp = NULL;
  84. size += 4096;
  85. tmp = realloc(buf, size);
  86. if (!tmp)
  87. goto error;
  88. buf = tmp;
  89. }
  90. buf[used++] = c;
  91. }
  92. str = json_stringn(buf ? buf : "", buf ? used : 0);
  93. error:
  94. free(buf);
  95. return str;
  96. }
  97. static json_t *
  98. compact_jwe(FILE *file)
  99. {
  100. json_auto_t *jwe = NULL;
  101. jwe = json_object();
  102. if (!jwe)
  103. return NULL;
  104. if (json_object_set_new(jwe, "protected", compact_field(file)) < 0)
  105. return NULL;
  106. if (json_object_set_new(jwe, "encrypted_key", compact_field(file)) < 0)
  107. return NULL;
  108. if (json_object_set_new(jwe, "iv", compact_field(file)) < 0)
  109. return NULL;
  110. return json_incref(jwe);
  111. }
  112. int
  113. main(int argc, char *argv[])
  114. {
  115. struct pin chldrn = { &chldrn, &chldrn };
  116. json_auto_t *pins = NULL;
  117. json_auto_t *hdr = NULL;
  118. json_auto_t *jwe = NULL;
  119. int ret = EXIT_FAILURE;
  120. json_t *p = NULL;
  121. json_int_t t = 1;
  122. int epoll = -1;
  123. size_t pl = 0;
  124. if (argc == 2 && strcmp(argv[1], "--summary") == 0)
  125. return EXIT_FAILURE;
  126. if (isatty(STDIN_FILENO) || argc != 1)
  127. goto usage;
  128. epoll = epoll_create1(EPOLL_CLOEXEC);
  129. if (epoll < 0)
  130. return ret;
  131. jwe = compact_jwe(stdin);
  132. if (!jwe)
  133. goto egress;
  134. hdr = jose_jwe_hdr(jwe, jwe);
  135. if (!hdr)
  136. goto egress;
  137. if (json_unpack(hdr, "{s:{s:{s:I,s:o,s:O}}}",
  138. "clevis", "sss", "t", &t, "p", &p, "jwe", &pins) != 0)
  139. goto egress;
  140. if (t < 1)
  141. goto egress;
  142. pl = jose_b64_dec(p, NULL, 0);
  143. if (pl == SIZE_MAX)
  144. goto egress;
  145. for (size_t i = 0; i < json_array_size(pins); i++) {
  146. char *args[] = { "clevis", "decrypt", NULL };
  147. const json_t *val = json_array_get(pins, i);
  148. struct pin *pin = NULL;
  149. if (!json_is_string(val))
  150. goto egress;
  151. pin = calloc(1, sizeof(*pin));
  152. if (!pin)
  153. goto egress;
  154. chldrn.next->prev = pin;
  155. pin->next = chldrn.next;
  156. pin->prev = &chldrn;
  157. chldrn.next = pin;
  158. pin->file = call(args, json_string_value(val),
  159. json_string_length(val), &pin->pid);
  160. if (!pin->file)
  161. goto egress;
  162. if (epoll_ctl(epoll, EPOLL_CTL_ADD, fileno(pin->file),
  163. &(struct epoll_event) {
  164. .events = EPOLLIN | EPOLLPRI,
  165. .data.fd = fileno(pin->file)
  166. }) < 0)
  167. goto egress;
  168. }
  169. json_decref(pins);
  170. pins = json_array();
  171. if (!pins)
  172. goto egress;
  173. for (struct epoll_event e; true; ) {
  174. int r = 0;
  175. r = epoll_wait(epoll, &e, 1, -1);
  176. if (r != 1)
  177. break;
  178. for (struct pin *pin = chldrn.next; pin != &chldrn; pin = pin->next) {
  179. if (!pin->file || e.data.fd != fileno(pin->file))
  180. continue;
  181. if (e.events & (EPOLLIN | EPOLLPRI)) {
  182. const size_t ptl = pl * 2;
  183. pin->pt = malloc(ptl);
  184. if (!pin->pt)
  185. goto egress;
  186. while (!feof(pin->file)) {
  187. uint8_t buf[ptl];
  188. size_t rd = 0;
  189. rd = fread(buf, 1, sizeof(buf), pin->file);
  190. if (ferror(pin->file) || pin->ptl + rd > ptl) {
  191. pin->ptl = 0;
  192. break;
  193. }
  194. memcpy(&pin->pt[pin->ptl], buf, rd);
  195. pin->ptl += rd;
  196. }
  197. if (pin->ptl != ptl) {
  198. free(pin->pt);
  199. pin->pt = NULL;
  200. goto egress;
  201. }
  202. }
  203. fclose(pin->file);
  204. pin->file = NULL;
  205. waitpid(pin->pid, NULL, 0);
  206. pin->pid = 0;
  207. if (!pin->pt) {
  208. pin->next->prev = pin->prev;
  209. pin->prev->next = pin->next;
  210. free(pin);
  211. }
  212. break;
  213. }
  214. if (nchldrn(&chldrn, false) < (size_t) t ||
  215. nchldrn(&chldrn, true) >= (size_t) t)
  216. break;
  217. }
  218. if (nchldrn(&chldrn, true) >= (size_t) t) {
  219. jose_io_auto_t *out = NULL;
  220. jose_io_auto_t *dec = NULL;
  221. jose_io_auto_t *b64 = NULL;
  222. json_auto_t *cek = NULL;
  223. const uint8_t *xy[t];
  224. size_t i = 0;
  225. for (struct pin *pin = chldrn.next; pin != &chldrn; pin = pin->next) {
  226. if (pin->pt && i < (size_t) t)
  227. xy[i++] = pin->pt;
  228. }
  229. cek = json_pack("{s:s,s:o}", "kty", "oct", "k", sss_recover(p, t, xy));
  230. if (!cek)
  231. goto egress;
  232. out = jose_io_file(NULL, stdout);
  233. dec = jose_jwe_dec_cek_io(NULL, jwe, cek, out);
  234. b64 = jose_b64_dec_io(dec);
  235. if (!out || !dec || !b64)
  236. goto egress;
  237. for (int b = fgetc(stdin); b != EOF && b != '.'; b = fgetc(stdin)) {
  238. char c = b;
  239. if (!b64->feed(b64, &c, 1))
  240. goto egress;
  241. }
  242. if (json_object_set_new(jwe, "tag", compact_field(stdin)) < 0)
  243. goto egress;
  244. if (!b64->done(b64))
  245. goto egress;
  246. ret = EXIT_SUCCESS;
  247. }
  248. egress:
  249. while (chldrn.next != &chldrn) {
  250. struct pin *pin = chldrn.next;
  251. if (pin->file)
  252. fclose(pin->file);
  253. if (pin->pid > 0) {
  254. kill(pin->pid, SIGTERM);
  255. waitpid(pin->pid, NULL, 0);
  256. }
  257. pin->next->prev = pin->prev;
  258. pin->prev->next = pin->next;
  259. free(pin->pt);
  260. free(pin);
  261. }
  262. close(epoll);
  263. return ret;
  264. usage:
  265. fprintf(stderr, "\n");
  266. fprintf(stderr, "Usage: clevis decrypt sss < JWE > PLAINTEXT\n");
  267. fprintf(stderr, "\n");
  268. return EXIT_FAILURE;
  269. }