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
);
264 file
= grub_file_open (args
[i
], GRUB_FILE_TYPE_TO_HASH
265 | (!uncompress
? GRUB_FILE_TYPE_NO_DECOMPRESS
266 : GRUB_FILE_TYPE_NONE
));
272 grub_errno
= GRUB_ERR_NONE
;
277 err
= hash_file (file
, hash
, result
);
278 grub_file_close (file
);
284 grub_errno
= GRUB_ERR_NONE
;
288 for (j
= 0; j
< hash
->mdlen
; j
++)
290 grub_printf ("%02x", ((grub_uint8_t
*) result
)[j
]);
291 len
+= grub_snprintf(hashsum
+ len
, sizeof(hashsum
) - len
, "%02x", ((grub_uint8_t
*) result
)[j
]);
293 grub_printf (" %s\n", vlnk
? grub_file_get_vlnk(args
[i
], NULL
) : args
[i
]);
294 grub_env_set("VT_LAST_CHECK_SUM", hashsum
);
298 return grub_error (GRUB_ERR_TEST_FAILURE
, "%d files couldn't be read",
300 return GRUB_ERR_NONE
;
303 static grub_extcmd_t cmd
, cmd_md5
, cmd_sha1
, cmd_sha256
, cmd_sha512
, cmd_crc
;
305 GRUB_MOD_INIT(hashsum
)
307 cmd
= grub_register_extcmd ("hashsum", grub_cmd_hashsum
, 0,
308 N_("-h HASH [-c FILE [-p PREFIX]] "
309 "[FILE1 [FILE2 ...]]"),
310 /* TRANSLATORS: "hash checksum" is just to
311 be a bit more precise, you can treat it as
313 N_("Compute or check hash checksum."),
315 cmd_md5
= grub_register_extcmd ("md5sum", grub_cmd_hashsum
, 0,
316 N_("[-c FILE [-p PREFIX]] "
317 "[FILE1 [FILE2 ...]]"),
318 N_("Compute or check hash checksum."),
320 cmd_sha1
= grub_register_extcmd ("sha1sum", grub_cmd_hashsum
, 0,
321 N_("[-c FILE [-p PREFIX]] "
322 "[FILE1 [FILE2 ...]]"),
323 N_("Compute or check hash checksum."),
325 cmd_sha256
= grub_register_extcmd ("sha256sum", grub_cmd_hashsum
, 0,
326 N_("[-c FILE [-p PREFIX]] "
327 "[FILE1 [FILE2 ...]]"),
328 N_("Compute or check hash checksum."),
330 cmd_sha512
= grub_register_extcmd ("sha512sum", grub_cmd_hashsum
, 0,
331 N_("[-c FILE [-p PREFIX]] "
332 "[FILE1 [FILE2 ...]]"),
333 N_("Compute or check hash checksum."),
336 cmd_crc
= grub_register_extcmd ("crc", grub_cmd_hashsum
, 0,
337 N_("[-c FILE [-p PREFIX]] "
338 "[FILE1 [FILE2 ...]]"),
339 N_("Compute or check hash checksum."),
343 GRUB_MOD_FINI(hashsum
)
345 grub_unregister_extcmd (cmd
);
346 grub_unregister_extcmd (cmd_md5
);
347 grub_unregister_extcmd (cmd_sha1
);
348 grub_unregister_extcmd (cmd_sha256
);
349 grub_unregister_extcmd (cmd_sha512
);
350 grub_unregister_extcmd (cmd_crc
);