hashsum.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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, GRUB_FILE_TYPE_HASHLIST);
  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. file = grub_file_open (filename, GRUB_FILE_TYPE_TO_HASH
  130. | (!uncompress ? GRUB_FILE_TYPE_NO_DECOMPRESS
  131. : GRUB_FILE_TYPE_NONE));
  132. grub_free (filename);
  133. }
  134. else
  135. file = grub_file_open (p, GRUB_FILE_TYPE_TO_HASH
  136. | (!uncompress ? GRUB_FILE_TYPE_NO_DECOMPRESS
  137. : GRUB_FILE_TYPE_NONE));
  138. if (!file)
  139. {
  140. grub_file_close (hashlist);
  141. grub_free (buf);
  142. return grub_errno;
  143. }
  144. err = hash_file (file, hash, actual);
  145. grub_file_close (file);
  146. if (err)
  147. {
  148. grub_printf_ (N_("%s: READ ERROR\n"), p);
  149. if (!keep)
  150. {
  151. grub_file_close (hashlist);
  152. grub_free (buf);
  153. return err;
  154. }
  155. grub_print_error ();
  156. grub_errno = GRUB_ERR_NONE;
  157. unread++;
  158. continue;
  159. }
  160. if (grub_crypto_memcmp (expected, actual, hash->mdlen) != 0)
  161. {
  162. grub_printf_ (N_("%s: HASH MISMATCH\n"), p);
  163. if (!keep)
  164. {
  165. grub_file_close (hashlist);
  166. grub_free (buf);
  167. return grub_error (GRUB_ERR_TEST_FAILURE,
  168. "hash of '%s' mismatches", p);
  169. }
  170. mismatch++;
  171. continue;
  172. }
  173. grub_printf_ (N_("%s: OK\n"), p);
  174. }
  175. if (mismatch || unread)
  176. return grub_error (GRUB_ERR_TEST_FAILURE,
  177. "%d files couldn't be read and hash "
  178. "of %d files mismatches", unread, mismatch);
  179. return GRUB_ERR_NONE;
  180. }
  181. static grub_err_t
  182. grub_cmd_hashsum (struct grub_extcmd_context *ctxt,
  183. int argc, char **args)
  184. {
  185. struct grub_arg_list *state = ctxt->state;
  186. const char *hashname = NULL;
  187. const char *prefix = NULL;
  188. const gcry_md_spec_t *hash;
  189. unsigned i;
  190. int keep = state[3].set;
  191. int uncompress = state[4].set;
  192. unsigned unread = 0;
  193. for (i = 0; i < ARRAY_SIZE (aliases); i++)
  194. if (grub_strcmp (ctxt->extcmd->cmd->name, aliases[i].name) == 0)
  195. hashname = aliases[i].hashname;
  196. if (state[0].set)
  197. hashname = state[0].arg;
  198. if (!hashname)
  199. return grub_error (GRUB_ERR_BAD_ARGUMENT, "no hash specified");
  200. hash = grub_crypto_lookup_md_by_name (hashname);
  201. if (!hash)
  202. return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown hash");
  203. if (hash->mdlen > GRUB_CRYPTO_MAX_MDLEN)
  204. return grub_error (GRUB_ERR_BUG, "mdlen is too long");
  205. if (state[2].set)
  206. prefix = state[2].arg;
  207. if (state[1].set)
  208. {
  209. if (argc != 0)
  210. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  211. "--check is incompatible with file list");
  212. return check_list (hash, state[1].arg, prefix, keep, uncompress);
  213. }
  214. for (i = 0; i < (unsigned) argc; i++)
  215. {
  216. GRUB_PROPERLY_ALIGNED_ARRAY (result, GRUB_CRYPTO_MAX_MDLEN);
  217. grub_file_t file;
  218. grub_err_t err;
  219. unsigned j;
  220. file = grub_file_open (args[i], GRUB_FILE_TYPE_TO_HASH
  221. | (!uncompress ? GRUB_FILE_TYPE_NO_DECOMPRESS
  222. : GRUB_FILE_TYPE_NONE));
  223. if (!file)
  224. {
  225. if (!keep)
  226. return grub_errno;
  227. grub_print_error ();
  228. grub_errno = GRUB_ERR_NONE;
  229. unread++;
  230. continue;
  231. }
  232. err = hash_file (file, hash, result);
  233. grub_file_close (file);
  234. if (err)
  235. {
  236. if (!keep)
  237. return err;
  238. grub_print_error ();
  239. grub_errno = GRUB_ERR_NONE;
  240. unread++;
  241. continue;
  242. }
  243. for (j = 0; j < hash->mdlen; j++)
  244. grub_printf ("%02x", ((grub_uint8_t *) result)[j]);
  245. grub_printf (" %s\n", args[i]);
  246. }
  247. if (unread)
  248. return grub_error (GRUB_ERR_TEST_FAILURE, "%d files couldn't be read",
  249. unread);
  250. return GRUB_ERR_NONE;
  251. }
  252. static grub_extcmd_t cmd, cmd_md5, cmd_sha1, cmd_sha256, cmd_sha512, cmd_crc;
  253. GRUB_MOD_INIT(hashsum)
  254. {
  255. cmd = grub_register_extcmd ("hashsum", grub_cmd_hashsum, 0,
  256. N_("-h HASH [-c FILE [-p PREFIX]] "
  257. "[FILE1 [FILE2 ...]]"),
  258. /* TRANSLATORS: "hash checksum" is just to
  259. be a bit more precise, you can treat it as
  260. just "hash". */
  261. N_("Compute or check hash checksum."),
  262. options);
  263. cmd_md5 = grub_register_extcmd ("md5sum", grub_cmd_hashsum, 0,
  264. N_("[-c FILE [-p PREFIX]] "
  265. "[FILE1 [FILE2 ...]]"),
  266. N_("Compute or check hash checksum."),
  267. options);
  268. cmd_sha1 = grub_register_extcmd ("sha1sum", grub_cmd_hashsum, 0,
  269. N_("[-c FILE [-p PREFIX]] "
  270. "[FILE1 [FILE2 ...]]"),
  271. N_("Compute or check hash checksum."),
  272. options);
  273. cmd_sha256 = grub_register_extcmd ("sha256sum", grub_cmd_hashsum, 0,
  274. N_("[-c FILE [-p PREFIX]] "
  275. "[FILE1 [FILE2 ...]]"),
  276. N_("Compute or check hash checksum."),
  277. options);
  278. cmd_sha512 = grub_register_extcmd ("sha512sum", grub_cmd_hashsum, 0,
  279. N_("[-c FILE [-p PREFIX]] "
  280. "[FILE1 [FILE2 ...]]"),
  281. N_("Compute or check hash checksum."),
  282. options);
  283. cmd_crc = grub_register_extcmd ("crc", grub_cmd_hashsum, 0,
  284. N_("[-c FILE [-p PREFIX]] "
  285. "[FILE1 [FILE2 ...]]"),
  286. N_("Compute or check hash checksum."),
  287. options);
  288. }
  289. GRUB_MOD_FINI(hashsum)
  290. {
  291. grub_unregister_extcmd (cmd);
  292. grub_unregister_extcmd (cmd_md5);
  293. grub_unregister_extcmd (cmd_sha1);
  294. grub_unregister_extcmd (cmd_sha256);
  295. grub_unregister_extcmd (cmd_sha512);
  296. grub_unregister_extcmd (cmd_crc);
  297. }