From: Arno Wagner Date: Mon, 8 Oct 2012 02:08:18 +0000 (+0200) Subject: added keyslot checker Redesigned to only use public definitions X-Git-Tag: upstream/1.6~169 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;ds=sidebyside;h=1b86b7cb4b208656bab766fc4d780c87521f7571;p=platform%2Fupstream%2Fcryptsetup.git added keyslot checker Redesigned to only use public definitions Signed-off-by: Arno Wagner --- diff --git a/ChangeLog b/ChangeLog index f80214a..b3328f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2012-10-11 Milan Broz + * Added keyslot checker (by Arno Wagner). + 2012-09-11 Milan Broz * Add crypt_keyslot_area() API call. diff --git a/misc/keyslot_checker/Makefile b/misc/keyslot_checker/Makefile new file mode 100644 index 0000000..3b159fd --- /dev/null +++ b/misc/keyslot_checker/Makefile @@ -0,0 +1,14 @@ +TARGETS=chk_luks_keyslots +CFLAGS=-O0 -g -Wall -D_GNU_SOURCE +LDLIBS=-lcryptsetup -lm +CC=gcc + +all: $(TARGETS) + +chk_luks_keyslots: chk_luks_keyslots.o + $(CC) -o $@ $^ $(LDLIBS) + +clean: + rm -f *.o *~ core $(TARGETS) + +.PHONY: clean diff --git a/misc/keyslot_checker/README b/misc/keyslot_checker/README new file mode 100644 index 0000000..a997789 --- /dev/null +++ b/misc/keyslot_checker/README @@ -0,0 +1,118 @@ +Purpose +======= + +chk_luks_keyslots is a tool that searches the keyslot area of a +LUKS container for positions where entropy is low and hence +there is a high probability of damage from overwrites of parts +of the key-slot with data such as a RAID superblock or a partition +table. + + +Installation +============ + +1. Install the version of cryptsetup the tool came with. +2. Compile with + gcc -lm -lcryptsetup chk_luks_keyslots.c -o chk_luks_keyslots + +Usage +===== + +Call chk_luks_keyslots without arguments for an option summary. + + +Example of a good keyslot area: +------------------------------- + +root> ./chk_luks_keyslots /dev/loop0 + +parameters (commandline and LUKS header): + sector size: 512 + threshold: 0.900000 + +- processing keyslot 0: start: 0x001000 end: 0x020400 +- processing keyslot 1: start: 0x021000 end: 0x040400 +- processing keyslot 2: start: 0x041000 end: 0x060400 +- processing keyslot 3: start: 0x061000 end: 0x080400 +- processing keyslot 4: start: 0x081000 end: 0x0a0400 +- processing keyslot 5: start: 0x0a1000 end: 0x0c0400 +- processing keyslot 6: start: 0x0c1000 end: 0x0e0400 +- processing keyslot 7: start: 0x0e1000 end: 0x100400 + + +Example of a fault in slot 8 at offset 0x100200: +----------------------------- + +root>./chk_luks_keyslots /dev/loop2 + +parameters (commandline and LUKS header): + sector size: 512 + threshold: 0.900000 + +- processing keyslot 0: start: 0x001000 end: 0x020400 +- processing keyslot 1: start: 0x021000 end: 0x040400 +- processing keyslot 2: start: 0x041000 end: 0x060400 +- processing keyslot 3: start: 0x061000 end: 0x080400 +- processing keyslot 4: start: 0x081000 end: 0x0a0400 +- processing keyslot 5: start: 0x0a1000 end: 0x0c0400 +- processing keyslot 6: start: 0x0c1000 end: 0x0e0400 +- processing keyslot 7: start: 0x0e1000 end: 0x100400 + low entropy at: 0x100200 entropy: 0.846546 + + +Same as last, but verbose: +-------------------------- +root>./chk_luks_keyslots -v /dev/loop2 + +parameters (commandline and LUKS header): + sector size: 512 + threshold: 0.900000 + +- processing keyslot 0: start: 0x001000 end: 0x020400 +- processing keyslot 1: start: 0x021000 end: 0x040400 +- processing keyslot 2: start: 0x041000 end: 0x060400 +- processing keyslot 3: start: 0x061000 end: 0x080400 +- processing keyslot 4: start: 0x081000 end: 0x0a0400 +- processing keyslot 5: start: 0x0a1000 end: 0x0c0400 +- processing keyslot 6: start: 0x0c1000 end: 0x0e0400 +- processing keyslot 7: start: 0x0e1000 end: 0x100400 + low entropy at: 0x100200 entropy: 0.846546 + Binary dump: + 0x100200 BD 0E C7 A8 7D EF 04 F6 AF 83 DF 74 94 FE 04 56 ....}......t...V + 0x100210 3B 64 BD 68 A9 F6 CF 3C 37 CD 66 B7 17 4D 63 2B ;d.h...<7.f..Mc+ + 0x100220 8F 6E 74 7E 96 7A 2B 27 32 1B F0 80 37 5A 9A 41 .nt~.z+'2...7Z.A + 0x100230 4A 6E CB C0 CF 39 95 45 92 90 E1 0B E6 08 EE 2A Jn...9.E.......* + 0x100240 FA 66 6D 67 49 89 76 B1 41 CD 24 57 AA 65 F7 69 .fmgI.v.A.$W.e.i + 0x100250 33 16 A7 C7 61 3D 43 B7 74 D6 86 83 1D 19 BF 85 3...a=C.t....... + 0x100260 E4 22 3E 16 66 1C B0 1E 11 0D D4 26 37 AD A4 02 .">.f......&7... + 0x100270 40 77 9A 5A B8 40 39 E3 A3 A0 96 08 4D 57 C5 0C @w.Z.@9.....MW.. + 0x100280 D4 74 89 45 FA 93 F7 FE A7 9D D3 99 43 77 8E 35 .t.E........Cw.5 + 0x100290 E0 55 90 3E 91 29 EA DB 5C 13 19 C9 83 CE D8 0C .U.>.)..\....... + 0x1002a0 85 7F 96 26 60 16 A0 0B E1 F9 01 13 1E 59 83 98 ...&`........Y.. + 0x1002b0 06 B5 1D 6F B6 81 9D 60 58 70 15 30 29 42 32 C6 ...o...`Xp.0)B2. + 0x1002c0 A7 55 64 00 65 ED 41 1C B4 C1 C7 10 E1 8E 60 B0 .Ud.e.A.......`. + 0x1002d0 F0 9E 9C 40 5A 84 92 8D 21 F0 B8 2D 61 4E 21 9D ...@Z...!..-aN!. + 0x1002e0 FA B8 18 D3 47 A4 4F D4 AB 73 C0 93 F3 8E 9A 95 ....G.O..s...... + 0x1002f0 A4 F1 6D EB 36 85 F4 F7 62 BA 26 D5 15 57 0D 0C ..m.6...b.&..W.. + 0x100300 C9 4E 19 F2 5B 5A F5 54 B8 F4 B5 57 72 08 1B 7A .N..[Z.T...Wr..z + 0x100310 C3 66 7F 82 1E 75 92 C2 E9 97 64 5E F7 FB A9 05 .f...u....d^.... + 0x100320 CF 30 C8 6A D1 35 9B 9D 22 52 22 46 0E 4B DE 53 .0.j.5.."R"F.K.S + 0x100330 68 C8 DA 5F C7 CA 31 D0 C9 B4 57 CF 0F 1F 4B 9C h.._..1...W...K. + 0x100340 DF 0C F8 7C F2 E3 32 52 3C 0D D2 DC 5C CF F0 00 ...|..2R<...\... + 0x100350 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 XXXXXXXXXXXXXXXX + 0x100360 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 XXXXXXXXXXXXXXXX + 0x100370 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 XXXXXXXXXXXXXXXX + 0x100380 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 XXXXXXXXXXXXXXXX + 0x100390 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 XXXXXXXXXXXXXXXX + 0x1003a0 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 XXXXXXXXXXXXXXXX + 0x1003b0 B4 81 7A F0 BE 38 7E 00 A4 61 41 06 ED 7B 40 D9 ..z..8~..aA..{@. + 0x1003c0 BF 58 51 C9 CD 37 78 4D 4D B3 6E B4 7D 86 3C CB .XQ..7xMM.n.}.<. + 0x1003d0 D5 39 2E FC 78 B1 3E DE C0 7F 55 25 65 71 AD 2A .9..x.>...U%eq.* + 0x1003e0 1E 68 D3 3B 78 17 5F D2 08 93 50 88 D8 0A 75 4F .h.;x._...P...uO + 0x1003f0 E5 AA 26 0F B4 F7 F5 88 65 2B E4 92 18 08 32 9E ..&.....e+....2. + + +---- +Copyright (C) 2012, Arno Wagner +This file is free documentation; the author gives +unlimited permission to copy, distribute and modify it. diff --git a/misc/keyslot_checker/chk_luks_keyslots.c b/misc/keyslot_checker/chk_luks_keyslots.c new file mode 100644 index 0000000..98f8f76 --- /dev/null +++ b/misc/keyslot_checker/chk_luks_keyslots.c @@ -0,0 +1,373 @@ +/* + * LUKS keyslot entropy tester. Works only for header version 1. + * + * Functionality: Determines sample entropy (symbols: bytes) for + * each (by default) 512B sector in each used keyslot. If it + * is lower than a threshold, the sector address is printed + * as it is suspected of having non-"random" data in it, indicating + * damage by overwriting. This can obviously not find overwriting + * with random or random-like data (encrypted, compressed). + * + * Version history: + * v0.1: 09.09.2012 Initial release + * v0.2: 08.10.2012 Converted to use libcryptsetup + * + * Copyright (C) 2012, Arno Wagner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +const char *help = +"Version 0.2 [8.10.2012]\n" +"\n" +" chk_luks_keyslots [options] luks-device \n" +"\n" +"This tool checks all keyslots of a LUKS device for \n" +"low entropy sections. If any are found, they are reported. \n" +"This allows to find areas damaged by things like filesystem \n" +"creation or RAID superblocks. \n" +"\n" +"Options: \n" +" -t Entropy threshold. Possible values 0.0 ... 1.0 \n" +" Default: 0.90, which works well for 512B sectors.\n" +" For 512B sectors, you will get frequent misdetections\n" +" at thresholds around 0.94\n" +" Higher value: more sensitive but more false detections.\n" +" -s Sector size. Must divide keyslot-size.\n" +" Default: 512 Bytes.\n" +" Values smaller than 128 are generally not very useful.\n" +" For values smaller than the default, you need to adjust\n" +" the threshold down to reduce misdetection. For values\n" +" larger than the default you need to adjust the threshold\n" +" up to retain sensitivity.\n" +" -v Print found suspicuous sectors verbosely. \n" +" -d Print decimal addresses instead of hex ones.\n" +"\n"; + + +/* Config defaults */ + +static int sector_size = 512; +static double threshold = 0.90; +static int print_decimal = 0; +static int verbose = 0; + +/* tools */ + +/* Calculates and returns sample entropy on byte level for + * The argument. + */ +static double ent_samp(unsigned char * buf, int len) +{ + int freq[256]; /* stores symbol frequencies */ + int i; + double e, f; + + /* 0. Plausibility checks */ + if (len <= 0) + return 0.0; + + /* 1. count all frequencies */ + for (i = 0; i < 256; i++) { + freq[i] = 0.0; + } + + for (i = 0; i < len; i ++) + freq[buf[i]]++; + + /* 2. calculate sample entropy */ + e = 0.0; + for (i = 0; i < 256; i++) { + f = freq[i]; + if (f > 0) { + f = f / (double)len; + e += f * log2(f); + } + } + + if (e != 0.0) + e = -1.0 * e; + + e = e / 8.0; + return e; +} + +static void print_address(FILE *out, uint64_t value) +{ + if (print_decimal) { + fprintf(out,"%08" PRIu64 " ", value); + } else { + fprintf(out,"%#08" PRIx64 " ", value); + } +} + +/* uses default "hd" style, i.e. 16 bytes followed by ASCII */ +static void hexdump_line(FILE *out, int address, unsigned char *buf) { + int i; + static char tbl[16] = "0123456789ABCDEF"; + + fprintf(out," "); + print_address(out, address); + fprintf(out," "); + + /* hex */ + for (i = 0; i < 16; i++) { + fprintf(out, "%c%c", + tbl[(unsigned char)buf[i]>> 4], + tbl[(unsigned char)buf[i] & 0x0f]); + fprintf(out," "); + if (i == 7) + fprintf(out," "); + } + + fprintf(out," "); + + /* ascii */ + for (i = 0; i < 16; i++) { + if (isprint(buf[i])) { + fprintf(out, "%c", buf[i]); + } else { + fprintf(out, "."); + } + } + fprintf(out, "\n"); +} + +static void hexdump_sector(FILE *out, unsigned char *buf, int address, int len) +{ + int done; + + done = 0; + while (len - done >= 16) { + hexdump_line(out, address + done, buf + done); + done += 16; + } +} + +static int check_keyslots(FILE *out, struct crypt_device *cd, int f_luks, unsigned char *buffer) +{ + int i, ofs; + unsigned j; + double ent; + uint64_t start, length, end; + crypt_keyslot_info ki; + + for (i = 0; i < crypt_keyslot_max(CRYPT_LUKS1) ; i++) { + fprintf(out, "- processing keyslot %d:", i); + ki = crypt_keyslot_status(cd, i); + if (ki == CRYPT_SLOT_INACTIVE) { + fprintf(out, " keyslot not in use\n"); + continue; + } + + if (ki == CRYPT_SLOT_INVALID) { + fprintf(out, "\nError: keyslot invalid.\n"); + return EXIT_FAILURE; + } + + if (crypt_keyslot_area(cd, i, &start, &length) < 0) { + fprintf(stderr,"\nError: querying keyslot area failed for slot %d\n", i); + perror(NULL); + return EXIT_FAILURE; + } + end = start + length; + + fprintf(out, " start: "); + print_address(out, start); + fprintf(out, " end: "); + print_address(out, end); + fprintf(out, "\n"); + + /* check whether sector-size divides size */ + if (length % sector_size != 0) { + fprintf(stderr,"\nError: Argument to -s does not divide keyslot size\n"); + return EXIT_FAILURE; + } + + for (j = start; j < end; j += sector_size) { + ofs = j; + lseek(f_luks, ofs, SEEK_SET); + read(f_luks, buffer, sector_size); + ent = ent_samp(buffer, sector_size); + // printf("slot: %d offset: %8x ent: %f\n", i, ofs, ent); + if (ent < threshold) { + fprintf(out, " low entropy at: "); + print_address(out, ofs); + fprintf(out, " entropy: %f\n", ent); + if (verbose) { + fprintf(out, " Binary dump:\n"); + hexdump_sector(out, buffer, ofs, sector_size); + fprintf(out,"\n"); + } + } + } + } + + return EXIT_SUCCESS; +} + +/* Main */ +int main(int argc, char **argv) +{ + /* for option processing */ + int c, r; + char *device; + unsigned char *buffer; + + /* for use of libcryptsetup */ + struct crypt_device *cd; + + /* Other vars */ + int f_luks; /* device file for the luks device */ + FILE *out; + + /* temporary helper vars */ + int res; + + /* getopt values */ + char *s, *end; + double tvalue; + int svalue; + + /* global initializations */ + out = stdout; + + /* get commandline parameters */ + while ((c = getopt (argc, argv, "t:s:vd")) != -1) { + switch (c) { + case 't': + s = optarg; + tvalue = strtod(s, &end); + if (s == end) { + fprintf(stderr, "\nError: Parsing of argument to -t failed.\n"); + exit(EXIT_FAILURE); + } + + if (tvalue < 0.0 || tvalue > 1.0) { + fprintf(stderr,"\nError: Argument to -t must be in 0.0 ... 1.0\n"); + exit(EXIT_FAILURE); + } + threshold = tvalue; + break; + case 's': + s = optarg; + svalue = strtol(s, &end, 10); + if (s == end) { + fprintf(stderr, "\nError: Parsing of argument to -s failed.\n"); + exit(EXIT_FAILURE); + } + + if (svalue < 1) { + fprintf(stderr,"\nError: Argument to -s must be >= 1 \n"); + exit(EXIT_FAILURE); + } + sector_size = svalue; + break; + case 'v': + verbose = 1; + break; + case 'd': + print_decimal = 1; + break; + case '?': + if (optopt == 't' || optopt == 's') + fprintf (stderr,"\nError: Option -%c requires an argument.\n", + optopt); + else if (isprint (optopt)) { + fprintf(stderr,"\nError: Unknown option `-%c'.\n", optopt); + fprintf(stderr,"\n\n%s", help); + } else { + fprintf (stderr, "\nError: Unknown option character `\\x%x'.\n", + optopt); + fprintf(stderr,"\n\n%s", help); + } + exit(EXIT_SUCCESS); + default: + exit(EXIT_FAILURE); + } + } + + /* parse non-option stuff. Should be exactly one, the device. */ + if (optind+1 != argc) { + fprintf(stderr,"\nError: exactly one non-option argument expected!\n"); + fprintf(stderr,"\n\n%s", help); + exit(EXIT_FAILURE); + } + device = argv[optind]; + + buffer = calloc(sector_size, 1); + if (!buffer) { + fprintf(stderr,"\nError: Cannot allocate buffer.\n"); + exit(EXIT_FAILURE); + } + + /* test whether we can open and read device */ + /* This is neded as we are reading the actual data + * in the keyslots dirtectly from the LUKS container. + */ + f_luks = open(device, O_RDONLY); + if (f_luks == -1) { + fprintf(stderr,"\nError: Opening of device %s failed:\n", device); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* now get the parameters we need via libcryptsetup */ + /* Basically we need all active keyslots and their placement on disk */ + + /* first init. This does the following: + * - gets us a crypt_device struct with some values filled in + * Note: This does some init stuff we do not need, but that + * should not cause trouble. + */ + + res = crypt_init(&cd, device); + if (res < 0) { + fprintf(stderr, "crypt_init() failed. Maybe not running as root?\n"); + close(f_luks); + exit(EXIT_FAILURE); + } + + /* now load LUKS header into the crypt_device + * This should also make sure a valid LUKS1 header is on disk + * and hence we should be able to skip magic and version checks. + */ + res = crypt_load(cd, CRYPT_LUKS1, NULL); + if (res < 0) { + fprintf(stderr, "crypt_load() failed. LUKS header too broken/absent?\n"); + crypt_free(cd); + close(f_luks); + exit(EXIT_FAILURE); + } + + fprintf(out, "\nparameters (commandline and LUKS header):\n"); + fprintf(out, " sector size: %d\n", sector_size); + fprintf(out, " threshold: %0f\n\n", threshold); + + r = check_keyslots(out, cd, f_luks, buffer); + + crypt_free(cd); + close(f_luks); + free(buffer); + return r; +}