2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2009 Free Software Foundation, Inc.
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.
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.
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/>.
20 #include <grub/extcmd.h>
21 #include <grub/file.h>
22 #include <grub/disk.h>
24 #include <grub/misc.h>
25 #include <grub/crypto.h>
26 #include <grub/normal.h>
27 #include <grub/i18n.h>
29 GRUB_MOD_LICENSE ("GPLv3+");
31 static const struct grub_arg_option options
[] = {
32 {"hash", 'h', 0, N_("Specify hash to use."), N_("HASH"), ARG_TYPE_STRING
},
33 {"check", 'c', 0, N_("Check hashes of files with hash list FILE."),
34 N_("FILE"), ARG_TYPE_STRING
},
35 {"prefix", 'p', 0, N_("Base directory for hash list."), N_("DIR"),
37 {"keep-going", 'k', 0, N_("Don't stop after first error."), 0, 0},
38 {"uncompress", 'u', 0, N_("Uncompress file before checksumming."), 0, 0},
42 static struct { const char *name
; const char *hashname
; } aliases
[] =
44 {"sha256sum", "sha256"},
45 {"sha512sum", "sha512"},
54 if (c
>= '0' && c
<= '9')
56 if (c
>= 'a' && c
<= 'f')
58 if (c
>= 'A' && c
<= 'F')
64 hash_file (grub_file_t file
, const gcry_md_spec_t
*hash
, void *result
)
68 grub_uint64_t div
= 0;
69 grub_uint64_t total
= 0;
71 grub_uint8_t
*readbuf
;
72 #define BUF_SIZE 1024 * 1024
73 readbuf
= grub_malloc (BUF_SIZE
);
76 context
= grub_zalloc (hash
->contextsize
);
77 if (!readbuf
|| !context
)
80 if (file
->size
> 16 * 1024 * 1024)
87 r
= grub_file_read (file
, readbuf
, BUF_SIZE
);
92 hash
->write (context
, readbuf
, r
);
96 div
= grub_divmod64(total
* 100, (grub_uint64_t
)file
->size
, &ro
);
97 grub_printf("\rCalculating %s %d%% ", hash
->name
, (int)div
);
101 hash
->final (context
);
102 grub_memcpy (result
, hash
->read (context
), hash
->mdlen
);
108 grub_printf("\rCalculating %s 100%% \n\r\n", hash
->name
);
111 return GRUB_ERR_NONE
;
120 check_list (const gcry_md_spec_t
*hash
, const char *hashfilename
,
121 const char *prefix
, int keep
, int uncompress
)
123 grub_file_t hashlist
, file
;
125 grub_uint8_t expected
[GRUB_CRYPTO_MAX_MDLEN
];
126 grub_uint8_t actual
[GRUB_CRYPTO_MAX_MDLEN
];
129 unsigned unread
= 0, mismatch
= 0;
131 if (hash
->mdlen
> GRUB_CRYPTO_MAX_MDLEN
)
132 return grub_error (GRUB_ERR_BUG
, "mdlen is too long");
134 hashlist
= grub_file_open (hashfilename
, GRUB_FILE_TYPE_HASHLIST
);
138 while (grub_free (buf
), (buf
= grub_file_getline (hashlist
)))
141 while (grub_isspace (p
[0]))
143 for (i
= 0; i
< hash
->mdlen
; i
++)
146 high
= hextoval (*p
++);
147 low
= hextoval (*p
++);
148 if (high
< 0 || low
< 0)
149 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "invalid hash list");
150 expected
[i
] = (high
<< 4) | low
;
152 if ((p
[0] != ' ' && p
[0] != '\t') || (p
[1] != ' ' && p
[1] != '\t'))
153 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "invalid hash list");
159 filename
= grub_xasprintf ("%s/%s", prefix
, p
);
162 file
= grub_file_open (filename
, GRUB_FILE_TYPE_TO_HASH
163 | (!uncompress
? GRUB_FILE_TYPE_NO_DECOMPRESS
164 : GRUB_FILE_TYPE_NONE
));
165 grub_free (filename
);
168 file
= grub_file_open (p
, GRUB_FILE_TYPE_TO_HASH
169 | (!uncompress
? GRUB_FILE_TYPE_NO_DECOMPRESS
170 : GRUB_FILE_TYPE_NONE
));
173 grub_file_close (hashlist
);
177 err
= hash_file (file
, hash
, actual
);
178 grub_file_close (file
);
181 grub_printf_ (N_("%s: READ ERROR\n"), p
);
184 grub_file_close (hashlist
);
189 grub_errno
= GRUB_ERR_NONE
;
193 if (grub_crypto_memcmp (expected
, actual
, hash
->mdlen
) != 0)
195 grub_printf_ (N_("%s: HASH MISMATCH\n"), p
);
198 grub_file_close (hashlist
);
200 return grub_error (GRUB_ERR_TEST_FAILURE
,
201 "hash of '%s' mismatches", p
);
206 grub_printf_ (N_("%s: OK\n"), p
);
208 if (mismatch
|| unread
)
209 return grub_error (GRUB_ERR_TEST_FAILURE
,
210 "%d files couldn't be read and hash "
211 "of %d files mismatches", unread
, mismatch
);
212 return GRUB_ERR_NONE
;
216 grub_cmd_hashsum (struct grub_extcmd_context
*ctxt
,
217 int argc
, char **args
)
219 struct grub_arg_list
*state
= ctxt
->state
;
220 const char *hashname
= NULL
;
221 const char *prefix
= NULL
;
222 const gcry_md_spec_t
*hash
;
224 int keep
= state
[3].set
;
225 int uncompress
= state
[4].set
;
230 for (i
= 0; i
< ARRAY_SIZE (aliases
); i
++)
231 if (grub_strcmp (ctxt
->extcmd
->cmd
->name
, aliases
[i
].name
) == 0)
232 hashname
= aliases
[i
].hashname
;
234 hashname
= state
[0].arg
;
237 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "no hash specified");
239 hash
= grub_crypto_lookup_md_by_name (hashname
);
241 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "unknown hash");
243 if (hash
->mdlen
> GRUB_CRYPTO_MAX_MDLEN
)
244 return grub_error (GRUB_ERR_BUG
, "mdlen is too long");
247 prefix
= state
[2].arg
;
252 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
253 "--check is incompatible with file list");
254 return check_list (hash
, state
[1].arg
, prefix
, keep
, uncompress
);
257 for (i
= 0; i
< (unsigned) argc
; i
++)
259 GRUB_PROPERLY_ALIGNED_ARRAY (result
, GRUB_CRYPTO_MAX_MDLEN
);
263 file
= grub_file_open (args
[i
], GRUB_FILE_TYPE_TO_HASH
264 | (!uncompress
? GRUB_FILE_TYPE_NO_DECOMPRESS
265 : GRUB_FILE_TYPE_NONE
));
271 grub_errno
= GRUB_ERR_NONE
;
275 err
= hash_file (file
, hash
, result
);
276 grub_file_close (file
);
282 grub_errno
= GRUB_ERR_NONE
;
286 for (j
= 0; j
< hash
->mdlen
; j
++)
288 grub_printf ("%02x", ((grub_uint8_t
*) result
)[j
]);
289 len
+= grub_snprintf(hashsum
+ len
, sizeof(hashsum
) - len
, "%02x", ((grub_uint8_t
*) result
)[j
]);
291 grub_printf (" %s\n", args
[i
]);
292 grub_env_set("VT_LAST_CHECK_SUM", hashsum
);
296 return grub_error (GRUB_ERR_TEST_FAILURE
, "%d files couldn't be read",
298 return GRUB_ERR_NONE
;
301 static grub_extcmd_t cmd
, cmd_md5
, cmd_sha1
, cmd_sha256
, cmd_sha512
, cmd_crc
;
303 GRUB_MOD_INIT(hashsum
)
305 cmd
= grub_register_extcmd ("hashsum", grub_cmd_hashsum
, 0,
306 N_("-h HASH [-c FILE [-p PREFIX]] "
307 "[FILE1 [FILE2 ...]]"),
308 /* TRANSLATORS: "hash checksum" is just to
309 be a bit more precise, you can treat it as
311 N_("Compute or check hash checksum."),
313 cmd_md5
= grub_register_extcmd ("md5sum", grub_cmd_hashsum
, 0,
314 N_("[-c FILE [-p PREFIX]] "
315 "[FILE1 [FILE2 ...]]"),
316 N_("Compute or check hash checksum."),
318 cmd_sha1
= grub_register_extcmd ("sha1sum", grub_cmd_hashsum
, 0,
319 N_("[-c FILE [-p PREFIX]] "
320 "[FILE1 [FILE2 ...]]"),
321 N_("Compute or check hash checksum."),
323 cmd_sha256
= grub_register_extcmd ("sha256sum", grub_cmd_hashsum
, 0,
324 N_("[-c FILE [-p PREFIX]] "
325 "[FILE1 [FILE2 ...]]"),
326 N_("Compute or check hash checksum."),
328 cmd_sha512
= grub_register_extcmd ("sha512sum", grub_cmd_hashsum
, 0,
329 N_("[-c FILE [-p PREFIX]] "
330 "[FILE1 [FILE2 ...]]"),
331 N_("Compute or check hash checksum."),
334 cmd_crc
= grub_register_extcmd ("crc", grub_cmd_hashsum
, 0,
335 N_("[-c FILE [-p PREFIX]] "
336 "[FILE1 [FILE2 ...]]"),
337 N_("Compute or check hash checksum."),
341 GRUB_MOD_FINI(hashsum
)
343 grub_unregister_extcmd (cmd
);
344 grub_unregister_extcmd (cmd_md5
);
345 grub_unregister_extcmd (cmd_sha1
);
346 grub_unregister_extcmd (cmd_sha256
);
347 grub_unregister_extcmd (cmd_sha512
);
348 grub_unregister_extcmd (cmd_crc
);