From owner-freebsd-hackers Wed Sep 25 16:22:13 2002 Delivered-To: freebsd-hackers@freebsd.org Received: from mx1.FreeBSD.org (mx1.FreeBSD.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 2B2B737B401 for ; Wed, 25 Sep 2002 16:22:06 -0700 (PDT) Received: from gatekeeper.oremut01.us.wh.verio.net (gatekeeper.oremut01.us.wh.verio.net [198.65.168.16]) by mx1.FreeBSD.org (Postfix) with ESMTP id 9056143E6A for ; Wed, 25 Sep 2002 16:22:05 -0700 (PDT) (envelope-from gritton@iserver.com) Received: from guppy.dmz.orem.verio.net (guppy.dmz.orem.verio.net [10.1.1.55]) by gatekeeper.oremut01.us.wh.verio.net (Postfix) with ESMTP id E064D3BF14C for ; Wed, 25 Sep 2002 17:22:04 -0600 (MDT) Received: from guppy.dmz.orem.verio.net by guppy.dmz.orem.verio.net; Wed, 25 Sep 2002 17:22:04 -0600 (MDT) Received: (from gritton@localhost) by guppy.dmz.orem.verio.net (8.12.3/8.12.3/Submit) id g8PNM494056009; Wed, 25 Sep 2002 17:22:04 -0600 (MDT) To: hackers@FreeBSD.ORG Subject: The poor man's cryptfs From: James Gritton Date: 25 Sep 2002 17:22:04 -0600 Message-ID: Lines: 290 X-Mailer: Gnus v5.7/Emacs 20.7 Sender: owner-freebsd-hackers@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.ORG After playing with a few encrypted filesystems, and giving up on them (after a kernel crash or two), I went looking for something else to encrypt. The logical choice is the device. Well, the virtual device. Like a cryptfs that's based on a loopback mount, I'm encrypting a virtual device based on the "vn" driver. This was only a few hours' work, though it's admittedly incomplete. This is based on the Blowfish code in the kernel used by ipsec and such, which an extra ioctl added to set the key. Only three source files require modification: sys/sys/vnioctl.h: Define the VNIOCSETKEY ioctl usr.sbin/vnconfig/vnconfig.c: Add a "-k" option to specify that an encryption key should be entered via getpass(), and passed in with the above ioctl. sys/dev/vn/vn.c Add a blowfish key entry to the softc structure. This is set via the above ioctl, which converts a passed-in string into the key data. Encryption is done around the vn's VOP_READ and VOP_WRITE calls, in 512-byte CBC chunks. That's it - 90 lines of new code. This is for my purposes complete and working, which is to say neither is quite true. For production-quality code, some work remains: * The vn.c code is tied to blowfish. It would be better to have some dynamically selectable encryption scheme. I like blowfish and it's available, but others may want something else. The vnconfig hack doesn't contain any mention of an encryption scheme - it just passes a key string. * It doesn't work with swap-backed vn devices. It wouldn't be hard to put the same encrypt-decrypt wrappers around the vm_pager_strategy() call, or perhaps even to merely move the wrappers further out to encompass both vnode and object access methods. * It doesn't work with labels, probably because of some kernel function(s) that make their own read/write calls outside of the vnstrategy's own VOP_READ and VOP_WRITE. To build a new filesystem, I had to newfs a swap-backed vn and dd it to the encrypted vn. But then I can mount the encrypted vn without worrying about labels. * It requires the blowfish functions to be linked into the kernel, which I accomplished by changing them from "optional ... ipsec" to "standard" in the kernel. I suppose I could have somehoe linked them to vn.ko directly, but that seems the wrong way around. So even though vn is a module, it requires a kernel recompile to use it, unless you're running ipsec or ipv6. The diffs for what I have are small. If someone wants to make it (more) complete, the further diffs should be small as well. - Jamie --- sys/sys/vnioctl.h.orig Wed Sep 25 16:38:53 2002 +++ sys/sys/vnioctl.h Tue Sep 24 22:17:26 2002 @@ -68,6 +68,9 @@ #define VNIOCGCLEAR _IOWR('F', 3, u_long ) /* reset --//-- */ #define VNIOCUSET _IOWR('F', 4, u_long ) /* set unit option */ #define VNIOCUCLEAR _IOWR('F', 5, u_long ) /* reset --//-- */ +#ifdef VNCRYPT +#define VNIOCSETKEY _IOW('F', 47, char * ) /* set key */ +#endif #define VN_LABELS 0x1 /* Use disk(/slice) labels */ #define VN_FOLLOW 0x2 /* Debug flow in vn driver */ --- usr.sbin/vnconfig/vnconfig.c.orig Wed Sep 25 16:38:38 2002 +++ usr.sbin/vnconfig/vnconfig.c Wed Sep 25 16:43:44 2002 @@ -88,6 +88,9 @@ #define VN_RESET 0x200 #define VN_TRUNCATE 0x400 #define VN_ZERO 0x800 +#ifdef VNCRYPT +#define VN_SETKEY 0x1000 +#endif int nvndisks; @@ -118,7 +121,11 @@ char *s; configfile = _PATH_VNTAB; +#ifdef VNCRYPT + while ((i = getopt(argc, argv, "acdef:gkr:s:S:TZL:uv")) != -1) +#else while ((i = getopt(argc, argv, "acdef:gr:s:S:TZL:uv")) != -1) +#endif switch (i) { /* all -- use config file */ @@ -154,6 +161,13 @@ global = 1 - global; break; +#ifdef VNCRYPT + /* set key */ + case 'k': + flags |= VN_SETKEY; + break; +#endif + /* reset options */ case 'r': for (s = strtok(optarg, ","); s; s = strtok(NULL, ",")) { @@ -399,6 +413,18 @@ dev, vnio.vn_size, file ); } +#ifdef VNCRYPT + if (flags & VN_SETKEY) { + char *key; + + /* Read an encryption key and set it */ + key = getpass("key: "); + if (!key[0]) + key = NULL; + if (ioctl(fileno(f), VNIOCSETKEY, &key)) + warn("VNIOCSETKEY"); + } +#endif /* * autolabel */ --- sys/dev/vn/vn.c.orig Wed Sep 25 16:39:19 2002 +++ sys/dev/vn/vn.c Wed Sep 25 16:43:24 2002 @@ -87,6 +87,10 @@ #include #include +#ifdef VNCRYPT +#include +#endif + static d_ioctl_t vnioctl; static d_open_t vnopen; static d_close_t vnclose; @@ -140,6 +144,9 @@ struct buf sc_tab; /* transfer queue */ u_long sc_options; /* options */ dev_t sc_devlist; /* devices that refer to this unit */ +#ifdef VNCRYPT + BF_KEY sc_key; /* blowfish key */ +#endif SLIST_ENTRY(vn_softc) sc_list; }; @@ -385,12 +392,53 @@ auio.uio_rw = UIO_WRITE; auio.uio_resid = bp->b_bcount; auio.uio_procp = curproc; +#ifdef VNCRYPT + if (vn->sc_key.P[0] && !(bp->b_flags & B_READ)) { + BF_LONG *blp; + + /* Encode data to be written using CBC. + * The chain size is the disk sector size - 512 bytes. + */ + for (blp = (BF_LONG *)bp->b_data; blp < + (BF_LONG *)((char *)bp->b_data + bp->b_bcount); + blp += 2) { + if (((char *)blp - (char *)bp->b_bcount) & + 0x1ff) + blp[0] ^= blp[-2], blp[1] ^= blp[-1]; + BF_encrypt(blp, &vn->sc_key); + } + } +#endif vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, curproc); if (bp->b_flags & B_READ) error = VOP_READ(vn->sc_vp, &auio, IO_DIRECT, vn->sc_cred); else error = VOP_WRITE(vn->sc_vp, &auio, IO_NOWDRAIN, vn->sc_cred); VOP_UNLOCK(vn->sc_vp, 0, curproc); +#ifdef VNCRYPT + if (vn->sc_key.P[0]) { + int len; + BF_LONG *blp; + BF_LONG blx[2], bly[2]; + + /* Decode data using CBC. This is for both reads + * (which are encoded in the vnode) and writes + * (which I just encoded before writing). + */ + len = (bp->b_flags & B_READ) + ? bp->b_bcount - auio.uio_resid : bp->b_bcount; + for (blp = (BF_LONG *)bp->b_data; + blp < (BF_LONG *)((char *)bp->b_data + len); + blp += 2) { + blx[0] = blp[0], blx[1] = blp[1]; + BF_decrypt(blp, &vn->sc_key); + if (((char *)blp - (char *)bp->b_bcount) & + 0x1ff) + blp[0] ^= bly[0], blp[1] ^= bly[1]; + bcopy(blx, bly, sizeof blx); + } + } +#endif bp->b_resid = auio.uio_resid; if (error) { @@ -443,6 +491,9 @@ case VNIOCGCLEAR: case VNIOCUSET: case VNIOCUCLEAR: +#ifdef VNCRYPT + case VNIOCSETKEY: +#endif goto vn_specific; } @@ -513,6 +564,42 @@ *f = vn->sc_options; break; +#ifdef VNCRYPT + case VNIOCSETKEY: + /* Set the blowfish key */ + { + int i, j; + unsigned char *kc, *kp; + unsigned char kbuf[(BF_ROUNDS + 2) * 4]; + + if (!*(char **)data) { + vn->sc_key.P[0] = 0; + break; + } + kp = zalloc(namei_zone); + /* Read in an ASCII string and convert it into binary form. + * This may not be a "standard" way to convert it, but I + * think it allows for good bit coverage in long strings. + */ + if ((error = copyinstr(*(char **)data, kp, MAXPATHLEN, NULL))) { + zfree(namei_zone, kp); + return error; + } + bzero(kbuf, sizeof kbuf); + for (kc = kp, i = j = 0; *kc; kc++, i++) { + if (i == sizeof kbuf) { + i = 0; + j = (j + 5) & 7; + } + kbuf[i] ^= (*kc >> j) | (*kc << (8 - j)); + } + zfree(namei_zone, kp); + /* Now convert that binary key into Blowfish's internal form */ + BF_set_key(&vn->sc_key, sizeof kbuf, kbuf); + break; + } +#endif + default: error = ENOTTY; break; @@ -740,6 +827,9 @@ vn->sc_object = NULL; } vn->sc_size = 0; +#ifdef VNCRYPT + vn->sc_key.P[0] = 0; +#endif } static int --- sys/conf/files.orig Wed Sep 25 16:37:47 2002 +++ sys/conf/files Tue Sep 24 23:16:16 2002 @@ -80,7 +80,7 @@ contrib/ipfilter/netinet/ip_proxy.c optional ipfilter inet contrib/ipfilter/netinet/ip_state.c optional ipfilter inet contrib/ipfilter/netinet/mlfk_ipl.c optional ipfilter inet -crypto/blowfish/bf_skey.c optional ipsec ipsec_esp +crypto/blowfish/bf_skey.c standard crypto/cast128/cast128.c optional ipsec ipsec_esp crypto/des/des_ecb.c optional ipsec ipsec_esp crypto/des/des_setkey.c optional ipsec ipsec_esp --- sys/conf/files.i386.orig Wed Sep 25 16:37:59 2002 +++ sys/conf/files.i386 Tue Sep 24 23:16:32 2002 @@ -73,7 +73,7 @@ contrib/dev/oltr/trlldbm.c optional oltr contrib/dev/oltr/trlldhm.c optional oltr contrib/dev/oltr/trlldmac.c optional oltr -bf_enc.o optional ipsec ipsec_esp \ +bf_enc.o standard \ dependency "$S/crypto/blowfish/arch/i386/bf_enc.S $S/crypto/blowfish/arch/i386/bf_enc_586.S $S/crypto/blowfish/arch/i386/bf_enc_686.S" \ compile-with "${CC} -c -I$S/crypto/blowfish/arch/i386 ${ASM_CFLAGS} ${WERROR} ${.IMPSRC}" \ no-implicit-rule To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-hackers" in the body of the message