Blame misc/keyslot_checker/chk_luks_keyslots.c

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
}