hashsum.c 8.8 KB

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