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