|
Packit |
94f725 |
/*
|
|
Packit |
94f725 |
* LUKS keyslot entropy tester. Works only for header version 1.
|
|
Packit |
94f725 |
*
|
|
Packit |
94f725 |
* Functionality: Determines sample entropy (symbols: bytes) for
|
|
Packit |
94f725 |
* each (by default) 512B sector in each used keyslot. If it
|
|
Packit |
94f725 |
* is lower than a threshold, the sector address is printed
|
|
Packit |
94f725 |
* as it is suspected of having non-"random" data in it, indicating
|
|
Packit |
94f725 |
* damage by overwriting. This can obviously not find overwriting
|
|
Packit |
94f725 |
* with random or random-like data (encrypted, compressed).
|
|
Packit |
94f725 |
*
|
|
Packit |
94f725 |
* Version history:
|
|
Packit |
94f725 |
* v0.1: 09.09.2012 Initial release
|
|
Packit |
94f725 |
* v0.2: 08.10.2012 Converted to use libcryptsetup
|
|
Packit |
94f725 |
*
|
|
Packit |
94f725 |
* Copyright (C) 2012, Arno Wagner <arno@wagner.name>
|
|
Packit |
94f725 |
*
|
|
Packit |
94f725 |
* This program is free software; you can redistribute it and/or
|
|
Packit |
94f725 |
* modify it under the terms of the GNU General Public License
|
|
Packit |
94f725 |
* as published by the Free Software Foundation; either version 2
|
|
Packit |
94f725 |
* of the License, or (at your option) any later version.
|
|
Packit |
94f725 |
*
|
|
Packit |
94f725 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
94f725 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
94f725 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
94f725 |
* GNU General Public License for more details.
|
|
Packit |
94f725 |
*
|
|
Packit |
94f725 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
94f725 |
* along with this program; if not, write to the Free Software
|
|
Packit |
94f725 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
Packit |
94f725 |
*/
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
#include <stdlib.h>
|
|
Packit |
94f725 |
#include <stdio.h>
|
|
Packit |
94f725 |
#include <unistd.h>
|
|
Packit |
94f725 |
#include <ctype.h>
|
|
Packit |
94f725 |
#include <math.h>
|
|
Packit |
94f725 |
#include <fcntl.h>
|
|
Packit |
94f725 |
#include <inttypes.h>
|
|
Packit |
94f725 |
#include <libcryptsetup.h>
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
const char *help =
|
|
Packit |
94f725 |
"Version 0.2 [8.10.2012]\n"
|
|
Packit |
94f725 |
"\n"
|
|
Packit |
94f725 |
" chk_luks_keyslots [options] luks-device \n"
|
|
Packit |
94f725 |
"\n"
|
|
Packit |
94f725 |
"This tool checks all keyslots of a LUKS device for \n"
|
|
Packit |
94f725 |
"low entropy sections. If any are found, they are reported. \n"
|
|
Packit |
94f725 |
"This allows to find areas damaged by things like filesystem \n"
|
|
Packit |
94f725 |
"creation or RAID superblocks. \n"
|
|
Packit |
94f725 |
"\n"
|
|
Packit |
94f725 |
"Options: \n"
|
|
Packit |
94f725 |
" -t <num> Entropy threshold. Possible values 0.0 ... 1.0 \n"
|
|
Packit |
94f725 |
" Default: 0.90, which works well for 512B sectors.\n"
|
|
Packit |
94f725 |
" For 512B sectors, you will get frequent misdetections\n"
|
|
Packit |
94f725 |
" at thresholds around 0.94\n"
|
|
Packit |
94f725 |
" Higher value: more sensitive but more false detections.\n"
|
|
Packit |
94f725 |
" -s <num> Sector size. Must divide keyslot-size.\n"
|
|
Packit |
94f725 |
" Default: 512 Bytes.\n"
|
|
Packit |
94f725 |
" Values smaller than 128 are generally not very useful.\n"
|
|
Packit |
94f725 |
" For values smaller than the default, you need to adjust\n"
|
|
Packit |
94f725 |
" the threshold down to reduce misdetection. For values\n"
|
|
Packit |
94f725 |
" larger than the default you need to adjust the threshold\n"
|
|
Packit |
94f725 |
" up to retain sensitivity.\n"
|
|
Packit |
94f725 |
" -v Print found suspicious sectors verbosely. \n"
|
|
Packit |
94f725 |
" -d Print decimal addresses instead of hex ones.\n"
|
|
Packit |
94f725 |
"\n";
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* Config defaults */
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
static int sector_size = 512;
|
|
Packit |
94f725 |
static double threshold = 0.90;
|
|
Packit |
94f725 |
static int print_decimal = 0;
|
|
Packit |
94f725 |
static int verbose = 0;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* tools */
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* Calculates and returns sample entropy on byte level for
|
|
Packit |
94f725 |
* The argument.
|
|
Packit |
94f725 |
*/
|
|
Packit |
94f725 |
static double ent_samp(unsigned char * buf, int len)
|
|
Packit |
94f725 |
{
|
|
Packit |
94f725 |
int freq[256]; /* stores symbol frequencies */
|
|
Packit |
94f725 |
int i;
|
|
Packit |
94f725 |
double e, f;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* 0. Plausibility checks */
|
|
Packit |
94f725 |
if (len <= 0)
|
|
Packit |
94f725 |
return 0.0;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* 1. count all frequencies */
|
|
Packit |
94f725 |
for (i = 0; i < 256; i++) {
|
|
Packit |
94f725 |
freq[i] = 0.0;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
for (i = 0; i < len; i ++)
|
|
Packit |
94f725 |
freq[buf[i]]++;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* 2. calculate sample entropy */
|
|
Packit |
94f725 |
e = 0.0;
|
|
Packit |
94f725 |
for (i = 0; i < 256; i++) {
|
|
Packit |
94f725 |
f = freq[i];
|
|
Packit |
94f725 |
if (f > 0) {
|
|
Packit |
94f725 |
f = f / (double)len;
|
|
Packit |
94f725 |
e += f * log2(f);
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
if (e != 0.0)
|
|
Packit |
94f725 |
e = -1.0 * e;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
e = e / 8.0;
|
|
Packit |
94f725 |
return e;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
static void print_address(FILE *out, uint64_t value)
|
|
Packit |
94f725 |
{
|
|
Packit |
94f725 |
if (print_decimal) {
|
|
Packit |
94f725 |
fprintf(out,"%08" PRIu64 " ", value);
|
|
Packit |
94f725 |
} else {
|
|
Packit |
94f725 |
fprintf(out,"%#08" PRIx64 " ", value);
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* uses default "hd" style, i.e. 16 bytes followed by ASCII */
|
|
Packit |
94f725 |
static void hexdump_line(FILE *out, uint64_t address, unsigned char *buf) {
|
|
Packit |
94f725 |
int i;
|
|
Packit |
94f725 |
static char tbl[16] = "0123456789ABCDEF";
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
fprintf(out," ");
|
|
Packit |
94f725 |
print_address(out, address);
|
|
Packit |
94f725 |
fprintf(out," ");
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* hex */
|
|
Packit |
94f725 |
for (i = 0; i < 16; i++) {
|
|
Packit |
94f725 |
fprintf(out, "%c%c",
|
|
Packit |
94f725 |
tbl[(unsigned char)buf[i]>> 4],
|
|
Packit |
94f725 |
tbl[(unsigned char)buf[i] & 0x0f]);
|
|
Packit |
94f725 |
fprintf(out," ");
|
|
Packit |
94f725 |
if (i == 7)
|
|
Packit |
94f725 |
fprintf(out," ");
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
fprintf(out," ");
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* ascii */
|
|
Packit |
94f725 |
for (i = 0; i < 16; i++) {
|
|
Packit |
94f725 |
if (isprint(buf[i])) {
|
|
Packit |
94f725 |
fprintf(out, "%c", buf[i]);
|
|
Packit |
94f725 |
} else {
|
|
Packit |
94f725 |
fprintf(out, ".");
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
fprintf(out, "\n");
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
static void hexdump_sector(FILE *out, unsigned char *buf, uint64_t address, int len)
|
|
Packit |
94f725 |
{
|
|
Packit |
94f725 |
int done;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
done = 0;
|
|
Packit |
94f725 |
while (len - done >= 16) {
|
|
Packit |
94f725 |
hexdump_line(out, address + done, buf + done);
|
|
Packit |
94f725 |
done += 16;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
static int check_keyslots(FILE *out, struct crypt_device *cd, int f_luks)
|
|
Packit |
94f725 |
{
|
|
Packit |
94f725 |
int i;
|
|
Packit |
94f725 |
double ent;
|
|
Packit |
94f725 |
off_t ofs;
|
|
Packit |
94f725 |
uint64_t start, length, end;
|
|
Packit |
94f725 |
crypt_keyslot_info ki;
|
|
Packit |
94f725 |
unsigned char buffer[sector_size];
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
for (i = 0; i < crypt_keyslot_max(CRYPT_LUKS1) ; i++) {
|
|
Packit |
94f725 |
fprintf(out, "- processing keyslot %d:", i);
|
|
Packit |
94f725 |
ki = crypt_keyslot_status(cd, i);
|
|
Packit |
94f725 |
if (ki == CRYPT_SLOT_INACTIVE) {
|
|
Packit |
94f725 |
fprintf(out, " keyslot not in use\n");
|
|
Packit |
94f725 |
continue;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
if (ki == CRYPT_SLOT_INVALID) {
|
|
Packit |
94f725 |
fprintf(out, "\nError: keyslot invalid.\n");
|
|
Packit |
94f725 |
return EXIT_FAILURE;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
if (crypt_keyslot_area(cd, i, &start, &length) < 0) {
|
|
Packit |
94f725 |
fprintf(stderr,"\nError: querying keyslot area failed for slot %d\n", i);
|
|
Packit |
94f725 |
perror(NULL);
|
|
Packit |
94f725 |
return EXIT_FAILURE;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
end = start + length;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
fprintf(out, " start: ");
|
|
Packit |
94f725 |
print_address(out, start);
|
|
Packit |
94f725 |
fprintf(out, " end: ");
|
|
Packit |
94f725 |
print_address(out, end);
|
|
Packit |
94f725 |
fprintf(out, "\n");
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* check whether sector-size divides size */
|
|
Packit |
94f725 |
if (length % sector_size != 0) {
|
|
Packit |
94f725 |
fprintf(stderr,"\nError: Argument to -s does not divide keyslot size\n");
|
|
Packit |
94f725 |
return EXIT_FAILURE;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
for (ofs = start; (uint64_t)ofs < end; ofs += sector_size) {
|
|
Packit |
94f725 |
if (lseek(f_luks, ofs, SEEK_SET) != ofs) {
|
|
Packit |
94f725 |
fprintf(stderr,"\nCannot seek to keyslot area.\n");
|
|
Packit |
94f725 |
return EXIT_FAILURE;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
if (read(f_luks, buffer, sector_size) != sector_size) {
|
|
Packit |
94f725 |
fprintf(stderr,"\nCannot read keyslot area.\n");
|
|
Packit |
94f725 |
return EXIT_FAILURE;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
ent = ent_samp(buffer, sector_size);
|
|
Packit |
94f725 |
if (ent < threshold) {
|
|
Packit |
94f725 |
fprintf(out, " low entropy at: ");
|
|
Packit |
94f725 |
print_address(out, ofs);
|
|
Packit |
94f725 |
fprintf(out, " entropy: %f\n", ent);
|
|
Packit |
94f725 |
if (verbose) {
|
|
Packit |
94f725 |
fprintf(out, " Binary dump:\n");
|
|
Packit |
94f725 |
hexdump_sector(out, buffer, (uint64_t)ofs, sector_size);
|
|
Packit |
94f725 |
fprintf(out,"\n");
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
return EXIT_SUCCESS;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* Main */
|
|
Packit |
94f725 |
int main(int argc, char **argv)
|
|
Packit |
94f725 |
{
|
|
Packit |
94f725 |
/* for option processing */
|
|
Packit |
94f725 |
int c, r;
|
|
Packit |
94f725 |
char *device;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* for use of libcryptsetup */
|
|
Packit |
94f725 |
struct crypt_device *cd;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* Other vars */
|
|
Packit |
94f725 |
int f_luks; /* device file for the luks device */
|
|
Packit |
94f725 |
FILE *out;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* temporary helper vars */
|
|
Packit |
94f725 |
int res;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* getopt values */
|
|
Packit |
94f725 |
char *s, *end;
|
|
Packit |
94f725 |
double tvalue;
|
|
Packit |
94f725 |
int svalue;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* global initializations */
|
|
Packit |
94f725 |
out = stdout;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* get commandline parameters */
|
|
Packit |
94f725 |
while ((c = getopt (argc, argv, "t:s:vd")) != -1) {
|
|
Packit |
94f725 |
switch (c) {
|
|
Packit |
94f725 |
case 't':
|
|
Packit |
94f725 |
s = optarg;
|
|
Packit |
94f725 |
tvalue = strtod(s, &end;;
|
|
Packit |
94f725 |
if (s == end) {
|
|
Packit |
94f725 |
fprintf(stderr, "\nError: Parsing of argument to -t failed.\n");
|
|
Packit |
94f725 |
exit(EXIT_FAILURE);
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
if (tvalue < 0.0 || tvalue > 1.0) {
|
|
Packit |
94f725 |
fprintf(stderr,"\nError: Argument to -t must be in 0.0 ... 1.0\n");
|
|
Packit |
94f725 |
exit(EXIT_FAILURE);
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
threshold = tvalue;
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
case 's':
|
|
Packit |
94f725 |
s = optarg;
|
|
Packit |
94f725 |
svalue = strtol(s, &end, 10);
|
|
Packit |
94f725 |
if (s == end) {
|
|
Packit |
94f725 |
fprintf(stderr, "\nError: Parsing of argument to -s failed.\n");
|
|
Packit |
94f725 |
exit(EXIT_FAILURE);
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
if (svalue < 1) {
|
|
Packit |
94f725 |
fprintf(stderr,"\nError: Argument to -s must be >= 1 \n");
|
|
Packit |
94f725 |
exit(EXIT_FAILURE);
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
sector_size = svalue;
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
case 'v':
|
|
Packit |
94f725 |
verbose = 1;
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
case 'd':
|
|
Packit |
94f725 |
print_decimal = 1;
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
case '?':
|
|
Packit |
94f725 |
if (optopt == 't' || optopt == 's')
|
|
Packit |
94f725 |
fprintf (stderr,"\nError: Option -%c requires an argument.\n",
|
|
Packit |
94f725 |
optopt);
|
|
Packit |
94f725 |
else if (isprint (optopt)) {
|
|
Packit |
94f725 |
fprintf(stderr,"\nError: Unknown option `-%c'.\n", optopt);
|
|
Packit |
94f725 |
fprintf(stderr,"\n\n%s", help);
|
|
Packit |
94f725 |
} else {
|
|
Packit |
94f725 |
fprintf (stderr, "\nError: Unknown option character `\\x%x'.\n",
|
|
Packit |
94f725 |
optopt);
|
|
Packit |
94f725 |
fprintf(stderr,"\n\n%s", help);
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
exit(EXIT_SUCCESS);
|
|
Packit |
94f725 |
default:
|
|
Packit |
94f725 |
exit(EXIT_FAILURE);
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* parse non-option stuff. Should be exactly one, the device. */
|
|
Packit |
94f725 |
if (optind+1 != argc) {
|
|
Packit |
94f725 |
fprintf(stderr,"\nError: exactly one non-option argument expected!\n");
|
|
Packit |
94f725 |
fprintf(stderr,"\n\n%s", help);
|
|
Packit |
94f725 |
exit(EXIT_FAILURE);
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
device = argv[optind];
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* test whether we can open and read device */
|
|
Packit |
94f725 |
/* This is needed as we are reading the actual data
|
|
Packit |
94f725 |
* in the keyslots directly from the LUKS container.
|
|
Packit |
94f725 |
*/
|
|
Packit |
94f725 |
f_luks = open(device, O_RDONLY);
|
|
Packit |
94f725 |
if (f_luks == -1) {
|
|
Packit |
94f725 |
fprintf(stderr,"\nError: Opening of device %s failed:\n", device);
|
|
Packit |
94f725 |
perror(NULL);
|
|
Packit |
94f725 |
exit(EXIT_FAILURE);
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* now get the parameters we need via libcryptsetup */
|
|
Packit |
94f725 |
/* Basically we need all active keyslots and their placement on disk */
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* first init. This does the following:
|
|
Packit |
94f725 |
* - gets us a crypt_device struct with some values filled in
|
|
Packit |
94f725 |
* Note: This does some init stuff we do not need, but that
|
|
Packit |
94f725 |
* should not cause trouble.
|
|
Packit |
94f725 |
*/
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
res = crypt_init(&cd, device);
|
|
Packit |
94f725 |
if (res < 0) {
|
|
Packit |
94f725 |
fprintf(stderr, "crypt_init() failed. Maybe not running as root?\n");
|
|
Packit |
94f725 |
close(f_luks);
|
|
Packit |
94f725 |
exit(EXIT_FAILURE);
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* now load LUKS header into the crypt_device
|
|
Packit |
94f725 |
* This should also make sure a valid LUKS1 header is on disk
|
|
Packit |
94f725 |
* and hence we should be able to skip magic and version checks.
|
|
Packit |
94f725 |
*/
|
|
Packit |
94f725 |
res = crypt_load(cd, CRYPT_LUKS1, NULL);
|
|
Packit |
94f725 |
if (res < 0) {
|
|
Packit |
94f725 |
fprintf(stderr, "crypt_load() failed. LUKS header too broken/absent?\n");
|
|
Packit |
94f725 |
crypt_free(cd);
|
|
Packit |
94f725 |
close(f_luks);
|
|
Packit |
94f725 |
exit(EXIT_FAILURE);
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
fprintf(out, "\nparameters (commandline and LUKS header):\n");
|
|
Packit |
94f725 |
fprintf(out, " sector size: %d\n", sector_size);
|
|
Packit |
94f725 |
fprintf(out, " threshold: %0f\n\n", threshold);
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
r = check_keyslots(out, cd, f_luks);
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
crypt_free(cd);
|
|
Packit |
94f725 |
close(f_luks);
|
|
Packit |
94f725 |
return r;
|
|
Packit |
94f725 |
}
|