Current-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: (almost) full disk encryption, cgdroot and firmware



On Mon, 20 Jun 2016 20:15:00 +0100
Alexander Nasonov <alnsn%yandex.ru@localhost> wrote:

> This is my story on (almost) full disk encryption.
> 
> I followed Pierre Proncher's instruction from Mar 2013. To my
> surprise, it worked on the first boot. However, networking didn't
> work because the kernel couldn't load iwm firmware.
> 
> After a couple of attempts to fix firmware loading, I gave up on
> cgdroot ramdisk and switched to a fake root on wd0a. It's similar
> to cgdroot but with modules and firmware files. I hard-linked few
> binaries including init from /rescue.
> 
> My setup has not one but two cgd devices: cgd0 for a real root and
> cgd1 for other partitions. cgd0's key is stored on unencrypted
> wd0a, cgd1's key is stored on the real root cgd0a. I have to enter
> two passwords instead of one but this setup gives me some protection
> from an unsophisticated keylogger. Since wd0a is read-only, I can
> add wd0a integrity check before running the second cgdconfig -C
> command and abort before entering the second password if the check
> fails. A real rootkit can easily fool the integrity checker, though.

If someone wants to add real kernel level cgd root configuration it is
actually not all that difficult to do. I'll attach the patch I've been
using for many years to this mail.

I never contributed it because it requires hardwiring the cgd
parameters into the kernel configuration and I think a proper
implementation should be more user friendly and be able to get the
parameters from the boot loader or some kind of super block.

Usage example:

options CGD_ROOT_FSDEV="\"cgd0a\""
options CGD_ROOT_FSMAJOR=20
options CGD_ROOT_FSMINOR=0

options CGD_ROOT_DEV="\"/dev/wd0e\""
options CGD_ROOT_MAJOR=0
options CGD_ROOT_MINOR=4
options CGD_ROOT_CIPHER="\"blowfish-cbc\""
options CGD_ROOT_KEYLEN=256
options
CGD_ROOT_SALT="0x01,0x01,0x02,0xaa,0xbb,0xaa,0xbb,0xaa,0xbb,0xaa,0xbb,0xaa,0xbb,0xaa,0xbb,0xaa"
options CGD_ROOT_ITERATIONS=12345

On bootup you should see a password prompt followed by:

cgdroot_finalize()
cgdroot_finalize: finished
root on cgd0a dumps on cgd0b

Kind regards,
-Tobias
Index: sys/kern/init_main.c
===================================================================
RCS file: /cvsroot/src/sys/kern/init_main.c,v
retrieving revision 1.481
diff -u -r1.481 init_main.c
--- sys/kern/init_main.c	4 Jun 2016 21:10:56 -0000	1.481
+++ sys/kern/init_main.c	20 Jun 2016 19:59:00 -0000
@@ -245,6 +245,8 @@
 static void configure(void);
 static void configure2(void);
 static void configure3(void);
+void cgdroot_finalize(void);
+void cgdroot_getpass(void);
 void main(void);
 
 /*
@@ -277,6 +279,13 @@
 	 * in case of early panic or other messages.
 	 */
 	consinit();
+#ifdef CGD_ROOT_DEV
+	/*
+	 * XXX if we are using a USB keyboard we have to do this later,
+         * after the keyboard is attached.
+	*/
+	cgdroot_getpass();
+#endif
 
 	kernel_lock_init();
 	once_init();
@@ -603,6 +612,14 @@
 
 	sysctl_finalize();
 
+#ifdef CGD_ROOT_DEV
+#if 0
+	/* if USB keyboard */
+	cgdroot_getpass();
+#endif
+	cgdroot_finalize();
+#endif
+
 	/*
 	 * Now that autoconfiguration has completed, we can determine
 	 * the root and dump devices.
Index: sys/dev/cgd.c
===================================================================
RCS file: /cvsroot/src/sys/dev/cgd.c,v
retrieving revision 1.106
diff -u -r1.106 cgd.c
--- sys/dev/cgd.c	28 Nov 2015 21:06:30 -0000	1.106
+++ sys/dev/cgd.c	20 Jun 2016 19:59:00 -0000
@@ -51,6 +51,8 @@
 #include <sys/vnode.h>
 #include <sys/conf.h>
 #include <sys/syslog.h>
+#include <dev/cons.h>
+#include <sys/sha1.h>
 
 #include <dev/dkvar.h>
 #include <dev/cgdvar.h>
@@ -115,6 +117,22 @@
 static void	cgd_cipher(struct cgd_softc *, void *, void *,
 			   size_t, daddr_t, size_t, int);
 
+#ifdef CGD_ROOT_DEV
+void cgdroot_getpass(void);
+static int	cgd_cngetsn(char *cp, int size);
+static int	cgd_pkcs5_pbkdf2_sha1(u_int8_t *r, size_t dkLen,
+				      const u_int8_t *P, size_t Plen,
+				      const u_int8_t *S, size_t Slen, size_t c);
+static void	cgd_sha1_hmac(const uint8_t *p, size_t Plen, uint8_t *data,
+			      size_t datalen, uint8_t out[20]);
+static void	cgd_prf_iterate(u_int8_t *r, const u_int8_t *P, size_t Plen,
+				const u_int8_t *S, size_t Slen, size_t c,
+				size_t ind);
+static int	cgd_pkcs5_pbkdf2_sha1(u_int8_t *r, size_t dkLen,
+				      const u_int8_t *P, size_t Plen,
+				      const u_int8_t *S, size_t Slen, size_t c);
+#endif
+
 static struct dkdriver cgddkdriver = {
         .d_minphys  = minphys,
         .d_open = cgdopen,
@@ -622,6 +640,8 @@
 	const char *cp;
 	struct pathbuf *pb;
 	char	 *inbuf;
+	char	 *tmppath;
+	size_t	 tmppathlen;
 	struct dk_softc *dksc = &cs->sc_dksc;
 
 	cp = ci->ci_disk;
@@ -637,8 +657,13 @@
 	}
 
 	inbuf = malloc(MAX_KEYSIZE, M_TEMP, M_WAITOK);
+	tmppath = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
+
+	ret = copyinstr(cp, tmppath, MAXPATHLEN, &tmppathlen);
+	if (ret)
+		goto bail;
 
-	if ((ret = cgdinit(cs, cp, vp, l)) != 0)
+	if ((ret = cgdinit(cs, tmppath, vp, l)) != 0)
 		goto bail;
 
 	(void)memset(inbuf, 0, MAX_KEYSIZE);
@@ -699,6 +724,7 @@
 		goto bail;
 	}
 	free(inbuf, M_TEMP);
+	free(tmppath, M_TEMP);
 
 	bufq_alloc(&dksc->sc_bufq, "fcfs", 0);
 
@@ -718,6 +744,7 @@
 
 bail:
 	free(inbuf, M_TEMP);
+	free(tmppath, M_TEMP);
 	(void)vn_close(vp, FREAD|FWRITE, l->l_cred);
 	return ret;
 }
@@ -794,22 +821,16 @@
 {
 	struct	disk_geom *dg;
 	int	ret;
-	char	*tmppath;
 	uint64_t psize;
 	unsigned secsize;
 	struct dk_softc *dksc = &cs->sc_dksc;
 
 	cs->sc_tvn = vp;
-	cs->sc_tpath = NULL;
-
-	tmppath = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
-	ret = copyinstr(cpath, tmppath, MAXPATHLEN, &cs->sc_tpathlen);
-	if (ret)
-		goto bail;
-	cs->sc_tpath = malloc(cs->sc_tpathlen, M_DEVBUF, M_WAITOK);
-	memcpy(cs->sc_tpath, tmppath, cs->sc_tpathlen);
 
 	cs->sc_tdev = vp->v_rdev;
+	cs->sc_tpathlen = strlen(cpath);
+	cs->sc_tpath = malloc(cs->sc_tpathlen + 1, M_DEVBUF, M_WAITOK);
+	strcpy(cs->sc_tpath, cpath);
 
 	if ((ret = getdisksize(vp, &psize, &secsize)) != 0)
 		goto bail;
@@ -834,7 +855,6 @@
 	dg->dg_ncylinders = dg->dg_secperunit / dg->dg_nsectors;
 
 bail:
-	free(tmppath, M_TEMP);
 	if (ret && cs->sc_tpath)
 		free(cs->sc_tpath, M_DEVBUF);
 	return ret;
@@ -963,6 +983,227 @@
 	}
 }
 
+#ifdef CGD_ROOT_DEV
+
+/* this is just a copy of cngetsn() with the twist that it doesn't echo */
+static int
+cgd_cngetsn(char *cp, int size)
+{
+	char *lp;
+	int c, len;
+
+	cnpollc(1);
+
+	lp = cp;
+	len = 0;
+	for (;;) {
+		c = cngetc();
+		switch (c) {
+		case '\n':
+		case '\r':
+			printf("\n");
+			*lp++ = '\0';
+			cnpollc(0);
+			return (len);
+		case '\b':
+		case '\177':
+		case '#':
+			if (len) {
+				--len;
+				--lp;
+				printf("\b \b");
+			}
+			continue;
+		case '@':
+		case 'u'&037:	/* CTRL-u */
+			len = 0;
+			lp = cp;
+			printf("\n");
+			continue;
+		default:
+			if (len + 1 >= size || c < ' ') {
+				printf("\007");
+				continue;
+			}
+			printf_nolog("*");
+			++len;
+			*lp++ = c;
+		}
+	}
+}
+
+
+static uint8_t cgdroot_passbuf[128];
+
+void cgdroot_getpass(void) {
+	printf("Password: ");
+	cgd_cngetsn(cgdroot_passbuf, sizeof(cgdroot_passbuf));
+	cgdroot_passbuf[sizeof(cgdroot_passbuf) - 1] = 0;
+}
+
+int cgdroot_finalize(void);
+int cgdroot_finalize(void) {
+        dev_t cgdev;
+        dev_t tdev;
+	int ret;
+	const char *tcp;
+        struct cgd_softc *cs;
+        vnode_t *tvp;
+	char keybuf[256];
+
+        uint8_t salt[] = {CGD_ROOT_SALT};
+	size_t iterations = CGD_ROOT_ITERATIONS;
+
+        printf("cgdroot_finalize()\n");
+        cgdev = makedev(CGD_ROOT_FSMAJOR, CGD_ROOT_FSMINOR);
+        tdev = makedev(CGD_ROOT_MAJOR, CGD_ROOT_MINOR);
+        bdevvp(tdev, &tvp);
+
+	if ((ret = VOP_OPEN(tvp, FREAD | FWRITE, NOCRED)) != 0) {
+		vrele(tvp);
+		goto bail;
+	}
+        mutex_enter(tvp->v_interlock);
+        tvp->v_writecount++;
+        mutex_exit(tvp->v_interlock);
+
+        GETCGD_SOFTC(cs, cgdev);
+	tcp = CGD_ROOT_DEV;
+
+        if ((ret = cgdinit(cs, tcp, tvp, curlwp)) != 0) {
+		printf("cgdinit failed\n");
+		vrele(tvp);
+		goto bail;
+	}
+
+	cgd_pkcs5_pbkdf2_sha1(keybuf, CGD_ROOT_KEYLEN / 8, cgdroot_passbuf, strlen(cgdroot_passbuf), salt, 16, iterations);
+	memset(cgdroot_passbuf, 0, sizeof(cgdroot_passbuf));
+
+        cs->sc_cfuncs = cryptfuncs_find(CGD_ROOT_CIPHER);
+
+        cs->sc_cdata.cf_blocksize = -1;
+        cs->sc_cdata.cf_mode = CGD_CIPHER_CBC_ENCBLKNO1;
+        cs->sc_cdata.cf_priv = cs->sc_cfuncs->cf_init(CGD_ROOT_KEYLEN, keybuf,
+            &cs->sc_cdata.cf_blocksize);
+
+        cs->sc_cdata.cf_blocksize /= 8;
+
+        bufq_alloc(&cs->sc_dksc.sc_bufq, "fcfs", 0);
+
+        cs->sc_data = malloc(MAXPHYS, M_DEVBUF, M_WAITOK);
+        cs->sc_data_used = 0;
+
+        /* Attach the disk. */
+        dk_attach(&cs->sc_dksc);
+        disk_attach(&cs->sc_dksc.sc_dkdev);
+
+        disk_set_info(cs->sc_dksc.sc_dev, &cs->sc_dksc.sc_dkdev, NULL);
+
+        /* Try and read the disklabel. */
+        dk_getdisklabel(&cs->sc_dksc, 0 /* XXX ? (cause of PR 41704) */);
+
+        /* Discover wedges on this disk. */
+        dkwedge_discover(&cs->sc_dksc.sc_dkdev);
+
+#ifdef CGD_ROOT_FSDEV
+	rootspec = CGD_ROOT_FSDEV;
+	rootdev = makedev(CGD_ROOT_FSMAJOR, CGD_ROOT_FSMINOR);
+#endif
+	printf("cgdroot_finalize: finished\n");
+	bail:
+	if (ret)
+		printf("cgdroot_finalize: %d\n", ret);
+        return ret;
+
+}
+
+#define PRF_BLOCKLEN	20
+
+static void cgd_sha1_hmac(const uint8_t *p, size_t Plen, uint8_t *data, size_t datalen, uint8_t out[20])
+{
+	SHA1_CTX ctx;
+	uint8_t pad[64];
+	int i;
+
+	if (Plen > sizeof(pad)) {
+		SHA1Init(&ctx);
+		SHA1Update(&ctx, p, Plen);
+		SHA1Final(pad, &ctx);
+		for (i = 0; i < 20; i++)
+			pad[i] ^= 0x36;
+		for (; i < sizeof(pad); i++)
+			pad[i] = 0x36;
+	}
+	else {
+		for (i = 0; i < Plen; i++)
+			pad[i] = p[i] ^ 0x36;
+		for (; i < sizeof(pad); i++)
+			pad[i] = 0x36;
+	}
+
+	SHA1Init(&ctx);
+	SHA1Update(&ctx, pad, sizeof(pad));
+	SHA1Update(&ctx, data, datalen);
+	SHA1Final(out, &ctx);
+
+	for (i = 0; i < sizeof(pad); i++)
+		pad[i] ^= (0x36 ^ 0x5c);
+
+	SHA1Init(&ctx);
+	SHA1Update(&ctx, pad, sizeof(pad));
+	SHA1Update(&ctx, out, 20);
+	SHA1Final(out, &ctx);
+
+}
+
+static void
+cgd_prf_iterate(u_int8_t *r, const u_int8_t *P, size_t Plen,
+	    const u_int8_t *S, size_t Slen, size_t c, size_t ind)
+{
+	size_t		 i;
+	size_t		 j;
+	size_t		 datalen;
+	u_int8_t	*data;
+	u_int8_t	 tmp[20];
+
+	data = malloc(Slen + 4, M_TEMP, M_WAITOK);
+	(void)memcpy(data, S, Slen);
+	be32enc(data + Slen, ind);
+	datalen = Slen + 4;
+	memset(r,0, PRF_BLOCKLEN);
+
+	for (i=0; i < c; i++) {
+		cgd_sha1_hmac(P, Plen, data, datalen, tmp);
+
+		for (j = 0; j < PRF_BLOCKLEN; j++)
+			r[j] ^= tmp[j];
+
+		memcpy(data, tmp, PRF_BLOCKLEN);
+		datalen = PRF_BLOCKLEN;
+	}
+
+	free(data, M_TEMP);
+}
+
+static int
+cgd_pkcs5_pbkdf2_sha1(u_int8_t *r, size_t dkLen, const u_int8_t *P, size_t Plen,
+	     const u_int8_t *S, size_t Slen, size_t c)
+{
+	size_t	i;
+	size_t	l;
+
+	/* Step 2 */
+        l = (dkLen + PRF_BLOCKLEN - 1) / PRF_BLOCKLEN;
+
+	/* Step 3 */
+	for (i = 0; i < l; i++)
+		cgd_prf_iterate(r + (PRF_BLOCKLEN * i), P, Plen, S, Slen, c,
+			i+1);
+
+	return 0;
+}
+#endif
+
 #ifdef DEBUG
 static void
 hexprint(const char *start, void *buf, int len)


Home | Main Index | Thread Index | Old Index