123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- /* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
- /*
- * Copyright (c) 2015 Red Hat, Inc.
- * Author: Nathaniel McCallum <npmccallum@redhat.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Additional permission under GPLv3 section 7:
- *
- * In the following paragraph, "GPL" means the GNU General Public
- * License, version 3 or any later version, and "Non-GPL Code" means
- * code that is governed neither by the GPL nor a license
- * compatible with the GPL.
- *
- * You may link the code of this Program with Non-GPL Code and convey
- * linked combinations including the two, provided that such Non-GPL
- * Code only links to the code of this Program through those well
- * defined interfaces identified in the file named EXCEPTION found in
- * the source code files (the "Approved Interfaces"). The files of
- * Non-GPL Code may instantiate templates or use macros or inline
- * functions from the Approved Interfaces without causing the resulting
- * work to be covered by the GPL. Only the copyright holders of this
- * Program may make changes or additions to the list of Approved
- * Interfaces.
- */
- #define _GNU_SOURCE
- #include "sss.h"
- #include <jose/b64.h>
- #include <jose/jwe.h>
- #include <sys/epoll.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <libgen.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <limits.h>
- #include <unistd.h>
- #include <signal.h>
- #include <stdbool.h>
- #include <stdint.h>
- #include <string.h>
- #include <ctype.h>
- struct pin {
- struct pin *prev;
- struct pin *next;
- uint8_t *pt;
- size_t ptl;
- FILE *file;
- pid_t pid;
- };
- static size_t
- nchldrn(const struct pin *pins, bool response)
- {
- size_t n = 0;
- for (const struct pin *p = pins->next; p != pins; p = p->next) {
- if (response && p->pt)
- n++;
- else if (!response)
- n++;
- }
- return n;
- }
- static json_t *
- compact_field(FILE *file)
- {
- json_t *str = NULL;
- char *buf = NULL;
- size_t used = 0;
- size_t size = 0;
- for (int c = fgetc(file); c != EOF && c != '.' && !isspace(c); c = fgetc(file)) {
- if (used >= size) {
- char *tmp = NULL;
- size += 4096;
- tmp = realloc(buf, size);
- if (!tmp)
- goto error;
- buf = tmp;
- }
- buf[used++] = c;
- }
- str = json_stringn(buf ? buf : "", buf ? used : 0);
- error:
- free(buf);
- return str;
- }
- static json_t *
- compact_jwe(FILE *file)
- {
- json_auto_t *jwe = NULL;
- jwe = json_object();
- if (!jwe)
- return NULL;
- if (json_object_set_new(jwe, "protected", compact_field(file)) < 0)
- return NULL;
- if (json_object_set_new(jwe, "encrypted_key", compact_field(file)) < 0)
- return NULL;
- if (json_object_set_new(jwe, "iv", compact_field(file)) < 0)
- return NULL;
- return json_incref(jwe);
- }
- int
- main(int argc, char *argv[])
- {
- struct pin chldrn = { &chldrn, &chldrn };
- json_auto_t *pins = NULL;
- json_auto_t *hdr = NULL;
- json_auto_t *jwe = NULL;
- int ret = EXIT_FAILURE;
- json_t *p = NULL;
- json_int_t t = 1;
- int epoll = -1;
- size_t pl = 0;
- if (argc == 2 && strcmp(argv[1], "--summary") == 0)
- return EXIT_FAILURE;
- if (isatty(STDIN_FILENO) || argc != 1)
- goto usage;
- epoll = epoll_create1(EPOLL_CLOEXEC);
- if (epoll < 0)
- return ret;
- jwe = compact_jwe(stdin);
- if (!jwe)
- goto egress;
- hdr = jose_jwe_hdr(jwe, jwe);
- if (!hdr)
- goto egress;
- if (json_unpack(hdr, "{s:{s:{s:I,s:o,s:O}}}",
- "clevis", "sss", "t", &t, "p", &p, "jwe", &pins) != 0)
- goto egress;
- if (t < 1)
- goto egress;
- pl = jose_b64_dec(p, NULL, 0);
- if (pl == SIZE_MAX)
- goto egress;
- for (size_t i = 0; i < json_array_size(pins); i++) {
- char *args[] = { "clevis", "decrypt", NULL };
- const json_t *val = json_array_get(pins, i);
- struct pin *pin = NULL;
- if (!json_is_string(val))
- goto egress;
- pin = calloc(1, sizeof(*pin));
- if (!pin)
- goto egress;
- chldrn.next->prev = pin;
- pin->next = chldrn.next;
- pin->prev = &chldrn;
- chldrn.next = pin;
- pin->file = call(args, json_string_value(val),
- json_string_length(val), &pin->pid);
- if (!pin->file)
- goto egress;
- if (epoll_ctl(epoll, EPOLL_CTL_ADD, fileno(pin->file),
- &(struct epoll_event) {
- .events = EPOLLIN | EPOLLPRI,
- .data.fd = fileno(pin->file)
- }) < 0)
- goto egress;
- }
- json_decref(pins);
- pins = json_array();
- if (!pins)
- goto egress;
- for (struct epoll_event e; true; ) {
- int r = 0;
- r = epoll_wait(epoll, &e, 1, -1);
- if (r != 1)
- break;
- for (struct pin *pin = chldrn.next; pin != &chldrn; pin = pin->next) {
- if (!pin->file || e.data.fd != fileno(pin->file))
- continue;
- if (e.events & (EPOLLIN | EPOLLPRI)) {
- const size_t ptl = pl * 2;
- pin->pt = malloc(ptl);
- if (!pin->pt)
- goto egress;
- while (!feof(pin->file)) {
- uint8_t buf[ptl];
- size_t rd = 0;
- rd = fread(buf, 1, sizeof(buf), pin->file);
- if (ferror(pin->file) || pin->ptl + rd > ptl) {
- pin->ptl = 0;
- break;
- }
- memcpy(&pin->pt[pin->ptl], buf, rd);
- pin->ptl += rd;
- }
- if (pin->ptl != ptl) {
- free(pin->pt);
- pin->pt = NULL;
- goto egress;
- }
- }
- fclose(pin->file);
- pin->file = NULL;
- waitpid(pin->pid, NULL, 0);
- pin->pid = 0;
- if (!pin->pt) {
- pin->next->prev = pin->prev;
- pin->prev->next = pin->next;
- free(pin);
- }
- break;
- }
- if (nchldrn(&chldrn, false) < (size_t) t ||
- nchldrn(&chldrn, true) >= (size_t) t)
- break;
- }
- if (nchldrn(&chldrn, true) >= (size_t) t) {
- jose_io_auto_t *out = NULL;
- jose_io_auto_t *dec = NULL;
- jose_io_auto_t *b64 = NULL;
- json_auto_t *cek = NULL;
- const uint8_t *xy[t];
- size_t i = 0;
- for (struct pin *pin = chldrn.next; pin != &chldrn; pin = pin->next) {
- if (pin->pt && i < (size_t) t)
- xy[i++] = pin->pt;
- }
- cek = json_pack("{s:s,s:o}", "kty", "oct", "k", sss_recover(p, t, xy));
- if (!cek)
- goto egress;
- out = jose_io_file(NULL, stdout);
- dec = jose_jwe_dec_cek_io(NULL, jwe, cek, out);
- b64 = jose_b64_dec_io(dec);
- if (!out || !dec || !b64)
- goto egress;
- for (int b = fgetc(stdin); b != EOF && b != '.'; b = fgetc(stdin)) {
- char c = b;
- if (!b64->feed(b64, &c, 1))
- goto egress;
- }
- if (json_object_set_new(jwe, "tag", compact_field(stdin)) < 0)
- goto egress;
- if (!b64->done(b64))
- goto egress;
- ret = EXIT_SUCCESS;
- }
- egress:
- while (chldrn.next != &chldrn) {
- struct pin *pin = chldrn.next;
- if (pin->file)
- fclose(pin->file);
- if (pin->pid > 0) {
- kill(pin->pid, SIGTERM);
- waitpid(pin->pid, NULL, 0);
- }
- pin->next->prev = pin->prev;
- pin->prev->next = pin->next;
- free(pin->pt);
- free(pin);
- }
- close(epoll);
- return ret;
- usage:
- fprintf(stderr, "\n");
- fprintf(stderr, "Usage: clevis decrypt sss < JWE > PLAINTEXT\n");
- fprintf(stderr, "\n");
- return EXIT_FAILURE;
- }
|