luksmeta.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. /* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
  2. /*
  3. * Copyright (c) 2016 Red Hat, Inc.
  4. * Author: Nathaniel McCallum <npmccallum@redhat.com>
  5. *
  6. * This program is free software: you can redistribute it and/or modify it
  7. * under the terms of the GNU Lesser General Public License as published by
  8. * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "luksmeta.h"
  20. #include <errno.h>
  21. #include <getopt.h>
  22. #include <stdbool.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <sysexits.h>
  27. #define UUID_TMPL \
  28. "%02hhx%02hhx%02hhx%02hhx-" \
  29. "%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-" \
  30. "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
  31. #define UUID_ARGS(u) \
  32. u[0x0], u[0x1], u[0x2], u[0x3], u[0x4], u[0x5], u[0x6], u[0x7], \
  33. u[0x8], u[0x9], u[0xa], u[0xb], u[0xc], u[0xd], u[0xe], u[0xf]
  34. struct options {
  35. const char *device;
  36. luksmeta_uuid_t uuid;
  37. bool have_uuid;
  38. bool force;
  39. bool nuke;
  40. int slot;
  41. };
  42. static int
  43. cmd_test(const struct options *opts, struct crypt_device *cd)
  44. {
  45. return luksmeta_test(cd) == 0 ? EX_OK : EX_OSFILE;
  46. }
  47. static int
  48. cmd_nuke(const struct options *opts, struct crypt_device *cd)
  49. {
  50. int r = 0;
  51. if (!opts->force) {
  52. int c = 'X';
  53. fprintf(stderr,
  54. "You are about to erase all data in the LUKSMeta storage area.\n"
  55. "A backup is advised before erasure is performed.\n\n");
  56. while (!strchr("YyNn", c)) {
  57. fprintf(stderr, "Do you wish to nuke %s? [yn] ",
  58. crypt_get_device_name(cd));
  59. c = getc(stdin);
  60. }
  61. if (strchr("Nn", c))
  62. return EX_NOPERM;
  63. }
  64. r = luksmeta_nuke(cd);
  65. switch (r) {
  66. case 0:
  67. return EX_OK;
  68. default:
  69. fprintf(stderr, "Error while erasing device (%s): %s\n",
  70. opts->device, strerror(-r));
  71. return EX_OSERR;
  72. }
  73. }
  74. static int
  75. cmd_init(const struct options *opts, struct crypt_device *cd)
  76. {
  77. int r = 0;
  78. if (!opts->force) {
  79. int c = 'X';
  80. fprintf(stderr,
  81. "You are about to initialize a LUKS device for metadata storage.\n"
  82. "Attempting to initialize it may result in data loss if data was\n"
  83. "already written into the LUKS header gap in a different format.\n"
  84. "A backup is advised before initialization is performed.\n\n");
  85. while (!strchr("YyNn", c)) {
  86. fprintf(stderr, "Do you wish to initialize %s? [yn] ",
  87. crypt_get_device_name(cd));
  88. c = getc(stdin);
  89. }
  90. if (strchr("Nn", c))
  91. return EX_NOPERM;
  92. }
  93. if (opts->nuke) {
  94. struct options o = { .force = true, .device = opts->device };
  95. r = cmd_nuke(&o, cd);
  96. if (r != EX_OK)
  97. return r;
  98. }
  99. r = luksmeta_init(cd);
  100. switch (r) {
  101. case 0: /* fallthrough */
  102. case -EALREADY:
  103. return EX_OK;
  104. case -ENOSPC:
  105. fprintf(stderr, "Insufficient space in the LUKS header (%s)\n",
  106. opts->device);
  107. return EX_CANTCREAT;
  108. default:
  109. fprintf(stderr, "Error while initializing device (%s): %s\n",
  110. opts->device, strerror(-r));
  111. return EX_OSERR;
  112. }
  113. return EX_OK;
  114. }
  115. static const char *
  116. status(struct crypt_device *cd, int keyslot)
  117. {
  118. switch (crypt_keyslot_status(cd, keyslot)) {
  119. case CRYPT_SLOT_INVALID: return "invalid";
  120. case CRYPT_SLOT_INACTIVE: return "inactive";
  121. case CRYPT_SLOT_ACTIVE: return "active";
  122. case CRYPT_SLOT_ACTIVE_LAST: return "active";
  123. default: return "unknown";
  124. }
  125. }
  126. static int
  127. cmd_show(const struct options *opts, struct crypt_device *cd)
  128. {
  129. luksmeta_uuid_t uuid = {};
  130. for (int i = 0, r = 0; i < crypt_keyslot_max(CRYPT_LUKS1); i++) {
  131. if (opts->slot >= 0 && i != opts->slot)
  132. continue;
  133. r = luksmeta_load(cd, i, uuid, NULL, 0);
  134. switch (r) {
  135. case -EBADSLT:
  136. fprintf(stderr, "Invalid slot (%d)\n", opts->slot);
  137. return EX_USAGE;
  138. case -ENOENT:
  139. fprintf(stderr, "Device is not initialized (%s)\n",
  140. opts->device);
  141. return EX_OSFILE;
  142. case -EINVAL:
  143. fprintf(stderr, "LUKSMeta data appears corrupt (%s)\n",
  144. opts->device);
  145. return EX_OSFILE;
  146. case -ENODATA:
  147. if (opts->slot < 0)
  148. fprintf(stdout, "%d %8s %s\n", i, status(cd, i), "empty");
  149. break;
  150. default:
  151. if (r < 0) {
  152. fprintf(stderr, "%d %8s %s\n",
  153. i, status(cd, i), "unknown error");
  154. } else {
  155. if (opts->slot < 0)
  156. fprintf(stdout, "%d %8s ", i, status(cd, i));
  157. fprintf(stdout, UUID_TMPL "\n", UUID_ARGS(uuid));
  158. }
  159. break;
  160. }
  161. }
  162. return EX_OK;
  163. }
  164. static int
  165. cmd_save(const struct options *opts, struct crypt_device *cd)
  166. {
  167. uint8_t *in = NULL;
  168. size_t inl = 0;
  169. int r = 0;
  170. if (!opts->have_uuid) {
  171. fprintf(stderr, "UUID required\n");
  172. return EX_USAGE;
  173. }
  174. while (!feof(stdin)) {
  175. uint8_t *tmp = NULL;
  176. tmp = realloc(in, inl + 4096);
  177. if (!tmp) {
  178. fprintf(stderr, "Out of memory\n");
  179. free(in);
  180. return EX_OSERR;
  181. }
  182. in = tmp;
  183. r = fread(&in[inl], 1, 4096, stdin);
  184. inl += r;
  185. if (r < 4096 && (ferror(stdin) || inl == 0)) {
  186. fprintf(stderr, "Error reading from standard input\n");
  187. free(in);
  188. return EX_NOINPUT;
  189. }
  190. }
  191. if (!in) {
  192. fprintf(stderr, "No data on standard input\n");
  193. return EX_NOINPUT;
  194. }
  195. r = luksmeta_save(cd, opts->slot, opts->uuid, in, inl);
  196. memset(in, 0, inl);
  197. free(in);
  198. switch (r) {
  199. case -ENOENT:
  200. fprintf(stderr, "Device is not initialized (%s)\n", opts->device);
  201. return EX_OSFILE;
  202. case -EINVAL:
  203. fprintf(stderr, "LUKSMeta data appears corrupt (%s)\n", opts->device);
  204. return EX_OSFILE;
  205. case -EBADSLT:
  206. fprintf(stderr, "The specified slot is invalid (%d)\n", opts->slot);
  207. return EX_USAGE;
  208. case -EKEYREJECTED:
  209. fprintf(stderr, "The specified UUID is reserved (" UUID_TMPL ")\n",
  210. UUID_ARGS(opts->uuid));
  211. return EX_USAGE;
  212. case -EALREADY:
  213. fprintf(stderr, "Will not overwrite existing slot (%d)\n", opts->slot);
  214. return EX_UNAVAILABLE;
  215. case -ENOSPC:
  216. fprintf(stderr, "Insufficient space in the LUKS header (%s)\n",
  217. opts->device);
  218. return EX_CANTCREAT;
  219. default:
  220. if (r < 0)
  221. fprintf(stderr, "An unknown error occurred\n");
  222. else if (opts->slot < 0)
  223. fprintf(stdout, "%d\n", r);
  224. return r < 0 ? EX_OSERR : EX_OK;
  225. }
  226. }
  227. static int
  228. cmd_load(const struct options *opts, struct crypt_device *cd)
  229. {
  230. luksmeta_uuid_t uuid = {};
  231. int r = 0;
  232. if (opts->slot < 0) {
  233. fprintf(stderr, "Slot required\n");
  234. return EX_USAGE;
  235. }
  236. r = luksmeta_load(cd, opts->slot, uuid, NULL, 0);
  237. if (r >= 0) {
  238. uint8_t *out = NULL;
  239. if (opts->have_uuid && memcmp(opts->uuid, uuid, sizeof(uuid)) != 0) {
  240. fprintf(stderr,
  241. "The given UUID does not match the slot UUID:\n"
  242. "UUID: " UUID_TMPL "\n"
  243. "SLOT: " UUID_TMPL "\n",
  244. UUID_ARGS(opts->uuid),
  245. UUID_ARGS(uuid));
  246. return EX_DATAERR;
  247. }
  248. out = malloc(r);
  249. if (!out) {
  250. fprintf(stderr, "Out of memory!\n");
  251. return EX_OSERR;
  252. }
  253. r = luksmeta_load(cd, opts->slot, uuid, out, r);
  254. if (r >= 0) {
  255. fwrite(out, 1, r, stdout);
  256. memset(out, 0, r);
  257. }
  258. free(out);
  259. }
  260. switch (r) {
  261. case -ENOENT:
  262. fprintf(stderr, "Device is not initialized (%s)\n", opts->device);
  263. return EX_OSFILE;
  264. case -EINVAL:
  265. fprintf(stderr, "LUKSMeta data appears corrupt (%s)\n", opts->device);
  266. return EX_OSFILE;
  267. case -EBADSLT:
  268. fprintf(stderr, "The specified slot is invalid (%d)\n", opts->slot);
  269. return EX_USAGE;
  270. case -ENODATA:
  271. fprintf(stderr, "The specified slot is empty (%d)\n", opts->slot);
  272. return EX_UNAVAILABLE;
  273. default:
  274. if (r < 0)
  275. fprintf(stderr, "An unknown error occurred\n");
  276. return r < 0 ? EX_OSERR : EX_OK;
  277. }
  278. }
  279. static int
  280. cmd_wipe(const struct options *opts, struct crypt_device *cd)
  281. {
  282. luksmeta_uuid_t uuid = {};
  283. int r = 0;
  284. if (opts->slot < 0) {
  285. fprintf(stderr, "Slot required\n");
  286. return EX_USAGE;
  287. }
  288. if (!opts->force) {
  289. int c = 'X';
  290. fprintf(stderr,
  291. "You are about to wipe a slot. This operation is unrecoverable.\n"
  292. "A backup is advised before proceeding.\n\n");
  293. while (!strchr("YyNn", c)) {
  294. fprintf(stderr, "Do you wish to erase slot %d on %s? [yn] ",
  295. opts->slot, crypt_get_device_name(cd));
  296. c = getc(stdin);
  297. }
  298. if (strchr("Nn", c))
  299. return EX_NOPERM;
  300. }
  301. r = luksmeta_wipe(cd, opts->slot, opts->have_uuid ? opts->uuid : NULL);
  302. switch (r) {
  303. case -EALREADY:
  304. return EX_OK;
  305. case -ENOENT:
  306. fprintf(stderr, "Device is not initialized (%s)\n", opts->device);
  307. return EX_OSFILE;
  308. case -EINVAL:
  309. fprintf(stderr, "LUKSMeta data appears corrupt (%s)\n", opts->device);
  310. return EX_OSFILE;
  311. case -EBADSLT:
  312. fprintf(stderr, "The specified slot is invalid (%d)\n", opts->slot);
  313. return EX_USAGE;
  314. case -EKEYREJECTED:
  315. r = luksmeta_load(cd, opts->slot, uuid, NULL, 0);
  316. if (r >= 0) {
  317. fprintf(stderr,
  318. "The given UUID does not match the slot UUID:\n"
  319. "UUID: " UUID_TMPL "\n"
  320. "SLOT: " UUID_TMPL "\n",
  321. UUID_ARGS(opts->uuid),
  322. UUID_ARGS(uuid));
  323. } else {
  324. fprintf(stderr,
  325. "The given UUID does not match the slot UUID:\n"
  326. "UUID: " UUID_TMPL "\n"
  327. "SLOT: UNKNOWN\n",
  328. UUID_ARGS(opts->uuid));
  329. }
  330. return EX_DATAERR;
  331. default:
  332. if (r < 0)
  333. fprintf(stderr, "An unknown error occurred\n");
  334. return r < 0 ? EX_OSERR : EX_OK;
  335. }
  336. }
  337. static const char *sopts = "hfnd:u:s:";
  338. static const struct option lopts[] = {
  339. { "help", .val = 'h' },
  340. { "nuke", no_argument, .val = 'n' },
  341. { "force", no_argument, .val = 'f' },
  342. { "device", required_argument, .val = 'd' },
  343. { "uuid", required_argument, .val = 'u' },
  344. { "slot", required_argument, .val = 's' },
  345. {}
  346. };
  347. static const struct {
  348. int (*func)(const struct options *opts, struct crypt_device *cd);
  349. const char *name;
  350. } commands[] = {
  351. { cmd_test, "test", },
  352. { cmd_nuke, "nuke", },
  353. { cmd_init, "init", },
  354. { cmd_show, "show", },
  355. { cmd_save, "save", },
  356. { cmd_load, "load", },
  357. { cmd_wipe, "wipe", },
  358. {}
  359. };
  360. int
  361. main(int argc, char *argv[])
  362. {
  363. struct options o = { .slot = CRYPT_ANY_SLOT };
  364. for (int c; (c = getopt_long(argc, argv, sopts, lopts, NULL)) != -1; ) {
  365. switch (c) {
  366. case 'h': goto usage;
  367. case 'd': o.device = optarg; break;
  368. case 'n': o.nuke = true; break;
  369. case 'f': o.force = true; break;
  370. case 'u':
  371. if (sscanf(optarg, UUID_TMPL, UUID_ARGS(&o.uuid)) != 16) {
  372. fprintf(stderr, "Invalid UUID (%s)\n", optarg);
  373. return EX_USAGE;
  374. }
  375. o.have_uuid = true;
  376. break;
  377. case 's':
  378. if (sscanf(optarg, "%d", &o.slot) != 1 || o.slot < 0 ||
  379. o.slot >= crypt_keyslot_max(CRYPT_LUKS1)) {
  380. fprintf(stderr, "Invalid slot (%s)\n", optarg);
  381. return EX_USAGE;
  382. }
  383. break;
  384. }
  385. }
  386. if (argc > 1 && !o.device) {
  387. fprintf(stderr, "Device must be specified\n\n");
  388. goto usage;
  389. }
  390. if (optind != argc - 1)
  391. goto usage;
  392. for (size_t i = 0; argc > 1 && commands[i].name; i++) {
  393. struct crypt_device *cd = NULL;
  394. const char *type = NULL;
  395. int r = 0;
  396. if (strcmp(argv[optind], commands[i].name) != 0)
  397. continue;
  398. r = crypt_init(&cd, o.device);
  399. if (r != 0) {
  400. fprintf(stderr, "Unable to open device (%s): %s\n",
  401. o.device, strerror(-r));
  402. return EX_IOERR;
  403. }
  404. r = crypt_load(cd, NULL, NULL);
  405. if (r != 0) {
  406. fprintf(stderr, "Unable to read LUKSv1 header (%s): %s\n",
  407. o.device, strerror(-r));
  408. crypt_free(cd);
  409. return EX_IOERR;
  410. }
  411. type = crypt_get_type(cd);
  412. if (type == NULL) {
  413. fprintf(stderr, "Unable to determine device type for %s\n",
  414. o.device);
  415. crypt_free(cd);
  416. return EX_OSFILE;
  417. }
  418. if (strcmp(type, CRYPT_LUKS1) != 0) {
  419. fprintf(stderr, "%s (%s) is not a LUKSv1 device\n", o.device, type);
  420. crypt_free(cd);
  421. return EX_OSFILE;
  422. }
  423. r = commands[i].func(&o, cd);
  424. crypt_free(cd);
  425. return r;
  426. }
  427. fprintf(stderr, "Invalid command\n\n");
  428. usage:
  429. fprintf(stderr,
  430. "Usage: luksmeta test -d DEVICE\n"
  431. " or: luksmeta nuke -d DEVICE [-f]\n"
  432. " or: luksmeta init -d DEVICE [-f] [-n]\n"
  433. " or: luksmeta show -d DEVICE [-s SLOT]\n"
  434. " or: luksmeta save -d DEVICE [-s SLOT] -u UUID < DATA\n"
  435. " or: luksmeta load -d DEVICE -s SLOT [-u UUID] > DATA\n"
  436. " or: luksmeta wipe -d DEVICE -s SLOT [-u UUID] [-f]\n");
  437. return EX_USAGE;
  438. }