-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow multi-pass overwriting with specified patterns
Also, update SPDX license tags and years.
- Loading branch information
Showing
5 changed files
with
109 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
dm-linear like target which provides discard, but replaces it with write of | ||
random data to a discarded region. Thus, discarded data is securely deleted. | ||
Because of abstract nature, it could support many file-systems which support | ||
discard (such as ext3, ext4, xfs, btrfs). | ||
erase pattern data to a discarded region. Thus, discarded data is securely | ||
deleted (sanitized). Because of abstract nature, it could support many | ||
file-systems which support discard (such as ext3, ext4, xfs, btrfs). | ||
|
||
Operation notes: | ||
|
||
|
@@ -10,21 +10,29 @@ mounted from that device and not from the underlying device. Make sure | |
file-system is mounted with `-o discard` option. Do not enable data journaling | ||
(such as `-o data=journal` do not enable it). Note, that when you `rm` files | ||
discards will be sent (and, thus, erasing will performed) asynchronously, so, | ||
to make sure data is already erased issue `sync` or mount file-system with `-o | ||
sync` option before `rm`. If you wish that filenames are wiped too, first, | ||
to make sure data is already erased issue `sync` or remount file-system with | ||
`-o sync` option before `rm`. If you wish that filenames are wiped too, first, | ||
make sure file-system is created completely without journaling (such as | ||
`mkfs.ext4 -O ^has_journal` or disable it using `tune2fs `, and second, delete | ||
the directory itself, so its blocks are discarded and erased. If you issue | ||
`fstrim` all free blocks of file-system will be discarded and thus erased too | ||
(make sure that file-system is still mounted with `-o discard` though.) | ||
`mkfs.ext4 -O ^has_journal`, and second, delete the directory itself, so its | ||
blocks are discarded and erased. If you issue `fstrim` all free blocks of | ||
file-system will be discarded and thus erased too (make sure that file-system | ||
is still mounted with `-o discard` though.) | ||
|
||
Usage: | ||
|
||
``` | ||
secdelsetup /dev/sda5 [/dev/mapper/secdel5] | ||
``` | ||
- will map `sda5` to `secdel5`. Then, file-system on `secdel5` should be | ||
mounted with `-o discard`. | ||
- will map `sda5` to `secdel5`. (With default erase more which is single pass of | ||
(crng) random data). Alternatively: | ||
|
||
``` | ||
secdelsetup /dev/sda5 [/dev/mapper/secdel5] 1R0 | ||
``` | ||
- Will work same as above but with with three pass overwriting: first pass of 1-bits, | ||
second pass of (crng) random bits, and third pass of 0-bits. | ||
|
||
Then, file-system on `secdel5` should be mounted with `-o discard`. | ||
|
||
``` | ||
secdeltab --all or secdeltab --list | ||
|
@@ -43,5 +51,5 @@ secdeltab --detach-all | |
- detach all active maps. | ||
|
||
Based on the code of `dm-linear` from Linux kernel of their respective authors. | ||
(C) 2018 <[email protected]>; License GPLv2. | ||
(C) 2018,2019 <[email protected]>; License GPL-2.0-only. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
/* | ||
* Copyright (C) 2001-2003 Sistina Software (UK) Limited. | ||
* dm-secdel: secure deletion on discard | ||
* (c) 2018-2019, [email protected]. | ||
* | ||
* (c) 2018, [email protected]: secure deletion on discard | ||
* Based on dm-linear: | ||
* Copyright (C) 2001-2003 Sistina Software (UK) Limited. | ||
* | ||
* This file is released under the GPL. | ||
* This file is licensed under the GPL-2.0-only. | ||
*/ | ||
|
||
#include <linux/module.h> | ||
|
@@ -24,12 +26,15 @@ MODULE_VERSION("1.0.4"); | |
|
||
#define DM_MSG_PREFIX "secdel" | ||
|
||
unsigned long empty_ff_page[PAGE_SIZE / sizeof(unsigned long)]; | ||
|
||
/* | ||
* Linear: maps a linear range of a device. | ||
*/ | ||
struct secdel_c { | ||
struct dm_dev *dev; | ||
sector_t start; | ||
char patterns[]; | ||
}; | ||
|
||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,18,0) | ||
|
@@ -40,21 +45,27 @@ static inline struct audit_context *audit_context(void) | |
#endif | ||
|
||
/* | ||
* Construct a linear mapping: <dev_path> <offset> | ||
* Construct a linear mapping: <dev_path> <offset> [patterns] | ||
*/ | ||
static int secdel_ctr(struct dm_target *ti, unsigned int argc, char **argv) | ||
{ | ||
struct secdel_c *lc; | ||
unsigned long long tmp; | ||
size_t passes = 0; | ||
char dummy; | ||
int ret; | ||
int i; | ||
|
||
if (argc != 2) { | ||
if (argc < 2) { | ||
ti->error = "Invalid argument count"; | ||
return -EINVAL; | ||
} | ||
|
||
lc = kmalloc(sizeof(*lc), GFP_KERNEL); | ||
if (argc > 2) | ||
passes = strlen(argv[2]); | ||
|
||
/* +2 to allow empty argv[2] to be replaced with "R" */ | ||
lc = kmalloc(sizeof(*lc) + passes + 2, GFP_KERNEL); | ||
if (lc == NULL) { | ||
ti->error = "Cannot allocate secdel context"; | ||
return -ENOMEM; | ||
|
@@ -73,6 +84,27 @@ static int secdel_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |
goto bad; | ||
} | ||
|
||
/* empty or unset argv[2] is replaced with "R" */ | ||
if (argc > 2 && passes > 1) | ||
strcpy(lc->patterns, argv[2]); | ||
else | ||
strcpy(lc->patterns, "R"); | ||
|
||
/* sanity check erasure patterns */ | ||
passes = strlen(lc->patterns); | ||
for (i = 0; i < passes; i++) { | ||
switch (lc->patterns[i]) { | ||
case '0': | ||
case '1': | ||
case 'R': | ||
continue; | ||
default: | ||
ti->error = "Invalid character in patterns"; | ||
ret = -EINVAL; | ||
goto bad; | ||
} | ||
} | ||
|
||
/* permit discards no matter if underlying device supports them */ | ||
ti->discards_supported = 1; | ||
|
||
|
@@ -93,9 +125,9 @@ static int secdel_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |
AUDIT_KERNEL_OTHER); | ||
if (ab) { | ||
audit_log_format(ab, "op=secdel action=start"); | ||
audit_log_format(ab, " dev=%s srcname=%s", | ||
audit_log_format(ab, " dev=%s srcname=%s patterns=%s", | ||
dm_device_name(dm_table_get_md(ti->table)), | ||
argv[0]); | ||
argv[0], lc->patterns); | ||
audit_log_end(ab); | ||
} | ||
} | ||
|
@@ -175,7 +207,8 @@ static void bio_end_erase(struct bio *bio) | |
#else | ||
bio_for_each_segment_all(bvec, bio, i) | ||
#endif | ||
if (bvec->bv_page != ZERO_PAGE(0)) | ||
if (bvec->bv_page != ZERO_PAGE(0) && | ||
bvec->bv_page != virt_to_page(empty_ff_page)) | ||
__free_page(bvec->bv_page); | ||
bio_put(bio); | ||
} | ||
|
@@ -206,12 +239,14 @@ static int op_discard(struct bio *bio) | |
|
||
/* | ||
* send amount of masking data to the device | ||
* @mode: 0 to write zeros, otherwise to write random data | ||
* @mode: 0 to write zeros, 1 to write ff-s, | ||
* -1 to write random data | ||
*/ | ||
static int issue_erase(struct block_device *bdev, sector_t sector, | ||
sector_t nr_sects, gfp_t gfp_mask, int mode) | ||
sector_t nr_sects, int mode) | ||
{ | ||
int ret = 0; | ||
const gfp_t gfp_mask = GFP_NOFS; | ||
|
||
while (nr_sects) { | ||
struct bio *bio; | ||
|
@@ -236,24 +271,26 @@ static int issue_erase(struct block_device *bdev, sector_t sector, | |
struct page *page = NULL; | ||
|
||
sz = min((sector_t)PAGE_SIZE >> 9, nr_sects); | ||
if (mode) { | ||
if (mode < 0) { | ||
page = alloc_page(gfp_mask); | ||
if (!page) { | ||
DMERR("issue_erase %lu[%lu]: no memory to allocate page for random data", | ||
sector, nr_sects); | ||
/* fallback to zero filling */ | ||
/* will fallback to zero filling */ | ||
} else { | ||
void *ptr; | ||
|
||
ptr = kmap_atomic(page); | ||
get_random_bytes(ptr, sz << 9); | ||
kunmap_atomic(ptr); | ||
} | ||
} | ||
} else if (mode == 1) | ||
page = virt_to_page(empty_ff_page); | ||
if (!page) | ||
page = ZERO_PAGE(0); | ||
ret = bio_add_page(bio, page, sz << 9, 0); | ||
if (!ret && page != ZERO_PAGE(0)) | ||
if (!ret && page != ZERO_PAGE(0) && | ||
page != virt_to_page(empty_ff_page)) | ||
__free_page(page); | ||
nr_sects -= ret >> 9; | ||
sector += ret >> 9; | ||
|
@@ -275,6 +312,7 @@ static int secdel_map_discard(struct dm_target *ti, struct bio *sbio) | |
struct block_device *bdev = lc->dev->bdev; | ||
sector_t sector = sbio->bi_iter.bi_sector; | ||
sector_t nr_sects = bio_sectors(sbio); | ||
size_t passes, i; | ||
|
||
if (!bio_sectors(sbio)) | ||
return 0; | ||
|
@@ -288,7 +326,19 @@ static int secdel_map_discard(struct dm_target *ti, struct bio *sbio) | |
|
||
bio_endio(sbio); | ||
|
||
issue_erase(bdev, sector, nr_sects, GFP_NOFS, 1); | ||
passes = strlen(lc->patterns); | ||
for (i = 0; i < passes; i++) { | ||
int mode; | ||
|
||
switch (lc->patterns[i]) { | ||
case '0': mode = 0; break; | ||
case '1': mode = 1; break; | ||
case 'R': | ||
default: | ||
mode = -1; | ||
} | ||
issue_erase(bdev, sector, nr_sects, mode); | ||
} | ||
return 1; | ||
} | ||
|
||
|
@@ -344,8 +394,9 @@ static void secdel_status(struct dm_target *ti, status_type_t type, | |
break; | ||
|
||
case STATUSTYPE_TABLE: | ||
snprintf(result, maxlen, "%s %llu", lc->dev->name, | ||
(unsigned long long)lc->start); | ||
snprintf(result, maxlen, "%s %llu %s", lc->dev->name, | ||
(unsigned long long)lc->start, | ||
lc->patterns); | ||
break; | ||
} | ||
} | ||
|
@@ -482,7 +533,7 @@ int __init dm_secdel_init(void) | |
|
||
if (r < 0) | ||
DMERR("register failed %d", r); | ||
|
||
memset(empty_ff_page, 0xff, sizeof(empty_ff_page)); | ||
return r; | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,8 +2,8 @@ | |
# | ||
# Setup script for dm-secdel | ||
# | ||
# (C) 2018 <[email protected]> | ||
# License: GPLv2 | ||
# (C) 2018,2019 <[email protected]> | ||
# License: GPL-2.0-only | ||
# | ||
|
||
set -efu | ||
|
@@ -17,7 +17,7 @@ error() { | |
|
||
usage() { | ||
echo "Usage:" | ||
echo " secdelsetup <source-device> [mapper-name]" | ||
echo " secdelsetup <source-device> [mapper-name] [erase-modes]" | ||
echo "Options:" | ||
echo " -d|--detach <device> detach device" | ||
echo " -D|--detach-all|--stop detach all devices" | ||
|
@@ -28,6 +28,14 @@ usage() { | |
echo " --start start devices from secdeltab" | ||
echo " --save save active divices to secdeltab" | ||
echo " -h|--help this text" | ||
echo "erase-modes should be set to string of 0, 1, or R characters" | ||
echo "without any separaters. Meaning of values are:" | ||
echo " 0 one pass of bit zeros" | ||
echo " 1 one pass of bit ones (i.e. 0xff bytes)" | ||
echo " R one pass of random data" | ||
echo "For example, when erase-modes is set to \"1R0\" it will erase" | ||
echo "with three passes if ones, random, and zero patterns." | ||
echo "Default value for erase-modes is R (one pass of random)." | ||
exit | ||
} | ||
|
||
|
@@ -50,7 +58,7 @@ list_lsblk() { | |
list_all() { | ||
local m=${1:-} | ||
|
||
check && dmsetup table --target secdel | while read devx x x x devn x; do | ||
check && dmsetup table --target secdel | while read devx x x x devn x opts; do | ||
dev=/dev/mapper/${devx%:} | ||
[ "$m" ] && [ "$m" != $dev ] && continue | ||
devidx=$(stat -L -c '%t:%T' $dev) | ||
|
@@ -59,7 +67,7 @@ list_all() { | |
if [ "$devn" = "$dn" ]; then | ||
uuid= | ||
[ "${UUID:-}" ] && uuid=$(find_uuid_by_hex_minmaj $devidx) | ||
echo "$dev ${uuid:-/dev/$dv}" | ||
echo "$dev ${uuid:-/dev/$dv} $opts" | ||
dev= | ||
break | ||
fi | ||
|
@@ -142,7 +150,7 @@ attach() { | |
fi | ||
srcdev=$(find_dev $srcdev) | ||
sz=$(blockdev --getsz $srcdev) | ||
dmsetup create $mapbase --table "0 $sz secdel $srcdev 0" && \ | ||
dmsetup create $mapbase --table "0 $sz secdel $srcdev 0 $opts" && \ | ||
echo "$mapname is attached to $srcdev" | ||
} | ||
|
||
|
@@ -156,7 +164,7 @@ secdeltab_save() { | |
|
||
secdeltab_start() { | ||
while read tgt src opts; do | ||
[ $(expr $tgt : ^# ) = 1 ] && continue | ||
[ $(expr $tgt : "^#") = 1 ] && continue | ||
attach $src $tgt $opts || : | ||
done < $secdeltab | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters