123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- /* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
- /*
- * Copyright (c) 2016 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include "luksmeta.h"
- #include <errno.h>
- #include <getopt.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sysexits.h>
- #define UUID_TMPL \
- "%02hhx%02hhx%02hhx%02hhx-" \
- "%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-" \
- "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
- #define UUID_ARGS(u) \
- u[0x0], u[0x1], u[0x2], u[0x3], u[0x4], u[0x5], u[0x6], u[0x7], \
- u[0x8], u[0x9], u[0xa], u[0xb], u[0xc], u[0xd], u[0xe], u[0xf]
- struct options {
- const char *device;
- luksmeta_uuid_t uuid;
- bool have_uuid;
- bool force;
- bool nuke;
- int slot;
- };
- static int
- cmd_test(const struct options *opts, struct crypt_device *cd)
- {
- return luksmeta_test(cd) == 0 ? EX_OK : EX_OSFILE;
- }
- static int
- cmd_nuke(const struct options *opts, struct crypt_device *cd)
- {
- int r = 0;
- if (!opts->force) {
- int c = 'X';
- fprintf(stderr,
- "You are about to erase all data in the LUKSMeta storage area.\n"
- "A backup is advised before erasure is performed.\n\n");
- while (!strchr("YyNn", c)) {
- fprintf(stderr, "Do you wish to nuke %s? [yn] ",
- crypt_get_device_name(cd));
- c = getc(stdin);
- }
- if (strchr("Nn", c))
- return EX_NOPERM;
- }
- r = luksmeta_nuke(cd);
- switch (r) {
- case 0:
- return EX_OK;
- default:
- fprintf(stderr, "Error while erasing device (%s): %s\n",
- opts->device, strerror(-r));
- return EX_OSERR;
- }
- }
- static int
- cmd_init(const struct options *opts, struct crypt_device *cd)
- {
- int r = 0;
- if (!opts->force) {
- int c = 'X';
- fprintf(stderr,
- "You are about to initialize a LUKS device for metadata storage.\n"
- "Attempting to initialize it may result in data loss if data was\n"
- "already written into the LUKS header gap in a different format.\n"
- "A backup is advised before initialization is performed.\n\n");
- while (!strchr("YyNn", c)) {
- fprintf(stderr, "Do you wish to initialize %s? [yn] ",
- crypt_get_device_name(cd));
- c = getc(stdin);
- }
- if (strchr("Nn", c))
- return EX_NOPERM;
- }
- if (opts->nuke) {
- struct options o = { .force = true, .device = opts->device };
- r = cmd_nuke(&o, cd);
- if (r != EX_OK)
- return r;
- }
- r = luksmeta_init(cd);
- switch (r) {
- case 0: /* fallthrough */
- case -EALREADY:
- return EX_OK;
- case -ENOSPC:
- fprintf(stderr, "Insufficient space in the LUKS header (%s)\n",
- opts->device);
- return EX_CANTCREAT;
- default:
- fprintf(stderr, "Error while initializing device (%s): %s\n",
- opts->device, strerror(-r));
- return EX_OSERR;
- }
- return EX_OK;
- }
- static const char *
- status(struct crypt_device *cd, int keyslot)
- {
- switch (crypt_keyslot_status(cd, keyslot)) {
- case CRYPT_SLOT_INVALID: return "invalid";
- case CRYPT_SLOT_INACTIVE: return "inactive";
- case CRYPT_SLOT_ACTIVE: return "active";
- case CRYPT_SLOT_ACTIVE_LAST: return "active";
- default: return "unknown";
- }
- }
- static int
- cmd_show(const struct options *opts, struct crypt_device *cd)
- {
- luksmeta_uuid_t uuid = {};
- for (int i = 0, r = 0; i < crypt_keyslot_max(CRYPT_LUKS1); i++) {
- if (opts->slot >= 0 && i != opts->slot)
- continue;
- r = luksmeta_load(cd, i, uuid, NULL, 0);
- switch (r) {
- case -EBADSLT:
- fprintf(stderr, "Invalid slot (%d)\n", opts->slot);
- return EX_USAGE;
- case -ENOENT:
- fprintf(stderr, "Device is not initialized (%s)\n",
- opts->device);
- return EX_OSFILE;
- case -EINVAL:
- fprintf(stderr, "LUKSMeta data appears corrupt (%s)\n",
- opts->device);
- return EX_OSFILE;
- case -ENODATA:
- if (opts->slot < 0)
- fprintf(stdout, "%d %8s %s\n", i, status(cd, i), "empty");
- break;
- default:
- if (r < 0) {
- fprintf(stderr, "%d %8s %s\n",
- i, status(cd, i), "unknown error");
- } else {
- if (opts->slot < 0)
- fprintf(stdout, "%d %8s ", i, status(cd, i));
- fprintf(stdout, UUID_TMPL "\n", UUID_ARGS(uuid));
- }
- break;
- }
- }
- return EX_OK;
- }
- static int
- cmd_save(const struct options *opts, struct crypt_device *cd)
- {
- uint8_t *in = NULL;
- size_t inl = 0;
- int r = 0;
- if (!opts->have_uuid) {
- fprintf(stderr, "UUID required\n");
- return EX_USAGE;
- }
- while (!feof(stdin)) {
- uint8_t *tmp = NULL;
- tmp = realloc(in, inl + 4096);
- if (!tmp) {
- fprintf(stderr, "Out of memory\n");
- free(in);
- return EX_OSERR;
- }
- in = tmp;
- r = fread(&in[inl], 1, 4096, stdin);
- inl += r;
- if (r < 4096 && (ferror(stdin) || inl == 0)) {
- fprintf(stderr, "Error reading from standard input\n");
- free(in);
- return EX_NOINPUT;
- }
- }
- if (!in) {
- fprintf(stderr, "No data on standard input\n");
- return EX_NOINPUT;
- }
- r = luksmeta_save(cd, opts->slot, opts->uuid, in, inl);
- memset(in, 0, inl);
- free(in);
- switch (r) {
- case -ENOENT:
- fprintf(stderr, "Device is not initialized (%s)\n", opts->device);
- return EX_OSFILE;
- case -EINVAL:
- fprintf(stderr, "LUKSMeta data appears corrupt (%s)\n", opts->device);
- return EX_OSFILE;
- case -EBADSLT:
- fprintf(stderr, "The specified slot is invalid (%d)\n", opts->slot);
- return EX_USAGE;
- case -EKEYREJECTED:
- fprintf(stderr, "The specified UUID is reserved (" UUID_TMPL ")\n",
- UUID_ARGS(opts->uuid));
- return EX_USAGE;
- case -EALREADY:
- fprintf(stderr, "Will not overwrite existing slot (%d)\n", opts->slot);
- return EX_UNAVAILABLE;
- case -ENOSPC:
- fprintf(stderr, "Insufficient space in the LUKS header (%s)\n",
- opts->device);
- return EX_CANTCREAT;
- default:
- if (r < 0)
- fprintf(stderr, "An unknown error occurred\n");
- else if (opts->slot < 0)
- fprintf(stdout, "%d\n", r);
- return r < 0 ? EX_OSERR : EX_OK;
- }
- }
- static int
- cmd_load(const struct options *opts, struct crypt_device *cd)
- {
- luksmeta_uuid_t uuid = {};
- int r = 0;
- if (opts->slot < 0) {
- fprintf(stderr, "Slot required\n");
- return EX_USAGE;
- }
- r = luksmeta_load(cd, opts->slot, uuid, NULL, 0);
- if (r >= 0) {
- uint8_t *out = NULL;
- if (opts->have_uuid && memcmp(opts->uuid, uuid, sizeof(uuid)) != 0) {
- fprintf(stderr,
- "The given UUID does not match the slot UUID:\n"
- "UUID: " UUID_TMPL "\n"
- "SLOT: " UUID_TMPL "\n",
- UUID_ARGS(opts->uuid),
- UUID_ARGS(uuid));
- return EX_DATAERR;
- }
- out = malloc(r);
- if (!out) {
- fprintf(stderr, "Out of memory!\n");
- return EX_OSERR;
- }
- r = luksmeta_load(cd, opts->slot, uuid, out, r);
- if (r >= 0) {
- fwrite(out, 1, r, stdout);
- memset(out, 0, r);
- }
- free(out);
- }
- switch (r) {
- case -ENOENT:
- fprintf(stderr, "Device is not initialized (%s)\n", opts->device);
- return EX_OSFILE;
- case -EINVAL:
- fprintf(stderr, "LUKSMeta data appears corrupt (%s)\n", opts->device);
- return EX_OSFILE;
- case -EBADSLT:
- fprintf(stderr, "The specified slot is invalid (%d)\n", opts->slot);
- return EX_USAGE;
- case -ENODATA:
- fprintf(stderr, "The specified slot is empty (%d)\n", opts->slot);
- return EX_UNAVAILABLE;
- default:
- if (r < 0)
- fprintf(stderr, "An unknown error occurred\n");
- return r < 0 ? EX_OSERR : EX_OK;
- }
- }
- static int
- cmd_wipe(const struct options *opts, struct crypt_device *cd)
- {
- luksmeta_uuid_t uuid = {};
- int r = 0;
- if (opts->slot < 0) {
- fprintf(stderr, "Slot required\n");
- return EX_USAGE;
- }
- if (!opts->force) {
- int c = 'X';
- fprintf(stderr,
- "You are about to wipe a slot. This operation is unrecoverable.\n"
- "A backup is advised before proceeding.\n\n");
- while (!strchr("YyNn", c)) {
- fprintf(stderr, "Do you wish to erase slot %d on %s? [yn] ",
- opts->slot, crypt_get_device_name(cd));
- c = getc(stdin);
- }
- if (strchr("Nn", c))
- return EX_NOPERM;
- }
- r = luksmeta_wipe(cd, opts->slot, opts->have_uuid ? opts->uuid : NULL);
- switch (r) {
- case -EALREADY:
- return EX_OK;
- case -ENOENT:
- fprintf(stderr, "Device is not initialized (%s)\n", opts->device);
- return EX_OSFILE;
- case -EINVAL:
- fprintf(stderr, "LUKSMeta data appears corrupt (%s)\n", opts->device);
- return EX_OSFILE;
- case -EBADSLT:
- fprintf(stderr, "The specified slot is invalid (%d)\n", opts->slot);
- return EX_USAGE;
- case -EKEYREJECTED:
- r = luksmeta_load(cd, opts->slot, uuid, NULL, 0);
- if (r >= 0) {
- fprintf(stderr,
- "The given UUID does not match the slot UUID:\n"
- "UUID: " UUID_TMPL "\n"
- "SLOT: " UUID_TMPL "\n",
- UUID_ARGS(opts->uuid),
- UUID_ARGS(uuid));
- } else {
- fprintf(stderr,
- "The given UUID does not match the slot UUID:\n"
- "UUID: " UUID_TMPL "\n"
- "SLOT: UNKNOWN\n",
- UUID_ARGS(opts->uuid));
- }
- return EX_DATAERR;
- default:
- if (r < 0)
- fprintf(stderr, "An unknown error occurred\n");
- return r < 0 ? EX_OSERR : EX_OK;
- }
- }
- static const char *sopts = "hfnd:u:s:";
- static const struct option lopts[] = {
- { "help", .val = 'h' },
- { "nuke", no_argument, .val = 'n' },
- { "force", no_argument, .val = 'f' },
- { "device", required_argument, .val = 'd' },
- { "uuid", required_argument, .val = 'u' },
- { "slot", required_argument, .val = 's' },
- {}
- };
- static const struct {
- int (*func)(const struct options *opts, struct crypt_device *cd);
- const char *name;
- } commands[] = {
- { cmd_test, "test", },
- { cmd_nuke, "nuke", },
- { cmd_init, "init", },
- { cmd_show, "show", },
- { cmd_save, "save", },
- { cmd_load, "load", },
- { cmd_wipe, "wipe", },
- {}
- };
- int
- main(int argc, char *argv[])
- {
- struct options o = { .slot = CRYPT_ANY_SLOT };
- for (int c; (c = getopt_long(argc, argv, sopts, lopts, NULL)) != -1; ) {
- switch (c) {
- case 'h': goto usage;
- case 'd': o.device = optarg; break;
- case 'n': o.nuke = true; break;
- case 'f': o.force = true; break;
- case 'u':
- if (sscanf(optarg, UUID_TMPL, UUID_ARGS(&o.uuid)) != 16) {
- fprintf(stderr, "Invalid UUID (%s)\n", optarg);
- return EX_USAGE;
- }
- o.have_uuid = true;
- break;
- case 's':
- if (sscanf(optarg, "%d", &o.slot) != 1 || o.slot < 0 ||
- o.slot >= crypt_keyslot_max(CRYPT_LUKS1)) {
- fprintf(stderr, "Invalid slot (%s)\n", optarg);
- return EX_USAGE;
- }
- break;
- }
- }
- if (argc > 1 && !o.device) {
- fprintf(stderr, "Device must be specified\n\n");
- goto usage;
- }
- if (optind != argc - 1)
- goto usage;
- for (size_t i = 0; argc > 1 && commands[i].name; i++) {
- struct crypt_device *cd = NULL;
- const char *type = NULL;
- int r = 0;
- if (strcmp(argv[optind], commands[i].name) != 0)
- continue;
- r = crypt_init(&cd, o.device);
- if (r != 0) {
- fprintf(stderr, "Unable to open device (%s): %s\n",
- o.device, strerror(-r));
- return EX_IOERR;
- }
- r = crypt_load(cd, NULL, NULL);
- if (r != 0) {
- fprintf(stderr, "Unable to read LUKSv1 header (%s): %s\n",
- o.device, strerror(-r));
- crypt_free(cd);
- return EX_IOERR;
- }
- type = crypt_get_type(cd);
- if (type == NULL) {
- fprintf(stderr, "Unable to determine device type for %s\n",
- o.device);
- crypt_free(cd);
- return EX_OSFILE;
- }
- if (strcmp(type, CRYPT_LUKS1) != 0) {
- fprintf(stderr, "%s (%s) is not a LUKSv1 device\n", o.device, type);
- crypt_free(cd);
- return EX_OSFILE;
- }
- r = commands[i].func(&o, cd);
- crypt_free(cd);
- return r;
- }
- fprintf(stderr, "Invalid command\n\n");
- usage:
- fprintf(stderr,
- "Usage: luksmeta test -d DEVICE\n"
- " or: luksmeta nuke -d DEVICE [-f]\n"
- " or: luksmeta init -d DEVICE [-f] [-n]\n"
- " or: luksmeta show -d DEVICE [-s SLOT]\n"
- " or: luksmeta save -d DEVICE [-s SLOT] -u UUID < DATA\n"
- " or: luksmeta load -d DEVICE -s SLOT [-u UUID] > DATA\n"
- " or: luksmeta wipe -d DEVICE -s SLOT [-u UUID] [-f]\n");
- return EX_USAGE;
- }
|