added keyslot checker Redesigned to only use public definitions
[platform/upstream/cryptsetup.git] / misc / keyslot_checker / chk_luks_keyslots.c
1 /*
2  * LUKS keyslot entropy tester. Works only for header version 1.
3  *
4  * Functionality: Determines sample entropy (symbols: bytes) for
5  * each (by default) 512B sector in each used keyslot. If it
6  * is lower than a threshold, the sector address is printed
7  * as it is suspected of having non-"random" data in it, indicating
8  * damage by overwriting. This can obviously not find overwriting
9  * with random or random-like data (encrypted, compressed).
10  *
11  * Version history:
12  *    v0.1: 09.09.2012 Initial release
13  *    v0.2: 08.10.2012 Converted to use libcryptsetup
14  *
15  * Copyright (C) 2012, Arno Wagner <arno@wagner.name>
16  *
17  * This program is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU General Public License
19  * version 2 as published by the Free Software Foundation.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29  */
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <ctype.h>
35 #include <math.h>
36 #include <fcntl.h>
37 #include <inttypes.h>
38 #include <libcryptsetup.h>
39
40 const char *help =
41 "Version 0.2 [8.10.2012]\n"
42 "\n"
43 "    chk_luks_keyslots [options] luks-device \n"
44 "\n"
45 "This tool checks all keyslots of a LUKS device for \n"
46 "low entropy sections. If any are found, they are reported. \n"
47 "This allows to find areas damaged by things like filesystem \n"
48 "creation or RAID superblocks. \n"
49 "\n"
50 "Options: \n"
51 "  -t <num>  Entropy threshold. Possible values 0.0 ... 1.0 \n"
52 "            Default: 0.90, which works well for 512B sectors.\n"
53 "            For 512B sectors, you will get frequent misdetections\n"
54 "            at thresholds around 0.94\n"
55 "            Higher value: more sensitive but more false detections.\n"
56 "  -s <num>  Sector size. Must divide keyslot-size.\n"
57 "            Default: 512 Bytes.\n"
58 "            Values smaller than 128 are generally not very useful.\n"
59 "            For values smaller than the default, you need to adjust\n"
60 "            the threshold down to reduce misdetection. For values\n"
61 "            larger than the default you need to adjust the threshold\n"
62 "            up to retain sensitivity.\n"
63 "  -v        Print found suspicuous sectors verbosely. \n"
64 "  -d        Print decimal addresses instead of hex ones.\n"
65 "\n";
66
67
68 /* Config defaults */
69
70 static int sector_size = 512;
71 static double threshold = 0.90;
72 static int print_decimal = 0;
73 static int verbose = 0;
74
75 /* tools */
76
77 /* Calculates and returns sample entropy on byte level for
78  * The argument.
79  */
80 static double ent_samp(unsigned char * buf, int len)
81 {
82         int freq[256];   /* stores symbol frequencies */
83         int i;
84         double e, f;
85
86         /* 0. Plausibility checks */
87         if (len <= 0)
88                 return 0.0;
89
90         /* 1. count all frequencies */
91         for (i = 0; i < 256; i++) {
92                 freq[i] = 0.0;
93         }
94
95         for (i = 0; i < len; i ++)
96                 freq[buf[i]]++;
97
98         /* 2. calculate sample entropy */
99         e = 0.0;
100         for (i = 0; i < 256; i++) {
101                 f = freq[i];
102                 if (f > 0) {
103                         f =  f / (double)len;
104                         e += f * log2(f);
105                 }
106         }
107
108         if (e != 0.0)
109                 e = -1.0 * e;
110
111         e = e / 8.0;
112         return e;
113 }
114
115 static void print_address(FILE *out, uint64_t value)
116 {
117         if (print_decimal) {
118                 fprintf(out,"%08" PRIu64 " ", value);
119         } else {
120                 fprintf(out,"%#08" PRIx64 " ", value);
121         }
122 }
123
124 /* uses default "hd" style, i.e. 16 bytes followed by ASCII */
125 static void hexdump_line(FILE *out, int address, unsigned char *buf) {
126         int i;
127         static char tbl[16] = "0123456789ABCDEF";
128
129         fprintf(out,"  ");
130         print_address(out, address);
131         fprintf(out," ");
132
133         /* hex */
134         for (i = 0; i < 16; i++) {
135                 fprintf(out, "%c%c",
136                         tbl[(unsigned char)buf[i]>> 4],
137                         tbl[(unsigned char)buf[i] & 0x0f]);
138                 fprintf(out," ");
139                 if (i == 7)
140                         fprintf(out," ");
141         }
142
143         fprintf(out," ");
144
145         /* ascii */
146         for (i = 0; i < 16; i++) {
147                 if (isprint(buf[i])) {
148                         fprintf(out, "%c", buf[i]);
149                 } else {
150                         fprintf(out, ".");
151                 }
152         }
153         fprintf(out, "\n");
154 }
155
156 static void hexdump_sector(FILE *out, unsigned char *buf, int address, int len)
157 {
158         int done;
159
160         done = 0;
161         while (len - done >= 16) {
162                 hexdump_line(out, address + done, buf + done);
163                 done += 16;
164         }
165 }
166
167 static int check_keyslots(FILE *out, struct crypt_device *cd, int f_luks, unsigned char *buffer)
168 {
169         int i, ofs;
170         unsigned j;
171         double ent;
172         uint64_t start, length, end;
173         crypt_keyslot_info ki;
174
175         for (i = 0; i < crypt_keyslot_max(CRYPT_LUKS1) ; i++) {
176                 fprintf(out, "- processing keyslot %d:", i);
177                 ki = crypt_keyslot_status(cd, i);
178                 if (ki == CRYPT_SLOT_INACTIVE) {
179                         fprintf(out, "  keyslot not in use\n");
180                         continue;
181                 }
182
183                 if (ki == CRYPT_SLOT_INVALID) {
184                         fprintf(out, "\nError: keyslot invalid.\n");
185                         return EXIT_FAILURE;
186                 }
187
188                 if (crypt_keyslot_area(cd, i, &start, &length) < 0) {
189                         fprintf(stderr,"\nError: querying keyslot area failed for slot %d\n", i);
190                         perror(NULL);
191                         return EXIT_FAILURE;
192                 }
193                 end = start + length;
194
195                 fprintf(out, "  start: ");
196                 print_address(out, start);
197                 fprintf(out, "  end: ");
198                 print_address(out, end);
199                 fprintf(out, "\n");
200
201                 /* check whether sector-size divides size */
202                 if (length % sector_size != 0) {
203                         fprintf(stderr,"\nError: Argument to -s does not divide keyslot size\n");
204                         return EXIT_FAILURE;
205                 }
206
207                 for (j = start; j < end; j += sector_size) {
208                         ofs = j;
209                         lseek(f_luks, ofs, SEEK_SET);
210                         read(f_luks, buffer, sector_size);
211                         ent = ent_samp(buffer, sector_size);
212                         // printf("slot: %d  offset: %8x  ent: %f\n", i, ofs, ent);
213                         if (ent < threshold) {
214                                 fprintf(out, "  low entropy at: ");
215                                 print_address(out, ofs);
216                                 fprintf(out, "   entropy: %f\n", ent);
217                                 if (verbose) {
218                                         fprintf(out, "  Binary dump:\n");
219                                         hexdump_sector(out, buffer, ofs, sector_size);
220                                         fprintf(out,"\n");
221                                 }
222                         }
223                 }
224         }
225
226         return EXIT_SUCCESS;
227 }
228
229 /* Main */
230 int main(int argc, char **argv)
231 {
232         /* for option processing */
233         int c, r;
234         char *device;
235         unsigned char *buffer;
236
237         /* for use of libcryptsetup */
238         struct crypt_device *cd;
239
240         /* Other vars */
241         int f_luks;   /* device file for the luks device */
242         FILE *out;
243
244         /* temporary helper vars */
245         int res;
246
247         /* getopt values */
248         char *s, *end;
249         double tvalue;
250         int svalue;
251
252         /* global initializations */
253         out = stdout;
254
255         /* get commandline parameters */
256         while ((c = getopt (argc, argv, "t:s:vd")) != -1) {
257                 switch (c) {
258                 case 't':
259                         s = optarg;
260                         tvalue = strtod(s, &end);
261                         if (s == end) {
262                                 fprintf(stderr, "\nError: Parsing of argument to -t failed.\n");
263                                 exit(EXIT_FAILURE);
264                         }
265
266                         if (tvalue < 0.0 || tvalue > 1.0) {
267                                 fprintf(stderr,"\nError: Argument to -t must be in 0.0 ... 1.0\n");
268                                 exit(EXIT_FAILURE);
269                         }
270                         threshold = tvalue;
271                         break;
272                 case 's':
273                         s = optarg;
274                         svalue = strtol(s, &end, 10);
275                         if (s == end) {
276                                 fprintf(stderr, "\nError: Parsing of argument to -s failed.\n");
277                                 exit(EXIT_FAILURE);
278                         }
279
280                         if (svalue < 1) {
281                                 fprintf(stderr,"\nError: Argument to -s must be >= 1 \n");
282                                 exit(EXIT_FAILURE);
283                         }
284                         sector_size = svalue;
285                         break;
286                 case 'v':
287                         verbose = 1;
288                         break;
289                 case 'd':
290                         print_decimal = 1;
291                         break;
292                 case '?':
293                         if (optopt == 't' || optopt == 's')
294                                 fprintf (stderr,"\nError: Option -%c requires an argument.\n",
295                                          optopt);
296                         else if (isprint (optopt)) {
297                                 fprintf(stderr,"\nError: Unknown option `-%c'.\n", optopt);
298                                 fprintf(stderr,"\n\n%s", help);
299                         } else {
300                                 fprintf (stderr, "\nError: Unknown option character `\\x%x'.\n",
301                                          optopt);
302                                 fprintf(stderr,"\n\n%s", help);
303                         }
304                         exit(EXIT_SUCCESS);
305                 default:
306                         exit(EXIT_FAILURE);
307                 }
308         }
309
310         /* parse non-option stuff. Should be exactly one, the device. */
311         if (optind+1 != argc) {
312                 fprintf(stderr,"\nError: exactly one non-option argument expected!\n");
313                 fprintf(stderr,"\n\n%s", help);
314                 exit(EXIT_FAILURE);
315         }
316         device = argv[optind];
317
318         buffer = calloc(sector_size, 1);
319         if (!buffer) {
320                 fprintf(stderr,"\nError: Cannot allocate buffer.\n");
321                 exit(EXIT_FAILURE);
322         }
323
324         /* test whether we can open and read device */
325         /* This is neded as we are reading the actual data
326         * in the keyslots dirtectly from the LUKS container.
327         */
328         f_luks = open(device, O_RDONLY);
329         if (f_luks == -1) {
330                 fprintf(stderr,"\nError: Opening of device %s failed:\n", device);
331                 perror(NULL);
332                 exit(EXIT_FAILURE);
333         }
334
335         /* now get the parameters we need via libcryptsetup */
336         /* Basically we need all active keyslots and their placement on disk */
337
338         /* first init. This does the following:
339          *   - gets us a crypt_device struct with some values filled in
340          *     Note: This does some init stuff we do not need, but that
341          *     should not cause trouble.
342          */
343
344         res = crypt_init(&cd, device);
345         if (res < 0) {
346                 fprintf(stderr, "crypt_init() failed. Maybe not running as root?\n");
347                 close(f_luks);
348                 exit(EXIT_FAILURE);
349         }
350
351         /* now load LUKS header into the crypt_device
352          * This should also make sure a valid LUKS1 header is on disk
353          * and hence we should be able to skip magic and version checks.
354          */
355         res = crypt_load(cd, CRYPT_LUKS1, NULL);
356         if (res < 0) {
357                 fprintf(stderr, "crypt_load() failed. LUKS header too broken/absent?\n");
358                 crypt_free(cd);
359                 close(f_luks);
360                 exit(EXIT_FAILURE);
361         }
362
363         fprintf(out, "\nparameters (commandline and LUKS header):\n");
364         fprintf(out, "  sector size: %d\n", sector_size);
365         fprintf(out, "  threshold:   %0f\n\n", threshold);
366
367         r = check_keyslots(out, cd, f_luks, buffer);
368
369         crypt_free(cd);
370         close(f_luks);
371         free(buffer);
372         return r;
373 }