From adb99a05b174ae25f4259713476878d244f80a1f Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Mon, 9 Jan 2012 17:05:03 +0100 Subject: [PATCH] storage: Support different wiping algorithms 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. --- configure.ac | 23 ++++++++ include/libvirt/libvirt.h.in | 32 +++++++++++ src/driver.h | 5 ++ src/libvirt.c | 49 ++++++++++++++++ src/libvirt_public.syms | 2 + src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 9 ++- src/remote_protocol-structs | 6 ++ src/storage/storage_driver.c | 107 ++++++++++++++++++++++++++++------- tools/virsh.c | 39 +++++++++++-- tools/virsh.pod | 26 ++++++++- 11 files changed, 270 insertions(+), 29 deletions(-) diff --git a/configure.ac b/configure.ac index 6709ebf182..9fb7bfcca9 100644 --- a/configure.ac +++ b/configure.ac @@ -2500,8 +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_macvtap=${withval}], + [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 # is included as part of the distribution tarball. diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 182065d3fd..e99cd003fe 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2207,6 +2207,35 @@ typedef enum { VIR_STORAGE_VOL_DELETE_ZEROED = 1 << 0, /* Clear all data to zeros (slow) */ } virStorageVolDeleteFlags; +typedef enum { + VIR_STORAGE_VOL_WIPE_ALG_ZERO = 0, /* 1-pass, all zeroes */ + VIR_STORAGE_VOL_WIPE_ALG_NNSA = 1, /* 4-pass NNSA Policy Letter + NAP-14.1-C (XVI-8) */ + VIR_STORAGE_VOL_WIPE_ALG_DOD = 2, /* 4-pass DoD 5220.22-M section + 8-306 procedure */ + VIR_STORAGE_VOL_WIPE_ALG_BSI = 3, /* 9-pass method recommended by the + German Center of Security in + Information Technologies */ + VIR_STORAGE_VOL_WIPE_ALG_GUTMANN = 4, /* The canonical 35-pass sequence */ + VIR_STORAGE_VOL_WIPE_ALG_SCHNEIER = 5, /* 7-pass method described by + Bruce Schneier in "Applied + Cryptography" (1996) */ + VIR_STORAGE_VOL_WIPE_ALG_PFITZNER7 = 6, /* 7-pass random */ + + VIR_STORAGE_VOL_WIPE_ALG_PFITZNER33 = 7, /* 33-pass random */ + + VIR_STORAGE_VOL_WIPE_ALG_RANDOM = 8, /* 1-pass random */ + +#ifdef VIR_ENUM_SENTINELS + /* + * NB: this enum value will increase over time as new algorithms are + * added to the libvirt API. It reflects the last algorithm supported + * by this version of the libvirt API. + */ + VIR_STORAGE_VOL_WIPE_ALG_LAST +#endif +} virStorageVolWipeAlgorithm; + typedef struct _virStorageVolInfo virStorageVolInfo; struct _virStorageVolInfo { @@ -2344,6 +2373,9 @@ int virStorageVolDelete (virStorageVolPtr vol, unsigned int flags); int virStorageVolWipe (virStorageVolPtr vol, unsigned int flags); +int virStorageVolWipePattern (virStorageVolPtr vol, + unsigned int algorithm, + unsigned int flags); int virStorageVolRef (virStorageVolPtr vol); int virStorageVolFree (virStorageVolPtr vol); diff --git a/src/driver.h b/src/driver.h index 6222bed166..df2aa60b93 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1230,6 +1230,10 @@ typedef int typedef int (*virDrvStorageVolWipe) (virStorageVolPtr vol, unsigned int flags); +typedef int + (*virDrvStorageVolWipePattern) (virStorageVolPtr vol, + unsigned int algorithm, + unsigned int flags); typedef int (*virDrvStorageVolGetInfo) (virStorageVolPtr vol, @@ -1315,6 +1319,7 @@ struct _virStorageDriver { virDrvStorageVolUpload volUpload; virDrvStorageVolDelete volDelete; virDrvStorageVolWipe volWipe; + virDrvStorageVolWipePattern volWipePattern; virDrvStorageVolGetInfo volGetInfo; virDrvStorageVolGetXMLDesc volGetXMLDesc; virDrvStorageVolGetPath volGetPath; diff --git a/src/libvirt.c b/src/libvirt.c index 722a2e2e12..8be4e13cec 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -12681,6 +12681,55 @@ error: } +/** + * virStorageVolWipePattern: + * @vol: pointer to storage volume + * @algorithm: one of virStorageVolWipeAlgorithm + * @flags: future flags, use 0 for now + * + * Similar to virStorageVolWipe, but one can choose + * between different wiping algorithms. + * + * Returns 0 on success, or -1 on error. + */ +int +virStorageVolWipePattern(virStorageVolPtr vol, + unsigned int algorithm, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("vol=%p, algorithm=%u, flags=%x", vol, algorithm, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STORAGE_VOL(vol)) { + virLibStorageVolError(VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = vol->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStorageVolError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->storageDriver && conn->storageDriver->volWipePattern) { + int ret; + ret = conn->storageDriver->volWipePattern(vol, algorithm, flags); + if (ret < 0) { + goto error; + } + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(vol->conn); + return -1; +} + /** * virStorageVolFree: * @vol: pointer to storage volume diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index e0cbdb448d..b7f1944e8b 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -519,5 +519,7 @@ LIBVIRT_0.9.9 { LIBVIRT_0.9.10 { global: virDomainShutdownFlags; + virStorageVolWipePattern; } LIBVIRT_0.9.9; + # .... define new API here using predicted next version number .... diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index f45a8feb40..f79f53e510 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -4833,6 +4833,7 @@ static virStorageDriver storage_driver = { .volUpload = remoteStorageVolUpload, /* 0.9.0 */ .volDelete = remoteStorageVolDelete, /* 0.4.1 */ .volWipe = remoteStorageVolWipe, /* 0.8.0 */ + .volWipePattern = remoteStorageVolWipePattern, /* 0.9.10 */ .volGetInfo = remoteStorageVolGetInfo, /* 0.4.1 */ .volGetXMLDesc = remoteStorageVolGetXMLDesc, /* 0.4.1 */ .volGetPath = remoteStorageVolGetPath, /* 0.4.1 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 6a20ae81d6..0f354bb242 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1643,6 +1643,12 @@ struct remote_storage_vol_wipe_args { unsigned int flags; }; +struct remote_storage_vol_wipe_pattern_args { + remote_nonnull_storage_vol vol; + unsigned int algorithm; + unsigned int flags; +}; + struct remote_storage_vol_get_xml_desc_args { remote_nonnull_storage_vol vol; unsigned int flags; @@ -2660,7 +2666,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_NUMA_PARAMETERS = 255, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_SET_INTERFACE_PARAMETERS = 256, /* autogen autogen */ REMOTE_PROC_DOMAIN_GET_INTERFACE_PARAMETERS = 257, /* skipgen skipgen */ - REMOTE_PROC_DOMAIN_SHUTDOWN_FLAGS = 258 /* autogen autogen */ + REMOTE_PROC_DOMAIN_SHUTDOWN_FLAGS = 258, /* autogen autogen */ + REMOTE_PROC_STORAGE_VOL_WIPE_PATTERN = 259 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 430d8e4a6c..de85862196 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1234,6 +1234,11 @@ struct remote_storage_vol_wipe_args { remote_nonnull_storage_vol vol; u_int flags; }; +struct remote_storage_vol_wipe_pattern_args { + remote_nonnull_storage_vol vol; + u_int algorithm; + u_int flags; +}; struct remote_storage_vol_get_xml_desc_args { remote_nonnull_storage_vol vol; u_int flags; @@ -2095,4 +2100,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SET_INTERFACE_PARAMETERS = 256, REMOTE_PROC_DOMAIN_GET_INTERFACE_PARAMETERS = 257, REMOTE_PROC_DOMAIN_SHUTDOWN_FLAGS = 258, + REMOTE_PROC_STORAGE_VOL_WIPE_PATTERN = 259, }; diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 8c2d6e1a1b..a332ada42e 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -1801,14 +1801,17 @@ out: static int -storageVolumeWipeInternal(virStorageVolDefPtr def) +storageVolumeWipeInternal(virStorageVolDefPtr def, + unsigned int algorithm) { 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' and algorithm %u", + def->target.path, algorithm); fd = open(def->target.path, O_RDWR); if (fd == -1) { @@ -1825,36 +1828,83 @@ 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(); - goto out; + if (algorithm != VIR_STORAGE_VOL_WIPE_ALG_ZERO) { + const char *alg_char ATTRIBUTE_UNUSED = NULL; + switch (algorithm) { +#ifdef SCRUB + case VIR_STORAGE_VOL_WIPE_ALG_NNSA: + alg_char = "nnsa"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_DOD: + alg_char = "dod"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_BSI: + alg_char = "bsi"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_GUTMANN: + alg_char = "gutmann"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_SCHNEIER: + alg_char = "shneier"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_PFITZNER7: + alg_char = "pfitzner7"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_PFITZNER33: + alg_char = " pfitzner33"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_RANDOM: + alg_char = "random"; + break; +#endif + default: + virStorageReportError(VIR_ERR_INVALID_ARG, + _("unsupported algorithm %d"), + algorithm); } +#ifdef SCRUB + cmd = virCommandNew(SCRUB); + virCommandAddArgList(cmd, "-f", "-p", alg_char, + def->target.path, NULL); - ret = storageWipeExtent(def, - fd, - 0, - def->allocation, - writebuf, - st.st_blksize, - &bytes_wiped); + if (virCommandRun(cmd, NULL) < 0) + goto out; + + ret = 0; +#endif + goto out; + } 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; } static int -storageVolumeWipe(virStorageVolPtr obj, - unsigned int flags) +storageVolumeWipePattern(virStorageVolPtr obj, + unsigned int algorithm, + unsigned int flags) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool = NULL; @@ -1863,6 +1913,13 @@ storageVolumeWipe(virStorageVolPtr obj, virCheckFlags(0, -1); + if (algorithm >= VIR_STORAGE_VOL_WIPE_ALG_LAST) { + virStorageReportError(VIR_ERR_INVALID_ARG, + _("wiping algorithm %d not supported"), + algorithm); + return -1; + } + storageDriverLock(driver); pool = virStoragePoolObjFindByName(&driver->pools, obj->pool); storageDriverUnlock(driver); @@ -1895,7 +1952,7 @@ storageVolumeWipe(virStorageVolPtr obj, goto out; } - if (storageVolumeWipeInternal(vol) == -1) { + if (storageVolumeWipeInternal(vol, algorithm) == -1) { goto out; } @@ -1910,6 +1967,13 @@ out: } +static int +storageVolumeWipe(virStorageVolPtr obj, + unsigned int flags) +{ + return storageVolumeWipePattern(obj, VIR_STORAGE_VOL_WIPE_ALG_ZERO, flags); +} + static int storageVolumeDelete(virStorageVolPtr obj, unsigned int flags) { @@ -2175,6 +2239,7 @@ static virStorageDriver storageDriver = { .volUpload = storageVolumeUpload, /* 0.9.0 */ .volDelete = storageVolumeDelete, /* 0.4.0 */ .volWipe = storageVolumeWipe, /* 0.8.0 */ + .volWipePattern = storageVolumeWipePattern, /* 0.9.10 */ .volGetInfo = storageVolumeGetInfo, /* 0.4.0 */ .volGetXMLDesc = storageVolumeGetXMLDesc, /* 0.4.0 */ .volGetPath = storageVolumeGetPath, /* 0.4.0 */ diff --git a/tools/virsh.c b/tools/virsh.c index 999941c52a..74655c22b6 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -11160,15 +11160,24 @@ 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} }; +VIR_ENUM_DECL(virStorageVolWipeAlgorithm) +VIR_ENUM_IMPL(virStorageVolWipeAlgorithm, VIR_STORAGE_VOL_WIPE_ALG_LAST, + "zero", "nnsa", "dod", "bsi", "gutmann", "schneier", + "pfitzner7", "pfitzner33", "random"); + static bool cmdVolWipe(vshControl *ctl, const vshCmd *cmd) { virStorageVolPtr vol; - bool ret = true; + bool ret = false; const char *name; + const char *algorithm_str = NULL; + int algorithm = VIR_STORAGE_VOL_WIPE_ALG_ZERO; + int funcRet; if (!vshConnectionUsability(ctl, ctl->conn)) return false; @@ -11177,13 +11186,31 @@ cmdVolWipe(vshControl *ctl, const vshCmd *cmd) return false; } - if (virStorageVolWipe(vol, 0) == 0) { - vshPrint(ctl, _("Vol %s wiped\n"), name); - } else { - vshError(ctl, _("Failed to wipe vol %s"), name); - ret = false; + if (vshCommandOptString(cmd, "algorithm", &algorithm_str) < 0) { + vshError(ctl, "%s", _("missing argument")); + goto out; } + if (algorithm_str && + (algorithm = virStorageVolWipeAlgorithmTypeFromString(algorithm_str)) < 0) { + vshError(ctl, _("Unsupported algorithm '%s'"), algorithm_str); + goto out; + } + + if ((funcRet = virStorageVolWipePattern(vol, algorithm, 0)) < 0) { + if (last_error->code == VIR_ERR_NO_SUPPORT && + algorithm == VIR_STORAGE_VOL_WIPE_ALG_ZERO) + funcRet = virStorageVolWipe(vol, 0); + } + + if (funcRet < 0) { + vshError(ctl, _("Failed to wipe vol %s"), name); + goto out; + } + + vshPrint(ctl, _("Vol %s wiped\n"), name); + ret = true; +out: virStorageVolFree(vol); return ret; } diff --git a/tools/virsh.pod b/tools/virsh.pod index e1d8774e39..8599f66e18 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1934,12 +1934,36 @@ I 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 [I<--pool> I] I +=item B [I<--pool> I] [I<--algorithm> I] +I Wipe a volume, ensure data previously on the volume is not accessible to future reads. I<--pool> I is the name or UUID of the storage pool the volume is in. I 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 + zero - 1-pass all zeroes + 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. =item B [I<--pool> I] I