added keyslot checker Redesigned to only use public definitions
authorArno Wagner <wagner.arno@gmail.com>
Mon, 8 Oct 2012 02:08:18 +0000 (04:08 +0200)
committerMilan Broz <gmazyland@gmail.com>
Thu, 11 Oct 2012 16:07:35 +0000 (18:07 +0200)
Signed-off-by: Arno Wagner <wagner.arno@gmail.com>
ChangeLog
misc/keyslot_checker/Makefile [new file with mode: 0644]
misc/keyslot_checker/README [new file with mode: 0644]
misc/keyslot_checker/chk_luks_keyslots.c [new file with mode: 0644]

index f80214a..b3328f6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+2012-10-11  Milan Broz  <gmazyland@gmail.com>
+       * Added keyslot checker (by Arno Wagner).
+
 2012-09-11  Milan Broz  <gmazyland@gmail.com>
        * Add crypt_keyslot_area() API call.
 
diff --git a/misc/keyslot_checker/Makefile b/misc/keyslot_checker/Makefile
new file mode 100644 (file)
index 0000000..3b159fd
--- /dev/null
@@ -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 (file)
index 0000000..a997789
--- /dev/null
@@ -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 <arno@wagner.name>
+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 (file)
index 0000000..98f8f76
--- /dev/null
@@ -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 <arno@wagner.name>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <math.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libcryptsetup.h>
+
+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 <num>  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 <num>  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;
+}