/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */ /* * Copyright (c) 2015 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 . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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; }