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