swapon - Implement encrypted swap
authorMatthew Dillon <dillon@apollo.backplane.com>
Sat, 16 May 2015 20:45:03 +0000 (13:45 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sat, 16 May 2015 20:45:03 +0000 (13:45 -0700)
Implement crypting of the swap device.  When enabled in this manner
/dev/urandom is used to generate a 256-bit random key and the base device
is automatically cryptsetup and mapped, making crypted swap trivial.

* Implement the 'crypt' fstab option, so swapon -a and swapoff -a work
  as expected for crypted swap.  Again, the base device (e.g. /dev/da0s1b)
  should be specified.  The option will automatically map it with cryptsetup
  and swap on the mapping.

  Also implement -c to crypt manual swapon/swapoff commands.  If used for
  swapon it must also be used for swapoff.  Again, specify the base device
  (e.g. /dev/da0s1b), not the /dev/mapper device, for both cases.

* Implement the 'trim' fstab option, allow trim to be specified in the fstab
  instead of requiring a rc.conf option.

* The trim option no longer exits with an error if the device does not
  support TRIM or TRIM is not enabled.

sbin/swapon/swapon.8
sbin/swapon/swapon.c

index 040f22c..a32cf64 100644 (file)
@@ -35,7 +35,7 @@
 .Nm swapon , swapoff , swapctl
 .Nd "specify devices for paging and swapping"
 .Sh SYNOPSIS
-.Nm swapon Fl aeiq | Ar
+.Nm swapon Fl aceiq | Ar
 .Nm swapoff Fl aq | Ar
 .Nm swapctl
 .Op Fl AeghklmsU
@@ -66,14 +66,30 @@ If the
 .Fl a
 option is used, all swap devices in
 .Pa /etc/fstab
-will be added, unless their
-.Dq noauto
-option is also set.
+will be added.
+The following options are supported:
+.Bl -tag -width indent
+.It Dq noauto
+The device is ignored and will not be added or removed with this option.
+.It Dq crypt
+Swap will be encrpted with a random key using a /dev/mapper name of
+swap-<device> ,
+for example 'swap-da0s1b'.
+This will also load the dm_target_crypt module if necessary.
+.It Dq trim
+Swap will be TRIMed if the device supports it, otherwise this option
+will be ignored.
+.El
+.Pp
 If the
 .Fl q
 option is used informational messages will not be
 written to standard output when a swap device is added.
 If the
+.Fl c
+option is used, the device will be encrypted with a random
+key.
+If the
 .Fl e
 option is used, the device will be trimmed if
 it supports trim and the trim_enabled sysctl is on.
@@ -92,6 +108,14 @@ will be removed, unless their
 .Dq noauto
 option is also set.
 If the
+.Fl c
+option is used the device is mapped to the appropriate crypto device
+and the crypto device is removed as well.
+If this option is specified in
+.Nm swapon
+then it should also be specified in
+.Nm swapoff .
+If the
 .Fl q
 option is used informational messages will not be
 written to standard output when a swap device is removed.
@@ -156,10 +180,14 @@ Output values in megabytes.
 List the devices making up system swap.
 .It Fl s
 Print a summary line for system swap.
+.If Fl c
+The swap is or should be crypted.
 .It Fl e
 Attempts to Trim the device if -[Aa] is used.
 .It Fl i
 Asks user confirmation when -a is used.
+.It Fl q
+Less noisy output.
 .Pp
 The
 .Ev BLOCKSIZE
index 1ff942d..508a7fe 100644 (file)
@@ -34,6 +34,7 @@
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/sysctl.h>
+#include <sys/linker.h>
 #include <sys/diskslice.h>
 #include <sys/ioctl_compat.h>
 #include <vm/vm_param.h>
 
 static void usage(void);
 static int swap_on_off(char *name, int doingall, int trim, int ask);
+static char *docrypt(char *fs_spec, int pass);
 static void swaplist(int lflag, int sflag, int hflag);
 
+static int qflag;
+
 enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL;
 
 int
@@ -61,7 +65,7 @@ main(int argc, char **argv)
        char *ptr;
        int ret;
        int ch;
-       int doall, sflag, lflag, hflag, qflag, eflag, iflag;
+       int doall, sflag, lflag, hflag, eflag, cflag, iflag;
 
        if ((ptr = strrchr(argv[0], '/')) == NULL)
                ptr = argv[0];
@@ -71,8 +75,8 @@ main(int argc, char **argv)
                which_prog = SWAPOFF;
        orig_prog = which_prog;
 
-       sflag = lflag = hflag = qflag = doall = eflag = iflag = 0;
-       while ((ch = getopt(argc, argv, "AadeghiklmqsU")) != -1) {
+       sflag = lflag = hflag = doall = eflag = cflag = iflag = 0;
+       while ((ch = getopt(argc, argv, "AacdeghiklmqsU")) != -1) {
                switch((char)ch) {
                case 'A':
                        if (which_prog == SWAPCTL) {
@@ -94,6 +98,9 @@ main(int argc, char **argv)
                        else
                                usage();
                        break;
+               case 'c':
+                       cflag = 1;
+                       break;
                case 'e':
                        eflag = 1;
                        break;
@@ -141,33 +148,65 @@ main(int argc, char **argv)
        if (which_prog == SWAPON || which_prog == SWAPOFF) {
                if (doall) {
                        while ((fsp = getfsent()) != NULL) {
+                               char *fs_spec;
+                               int dotrim = eflag;
+
                                if (strcmp(fsp->fs_type, FSTAB_SW))
                                        continue;
                                if (strstr(fsp->fs_mntops, "noauto"))
                                        continue;
-                               if (swap_on_off(fsp->fs_spec, 1, eflag, iflag)) {
+
+                               if (strstr(fsp->fs_mntops, "notrim"))
+                                       dotrim = 0;
+                               else if (strstr(fsp->fs_mntops, "trim"))
+                                       dotrim = 1;
+
+                               if (cflag || strstr(fsp->fs_mntops, "crypt"))
+                                       fs_spec = docrypt(fsp->fs_spec, 1);
+                               else
+                                       fs_spec = strdup(fsp->fs_spec);
+                               if (swap_on_off(fs_spec, 1, dotrim, iflag)) {
                                        ret = 1;
                                } else {
+                                       if (cflag ||
+                                           strstr(fsp->fs_mntops, "crypt")) {
+                                               docrypt(fsp->fs_spec, 2);
+                                       }
                                        if (!qflag) {
-                                               printf("%s: %sing %s as swap device\n",
+                                               printf("%s: %sing %s as swap "
+                                                      "device\n",
                                                    getprogname(),
-                                                   which_prog == SWAPOFF ? "remov" : "add",
-                                                   fsp->fs_spec);
+                                                   (which_prog == SWAPOFF ?
+                                                           "remov" : "add"),
+                                                   fs_spec);
                                        }
                                }
+                               free(fs_spec);
                        }
                } else if (*argv == NULL) {
                        usage();
                }
                for (; *argv; ++argv) {
-                       if (swap_on_off(getdevpath(*argv, 0), 0, eflag, iflag)) {
+                       char *ospec = getdevpath(*argv, 0);
+                       char *fs_spec;
+                       if (cflag)
+                               fs_spec = docrypt(ospec, 1);
+                       else
+                               fs_spec = strdup(ospec);
+                       if (swap_on_off(fs_spec, 0, eflag, iflag)) {
                                ret = 1;
-                       } else if (orig_prog == SWAPCTL) {
-                               printf("%s: %sing %s as swap device\n",
-                                   getprogname(),
-                                   which_prog == SWAPOFF ? "remov" : "add",
-                                   *argv);
+                       } else {
+                               if (cflag)
+                                       docrypt(ospec, 2);
+                               if (!qflag) {
+                                       printf("%s: %sing %s as swap device\n",
+                                           getprogname(),
+                                           (which_prog == SWAPOFF ?
+                                               "remov" : "add"),
+                                           fs_spec);
+                               }
                        }
+                       free(fs_spec);
                }
        } else {
                if (lflag || sflag)
@@ -178,6 +217,68 @@ main(int argc, char **argv)
        exit(ret);
 }
 
+static
+char *
+docrypt(char *fs_spec, int pass)
+{
+       char *id;
+       char *res;
+       char *buf;
+
+       if ((id = strrchr(fs_spec, '/')) == NULL)
+               id = fs_spec;
+       else
+               ++id;
+       asprintf(&id, "swap-%s", id);
+       asprintf(&res, "/dev/mapper/%s", id);
+
+       switch(which_prog) {
+       case SWAPOFF:
+               if (pass != 2)
+                       break;
+               asprintf(&buf, "/sbin/cryptsetup remove %s", id);
+               system(buf);
+               free(id);
+               break;
+       case SWAPON:
+               if (pass != 1)
+                       break;
+               if (kldfind("dm_target_crypt") < 0)
+                       kldload("dm_target_crypt");
+
+               asprintf(&buf,
+                        "/sbin/cryptsetup --key-file /dev/urandom "
+                        "--key-size 256 create %s %s",
+                        id, fs_spec);
+               if (qflag == 0)
+                       printf("%s\n", buf);
+               system(buf);
+               free(buf);
+               free(id);
+
+               /*
+                * NOTE: Don't revert to /dev/da* on error because this could
+                *       inadvertently add both /dev/da* and
+                *       /dev/mapper/swap-da*.
+                *
+                *       Allow the swapon operation to report failure or
+                *       report a duplicate.
+                */
+               break;
+       default:
+               free(res);
+               free(id);
+               res = strdup(fs_spec);
+               break;
+       }
+
+       if (pass == 2) {
+               free (res);
+               res = NULL;
+       }
+       return res;
+}
+
 /*
  * TRIM the device
  */
@@ -261,7 +362,7 @@ swap_on_off(char *name, int doingall, int trim, int ask)
                        return(1);
 
        }
-       if (which_prog == SWAPON && trim){
+       if (which_prog == SWAPON && trim) {
                char sysctl_name[64];
                int trim_enabled = 0;
                size_t olen = sizeof(trim_enabled);
@@ -269,19 +370,22 @@ swap_on_off(char *name, int doingall, int trim, int ask)
                dev_name = strtok(dev_name + strlen("/dev/da"),"s");
                sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", dev_name);
                sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
-               if(errno == ENOENT) {
-                       printf("Device:%s does not support the TRIM command\n",
-                           name);
-                       usage();
-               }
-               if(!trim_enabled) {
-                       printf("Erase device option selected, but sysctl (%s) "
-                           "is not enabled\n",sysctl_name);
-                       usage();
+               if (errno == ENOENT) {
+                       if (qflag == 0) {
+                               printf("TRIM not supported on %s, "
+                                      "ignoring\n",
+                                      name);
+                       }
+                       errno = 0;
+               } else if (!trim_enabled) {
+                       if (qflag == 0) {
+                               printf("TRIM not enabled on %s (%s), "
+                                      "ignoring\n",
+                                      name, sysctl_name);
+                       }
+               } else {
+                       trim_volume(name);
                }
-
-               trim_volume(name);
-
        }
        if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
                switch(errno) {