/* * VasEBoot -- GRand Unified Bootloader * Copyright (C) 2009 Free Software Foundation, Inc. * * VasEBoot is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VasEBoot is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VasEBoot. If not, see . */ #include #include #include #include #include #include #include #include #include VasEBoot_MOD_LICENSE ("GPLv3+"); static const struct VasEBoot_arg_option options[] = { {"hash", 'h', 0, N_("Specify hash to use."), N_("HASH"), ARG_TYPE_STRING}, {"check", 'c', 0, N_("Check hashes of files with hash list FILE."), N_("FILE"), ARG_TYPE_STRING}, {"prefix", 'p', 0, N_("Base directory for hash list."), N_("DIR"), ARG_TYPE_STRING}, {"keep-going", 'k', 0, N_("Don't stop after first error."), 0, 0}, {"uncompress", 'u', 0, N_("Uncompress file before checksumming."), 0, 0}, {0, 0, 0, 0, 0, 0} }; static struct { const char *name; const char *hashname; } aliases[] = { {"sha256sum", "sha256"}, {"sha512sum", "sha512"}, {"sha1sum", "sha1"}, {"md5sum", "md5"}, {"crc", "crc32"}, }; static inline int hextoval (char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; } static VasEBoot_err_t hash_file (VasEBoot_file_t file, const gcry_md_spec_t *hash, void *result) { void *context; VasEBoot_uint8_t *readbuf; #define BUF_SIZE 4096 readbuf = VasEBoot_malloc (BUF_SIZE); if (!readbuf) return VasEBoot_errno; context = VasEBoot_zalloc (hash->contextsize); if (!readbuf || !context) goto fail; hash->init (context); while (1) { VasEBoot_ssize_t r; r = VasEBoot_file_read (file, readbuf, BUF_SIZE); if (r < 0) goto fail; if (r == 0) break; hash->write (context, readbuf, r); } hash->final (context); VasEBoot_memcpy (result, hash->read (context), hash->mdlen); VasEBoot_free (readbuf); VasEBoot_free (context); return VasEBoot_ERR_NONE; fail: VasEBoot_free (readbuf); VasEBoot_free (context); return VasEBoot_errno; } static VasEBoot_err_t check_list (const gcry_md_spec_t *hash, const char *hashfilename, const char *prefix, int keep, int uncompress) { VasEBoot_file_t hashlist, file; char *buf = NULL; VasEBoot_uint8_t expected[VasEBoot_CRYPTO_MAX_MDLEN]; VasEBoot_uint8_t actual[VasEBoot_CRYPTO_MAX_MDLEN]; VasEBoot_err_t err; unsigned i; unsigned unread = 0, mismatch = 0; if (hash->mdlen > VasEBoot_CRYPTO_MAX_MDLEN) return VasEBoot_error (VasEBoot_ERR_BUG, "mdlen is too long"); hashlist = VasEBoot_file_open (hashfilename); if (!hashlist) return VasEBoot_errno; while (VasEBoot_free (buf), (buf = VasEBoot_file_getline (hashlist))) { const char *p = buf; while (VasEBoot_isspace (p[0])) p++; for (i = 0; i < hash->mdlen; i++) { int high, low; high = hextoval (*p++); low = hextoval (*p++); if (high < 0 || low < 0) return VasEBoot_error (VasEBoot_ERR_BAD_FILE_TYPE, "invalid hash list"); expected[i] = (high << 4) | low; } if ((p[0] != ' ' && p[0] != '\t') || (p[1] != ' ' && p[1] != '\t')) return VasEBoot_error (VasEBoot_ERR_BAD_FILE_TYPE, "invalid hash list"); p += 2; if (prefix) { char *filename; filename = VasEBoot_xasprintf ("%s/%s", prefix, p); if (!filename) return VasEBoot_errno; if (!uncompress) VasEBoot_file_filter_disable_compression (); file = VasEBoot_file_open (filename); VasEBoot_free (filename); } else { if (!uncompress) VasEBoot_file_filter_disable_compression (); file = VasEBoot_file_open (p); } if (!file) { VasEBoot_file_close (hashlist); VasEBoot_free (buf); return VasEBoot_errno; } err = hash_file (file, hash, actual); VasEBoot_file_close (file); if (err) { VasEBoot_printf_ (N_("%s: READ ERROR\n"), p); if (!keep) { VasEBoot_file_close (hashlist); VasEBoot_free (buf); return err; } VasEBoot_print_error (); VasEBoot_errno = VasEBoot_ERR_NONE; unread++; continue; } if (VasEBoot_crypto_memcmp (expected, actual, hash->mdlen) != 0) { VasEBoot_printf_ (N_("%s: HASH MISMATCH\n"), p); if (!keep) { VasEBoot_file_close (hashlist); VasEBoot_free (buf); return VasEBoot_error (VasEBoot_ERR_TEST_FAILURE, "hash of '%s' mismatches", p); } mismatch++; continue; } VasEBoot_printf_ (N_("%s: OK\n"), p); } if (mismatch || unread) return VasEBoot_error (VasEBoot_ERR_TEST_FAILURE, "%d files couldn't be read and hash " "of %d files mismatches", unread, mismatch); return VasEBoot_ERR_NONE; } static VasEBoot_err_t VasEBoot_cmd_hashsum (struct VasEBoot_extcmd_context *ctxt, int argc, char **args) { struct VasEBoot_arg_list *state = ctxt->state; const char *hashname = NULL; const char *prefix = NULL; const gcry_md_spec_t *hash; unsigned i; int keep = state[3].set; int uncompress = state[4].set; unsigned unread = 0; for (i = 0; i < ARRAY_SIZE (aliases); i++) if (VasEBoot_strcmp (ctxt->extcmd->cmd->name, aliases[i].name) == 0) hashname = aliases[i].hashname; if (state[0].set) hashname = state[0].arg; if (!hashname) return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, "no hash specified"); hash = VasEBoot_crypto_lookup_md_by_name (hashname); if (!hash) return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, "unknown hash"); if (hash->mdlen > VasEBoot_CRYPTO_MAX_MDLEN) return VasEBoot_error (VasEBoot_ERR_BUG, "mdlen is too long"); if (state[2].set) prefix = state[2].arg; if (state[1].set) { if (argc != 0) return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, "--check is incompatible with file list"); return check_list (hash, state[1].arg, prefix, keep, uncompress); } for (i = 0; i < (unsigned) argc; i++) { VasEBoot_PROPERLY_ALIGNED_ARRAY (result, VasEBoot_CRYPTO_MAX_MDLEN); VasEBoot_file_t file; VasEBoot_err_t err; unsigned j; if (!uncompress) VasEBoot_file_filter_disable_compression (); file = VasEBoot_file_open (args[i]); if (!file) { if (!keep) return VasEBoot_errno; VasEBoot_print_error (); VasEBoot_errno = VasEBoot_ERR_NONE; unread++; continue; } err = hash_file (file, hash, result); VasEBoot_file_close (file); if (err) { if (!keep) return err; VasEBoot_print_error (); VasEBoot_errno = VasEBoot_ERR_NONE; unread++; continue; } for (j = 0; j < hash->mdlen; j++) VasEBoot_printf ("%02x", ((VasEBoot_uint8_t *) result)[j]); VasEBoot_printf (" %s\n", args[i]); } if (unread) return VasEBoot_error (VasEBoot_ERR_TEST_FAILURE, "%d files couldn't be read", unread); return VasEBoot_ERR_NONE; } static VasEBoot_extcmd_t cmd, cmd_md5, cmd_sha1, cmd_sha256, cmd_sha512, cmd_crc; VasEBoot_MOD_INIT(hashsum) { cmd = VasEBoot_register_extcmd ("hashsum", VasEBoot_cmd_hashsum, 0, N_("-h HASH [-c FILE [-p PREFIX]] " "[FILE1 [FILE2 ...]]"), /* TRANSLATORS: "hash checksum" is just to be a bit more precise, you can treat it as just "hash". */ N_("Compute or check hash checksum."), options); cmd_md5 = VasEBoot_register_extcmd ("md5sum", VasEBoot_cmd_hashsum, 0, N_("[-c FILE [-p PREFIX]] " "[FILE1 [FILE2 ...]]"), N_("Compute or check hash checksum."), options); cmd_sha1 = VasEBoot_register_extcmd ("sha1sum", VasEBoot_cmd_hashsum, 0, N_("[-c FILE [-p PREFIX]] " "[FILE1 [FILE2 ...]]"), N_("Compute or check hash checksum."), options); cmd_sha256 = VasEBoot_register_extcmd ("sha256sum", VasEBoot_cmd_hashsum, 0, N_("[-c FILE [-p PREFIX]] " "[FILE1 [FILE2 ...]]"), N_("Compute or check hash checksum."), options); cmd_sha512 = VasEBoot_register_extcmd ("sha512sum", VasEBoot_cmd_hashsum, 0, N_("[-c FILE [-p PREFIX]] " "[FILE1 [FILE2 ...]]"), N_("Compute or check hash checksum."), options); cmd_crc = VasEBoot_register_extcmd ("crc", VasEBoot_cmd_hashsum, 0, N_("[-c FILE [-p PREFIX]] " "[FILE1 [FILE2 ...]]"), N_("Compute or check hash checksum."), options); } VasEBoot_MOD_FINI(hashsum) { VasEBoot_unregister_extcmd (cmd); VasEBoot_unregister_extcmd (cmd_md5); VasEBoot_unregister_extcmd (cmd_sha1); VasEBoot_unregister_extcmd (cmd_sha256); VasEBoot_unregister_extcmd (cmd_sha512); VasEBoot_unregister_extcmd (cmd_crc); }