luksmeta.c 15 KB

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