plainmount.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2022 Free Software Foundation, Inc.
  4. *
  5. * GRUB is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * GRUB is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /* plaimount.c - Open device encrypted in plain mode. */
  19. #include <grub/cryptodisk.h>
  20. #include <grub/dl.h>
  21. #include <grub/err.h>
  22. #include <grub/extcmd.h>
  23. #include <grub/partition.h>
  24. #include <grub/file.h>
  25. GRUB_MOD_LICENSE ("GPLv3+");
  26. #define PLAINMOUNT_DEFAULT_SECTOR_SIZE 512
  27. #define PLAINMOUNT_DEFAULT_UUID "109fea84-a6b7-34a8-4bd1-1c506305a400"
  28. enum PLAINMOUNT_OPTION
  29. {
  30. OPTION_HASH,
  31. OPTION_CIPHER,
  32. OPTION_KEY_SIZE,
  33. OPTION_SECTOR_SIZE,
  34. OPTION_PASSWORD,
  35. OPTION_KEYFILE,
  36. OPTION_KEYFILE_OFFSET,
  37. OPTION_UUID
  38. };
  39. static const struct grub_arg_option options[] =
  40. {
  41. /* TRANSLATORS: It's still restricted to this module only. */
  42. {"hash", 'h', 0, N_("Password hash"), 0, ARG_TYPE_STRING},
  43. {"cipher", 'c', 0, N_("Password cipher"), 0, ARG_TYPE_STRING},
  44. {"key-size", 's', 0, N_("Key size (in bits)"), 0, ARG_TYPE_INT},
  45. {"sector-size", 'S', 0, N_("Device sector size"), 0, ARG_TYPE_INT},
  46. {"password", 'p', 0, N_("Password (key)"), 0, ARG_TYPE_STRING},
  47. {"keyfile", 'd', 0, N_("Keyfile path"), 0, ARG_TYPE_STRING},
  48. {"keyfile-offset", 'O', 0, N_("Keyfile offset"), 0, ARG_TYPE_INT},
  49. {"uuid", 'u', 0, N_("Set device UUID"), 0, ARG_TYPE_STRING},
  50. {0, 0, 0, 0, 0, 0}
  51. };
  52. /* Cryptodisk setkey() function wrapper */
  53. static grub_err_t
  54. plainmount_setkey (grub_cryptodisk_t dev, grub_uint8_t *key,
  55. grub_size_t size)
  56. {
  57. gcry_err_code_t code = grub_cryptodisk_setkey (dev, key, size);
  58. if (code != GPG_ERR_NO_ERROR)
  59. {
  60. grub_dprintf ("plainmount", "failed to set cipher key with code: %d\n", code);
  61. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("cannot set specified key"));
  62. }
  63. return GRUB_ERR_NONE;
  64. }
  65. /* Configure cryptodisk uuid */
  66. static void plainmount_set_uuid (grub_cryptodisk_t dev, const char *user_uuid)
  67. {
  68. grub_size_t pos = 0;
  69. /* Size of user_uuid is checked in main func */
  70. if (user_uuid != NULL)
  71. grub_strcpy (dev->uuid, user_uuid);
  72. else
  73. {
  74. /*
  75. * Set default UUID. Last digits start from 1 and are incremented for
  76. * each new plainmount device by snprintf().
  77. */
  78. grub_snprintf (dev->uuid, sizeof (dev->uuid) - 1, "%36lx", dev->id + 1);
  79. while (dev->uuid[++pos] == ' ');
  80. grub_memcpy (dev->uuid, PLAINMOUNT_DEFAULT_UUID, pos);
  81. }
  82. COMPILE_TIME_ASSERT (sizeof (dev->uuid) >= sizeof (PLAINMOUNT_DEFAULT_UUID));
  83. }
  84. /* Configure cryptodevice sector size (-S option) */
  85. static grub_err_t
  86. plainmount_configure_sectors (grub_cryptodisk_t dev, grub_disk_t disk,
  87. grub_size_t sector_size)
  88. {
  89. dev->total_sectors = grub_disk_native_sectors (disk);
  90. if (dev->total_sectors == GRUB_DISK_SIZE_UNKNOWN)
  91. return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot determine disk %s size"),
  92. disk->name);
  93. /* Convert size to sectors */
  94. dev->log_sector_size = grub_log2ull (sector_size);
  95. dev->total_sectors = grub_convert_sector (dev->total_sectors,
  96. GRUB_DISK_SECTOR_BITS,
  97. dev->log_sector_size);
  98. if (dev->total_sectors == 0)
  99. return grub_error (GRUB_ERR_BAD_DEVICE,
  100. N_("cannot set specified sector size on disk %s"),
  101. disk->name);
  102. grub_dprintf ("plainmount", "log_sector_size=%d, total_sectors=%"
  103. PRIuGRUB_UINT64_T"\n", dev->log_sector_size, dev->total_sectors);
  104. return GRUB_ERR_NONE;
  105. }
  106. /* Hashes a password into a key and stores it with the cipher. */
  107. static grub_err_t
  108. plainmount_configure_password (grub_cryptodisk_t dev, const char *hash,
  109. grub_uint8_t *key_data, grub_size_t key_size,
  110. grub_size_t password_size)
  111. {
  112. grub_uint8_t *derived_hash, *dh;
  113. char *p;
  114. unsigned int round, i, len, size;
  115. grub_size_t alloc_size;
  116. grub_err_t err = GRUB_ERR_NONE;
  117. /* Support none (plain) hash */
  118. if (grub_strcmp (hash, "plain") == 0)
  119. {
  120. dev->hash = NULL;
  121. return err;
  122. }
  123. /* Hash argument was checked at main func */
  124. dev->hash = grub_crypto_lookup_md_by_name (hash);
  125. len = dev->hash->mdlen;
  126. alloc_size = grub_max (password_size, key_size);
  127. /*
  128. * Allocate buffer for the password and for an added prefix character
  129. * for each hash round ('alloc_size' may not be a multiple of 'len').
  130. */
  131. p = grub_zalloc (alloc_size + (alloc_size / len) + 1);
  132. derived_hash = grub_zalloc (GRUB_CRYPTODISK_MAX_KEYLEN * 2);
  133. if (p == NULL || derived_hash == NULL)
  134. {
  135. err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
  136. goto fail;
  137. }
  138. dh = derived_hash;
  139. /*
  140. * Hash password. Adapted from cryptsetup.
  141. * https://gitlab.com/cryptsetup/cryptsetup/-/blob/main/lib/crypt_plain.c
  142. */
  143. for (round = 0, size = alloc_size; size; round++, dh += len, size -= len)
  144. {
  145. for (i = 0; i < round; i++)
  146. p[i] = 'A';
  147. grub_memcpy (p + i, (char*) key_data, password_size);
  148. if (len > size)
  149. len = size;
  150. grub_crypto_hash (dev->hash, dh, p, password_size + round);
  151. }
  152. grub_memcpy (key_data, derived_hash, key_size);
  153. fail:
  154. grub_free (p);
  155. grub_free (derived_hash);
  156. return err;
  157. }
  158. /* Read key material from keyfile */
  159. static grub_err_t
  160. plainmount_configure_keyfile (char *keyfile, grub_uint8_t *key_data,
  161. grub_size_t key_size, grub_size_t keyfile_offset)
  162. {
  163. grub_file_t g_keyfile = grub_file_open (keyfile, GRUB_FILE_TYPE_NONE);
  164. if (g_keyfile == NULL)
  165. return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("cannot open keyfile %s"),
  166. keyfile);
  167. if (grub_file_seek (g_keyfile, keyfile_offset) == (grub_off_t) - 1)
  168. return grub_error (GRUB_ERR_FILE_READ_ERROR,
  169. N_("cannot seek keyfile at offset %"PRIuGRUB_SIZE),
  170. keyfile_offset);
  171. if (key_size > (g_keyfile->size - keyfile_offset))
  172. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Specified key size (%"
  173. PRIuGRUB_SIZE") is too small for keyfile size (%"
  174. PRIuGRUB_UINT64_T") and offset (%"PRIuGRUB_SIZE")"),
  175. key_size, g_keyfile->size, keyfile_offset);
  176. if (grub_file_read (g_keyfile, key_data, key_size) != (grub_ssize_t) key_size)
  177. return grub_error (GRUB_ERR_FILE_READ_ERROR, N_("error reading key file"));
  178. return GRUB_ERR_NONE;
  179. }
  180. /* Plainmount command entry point */
  181. static grub_err_t
  182. grub_cmd_plainmount (grub_extcmd_context_t ctxt, int argc, char **args)
  183. {
  184. struct grub_arg_list *state = ctxt->state;
  185. grub_cryptodisk_t dev = NULL;
  186. grub_disk_t disk = NULL;
  187. const gcry_md_spec_t *gcry_hash;
  188. char *diskname, *disklast = NULL, *cipher, *mode, *hash, *keyfile, *uuid;
  189. grub_size_t len, key_size, sector_size, keyfile_offset = 0, password_size = 0;
  190. grub_err_t err;
  191. const char *p;
  192. grub_uint8_t *key_data;
  193. if (argc < 1)
  194. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("device name required"));
  195. /* Check whether required arguments are specified */
  196. if (!state[OPTION_CIPHER].set || !state[OPTION_KEY_SIZE].set)
  197. return grub_error (GRUB_ERR_BAD_ARGUMENT, "cipher and key size must be set");
  198. if (!state[OPTION_HASH].set && !state[OPTION_KEYFILE].set)
  199. return grub_error (GRUB_ERR_BAD_ARGUMENT, "hash algorithm must be set");
  200. /* Check hash */
  201. if (!state[OPTION_KEYFILE].set)
  202. {
  203. gcry_hash = grub_crypto_lookup_md_by_name (state[OPTION_HASH].arg);
  204. if (!gcry_hash)
  205. return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("couldn't load hash %s"),
  206. state[OPTION_HASH].arg);
  207. if (gcry_hash->mdlen > GRUB_CRYPTODISK_MAX_KEYLEN)
  208. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  209. N_("hash length %"PRIuGRUB_SIZE" exceeds maximum %d bits"),
  210. gcry_hash->mdlen * GRUB_CHAR_BIT,
  211. GRUB_CRYPTODISK_MAX_KEYLEN * GRUB_CHAR_BIT);
  212. }
  213. /* Check cipher mode */
  214. if (!grub_strchr (state[OPTION_CIPHER].arg,'-'))
  215. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  216. N_("invalid cipher mode, must be of format cipher-mode"));
  217. /* Check password size */
  218. if (state[OPTION_PASSWORD].set && grub_strlen (state[OPTION_PASSWORD].arg) >
  219. GRUB_CRYPTODISK_MAX_PASSPHRASE)
  220. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  221. N_("password exceeds maximium size"));
  222. /* Check uuid length */
  223. if (state[OPTION_UUID].set && grub_strlen (state[OPTION_UUID].arg) >
  224. GRUB_CRYPTODISK_MAX_UUID_LENGTH - 1)
  225. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  226. N_("specified UUID exceeds maximum size"));
  227. if (state[OPTION_UUID].set && grub_strlen (state[OPTION_UUID].arg) == 1)
  228. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("specified UUID too short"));
  229. /* Parse plainmount arguments */
  230. grub_errno = GRUB_ERR_NONE;
  231. keyfile_offset = state[OPTION_KEYFILE_OFFSET].set ?
  232. grub_strtoull (state[OPTION_KEYFILE_OFFSET].arg, &p, 0) : 0;
  233. if (state[OPTION_KEYFILE_OFFSET].set &&
  234. (state[OPTION_KEYFILE_OFFSET].arg[0] == '\0' || *p != '\0' ||
  235. grub_errno != GRUB_ERR_NONE))
  236. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized keyfile offset"));
  237. sector_size = state[OPTION_SECTOR_SIZE].set ?
  238. grub_strtoull (state[OPTION_SECTOR_SIZE].arg, &p, 0) :
  239. PLAINMOUNT_DEFAULT_SECTOR_SIZE;
  240. if (state[OPTION_SECTOR_SIZE].set && (state[OPTION_SECTOR_SIZE].arg[0] == '\0' ||
  241. *p != '\0' || grub_errno != GRUB_ERR_NONE))
  242. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized sector size"));
  243. /* Check key size */
  244. key_size = grub_strtoull (state[OPTION_KEY_SIZE].arg, &p, 0);
  245. if (state[OPTION_KEY_SIZE].arg[0] == '\0' || *p != '\0' ||
  246. grub_errno != GRUB_ERR_NONE)
  247. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized key size"));
  248. if ((key_size % GRUB_CHAR_BIT) != 0)
  249. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  250. N_("key size is not multiple of %d bits"), GRUB_CHAR_BIT);
  251. key_size = key_size / GRUB_CHAR_BIT;
  252. if (key_size > GRUB_CRYPTODISK_MAX_KEYLEN)
  253. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  254. N_("key size %"PRIuGRUB_SIZE" exceeds maximum %d bits"),
  255. key_size * GRUB_CHAR_BIT,
  256. GRUB_CRYPTODISK_MAX_KEYLEN * GRUB_CHAR_BIT);
  257. /* Check disk sector size */
  258. if (sector_size < GRUB_DISK_SECTOR_SIZE)
  259. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  260. N_("sector size -S must be at least %d"),
  261. GRUB_DISK_SECTOR_SIZE);
  262. if ((sector_size & (sector_size - 1)) != 0)
  263. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  264. N_("sector size -S %"PRIuGRUB_SIZE" is not power of 2"),
  265. sector_size);
  266. /* Allocate all stuff here */
  267. hash = state[OPTION_HASH].set ? grub_strdup (state[OPTION_HASH].arg) : NULL;
  268. cipher = grub_strdup (state[OPTION_CIPHER].arg);
  269. keyfile = state[OPTION_KEYFILE].set ?
  270. grub_strdup (state[OPTION_KEYFILE].arg) : NULL;
  271. dev = grub_zalloc (sizeof *dev);
  272. key_data = grub_zalloc (GRUB_CRYPTODISK_MAX_PASSPHRASE);
  273. uuid = state[OPTION_UUID].set ? grub_strdup (state[OPTION_UUID].arg) : NULL;
  274. if ((state[OPTION_HASH].set && hash == NULL) || cipher == NULL || dev == NULL ||
  275. (state[OPTION_KEYFILE].set && keyfile == NULL) || key_data == NULL ||
  276. (state[OPTION_UUID].set && uuid == NULL))
  277. {
  278. err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
  279. goto fail;
  280. }
  281. /* Copy user password from -p option */
  282. if (state[OPTION_PASSWORD].set)
  283. {
  284. /*
  285. * Password from the '-p' option is limited to C-string.
  286. * Arbitrary data keys are supported via keyfiles.
  287. */
  288. password_size = grub_strlen (state[OPTION_PASSWORD].arg);
  289. grub_strcpy ((char*) key_data, state[OPTION_PASSWORD].arg);
  290. }
  291. /* Set cipher mode (tested above) */
  292. mode = grub_strchr (cipher,'-');
  293. *mode++ = '\0';
  294. /* Check cipher */
  295. err = grub_cryptodisk_setcipher (dev, cipher, mode);
  296. if (err != GRUB_ERR_NONE)
  297. {
  298. if (err == GRUB_ERR_FILE_NOT_FOUND)
  299. err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid cipher %s"), cipher);
  300. else if (err == GRUB_ERR_BAD_ARGUMENT)
  301. err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid mode %s"), mode);
  302. else
  303. err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid cipher %s or mode %s"),
  304. cipher, mode);
  305. goto fail;
  306. }
  307. /* Open SOURCE disk */
  308. diskname = args[0];
  309. len = grub_strlen (diskname);
  310. if (len && diskname[0] == '(' && diskname[len - 1] == ')')
  311. {
  312. disklast = &diskname[len - 1];
  313. *disklast = '\0';
  314. diskname++;
  315. }
  316. disk = grub_disk_open (diskname);
  317. if (disk == NULL)
  318. {
  319. if (disklast)
  320. *disklast = ')';
  321. err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("cannot open disk %s"), diskname);
  322. goto fail;
  323. }
  324. /* Get password from console */
  325. if (!state[OPTION_KEYFILE].set && key_data[0] == '\0')
  326. {
  327. char *part = grub_partition_get_name (disk->partition);
  328. grub_printf_ (N_("Enter passphrase for %s%s%s: "), disk->name,
  329. disk->partition != NULL ? "," : "",
  330. part != NULL ? part : N_("UNKNOWN"));
  331. grub_free (part);
  332. if (!grub_password_get ((char*) key_data, GRUB_CRYPTODISK_MAX_PASSPHRASE - 1))
  333. {
  334. err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("error reading password"));
  335. goto fail;
  336. }
  337. /*
  338. * Password from interactive console is limited to C-string.
  339. * Arbitrary data keys are supported via keyfiles.
  340. */
  341. password_size = grub_strlen ((char*) key_data);
  342. }
  343. /* Warn if hash and keyfile are both provided */
  344. if (state[OPTION_KEYFILE].set && state[OPTION_HASH].arg)
  345. grub_printf_ (N_("warning: hash is ignored if keyfile is specified\n"));
  346. /* Warn if -p option is specified with keyfile */
  347. if (state[OPTION_PASSWORD].set && state[OPTION_KEYFILE].set)
  348. grub_printf_ (N_("warning: password specified with -p option "
  349. "is ignored if keyfile is provided\n"));
  350. /* Warn of -O is provided without keyfile */
  351. if (state[OPTION_KEYFILE_OFFSET].set && !state[OPTION_KEYFILE].set)
  352. grub_printf_ (N_("warning: keyfile offset option -O "
  353. "specified without keyfile option -d\n"));
  354. grub_dprintf ("plainmount", "parameters: cipher=%s, hash=%s, key_size=%"
  355. PRIuGRUB_SIZE ", keyfile=%s, keyfile offset=%" PRIuGRUB_SIZE "\n",
  356. cipher, hash, key_size, keyfile, keyfile_offset);
  357. err = plainmount_configure_sectors (dev, disk, sector_size);
  358. if (err != GRUB_ERR_NONE)
  359. goto fail;
  360. /* Configure keyfile or password */
  361. if (state[OPTION_KEYFILE].set)
  362. err = plainmount_configure_keyfile (keyfile, key_data, key_size, keyfile_offset);
  363. else
  364. err = plainmount_configure_password (dev, hash, key_data, key_size, password_size);
  365. if (err != GRUB_ERR_NONE)
  366. goto fail;
  367. err = plainmount_setkey (dev, key_data, key_size);
  368. if (err != GRUB_ERR_NONE)
  369. goto fail;
  370. err = grub_cryptodisk_insert (dev, diskname, disk);
  371. if (err != GRUB_ERR_NONE)
  372. goto fail;
  373. dev->modname = "plainmount";
  374. dev->source_disk = disk;
  375. plainmount_set_uuid (dev, uuid);
  376. fail:
  377. grub_free (hash);
  378. grub_free (cipher);
  379. grub_free (keyfile);
  380. grub_free (key_data);
  381. grub_free (uuid);
  382. if (err != GRUB_ERR_NONE && disk != NULL)
  383. grub_disk_close (disk);
  384. if (err != GRUB_ERR_NONE)
  385. grub_free (dev);
  386. return err;
  387. }
  388. static grub_extcmd_t cmd;
  389. GRUB_MOD_INIT (plainmount)
  390. {
  391. cmd = grub_register_extcmd ("plainmount", grub_cmd_plainmount, 0,
  392. N_("-c cipher -s key-size [-h hash] [-S sector-size]"
  393. " [-o offset] [-p password] [-u uuid] "
  394. " [[-d keyfile] [-O keyfile offset]] <SOURCE>"),
  395. N_("Open partition encrypted in plain mode."),
  396. options);
  397. }
  398. GRUB_MOD_FINI (plainmount)
  399. {
  400. grub_unregister_extcmd (cmd);
  401. }