7a9b391d661109b452a683a1c488a22795eea61f
[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, uint64_t 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, uint64_t 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)
168 {
169         int i;
170         double ent;
171         off_t ofs;
172         uint64_t start, length, end;
173         crypt_keyslot_info ki;
174         unsigned char buffer[sector_size];
175
176         for (i = 0; i < crypt_keyslot_max(CRYPT_LUKS1) ; i++) {
177                 fprintf(out, "- processing keyslot %d:", i);
178                 ki = crypt_keyslot_status(cd, i);
179                 if (ki == CRYPT_SLOT_INACTIVE) {
180                         fprintf(out, "  keyslot not in use\n");
181                         continue;
182                 }
183
184                 if (ki == CRYPT_SLOT_INVALID) {
185                         fprintf(out, "\nError: keyslot invalid.\n");
186                         return EXIT_FAILURE;
187                 }
188
189                 if (crypt_keyslot_area(cd, i, &start, &length) < 0) {
190                         fprintf(stderr,"\nError: querying keyslot area failed for slot %d\n", i);
191                         perror(NULL);
192                         return EXIT_FAILURE;
193                 }
194                 end = start + length;
195
196                 fprintf(out, "  start: ");
197                 print_address(out, start);
198                 fprintf(out, "  end: ");
199                 print_address(out, end);
200                 fprintf(out, "\n");
201
202                 /* check whether sector-size divides size */
203                 if (length % sector_size != 0) {
204                         fprintf(stderr,"\nError: Argument to -s does not divide keyslot size\n");
205                         return EXIT_FAILURE;
206                 }
207
208                 for (ofs = start; (uint64_t)ofs < end; ofs += sector_size) {
209                         if (lseek(f_luks, ofs, SEEK_SET) != ofs) {
210                                 fprintf(stderr,"\nCannot seek to keyslot area.\n");
211                                 return EXIT_FAILURE;
212                         }
213                         if (read(f_luks, buffer, sector_size) != sector_size) {
214                                 fprintf(stderr,"\nCannot read keyslot area.\n");
215                                 return EXIT_FAILURE;
216                         }
217                         ent = ent_samp(buffer, sector_size);
218                         if (ent < threshold) {
219                                 fprintf(out, "  low entropy at: ");
220                                 print_address(out, ofs);
221                                 fprintf(out, "   entropy: %f\n", ent);
222                                 if (verbose) {
223                                         fprintf(out, "  Binary dump:\n");
224                                         hexdump_sector(out, buffer, (uint64_t)ofs, sector_size);
225                                         fprintf(out,"\n");
226                                 }
227                         }
228                 }
229         }
230
231         return EXIT_SUCCESS;
232 }
233
234 /* Main */
235 int main(int argc, char **argv)
236 {
237         /* for option processing */
238         int c, r;
239         char *device;
240
241         /* for use of libcryptsetup */
242         struct crypt_device *cd;
243
244         /* Other vars */
245         int f_luks;   /* device file for the luks device */
246         FILE *out;
247
248         /* temporary helper vars */
249         int res;
250
251         /* getopt values */
252         char *s, *end;
253         double tvalue;
254         int svalue;
255
256         /* global initializations */
257         out = stdout;
258
259         /* get commandline parameters */
260         while ((c = getopt (argc, argv, "t:s:vd")) != -1) {
261                 switch (c) {
262                 case 't':
263                         s = optarg;
264                         tvalue = strtod(s, &end);
265                         if (s == end) {
266                                 fprintf(stderr, "\nError: Parsing of argument to -t failed.\n");
267                                 exit(EXIT_FAILURE);
268                         }
269
270                         if (tvalue < 0.0 || tvalue > 1.0) {
271                                 fprintf(stderr,"\nError: Argument to -t must be in 0.0 ... 1.0\n");
272                                 exit(EXIT_FAILURE);
273                         }
274                         threshold = tvalue;
275                         break;
276                 case 's':
277                         s = optarg;
278                         svalue = strtol(s, &end, 10);
279                         if (s == end) {
280                                 fprintf(stderr, "\nError: Parsing of argument to -s failed.\n");
281                                 exit(EXIT_FAILURE);
282                         }
283
284                         if (svalue < 1) {
285                                 fprintf(stderr,"\nError: Argument to -s must be >= 1 \n");
286                                 exit(EXIT_FAILURE);
287                         }
288                         sector_size = svalue;
289                         break;
290                 case 'v':
291                         verbose = 1;
292                         break;
293                 case 'd':
294                         print_decimal = 1;
295                         break;
296                 case '?':
297                         if (optopt == 't' || optopt == 's')
298                                 fprintf (stderr,"\nError: Option -%c requires an argument.\n",
299                                          optopt);
300                         else if (isprint (optopt)) {
301                                 fprintf(stderr,"\nError: Unknown option `-%c'.\n", optopt);
302                                 fprintf(stderr,"\n\n%s", help);
303                         } else {
304                                 fprintf (stderr, "\nError: Unknown option character `\\x%x'.\n",
305                                          optopt);
306                                 fprintf(stderr,"\n\n%s", help);
307                         }
308                         exit(EXIT_SUCCESS);
309                 default:
310                         exit(EXIT_FAILURE);
311                 }
312         }
313
314         /* parse non-option stuff. Should be exactly one, the device. */
315         if (optind+1 != argc) {
316                 fprintf(stderr,"\nError: exactly one non-option argument expected!\n");
317                 fprintf(stderr,"\n\n%s", help);
318                 exit(EXIT_FAILURE);
319         }
320         device = argv[optind];
321
322         /* test whether we can open and read device */
323         /* This is neded as we are reading the actual data
324         * in the keyslots dirtectly from the LUKS container.
325         */
326         f_luks = open(device, O_RDONLY);
327         if (f_luks == -1) {
328                 fprintf(stderr,"\nError: Opening of device %s failed:\n", device);
329                 perror(NULL);
330                 exit(EXIT_FAILURE);
331         }
332
333         /* now get the parameters we need via libcryptsetup */
334         /* Basically we need all active keyslots and their placement on disk */
335
336         /* first init. This does the following:
337          *   - gets us a crypt_device struct with some values filled in
338          *     Note: This does some init stuff we do not need, but that
339          *     should not cause trouble.
340          */
341
342         res = crypt_init(&cd, device);
343         if (res < 0) {
344                 fprintf(stderr, "crypt_init() failed. Maybe not running as root?\n");
345                 close(f_luks);
346                 exit(EXIT_FAILURE);
347         }
348
349         /* now load LUKS header into the crypt_device
350          * This should also make sure a valid LUKS1 header is on disk
351          * and hence we should be able to skip magic and version checks.
352          */
353         res = crypt_load(cd, CRYPT_LUKS1, NULL);
354         if (res < 0) {
355                 fprintf(stderr, "crypt_load() failed. LUKS header too broken/absent?\n");
356                 crypt_free(cd);
357                 close(f_luks);
358                 exit(EXIT_FAILURE);
359         }
360
361         fprintf(out, "\nparameters (commandline and LUKS header):\n");
362         fprintf(out, "  sector size: %d\n", sector_size);
363         fprintf(out, "  threshold:   %0f\n\n", threshold);
364
365         r = check_keyslots(out, cd, f_luks);
366
367         crypt_free(cd);
368         close(f_luks);
369         return r;
370 }