"Fossies" - the Fresh Open Source Software Archive

Member "cryptsetup-2.4.3/misc/keyslot_checker/chk_luks_keyslots.c" (24 Jul 2021, 9709 Bytes) of package /linux/misc/cryptsetup-2.4.3.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "chk_luks_keyslots.c" see the Fossies "Dox" file reference documentation.

    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 }