Currently, we support only filling a volume with zeroes on wiping.
However, it is not enough as data might still be readable by
experienced and equipped attacker. Many technical papers have been
written, therefore we should support other wiping algorithms.
---
Okay, this is not as complete as I'd like it. Wiping could take ages,
therefore we *want* it to be asynchronous. But that would mean:
1) Create new API to answer: "Are we done yet?"
2) Create event emitting system - like we have for domains
3) Combination of previous two
In fact, I've started writing events, but it turned out to be
huge and I am not even in a half yet. I need to find a way of
re-using event code we already have. But thats another day and
another patch set.
For those interested, take a look at Section 4.1.11 of DSS_PCI
requirements at [1].
1:
https://www.pcisecuritystandards.org/documents/Virtualization_InfoSupp_v2...
configure.ac | 26 ++++++++++-
include/libvirt/libvirt.h.in | 24 ++++++++++
src/libvirt.c | 6 ++-
src/storage/storage_driver.c | 103 ++++++++++++++++++++++++++++++++++-------
tools/virsh.c | 41 +++++++++++++++-
tools/virsh.pod | 25 ++++++++++-
6 files changed, 201 insertions(+), 24 deletions(-)
diff --git a/configure.ac b/configure.ac
index 46a9129..fb5f669 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2500,7 +2500,31 @@ AM_CONDITIONAL([HAVE_LIBNL], [test "$have_libnl" =
"yes"])
AC_SUBST([LIBNL_CFLAGS])
AC_SUBST([LIBNL_LIBS])
-
+dnl scrub program for different volume wiping algorithms
+
+AC_ARG_WITH([scrub],
+ AC_HELP_STRING([--with-scrub], [enable different volume wiping algorithms
+ @<:@default=check@:>@]),
+ [].
+ [with_scrub=check])
+
+if test "$with_scrub" != "no"; then
+ AC_PATH_PROG([SCRUB], [scrub])
+ if test -z "$SCRUB" ; then
+ if test "$with_scrub" = "check"; then
+ with_scrub=no
+ else
+ AC_MSG_ERROR([You must install the 'scrub' binary to enable
+ different volume wiping algorithms])
+ fi
+ else
+ with_scrub=yes
+ fi
+ if test "$with_scrub" = "yes"; then
+ AC_DEFINE_UNQUOTED([SCRUB], ["$SCRUB"],
+ [Location of the scrub program])
+ fi
+fi
# Only COPYING.LIB is under version control, yet COPYING
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index ad6fcce..caa9127 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -2118,6 +2118,30 @@ typedef enum {
VIR_STORAGE_VOL_DELETE_ZEROED = 1, /* Clear all data to zeros (slow) */
} virStorageVolDeleteFlags;
+typedef enum {
+ /* Keep this place for async flag. Currently,
+ * it is not implemented because of lack of
+ * events support within storage driver.
+ * VIR_STORAGE_VOL_WIPE_ASYNC = (1 << 0),
+ */
+ VIR_STORAGE_VOL_WIPE_ALG_NNSA = (1 << 1), /* 4-pass NNSA Policy Letter
+ NAP-14.1-C (XVI-8) */
+ VIR_STORAGE_VOL_WIPE_ALG_DOD = (1 << 2), /* 4-pass DoD 5220.22-M section
+ 8-306 procedure */
+ VIR_STORAGE_VOL_WIPE_ALG_BSI = (1 << 3), /* 9-pass method recommended by the
+ German Center of Security in
+ Information Technologies */
+ VIR_STORAGE_VOL_WIPE_ALG_GUTMANN = (1 << 4), /* The canonical 35-pass sequence
*/
+ VIR_STORAGE_VOL_WIPE_ALG_SCHNEIER = (1 << 5), /* 7-pass method described by
+ Bruce Schneier in "Applied
+ Cryptography" (1996) */
+ VIR_STORAGE_VOL_WIPE_ALG_PFITZNER7 = (1 << 6), /* 7-pass random */
+
+ VIR_STORAGE_VOL_WIPE_ALG_PFITZNER33 = (1 << 7), /* 33-pass random */
+
+ VIR_STORAGE_VOL_WIPE_ALG_RANDOM = (1 << 8), /* 1-pass random */
+} virStorageVolWipeFlags;
+
typedef struct _virStorageVolInfo virStorageVolInfo;
struct _virStorageVolInfo {
diff --git a/src/libvirt.c b/src/libvirt.c
index feb3ca6..c3b3665 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -12557,7 +12557,11 @@ error:
* @vol: pointer to storage volume
* @flags: future flags, use 0 for now
*
- * Ensure data previously on a volume is not accessible to future reads
+ * Ensure data previously on a volume is not accessible to future reads.
+ * When specifying wipe algorithm only one algorithm flag is allowed.
+ * Therefore if one wants to wipe a volume with say NNSA and DOD algorithms,
+ * he/she needs two subsequent calls. First with _ALG_NNSA flag, second
+ * with _ALG_DOD.
*
* Returns 0 on success, or -1 on error
*/
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index 8c2d6e1..f3a084a 100644
--- a/src/storage/storage_driver.c
+++ b/src/storage/storage_driver.c
@@ -1801,14 +1801,16 @@ out:
static int
-storageVolumeWipeInternal(virStorageVolDefPtr def)
+storageVolumeWipeInternal(virStorageVolDefPtr def,
+ unsigned int flags)
{
int ret = -1, fd = -1;
struct stat st;
char *writebuf = NULL;
size_t bytes_wiped = 0;
+ virCommandPtr cmd = NULL;
- VIR_DEBUG("Wiping volume with path '%s'", def->target.path);
+ VIR_DEBUG("Wiping volume with path '%s' flags %x",
def->target.path, flags);
fd = open(def->target.path, O_RDWR);
if (fd == -1) {
@@ -1825,29 +1827,62 @@ storageVolumeWipeInternal(virStorageVolDefPtr def)
goto out;
}
- if (S_ISREG(st.st_mode) && st.st_blocks < (st.st_size / DEV_BSIZE)) {
- ret = storageVolumeZeroSparseFile(def, st.st_size, fd);
- } else {
-
- if (VIR_ALLOC_N(writebuf, st.st_blksize) != 0) {
- virReportOOMError();
+ if (flags) {
+ cmd = virCommandNew(SCRUB);
+ virCommandAddArg(cmd, "-f");
+ if (flags &VIR_STORAGE_VOL_WIPE_ALG_NNSA) {
+ virCommandAddArgList(cmd, "-p", "nnsa", NULL);
+ } else if (flags & VIR_STORAGE_VOL_WIPE_ALG_DOD) {
+ virCommandAddArgList(cmd, "-p", "dod", NULL);
+ } else if (flags & VIR_STORAGE_VOL_WIPE_ALG_BSI) {
+ virCommandAddArgList(cmd, "-p", "bsi", NULL);
+ } else if (flags & VIR_STORAGE_VOL_WIPE_ALG_GUTMANN) {
+ virCommandAddArgList(cmd, "-p", "gutmann", NULL);
+ } else if (flags & VIR_STORAGE_VOL_WIPE_ALG_SCHNEIER) {
+ virCommandAddArgList(cmd, "-p", "schneier", NULL);
+ } else if (flags & VIR_STORAGE_VOL_WIPE_ALG_PFITZNER7) {
+ virCommandAddArgList(cmd, "-p", "pfitzner7", NULL);
+ } else if (flags & VIR_STORAGE_VOL_WIPE_ALG_PFITZNER33) {
+ virCommandAddArgList(cmd, "-p", "pfitzner33", NULL);
+ } else if (flags & VIR_STORAGE_VOL_WIPE_ALG_RANDOM) {
+ virCommandAddArgList(cmd, "-p", "random", NULL);
+ } else {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unsupported flags %x"),
+ flags);
goto out;
}
- ret = storageWipeExtent(def,
- fd,
- 0,
- def->allocation,
- writebuf,
- st.st_blksize,
- &bytes_wiped);
+ virCommandAddArg(cmd, def->target.path);
+
+ if (virCommandRun(cmd, NULL) < 0)
+ goto out;
+
+ ret = 0;
+ } else {
+ if (S_ISREG(st.st_mode) && st.st_blocks < (st.st_size / DEV_BSIZE)) {
+ ret = storageVolumeZeroSparseFile(def, st.st_size, fd);
+ } else {
+
+ if (VIR_ALLOC_N(writebuf, st.st_blksize) != 0) {
+ virReportOOMError();
+ goto out;
+ }
+
+ ret = storageWipeExtent(def,
+ fd,
+ 0,
+ def->allocation,
+ writebuf,
+ st.st_blksize,
+ &bytes_wiped);
+ }
}
out:
+ virCommandFree(cmd);
VIR_FREE(writebuf);
-
VIR_FORCE_CLOSE(fd);
-
return ret;
}
@@ -1859,9 +1894,41 @@ storageVolumeWipe(virStorageVolPtr obj,
virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
virStoragePoolObjPtr pool = NULL;
virStorageVolDefPtr vol = NULL;
+ unsigned int alg = 0;
+ unsigned int supported_algs = 0;
+ unsigned int count = 0;
int ret = -1;
+ supported_algs = VIR_STORAGE_VOL_WIPE_ALG_NNSA |
+ VIR_STORAGE_VOL_WIPE_ALG_DOD |
+ VIR_STORAGE_VOL_WIPE_ALG_BSI |
+ VIR_STORAGE_VOL_WIPE_ALG_GUTMANN |
+ VIR_STORAGE_VOL_WIPE_ALG_SCHNEIER |
+ VIR_STORAGE_VOL_WIPE_ALG_PFITZNER7 |
+ VIR_STORAGE_VOL_WIPE_ALG_PFITZNER33 |
+ VIR_STORAGE_VOL_WIPE_ALG_RANDOM;
+
+#ifdef SCRUB
+ virCheckFlags(supported_algs, -1);
+#else
virCheckFlags(0, -1);
+#endif
+
+ alg = flags & supported_algs;
+ /* As we support only one algorithm in @flags,
+ * check if somebody didn't pass two or more
+ */
+ while (alg) {
+ count += alg & 1L;
+ alg >>= 1;
+ }
+
+ if (count > 1) {
+ virStorageReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("only one wiping algorithm "
+ "per call is allowed"));
+ return -1;
+ }
storageDriverLock(driver);
pool = virStoragePoolObjFindByName(&driver->pools, obj->pool);
@@ -1895,7 +1962,7 @@ storageVolumeWipe(virStorageVolPtr obj,
goto out;
}
- if (storageVolumeWipeInternal(vol) == -1) {
+ if (storageVolumeWipeInternal(vol, flags) == -1) {
goto out;
}
diff --git a/tools/virsh.c b/tools/virsh.c
index 0bc0519..3cb4109 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -10961,6 +10961,7 @@ static const vshCmdInfo info_vol_wipe[] = {
static const vshCmdOptDef opts_vol_wipe[] = {
{"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or
path")},
{"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+ {"algorithm", VSH_OT_STRING, 0, N_("perform selected wiping
algorithm")},
{NULL, 0, 0, NULL}
};
@@ -10968,8 +10969,10 @@ static bool
cmdVolWipe(vshControl *ctl, const vshCmd *cmd)
{
virStorageVolPtr vol;
- bool ret = true;
+ bool ret = false;
const char *name;
+ const char *algorithm = NULL;
+ unsigned int flags = 0;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
@@ -10978,13 +10981,45 @@ cmdVolWipe(vshControl *ctl, const vshCmd *cmd)
return false;
}
- if (virStorageVolWipe(vol, 0) == 0) {
+ if (vshCommandOptString(cmd, "algorithm", &algorithm) < 0) {
+ vshError(ctl, "%s", _("missing argument"));
+ goto out;
+ }
+
+ if (algorithm) {
+ if (STREQ(algorithm, "nnsa"))
+ flags |= VIR_STORAGE_VOL_WIPE_ALG_NNSA;
+ else if (STREQ(algorithm, "dod"))
+ flags |= VIR_STORAGE_VOL_WIPE_ALG_DOD;
+ else if (STREQ(algorithm, "bsi"))
+ flags |= VIR_STORAGE_VOL_WIPE_ALG_BSI;
+ else if (STREQ(algorithm, "gutmann"))
+ flags |= VIR_STORAGE_VOL_WIPE_ALG_GUTMANN;
+ else if (STREQ(algorithm, "schneier"))
+ flags |= VIR_STORAGE_VOL_WIPE_ALG_SCHNEIER;
+ else if (STREQ(algorithm, "pfitzner7"))
+ flags |= VIR_STORAGE_VOL_WIPE_ALG_PFITZNER7;
+ else if (STREQ(algorithm, "pfitzner33"))
+ flags |= VIR_STORAGE_VOL_WIPE_ALG_PFITZNER33;
+ else if (STREQ(algorithm, "random"))
+ flags |= VIR_STORAGE_VOL_WIPE_ALG_RANDOM;
+ else {
+ vshError(ctl, _("Unsupported algorithm '%s'"),
+ algorithm);
+ goto out;
+ }
+ }
+
+ if (virStorageVolWipe(vol, flags) == 0) {
vshPrint(ctl, _("Vol %s wiped\n"), name);
} else {
vshError(ctl, _("Failed to wipe vol %s"), name);
- ret = false;
+ goto out;
}
+ ret = true;
+
+out:
virStorageVolFree(vol);
return ret;
}
diff --git a/tools/virsh.pod b/tools/virsh.pod
index 138f886..d96e8c8 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1912,12 +1912,35 @@ I<vol-name-or-key-or-path> is the name or key or path of the
volume to wipe.
I<--offset> is the position in the storage volume at which to start reading
the data. I<--length> is an upper bound of the amount of data to be downloaded.
-=item B<vol-wipe> [I<--pool> I<pool-or-uuid>]
I<vol-name-or-key-or-path>
+=item B<vol-wipe> [I<--pool> I<pool-or-uuid>] [I<--algorithm>
I<algorithm>]
+I<vol-name-or-key-or-path>
Wipe a volume, ensure data previously on the volume is not accessible to
future reads. I<--pool> I<pool-or-uuid> is the name or UUID of the storage
pool the volume is in.
I<vol-name-or-key-or-path> is the name or key or path of the volume to wipe.
+It is possible to choose different wiping algorithms instead of re-writing
+volume with zeroes. This can be done via I<--algorithm> switch.
+
+B<Supported algorithms>
+ nnsa - 4-pass NNSA Policy Letter NAP-14.1-C (XVI-8) for
+ sanitizing removable and non-removable hard disks:
+ random(x2), 0x00, verify.
+ dod - 4-pass DoD 5220.22-M section 8-306 procedure for
+ sanitizing removeable and non-removeable rigid
+ disks: random, 0x00, 0xff, verify.
+ bsi - 9-pass method recommended by the German Center of
+ Security in Information Technologies
+ (
http://www.bsi.bund.de): 0xff, 0xfe, 0xfd, 0xfb,
+ 0xf7, 0xef, 0xdf, 0xbf, 0x7f.
+ gutmann - The canonical 35-pass sequence described in
+ Gutmann's paper.
+ schneier - 7-pass method described by Bruce Schneier in
+ "Applied Cryptography" (1996): 0x00, 0xff,
+ random(x5).
+ pfitzner7 - Roy Pfitzner's 7-random-pass method: random(x7).
+ pfitzner33 - Roy Pfitzner's 33-random-pass method: random(x33).
+ random - 1-pass pattern: random(x1).
=item B<vol-dumpxml> [I<--pool> I<pool-or-uuid>]
I<vol-name-or-key-or-path>
--
1.7.3.4