loadenv.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. /* loadenv.c - command to load/save environment variable. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2008,2009,2010 Free Software Foundation, Inc.
  5. *
  6. * GRUB is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * GRUB 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 General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <grub/dl.h>
  20. #include <grub/mm.h>
  21. #include <grub/file.h>
  22. #include <grub/disk.h>
  23. #include <grub/misc.h>
  24. #include <grub/env.h>
  25. #include <grub/partition.h>
  26. #include <grub/lib/envblk.h>
  27. #include <grub/extcmd.h>
  28. #include <grub/i18n.h>
  29. GRUB_MOD_LICENSE ("GPLv3+");
  30. static const struct grub_arg_option options[] =
  31. {
  32. /* TRANSLATORS: This option is used to override default filename
  33. for loading and storing environment. */
  34. {"file", 'f', 0, N_("Specify filename."), 0, ARG_TYPE_PATHNAME},
  35. {"skip-sig", 's', 0,
  36. N_("Skip signature-checking of the environment file."), 0, ARG_TYPE_NONE},
  37. {0, 0, 0, 0, 0, 0}
  38. };
  39. /* Opens 'filename' with compression filters disabled. Optionally disables the
  40. PUBKEY filter (that insists upon properly signed files) as well. PUBKEY
  41. filter is restored before the function returns. */
  42. static grub_file_t
  43. open_envblk_file (char *filename, int untrusted)
  44. {
  45. grub_file_t file;
  46. char *buf = 0;
  47. if (! filename)
  48. {
  49. const char *prefix;
  50. int len;
  51. prefix = grub_env_get ("prefix");
  52. if (! prefix)
  53. {
  54. grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix");
  55. return 0;
  56. }
  57. len = grub_strlen (prefix);
  58. buf = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG));
  59. if (! buf)
  60. return 0;
  61. filename = buf;
  62. grub_strcpy (filename, prefix);
  63. filename[len] = '/';
  64. grub_strcpy (filename + len + 1, GRUB_ENVBLK_DEFCFG);
  65. }
  66. /* The filters that are disabled will be re-enabled by the call to
  67. grub_file_open() after this particular file is opened. */
  68. grub_file_filter_disable_compression ();
  69. if (untrusted)
  70. grub_file_filter_disable_pubkey ();
  71. file = grub_file_open (filename);
  72. grub_free (buf);
  73. return file;
  74. }
  75. static grub_envblk_t
  76. read_envblk_file (grub_file_t file)
  77. {
  78. grub_off_t offset = 0;
  79. char *buf;
  80. grub_size_t size = grub_file_size (file);
  81. grub_envblk_t envblk;
  82. buf = grub_malloc (size);
  83. if (! buf)
  84. return 0;
  85. while (size > 0)
  86. {
  87. grub_ssize_t ret;
  88. ret = grub_file_read (file, buf + offset, size);
  89. if (ret <= 0)
  90. {
  91. grub_free (buf);
  92. return 0;
  93. }
  94. size -= ret;
  95. offset += ret;
  96. }
  97. envblk = grub_envblk_open (buf, offset);
  98. if (! envblk)
  99. {
  100. grub_free (buf);
  101. grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
  102. return 0;
  103. }
  104. return envblk;
  105. }
  106. struct grub_env_whitelist
  107. {
  108. grub_size_t len;
  109. char **list;
  110. };
  111. typedef struct grub_env_whitelist grub_env_whitelist_t;
  112. static int
  113. test_whitelist_membership (const char* name,
  114. const grub_env_whitelist_t* whitelist)
  115. {
  116. grub_size_t i;
  117. for (i = 0; i < whitelist->len; i++)
  118. if (grub_strcmp (name, whitelist->list[i]) == 0)
  119. return 1; /* found it */
  120. return 0; /* not found */
  121. }
  122. /* Helper for grub_cmd_load_env. */
  123. static int
  124. set_var (const char *name, const char *value, void *whitelist)
  125. {
  126. if (! whitelist)
  127. {
  128. grub_env_set (name, value);
  129. return 0;
  130. }
  131. if (test_whitelist_membership (name,
  132. (const grub_env_whitelist_t *) whitelist))
  133. grub_env_set (name, value);
  134. return 0;
  135. }
  136. static grub_err_t
  137. grub_cmd_load_env (grub_extcmd_context_t ctxt, int argc, char **args)
  138. {
  139. struct grub_arg_list *state = ctxt->state;
  140. grub_file_t file;
  141. grub_envblk_t envblk;
  142. grub_env_whitelist_t whitelist;
  143. whitelist.len = argc;
  144. whitelist.list = args;
  145. /* state[0] is the -f flag; state[1] is the --skip-sig flag */
  146. file = open_envblk_file ((state[0].set) ? state[0].arg : 0, state[1].set);
  147. if (! file)
  148. return grub_errno;
  149. envblk = read_envblk_file (file);
  150. if (! envblk)
  151. goto fail;
  152. /* argc > 0 indicates caller provided a whitelist of variables to read. */
  153. grub_envblk_iterate (envblk, argc > 0 ? &whitelist : 0, set_var);
  154. grub_envblk_close (envblk);
  155. fail:
  156. grub_file_close (file);
  157. return grub_errno;
  158. }
  159. /* Print all variables in current context. */
  160. static int
  161. print_var (const char *name, const char *value,
  162. void *hook_data __attribute__ ((unused)))
  163. {
  164. grub_printf ("%s=%s\n", name, value);
  165. return 0;
  166. }
  167. static grub_err_t
  168. grub_cmd_list_env (grub_extcmd_context_t ctxt,
  169. int argc __attribute__ ((unused)),
  170. char **args __attribute__ ((unused)))
  171. {
  172. struct grub_arg_list *state = ctxt->state;
  173. grub_file_t file;
  174. grub_envblk_t envblk;
  175. file = open_envblk_file ((state[0].set) ? state[0].arg : 0, 0);
  176. if (! file)
  177. return grub_errno;
  178. envblk = read_envblk_file (file);
  179. if (! envblk)
  180. goto fail;
  181. grub_envblk_iterate (envblk, NULL, print_var);
  182. grub_envblk_close (envblk);
  183. fail:
  184. grub_file_close (file);
  185. return grub_errno;
  186. }
  187. /* Used to maintain a variable length of blocklists internally. */
  188. struct blocklist
  189. {
  190. grub_disk_addr_t sector;
  191. unsigned offset;
  192. unsigned length;
  193. struct blocklist *next;
  194. };
  195. static void
  196. free_blocklists (struct blocklist *p)
  197. {
  198. struct blocklist *q;
  199. for (; p; p = q)
  200. {
  201. q = p->next;
  202. grub_free (p);
  203. }
  204. }
  205. static grub_err_t
  206. check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
  207. grub_file_t file)
  208. {
  209. grub_size_t total_length;
  210. grub_size_t index;
  211. grub_disk_t disk;
  212. grub_disk_addr_t part_start;
  213. struct blocklist *p;
  214. char *buf;
  215. /* Sanity checks. */
  216. total_length = 0;
  217. for (p = blocklists; p; p = p->next)
  218. {
  219. struct blocklist *q;
  220. /* Check if any pair of blocks overlap. */
  221. for (q = p->next; q; q = q->next)
  222. {
  223. grub_disk_addr_t s1, s2;
  224. grub_disk_addr_t e1, e2;
  225. s1 = p->sector;
  226. e1 = s1 + ((p->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);
  227. s2 = q->sector;
  228. e2 = s2 + ((q->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);
  229. if (s1 < e2 && s2 < e1)
  230. {
  231. /* This might be actually valid, but it is unbelievable that
  232. any filesystem makes such a silly allocation. */
  233. return grub_error (GRUB_ERR_BAD_FS, "malformed file");
  234. }
  235. }
  236. total_length += p->length;
  237. }
  238. if (total_length != grub_file_size (file))
  239. {
  240. /* Maybe sparse, unallocated sectors. No way in GRUB. */
  241. return grub_error (GRUB_ERR_BAD_FILE_TYPE, "sparse file not allowed");
  242. }
  243. /* One more sanity check. Re-read all sectors by blocklists, and compare
  244. those with the data read via a file. */
  245. disk = file->device->disk;
  246. part_start = grub_partition_get_start (disk->partition);
  247. buf = grub_envblk_buffer (envblk);
  248. char *blockbuf = NULL;
  249. grub_size_t blockbuf_len = 0;
  250. for (p = blocklists, index = 0; p; index += p->length, p = p->next)
  251. {
  252. if (p->length > blockbuf_len)
  253. {
  254. grub_free (blockbuf);
  255. blockbuf_len = 2 * p->length;
  256. blockbuf = grub_malloc (blockbuf_len);
  257. if (!blockbuf)
  258. return grub_errno;
  259. }
  260. if (grub_disk_read (disk, p->sector - part_start,
  261. p->offset, p->length, blockbuf))
  262. return grub_errno;
  263. if (grub_memcmp (buf + index, blockbuf, p->length) != 0)
  264. return grub_error (GRUB_ERR_FILE_READ_ERROR, "invalid blocklist");
  265. }
  266. return GRUB_ERR_NONE;
  267. }
  268. static int
  269. write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
  270. grub_file_t file)
  271. {
  272. char *buf;
  273. grub_disk_t disk;
  274. grub_disk_addr_t part_start;
  275. struct blocklist *p;
  276. grub_size_t index;
  277. buf = grub_envblk_buffer (envblk);
  278. disk = file->device->disk;
  279. part_start = grub_partition_get_start (disk->partition);
  280. index = 0;
  281. for (p = blocklists; p; index += p->length, p = p->next)
  282. {
  283. if (grub_disk_write (disk, p->sector - part_start,
  284. p->offset, p->length, buf + index))
  285. return 0;
  286. }
  287. return 1;
  288. }
  289. /* Context for grub_cmd_save_env. */
  290. struct grub_cmd_save_env_ctx
  291. {
  292. struct blocklist *head, *tail;
  293. };
  294. /* Store blocklists in a linked list. */
  295. static void
  296. save_env_read_hook (grub_disk_addr_t sector, unsigned offset, unsigned length,
  297. void *data)
  298. {
  299. struct grub_cmd_save_env_ctx *ctx = data;
  300. struct blocklist *block;
  301. block = grub_malloc (sizeof (*block));
  302. if (! block)
  303. return;
  304. block->sector = sector;
  305. block->offset = offset;
  306. block->length = length;
  307. /* Slightly complicated, because the list should be FIFO. */
  308. block->next = 0;
  309. if (ctx->tail)
  310. ctx->tail->next = block;
  311. ctx->tail = block;
  312. if (! ctx->head)
  313. ctx->head = block;
  314. }
  315. static grub_err_t
  316. grub_cmd_save_env (grub_extcmd_context_t ctxt, int argc, char **args)
  317. {
  318. struct grub_arg_list *state = ctxt->state;
  319. grub_file_t file;
  320. grub_envblk_t envblk;
  321. struct grub_cmd_save_env_ctx ctx = {
  322. .head = 0,
  323. .tail = 0
  324. };
  325. if (! argc)
  326. return grub_error (GRUB_ERR_BAD_ARGUMENT, "no variable is specified");
  327. file = open_envblk_file ((state[0].set) ? state[0].arg : 0,
  328. 1 /* allow untrusted */);
  329. if (! file)
  330. return grub_errno;
  331. if (! file->device->disk)
  332. {
  333. grub_file_close (file);
  334. return grub_error (GRUB_ERR_BAD_DEVICE, "disk device required");
  335. }
  336. file->read_hook = save_env_read_hook;
  337. file->read_hook_data = &ctx;
  338. envblk = read_envblk_file (file);
  339. file->read_hook = 0;
  340. if (! envblk)
  341. goto fail;
  342. if (check_blocklists (envblk, ctx.head, file))
  343. goto fail;
  344. while (argc)
  345. {
  346. const char *value;
  347. value = grub_env_get (args[0]);
  348. if (value)
  349. {
  350. if (! grub_envblk_set (envblk, args[0], value))
  351. {
  352. grub_error (GRUB_ERR_BAD_ARGUMENT, "environment block too small");
  353. goto fail;
  354. }
  355. }
  356. else
  357. grub_envblk_delete (envblk, args[0]);
  358. argc--;
  359. args++;
  360. }
  361. write_blocklists (envblk, ctx.head, file);
  362. fail:
  363. if (envblk)
  364. grub_envblk_close (envblk);
  365. free_blocklists (ctx.head);
  366. grub_file_close (file);
  367. return grub_errno;
  368. }
  369. static grub_extcmd_t cmd_load, cmd_list, cmd_save;
  370. GRUB_MOD_INIT(loadenv)
  371. {
  372. cmd_load =
  373. grub_register_extcmd ("load_env", grub_cmd_load_env, 0,
  374. N_("[-f FILE] [-s|--skip-sig] [variable_name_to_whitelist] [...]"),
  375. N_("Load variables from environment block file."),
  376. options);
  377. cmd_list =
  378. grub_register_extcmd ("list_env", grub_cmd_list_env, 0, N_("[-f FILE]"),
  379. N_("List variables from environment block file."),
  380. options);
  381. cmd_save =
  382. grub_register_extcmd ("save_env", grub_cmd_save_env, 0,
  383. N_("[-f FILE] variable_name [...]"),
  384. N_("Save variables to environment block file."),
  385. options);
  386. }
  387. GRUB_MOD_FINI(loadenv)
  388. {
  389. grub_unregister_extcmd (cmd_load);
  390. grub_unregister_extcmd (cmd_list);
  391. grub_unregister_extcmd (cmd_save);
  392. }