hashsum.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2009 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. #include <grub/dl.h>
  19. #include <grub/extcmd.h>
  20. #include <grub/file.h>
  21. #include <grub/disk.h>
  22. #include <grub/mm.h>
  23. #include <grub/misc.h>
  24. #include <grub/crypto.h>
  25. #include <grub/normal.h>
  26. #include <grub/i18n.h>
  27. GRUB_MOD_LICENSE ("GPLv3+");
  28. static const struct grub_arg_option options[] = {
  29. {"hash", 'h', 0, N_("Specify hash to use."), N_("HASH"), ARG_TYPE_STRING},
  30. {"check", 'c', 0, N_("Check hashes of files with hash list FILE."),
  31. N_("FILE"), ARG_TYPE_STRING},
  32. {"prefix", 'p', 0, N_("Base directory for hash list."), N_("DIR"),
  33. ARG_TYPE_STRING},
  34. {"keep-going", 'k', 0, N_("Don't stop after first error."), 0, 0},
  35. {"uncompress", 'u', 0, N_("Uncompress file before checksumming."), 0, 0},
  36. {0, 0, 0, 0, 0, 0}
  37. };
  38. static struct { const char *name; const char *hashname; } aliases[] =
  39. {
  40. {"sha256sum", "sha256"},
  41. {"sha512sum", "sha512"},
  42. {"sha1sum", "sha1"},
  43. {"md5sum", "md5"},
  44. {"crc", "crc32"},
  45. };
  46. static inline int
  47. hextoval (char c)
  48. {
  49. if (c >= '0' && c <= '9')
  50. return c - '0';
  51. if (c >= 'a' && c <= 'f')
  52. return c - 'a' + 10;
  53. if (c >= 'A' && c <= 'F')
  54. return c - 'A' + 10;
  55. return -1;
  56. }
  57. static grub_err_t
  58. hash_file (grub_file_t file, const gcry_md_spec_t *hash, void *result)
  59. {
  60. void *context;
  61. grub_uint8_t *readbuf;
  62. #define BUF_SIZE 4096
  63. readbuf = grub_malloc (BUF_SIZE);
  64. if (!readbuf)
  65. return grub_errno;
  66. context = grub_zalloc (hash->contextsize);
  67. if (!readbuf || !context)
  68. goto fail;
  69. hash->init (context);
  70. while (1)
  71. {
  72. grub_ssize_t r;
  73. r = grub_file_read (file, readbuf, BUF_SIZE);
  74. if (r < 0)
  75. goto fail;
  76. if (r == 0)
  77. break;
  78. hash->write (context, readbuf, r);
  79. }
  80. hash->final (context);
  81. grub_memcpy (result, hash->read (context), hash->mdlen);
  82. grub_free (readbuf);
  83. grub_free (context);
  84. return GRUB_ERR_NONE;
  85. fail:
  86. grub_free (readbuf);
  87. grub_free (context);
  88. return grub_errno;
  89. }
  90. static grub_err_t
  91. check_list (const gcry_md_spec_t *hash, const char *hashfilename,
  92. const char *prefix, int keep, int uncompress)
  93. {
  94. grub_file_t hashlist, file;
  95. char *buf = NULL;
  96. grub_uint8_t expected[GRUB_CRYPTO_MAX_MDLEN];
  97. grub_uint8_t actual[GRUB_CRYPTO_MAX_MDLEN];
  98. grub_err_t err;
  99. unsigned i;
  100. unsigned unread = 0, mismatch = 0;
  101. if (hash->mdlen > GRUB_CRYPTO_MAX_MDLEN)
  102. return grub_error (GRUB_ERR_BUG, "mdlen is too long");
  103. hashlist = grub_file_open (hashfilename);
  104. if (!hashlist)
  105. return grub_errno;
  106. while (grub_free (buf), (buf = grub_file_getline (hashlist)))
  107. {
  108. const char *p = buf;
  109. while (grub_isspace (p[0]))
  110. p++;
  111. for (i = 0; i < hash->mdlen; i++)
  112. {
  113. int high, low;
  114. high = hextoval (*p++);
  115. low = hextoval (*p++);
  116. if (high < 0 || low < 0)
  117. return grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid hash list");
  118. expected[i] = (high << 4) | low;
  119. }
  120. if ((p[0] != ' ' && p[0] != '\t') || (p[1] != ' ' && p[1] != '\t'))
  121. return grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid hash list");
  122. p += 2;
  123. if (prefix)
  124. {
  125. char *filename;
  126. filename = grub_xasprintf ("%s/%s", prefix, p);
  127. if (!filename)
  128. return grub_errno;
  129. if (!uncompress)
  130. grub_file_filter_disable_compression ();
  131. file = grub_file_open (filename);
  132. grub_free (filename);
  133. }
  134. else
  135. {
  136. if (!uncompress)
  137. grub_file_filter_disable_compression ();
  138. file = grub_file_open (p);
  139. }
  140. if (!file)
  141. {
  142. grub_file_close (hashlist);
  143. grub_free (buf);
  144. return grub_errno;
  145. }
  146. err = hash_file (file, hash, actual);
  147. grub_file_close (file);
  148. if (err)
  149. {
  150. grub_printf_ (N_("%s: READ ERROR\n"), p);
  151. if (!keep)
  152. {
  153. grub_file_close (hashlist);
  154. grub_free (buf);
  155. return err;
  156. }
  157. grub_print_error ();
  158. grub_errno = GRUB_ERR_NONE;
  159. unread++;
  160. continue;
  161. }
  162. if (grub_crypto_memcmp (expected, actual, hash->mdlen) != 0)
  163. {
  164. grub_printf_ (N_("%s: HASH MISMATCH\n"), p);
  165. if (!keep)
  166. {
  167. grub_file_close (hashlist);
  168. grub_free (buf);
  169. return grub_error (GRUB_ERR_TEST_FAILURE,
  170. "hash of '%s' mismatches", p);
  171. }
  172. mismatch++;
  173. continue;
  174. }
  175. grub_printf_ (N_("%s: OK\n"), p);
  176. }
  177. if (mismatch || unread)
  178. return grub_error (GRUB_ERR_TEST_FAILURE,
  179. "%d files couldn't be read and hash "
  180. "of %d files mismatches", unread, mismatch);
  181. return GRUB_ERR_NONE;
  182. }
  183. static grub_err_t
  184. grub_cmd_hashsum (struct grub_extcmd_context *ctxt,
  185. int argc, char **args)
  186. {
  187. struct grub_arg_list *state = ctxt->state;
  188. const char *hashname = NULL;
  189. const char *prefix = NULL;
  190. const gcry_md_spec_t *hash;
  191. unsigned i;
  192. int keep = state[3].set;
  193. int uncompress = state[4].set;
  194. unsigned unread = 0;
  195. for (i = 0; i < ARRAY_SIZE (aliases); i++)
  196. if (grub_strcmp (ctxt->extcmd->cmd->name, aliases[i].name) == 0)
  197. hashname = aliases[i].hashname;
  198. if (state[0].set)
  199. hashname = state[0].arg;
  200. if (!hashname)
  201. return grub_error (GRUB_ERR_BAD_ARGUMENT, "no hash specified");
  202. hash = grub_crypto_lookup_md_by_name (hashname);
  203. if (!hash)
  204. return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown hash");
  205. if (hash->mdlen > GRUB_CRYPTO_MAX_MDLEN)
  206. return grub_error (GRUB_ERR_BUG, "mdlen is too long");
  207. if (state[2].set)
  208. prefix = state[2].arg;
  209. if (state[1].set)
  210. {
  211. if (argc != 0)
  212. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  213. "--check is incompatible with file list");
  214. return check_list (hash, state[1].arg, prefix, keep, uncompress);
  215. }
  216. for (i = 0; i < (unsigned) argc; i++)
  217. {
  218. GRUB_PROPERLY_ALIGNED_ARRAY (result, GRUB_CRYPTO_MAX_MDLEN);
  219. grub_file_t file;
  220. grub_err_t err;
  221. unsigned j;
  222. if (!uncompress)
  223. grub_file_filter_disable_compression ();
  224. file = grub_file_open (args[i]);
  225. if (!file)
  226. {
  227. if (!keep)
  228. return grub_errno;
  229. grub_print_error ();
  230. grub_errno = GRUB_ERR_NONE;
  231. unread++;
  232. continue;
  233. }
  234. err = hash_file (file, hash, result);
  235. grub_file_close (file);
  236. if (err)
  237. {
  238. if (!keep)
  239. return err;
  240. grub_print_error ();
  241. grub_errno = GRUB_ERR_NONE;
  242. unread++;
  243. continue;
  244. }
  245. for (j = 0; j < hash->mdlen; j++)
  246. grub_printf ("%02x", ((grub_uint8_t *) result)[j]);
  247. grub_printf (" %s\n", args[i]);
  248. }
  249. if (unread)
  250. return grub_error (GRUB_ERR_TEST_FAILURE, "%d files couldn't be read",
  251. unread);
  252. return GRUB_ERR_NONE;
  253. }
  254. static grub_extcmd_t cmd, cmd_md5, cmd_sha1, cmd_sha256, cmd_sha512, cmd_crc;
  255. GRUB_MOD_INIT(hashsum)
  256. {
  257. cmd = grub_register_extcmd ("hashsum", grub_cmd_hashsum, 0,
  258. N_("-h HASH [-c FILE [-p PREFIX]] "
  259. "[FILE1 [FILE2 ...]]"),
  260. /* TRANSLATORS: "hash checksum" is just to
  261. be a bit more precise, you can treat it as
  262. just "hash". */
  263. N_("Compute or check hash checksum."),
  264. options);
  265. cmd_md5 = grub_register_extcmd ("md5sum", grub_cmd_hashsum, 0,
  266. N_("[-c FILE [-p PREFIX]] "
  267. "[FILE1 [FILE2 ...]]"),
  268. N_("Compute or check hash checksum."),
  269. options);
  270. cmd_sha1 = grub_register_extcmd ("sha1sum", grub_cmd_hashsum, 0,
  271. N_("[-c FILE [-p PREFIX]] "
  272. "[FILE1 [FILE2 ...]]"),
  273. N_("Compute or check hash checksum."),
  274. options);
  275. cmd_sha256 = grub_register_extcmd ("sha256sum", grub_cmd_hashsum, 0,
  276. N_("[-c FILE [-p PREFIX]] "
  277. "[FILE1 [FILE2 ...]]"),
  278. N_("Compute or check hash checksum."),
  279. options);
  280. cmd_sha512 = grub_register_extcmd ("sha512sum", grub_cmd_hashsum, 0,
  281. N_("[-c FILE [-p PREFIX]] "
  282. "[FILE1 [FILE2 ...]]"),
  283. N_("Compute or check hash checksum."),
  284. options);
  285. cmd_crc = grub_register_extcmd ("crc", grub_cmd_hashsum, 0,
  286. N_("[-c FILE [-p PREFIX]] "
  287. "[FILE1 [FILE2 ...]]"),
  288. N_("Compute or check hash checksum."),
  289. options);
  290. }
  291. GRUB_MOD_FINI(hashsum)
  292. {
  293. grub_unregister_extcmd (cmd);
  294. grub_unregister_extcmd (cmd_md5);
  295. grub_unregister_extcmd (cmd_sha1);
  296. grub_unregister_extcmd (cmd_sha256);
  297. grub_unregister_extcmd (cmd_sha512);
  298. grub_unregister_extcmd (cmd_crc);
  299. }