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