diff --git a/tests/Makefile.am b/tests/Makefile.am index d7789782..8fc0151c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -22,6 +22,7 @@ TESTS = 00modules-test \ bitlk-compat-test \ run-all-symbols \ unit-utils-crypt-test \ + unit-wipe-test \ reencryption-compat-test \ luks2-reencryption-test \ luks2-reencryption-mangle-test @@ -129,6 +130,12 @@ unit_utils_crypt_test_LDFLAGS = $(AM_LDFLAGS) -static unit_utils_crypt_test_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/lib unit_utils_crypt_test_CPPFLAGS = $(AM_CPPFLAGS) -include config.h +unit_wipe_SOURCES = unit-wipe.c +unit_wipe_LDADD = ../libcryptsetup.la +unit_wipe_LDFLAGS = $(AM_LDFLAGS) -static +unit_wipe_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/lib +unit_wipe_CPPFLAGS = $(AM_CPPFLAGS) + BUILT_SOURCES = test-symbols-list.h test-symbols-list.h: $(top_srcdir)/lib/libcryptsetup.sym generate-symbols-list @@ -141,7 +148,7 @@ all_symbols_test_LDFLAGS = $(AM_LDFLAGS) -ldl all_symbols_test_CFLAGS = $(AM_CFLAGS) all_symbols_test_CPPFLAGS = $(AM_CPPFLAGS) -D_GNU_SOURCE -check_PROGRAMS = api-test api-test-2 differ vectors-test unit-utils-io unit-utils-crypt-test all-symbols-test +check_PROGRAMS = api-test api-test-2 differ vectors-test unit-utils-io unit-utils-crypt-test unit-wipe all-symbols-test check-programs: test-symbols-list.h $(check_PROGRAMS) fake_token_path.so diff --git a/tests/api-test.c b/tests/api-test.c index a59ef5da..80d0d332 100644 --- a/tests/api-test.c +++ b/tests/api-test.c @@ -2041,6 +2041,25 @@ static void IntegrityTest(void) CRYPT_FREE(cd); } +static void WipeTest(void) +{ + OK_(crypt_init(&cd, NULL)); + FAIL_(crypt_wipe(cd, NULL, CRYPT_WIPE_ZERO, 0, 4096, 0, 0, NULL, NULL), "No device"); + FAIL_(crypt_wipe(cd, DEVICE_WRONG, CRYPT_WIPE_ZERO, 0, 4096, 0, 0, NULL, NULL), "Wrong device"); + OK_(crypt_wipe(cd, DEVICE_1, CRYPT_WIPE_ZERO, 0, 4096, 0, 0, NULL, NULL)); + OK_(crypt_wipe(cd, DEVICE_1, CRYPT_WIPE_RANDOM, 0, 4096, 0, 0, NULL, NULL)); + OK_(crypt_wipe(cd, DEVICE_1, CRYPT_WIPE_RANDOM, 0, 4096, 0, CRYPT_WIPE_NO_DIRECT_IO, NULL, NULL)); + CRYPT_FREE(cd); + + OK_(crypt_init(&cd, DEVICE_1)); + OK_(crypt_wipe(cd, NULL, CRYPT_WIPE_ZERO, 0, 4096, 0, 0, NULL, NULL)); + OK_(crypt_wipe(cd, NULL, CRYPT_WIPE_RANDOM, 0, 4096, TST_SECTOR_SIZE, 0, NULL, NULL)); + FAIL_(crypt_wipe(cd, NULL, CRYPT_WIPE_RANDOM, 0, 4096, TST_SECTOR_SIZE-1, 0, NULL, NULL), "Sector size"); + FAIL_(crypt_wipe(cd, NULL, CRYPT_WIPE_RANDOM, 0, 4096 - 1, 0, 0, NULL, NULL), "Length size not aligned"); + FAIL_(crypt_wipe(cd, NULL, CRYPT_WIPE_RANDOM, 1, 4096, 0, 0, NULL, NULL), "Offset not aligned"); + CRYPT_FREE(cd); +} + // Check that gcrypt is properly initialised in format static void NonFIPSAlg(void) { @@ -2134,6 +2153,7 @@ int main(int argc, char *argv[]) RUN_(IntegrityTest, "Integrity API"); RUN_(ResizeIntegrity, "Integrity raw resize"); RUN_(ResizeIntegrityWithKey, "Integrity raw resize with key"); + RUN_(WipeTest, "Wipe device"); _cleanup(); return 0; diff --git a/tests/unit-wipe-test b/tests/unit-wipe-test new file mode 100755 index 00000000..208e05fa --- /dev/null +++ b/tests/unit-wipe-test @@ -0,0 +1,164 @@ +#!/bin/bash + +WIPE_UNIT=./unit-wipe +FILE=./wipe_localfile +FILE_RAND=./wipe_random_localfile +MB_BYTES=$((1024*1024)) +DEVSIZEMB=8 +DEVSIZE=$((DEVSIZEMB*$MB_BYTES)) + +HASH_EMPTY=2daeb1f36095b44b318410b3f4e8b5d989dcc7bb023d1426c492dab0a3053e74 + +function cleanup() { + rm -f $FILE $FILE_RAND 2> /dev/null + sleep 1 + rmmod scsi_debug >/dev/null 2>&1 +} + +function fail() +{ + if [ -n "$1" ] ; then echo "FAIL $1" ; else echo "FAIL" ; fi + echo "FAILED backtrace:" + while caller $frame; do ((frame++)); done + cleanup + exit 100 +} + +function skip() +{ + echo "TEST SKIPPED: $1" + cleanup + exit 0 +} + +function add_device() +{ + rmmod scsi_debug >/dev/null 2>&1 + if [ -d /sys/module/scsi_debug ] ; then + echo "Cannot use scsi_debug module (in use or compiled-in), test skipped." + exit 77 + fi + modprobe scsi_debug dev_size_mb=$DEVSIZEMB num_tgts=1 delay=0 >/dev/null 2>&1 + if [ $? -ne 0 ] ; then + echo "This kernel seems to not support proper scsi_debug module, test skipped." + exit 77 + fi + DEV=$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /) + DEV="/dev/$DEV" + [ -b $DEV ] || fail "Cannot find $DEV." +} + +function check_hash() # $1 dev, $2 hash +{ + local HASH=$(sha256sum $1 | cut -d' ' -f 1) + [ $HASH == "$2" ] +} + +function init_hash_dd() # $1 dev, $dev orig +{ + dd if=/dev/urandom of=$2 bs=1M count=$DEVSIZEMB conv=notrunc 2> /dev/null + dd if=$2 of=$1 bs=1M conv=notrunc 2> /dev/null + HASH_0=$(sha256sum $1 | cut -d' ' -f 1) + # second MB wiped + dd if=/dev/zero of=$1 bs=1M seek=1 count=1 conv=notrunc 2> /dev/null + HASH_1=$(sha256sum $1 | cut -d' ' -f 1) + # 4,5,6 MB wiped + dd if=/dev/zero of=$1 bs=1M seek=4 count=3 conv=notrunc 2> /dev/null + HASH_2=$(sha256sum $1 | cut -d' ' -f 1) + dd if=$2 of=$1 bs=1M conv=notrunc 2> /dev/null +} + +function add_file() +{ + dd if=/dev/zero of=$FILE bs=1M count=$DEVSIZEMB 2> /dev/null || fial + dd if=/dev/zero of=$FILE_RAND bs=1M count=$DEVSIZEMB 2> /dev/null || fail + check_hash $FILE $HASH_EMPTY || fail + check_hash $FILE_RAND $HASH_EMPTY || fail +} + +function test_wipe_full() # $1 dev, $2 block size, [$3 flags] +{ + # wipe random and back to zero + $WIPE_UNIT $1 random 0 $DEVSIZE $2 $3 || fail + check_hash $1 $HASH_EMPTY && fail "Failed random wipe" + $WIPE_UNIT $1 zero 0 $DEVSIZE $2 $3 || fail + check_hash $1 $HASH_EMPTY || fail "Failed zero wipe" +} + +# wipe MB blocks, with zero, random and special and back to original +function test_wipe_blocks() # $1 dev $2 block sizem [$3 flags] +{ + init_hash_dd $1 $FILE_RAND + check_hash $1 $HASH_0 || fail + + $WIPE_UNIT $1 zero $((1*$MB_BYTES)) $((1*$MB_BYTES)) $2 $3 || fail + check_hash $1 $HASH_1 || fail + $WIPE_UNIT $1 random $((1*$MB_BYTES)) $((1*$MB_BYTES)) $2 $3 || fail + check_hash $1 $HASH_1 && fail + $WIPE_UNIT $1 special $((1*$MB_BYTES)) $((1*$MB_BYTES)) $2 $3 || fail + check_hash $1 $HASH_1 && fail + $WIPE_UNIT $1 zero $((1*$MB_BYTES)) $((1*$MB_BYTES)) $2 $3 || fail + check_hash $1 $HASH_1 || fail + + $WIPE_UNIT $1 zero $((4*$MB_BYTES)) $((3*$MB_BYTES)) $2 $3 || fail + check_hash $1 $HASH_2 || fail + $WIPE_UNIT $1 random $((4*$MB_BYTES)) $((3*$MB_BYTES)) $2 $3 || fail + check_hash $1 $HASH_2 && fail + $WIPE_UNIT $1 special $((4*$MB_BYTES)) $((3*$MB_BYTES)) $2 $3 || fail + check_hash $1 $HASH_2 && fail + $WIPE_UNIT $1 zero $((4*$MB_BYTES)) $((3*$MB_BYTES)) $2 $3 || fail + check_hash $1 $HASH_2 || fail +} + +[ -n "$CRYPTSETUP_PATH" ] && skip "Cannot run this test with CRYPTSETUP_PATH set." + +test -x $WIPE_UNIT || skip "Run \"make `basename $WIPE_UNIT`\" first" + +cleanup +add_file + +echo -n "[1] Wipe full file " +for bs in 0 $MB_BYTES $((4*$MB_BYTES)); do + echo -n [$bs/DIO] + test_wipe_full $FILE $bs + echo -n [$bs] + test_wipe_full $FILE $bs no-dio +done +echo "[OK]" + +echo -n "[2] Wipe blocks in file " +for bs in 0 $MB_BYTES $((4*$MB_BYTES)); do + echo -n [$bs/DIO] + test_wipe_blocks $FILE $bs + echo -n [$bs] + test_wipe_blocks $FILE $bs no-dio +done +echo "[OK]" + +[ $(id -u) -eq 0 ] || { + echo "WARNING: You must be root to run remaining tests." + cleanup + exit 0 +} + +add_device + +echo -n "[3] Wipe full block device " +for bs in 0 $MB_BYTES $((4*$MB_BYTES)); do + echo -n [$bs/DIO] + test_wipe_full $DEV $bs + echo -n [$bs] + test_wipe_full $DEV $bs no-dio +done +echo "[OK]" + +echo -n "[4] Wipe blocks in block device " +for bs in 0 $MB_BYTES $((4*$MB_BYTES)); do + echo -n [$bs/DIO] + test_wipe_blocks $DEV $bs + echo -n [$bs] + test_wipe_blocks $DEV $bs no-dio +done +echo "[OK]" + +cleanup diff --git a/tests/unit-wipe.c b/tests/unit-wipe.c new file mode 100644 index 00000000..478a7c2a --- /dev/null +++ b/tests/unit-wipe.c @@ -0,0 +1,131 @@ +/* + * unit test helper for crypt_wipe API call + * + * Copyright (C) 2022 Milan Broz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "libcryptsetup.h" + +const char *test_file; +uint64_t test_offset, test_length, test_block; +uint32_t flags; +crypt_wipe_pattern pattern; + +static void usage(void) +{ + fprintf(stderr, "Use:\tunit-wipe file/device zero|random|special offset length bsize [no-dio].\n"); +} + +static bool parse_u64(const char *arg, uint64_t *u64) +{ + unsigned long long ull; + char *end; + + ull = strtoull(arg, &end, 10); + if (*end || !*arg || errno == ERANGE) + return false; + + if (ull % 512) + return false; + + *u64 = ull; + return true; +} + +static bool parse_input_params(int argc, char **argv) +{ + struct stat st; + + if (argc < 6 || argc > 7) { + usage(); + return false; + } + + if (stat(argv[1], &st)) { + fprintf(stderr, "File/device %s is missing?\n", argv[1]); + return false; + } + test_file = argv[1]; + + if (!strcmp(argv[2], "random")) + pattern = CRYPT_WIPE_RANDOM; + else if (!strcmp(argv[2], "zero")) + pattern = CRYPT_WIPE_ZERO; + else if (!strcmp(argv[2], "special")) + pattern = CRYPT_WIPE_SPECIAL; + else { + fprintf(stderr, "Wrong pattern specification.\n"); + return false; + } + + if (!parse_u64(argv[3], &test_offset)) { + fprintf(stderr, "Wrong offset specification.\n"); + return false; + } + + if (!parse_u64(argv[4], &test_length)) { + fprintf(stderr, "Wrong length specification.\n"); + return false; + } + + if (!parse_u64(argv[5], &test_block)) { + fprintf(stderr, "Wrong block length specification.\n"); + return false; + } + + if (argc > 6) { + if (!strcmp(argv[6], "no-dio")) + flags = CRYPT_WIPE_NO_DIRECT_IO; + else { + fprintf(stderr, "Wrong flags specification.\n"); + return false; + } + } + + return true; +} + +int main(int argc, char **argv) +{ + struct crypt_device *cd; + int r; + + if (!parse_input_params(argc, argv)) + return EXIT_FAILURE; + + r = crypt_init(&cd, NULL); + if (r < 0) { + fprintf(stderr, "Context init failure %i.\n", r); + return EXIT_FAILURE; + } + + r = crypt_wipe(cd, test_file, pattern, test_offset, test_length, + test_block, flags, NULL, NULL); + crypt_free(cd); + + if (r) + fprintf(stderr, "Failure %i\n", r); + + return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +}