]> xenbits.xensource.com Git - libvirt.git/commitdiff
storage: Support different wiping algorithms
authorMichal Privoznik <mprivozn@redhat.com>
Mon, 9 Jan 2012 16:05:03 +0000 (17:05 +0100)
committerMichal Privoznik <mprivozn@redhat.com>
Thu, 26 Jan 2012 12:59:30 +0000 (13:59 +0100)
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
include/libvirt/libvirt.h.in
src/driver.h
src/libvirt.c
src/libvirt_public.syms
src/remote/remote_driver.c
src/remote/remote_protocol.x
src/remote_protocol-structs
src/storage/storage_driver.c
tools/virsh.c
tools/virsh.pod

index 6709ebf18284c86106148a2010a7fb58b184fd53..9fb7bfcca90e1237f63e9dbf8304a84f0f2252b2 100644 (file)
@@ -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.
index 182065d3fd7b24d861cbeb1eb15fbcc41d45402c..e99cd003fed29101408466f262adab79025575f5 100644 (file)
@@ -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);
 
index 6222bed1669e497d4da98c8897def863489a4e6e..df2aa60b93b18f2b59fcdb976f78d2b8a9e1199d 100644 (file)
@@ -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;
index 722a2e2e12423251c5038f89aa8f03b0dfe6721b..8be4e13cec9920e30191ed4c4ed2ebd0bf0bd19b 100644 (file)
@@ -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
index e0cbdb448d4696d6531b7faa65b1562aded9426c..b7f1944e8bc7bbf24efa8d11d24fea1343367968 100644 (file)
@@ -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 ....
index f45a8feb4047a20665535811c5e0ceb5f305ab2f..f79f53e5104e762c9c0f59380ed57dd25aaa7599 100644 (file)
@@ -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 */
index 6a20ae81d645430bf24499e7dccf56b691c8db26..0f354bb242809d009c921b732e5a6ba7a9c9d59c 100644 (file)
@@ -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 ?
index 430d8e4a6caffd73417708515b9a06b4e8914421..de8586219667cbf61d8e4453ebccb90ed8c953b0 100644 (file)
@@ -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,
 };
index 8c2d6e1a1bd1daeff1d2225af73c003e39b9e0da..a332ada42ed32d2dd9f11c8bc7f5f887b80387bf 100644 (file)
@@ -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 (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);
 
-        if (VIR_ALLOC_N(writebuf, st.st_blksize) != 0) {
-            virReportOOMError();
+        if (virCommandRun(cmd, NULL) < 0)
             goto out;
-        }
 
-        ret = storageWipeExtent(def,
-                                fd,
-                                0,
-                                def->allocation,
-                                writebuf,
-                                st.st_blksize,
-                                &bytes_wiped);
+        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 */
index 999941c52a3b7a8c6fe1363201f5e2196e278652..74655c22b6cb78aeb993347090c1c9bc647edebf 100644 (file)
@@ -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 {
+    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);
-        ret = false;
+        goto out;
     }
 
+    vshPrint(ctl, _("Vol %s wiped\n"), name);
+    ret = true;
+out:
     virStorageVolFree(vol);
     return ret;
 }
index e1d8774e39149ac0c8d942ced0f84bde8c3ccd86..8599f66e18bcf4d90119ba2e43e8d4d5c6116857 100644 (file)
@@ -1934,12 +1934,36 @@ 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>
+  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<vol-dumpxml> [I<--pool> I<pool-or-uuid>] I<vol-name-or-key-or-path>