socsvn commit: r239343 - in soc2012/gpf/pefs_kmod: sbin/pefs sys/fs/pefs

gpf at FreeBSD.org gpf at FreeBSD.org
Fri Jul 13 14:36:07 UTC 2012


Author: gpf
Date: Fri Jul 13 14:36:05 2012
New Revision: 239343
URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=239343

Log:
  - sanitize .pefs.checksum global file header during VFS_MOUNT()
  - inline mini function that tells if a vnode needs integrity
  checking or not.
  - another inline func to tell if a given index entry is empty
  or not. (nhashes value of 0 symbolizes an empty index entry
  in the hash tables)
  - split code that reads an index entry from the hash tables
  to different function
  
  first steps towards actual integrity checking:
  
  During VOP_READ(), if a vnode has a key, then we try to check the integrity
  of the data before decrypting the data. We compare the stored checksum in
  .pefs.checksum vs the one we generate at that time. If integrity errors
  occurs, we mark the vnode as 'hacked' and any future attempts to verify
  the data will fail. Also, check the size of the file against the nhashes
  value of the file's index entry.
  
  all respective code can be found in pefs_checksum.c
  

Modified:
  soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c
  soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs.h
  soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_checksum.c
  soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_checksum.h
  soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_vfsops.c
  soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_vnops.c

Modified: soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c
==============================================================================
--- soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c	Fri Jul 13 13:24:33 2012	(r239342)
+++ soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c	Fri Jul 13 14:36:05 2012	(r239343)
@@ -476,6 +476,7 @@
 {
 	struct checksum *csp, *tcsp;
 	if (fhp != NULL) {
+		/* XXXgpf: [TODO] should probably call pefs_close_file() at this point */
 		TAILQ_FOREACH_SAFE(csp, &(fhp->checksums), checksum_entries, tcsp) {
 			TAILQ_REMOVE(&(fhp->checksums), csp, checksum_entries);
 			if (csp->hash != NULL)

Modified: soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs.h
==============================================================================
--- soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs.h	Fri Jul 13 13:24:33 2012	(r239342)
+++ soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs.h	Fri Jul 13 14:36:05 2012	(r239343)
@@ -122,7 +122,8 @@
 #define	PN_WANTRECYCLE			0x000100
 #define	PN_LOCKBUF_SMALL		0x001000
 #define	PN_LOCKBUF_LARGE		0x002000
-#define	PN_NO_CHECKSUM		0x000010
+#define	PN_NO_CHECKSUM			0x000010
+#define	PN_WRONG_CHECKSUM		0x000020
 
 struct pefs_node {
 	LIST_ENTRY(pefs_node)	pn_listentry;
@@ -146,7 +147,8 @@
 	uint8_t pcs_version;
 	uint8_t pcs_reserved;
 	uint8_t pcs_hash_len;
-	uint8_t pcs_hash_algo[8];
+	uint8_t pcs_hash_algo;
+	uint8_t pcs_hash_algo_name[8];
 	uint8_t pcs_offset_to_hash_table;
 	uint32_t pcs_hash_table_size;
 	char *pcs_table1, *pcs_table2;

Modified: soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_checksum.c
==============================================================================
--- soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_checksum.c	Fri Jul 13 13:24:33 2012	(r239342)
+++ soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_checksum.c	Fri Jul 13 14:36:05 2012	(r239343)
@@ -40,9 +40,45 @@
 #include <sys/endian.h>
 #include <sys/fnv_hash.h>
 
+#include <crypto/sha2/sha2.h>
+
 #include <fs/pefs/pefs.h>
 #include <fs/pefs/pefs_checksum.h>
 
+const char *pefs_checksum_supported_digests[] = {"sha256","sha512"};
+uint8_t pefs_checksum_supported_hash_lengths[] = {32, 64};
+
+int
+pefs_sanitize_checksum_header(struct pefs_checksum *pcs)
+{
+	int error, i;
+
+	error = 0;
+	for (i=0; i < PEFS_CHECKSUM_SUPPORTED_DIGESTS; i++)
+		if (strncmp(pefs_checksum_supported_digests[i], pcs->pcs_hash_algo_name,
+			sizeof(pcs->pcs_hash_algo_name)) == 0)
+			break;
+
+	pcs->pcs_hash_algo = i;
+	switch(pcs->pcs_hash_algo) {
+	/* FALLTHROUGH */
+	case (PEFS_SHA256):
+	case (PEFS_SHA512):
+		printf("digest: %s\n", pcs->pcs_hash_algo_name);
+		if (pcs->pcs_hash_len != pefs_checksum_supported_hash_lengths[i]) {
+			printf("pefs_sanitize invalid algo len %u\n", pcs->pcs_hash_len);
+			error = EINVAL;
+		}
+		break;
+	default:
+		printf("pefs_sanitize invalid algo %s\n", pcs->pcs_hash_algo_name);
+		error = ENODEV;
+		break;
+	}
+
+	return (error);
+}
+
 static uint32_t
 pefs_checksum_hash1(struct pefs_checksum *pc, struct pefs_checksum_index_entry *pcie)
 {
@@ -65,6 +101,26 @@
 	return (nbucket);
 }
 
+/* fill out pcie for from data pointed to by p */
+static void
+pefs_get_index_entry(char *p, struct pefs_checksum_index_entry *pcie)
+{
+	MPASS(p != NULL);
+
+	memcpy(&(pcie->pcie_nhashes), p, sizeof(pcie->pcie_nhashes));
+	pcie->pcie_nhashes = le32toh(pcie->pcie_nhashes);
+	if (pcie->pcie_nhashes != 0) {
+		p+=sizeof(pcie->pcie_nhashes);
+
+		memcpy(&(pcie->pcie_offset), p, sizeof(pcie->pcie_offset));
+		pcie->pcie_offset = le32toh(pcie->pcie_offset);
+		p+=sizeof(pcie->pcie_offset);
+
+		memcpy(&(pcie->pcie_file_id), p, sizeof(pcie->pcie_file_id));
+		pcie->pcie_file_id = le64toh(pcie->pcie_file_id);
+	}
+}
+
 static int
 pefs_checksum_index_lookup(struct pefs_checksum_index_entry *pcie, struct vnode *vp)
 {
@@ -72,28 +128,14 @@
 	struct pefs_mount *pm = VFS_TO_PEFS(vp->v_mount);
 	struct pefs_checksum *pcs = &(pm->pm_checksum);
 	struct pefs_node *pn = VP_TO_PN(vp);
-	char *start, *p;
+	char *start;
 	uint32_t pos;
 
 	pos = pefs_checksum_hash1(pcs, pcie);
 	start = &(pcs->pcs_table1[pos * PEFS_HT_CELL_SIZE]);
-	p = start;
+	pefs_get_index_entry(start, &target_pcie);
 
-	/* 
-	 * XXXgpf: [TODO] move the reading of a checksum index entry to a different function.
-	 * Also, perhaps a macro to tell if an index entry is empty or not (nhashes == 0).
-	 */
-	memcpy(&(target_pcie.pcie_nhashes), p, sizeof(target_pcie.pcie_nhashes));
-	target_pcie.pcie_nhashes = le32toh(target_pcie.pcie_nhashes);
-	if (target_pcie.pcie_nhashes != 0) {
-		p+=sizeof(target_pcie.pcie_nhashes);
-
-		memcpy(&(target_pcie.pcie_offset), p, sizeof(target_pcie.pcie_offset));
-		target_pcie.pcie_offset = le32toh(target_pcie.pcie_offset);
-		p+=sizeof(target_pcie.pcie_offset);
-
-		memcpy(&(target_pcie.pcie_file_id), p, sizeof(target_pcie.pcie_file_id));
-		target_pcie.pcie_file_id = le64toh(target_pcie.pcie_file_id);
+	if (!PEFS_EMPTY_INDEX_ENTRY(&target_pcie)) {
 		printf("cell %d:\n", pos);
 		printf("\thashes = %d\n\toffset = %d\n\tfile id = %llu\n",
 			target_pcie.pcie_nhashes, target_pcie.pcie_offset, target_pcie.pcie_file_id);
@@ -107,19 +149,9 @@
 
 	pos = pefs_checksum_hash2(pcs, pcie);
 	start = &(pcs->pcs_table2[pos * PEFS_HT_CELL_SIZE]);
-	p = start;
-
-	memcpy(&(target_pcie.pcie_nhashes), p, sizeof(target_pcie.pcie_nhashes));
-	target_pcie.pcie_nhashes = le32toh(target_pcie.pcie_nhashes);
-	if (target_pcie.pcie_nhashes != 0) {
-		p+=sizeof(target_pcie.pcie_nhashes);
-
-		memcpy(&(target_pcie.pcie_offset), p, sizeof(target_pcie.pcie_offset));
-		target_pcie.pcie_offset = le32toh(target_pcie.pcie_offset);
-		p+=sizeof(target_pcie.pcie_offset);
+	pefs_get_index_entry(start, &target_pcie);
 
-		memcpy(&(target_pcie.pcie_file_id), p, sizeof(target_pcie.pcie_file_id));
-		target_pcie.pcie_file_id = le64toh(target_pcie.pcie_file_id);
+	if (!PEFS_EMPTY_INDEX_ENTRY(&target_pcie)) {
 		printf("cell %d:\n", pos);
 		printf("\thashes = %d\n\toffset = %d\n\tfile id = %llu\n",
 			target_pcie.pcie_nhashes, target_pcie.pcie_offset, target_pcie.pcie_file_id);
@@ -137,7 +169,8 @@
 }
 
 void
-pefs_checksum_lookup(char *enc_name, size_t enc_name_len, struct componentname *cnp, struct vnode *vp)
+pefs_checksum_lookup(char *enc_name, size_t enc_name_len,
+	struct componentname *cnp, struct vnode *vp)
 {
 	struct pefs_checksum_index_entry pcie;
 	struct pefs_node *pn = VP_TO_PN(vp);
@@ -146,7 +179,8 @@
 	int error, r;
 
 	printf("gpf: checksum code @ lookup\n");
-	if ((cnp != NULL && cnp->cn_nameiop != LOOKUP) || (vp->v_type != VREG && vp->v_type != VLNK)
+	if ((cnp != NULL && cnp->cn_nameiop != LOOKUP) || (vp->v_type != VREG &&
+		vp->v_type != VLNK)
 		|| ((pn->pn_flags & PN_NO_CHECKSUM) != 0))
 		goto not_found;
 
@@ -185,3 +219,165 @@
 not_found:
 	pn->pn_flags|= PN_NO_CHECKSUM;
 }
+
+static int
+pefs_generate_checksum(struct vnode *vp, char *data, ssize_t data_len,
+	unsigned char **digest, ssize_t *digest_len)
+{
+	struct pefs_mount *pm = VFS_TO_PEFS(vp->v_mount);
+	struct pefs_checksum *pcs = &(pm->pm_checksum);
+	unsigned char *dig;
+
+	switch(pcs->pcs_hash_algo) {
+	case (PEFS_SHA256):
+		*digest_len = SHA256_DIGEST_STRING_LENGTH;
+		dig = malloc(*digest_len, M_TEMP, M_WAITOK);
+		/*
+		 * XXXgpf: Does this interface work for any length input?
+		 * Also, I should either use a different interface or store the checksums
+		 * in hex during .pefs.checksum creation because turning them to hex
+		 * at this point every single time we have a read is just silly.
+		 */
+		SHA256_Data(data, data_len, dig);
+		break;
+	case (PEFS_SHA512):
+		*digest_len = SHA512_DIGEST_STRING_LENGTH;
+		dig = malloc(*digest_len, M_TEMP, M_WAITOK);
+		SHA512_Data(data, data_len, dig);
+		break;
+	default:
+		printf("pefs_sanitize invalid algo %s\n", pcs->pcs_hash_algo_name);
+		return (ENODEV);
+	}
+
+	*digest = dig;
+	return (0);
+}
+
+static int
+pefs_retrieve_checksum(struct vnode *vp, struct pefs_checksum_index_entry *pcie,
+	off_t block_offset, unsigned char **digest, ssize_t digest_len)
+{
+	struct pefs_mount *pm = VFS_TO_PEFS(vp->v_mount);
+	struct pefs_checksum *pcs = &(pm->pm_checksum);
+	struct ucred *cred = vp->v_mount->mnt_cred;
+	struct uio *puio;
+	struct pefs_chunk pc;
+	unsigned char *dig;
+	off_t checksum_offset;
+	int error, i;
+
+	pefs_chunk_create(&pc, NULL, pcs->pcs_hash_len);
+	checksum_offset = pcie->pcie_offset + pcs->pcs_hash_len *
+		(block_offset / PEFS_SECTOR_SIZE);
+	puio = pefs_chunk_uio(&pc, checksum_offset, UIO_READ);
+
+	/* XXXgpf: gleb says  I should use vn_rdwr instead of VOP_READ */
+	error = VOP_READ(pcs->pcs_checksumvp, puio, IO_UNIT, cred);
+	if (error != 0) {
+		printf("pefs_retrieve_checksum read error %d\n", error);
+		return (error);
+	}
+
+	dig = malloc(digest_len, M_TEMP, M_WAITOK);
+	for (i=0; i < pcs->pcs_hash_len; i++)
+		sprintf(&dig[i*2],"%02x", ((unsigned char *)pc.pc_base)[i]);
+	dig[i*2] = '\0';
+
+	pefs_chunk_free(&pc, NULL);
+	*digest = dig;
+
+	return (0);
+}
+
+static int
+pefs_compare_checksums(unsigned char *digest1, unsigned char *digest2,
+	ssize_t digest_len)
+{
+	int error;
+
+	printf("compare dig1: %s\n", digest1);
+	printf("compare dig2: %s\n", digest2);
+
+	error = memcmp(digest1, digest2, digest_len);
+	if (error != 0) {
+		printf("checksum mismatch!\n");
+		error = EAUTH;
+	}
+
+	return (error);
+}
+
+int
+pefs_integrity_check(struct vnode *vp, off_t offset, u_quad_t fsize,
+	struct pefs_chunk *pc)
+{
+	struct pefs_checksum_index_entry pcie;
+	struct pefs_node *pn = VP_TO_PN(vp);
+	ssize_t digest_len, resid;
+	unsigned char *digest1, *digest2;
+	char *buf, *end;
+	long *p;
+	int error;
+
+	printf("integrity checking!\noffset %llu\n", offset);
+
+	if ((pn->pn_flags & PN_WRONG_CHECKSUM) != 0)
+		return (EAUTH);
+
+	pefs_get_index_entry(pn->pn_checksum_index_entry, &pcie);
+
+	printf("id: %llu\n", pcie.pcie_file_id);
+
+	buf = (char *)pc->pc_base;
+	end = buf + pc->pc_size;
+
+	if ((fsize > pcie.pcie_nhashes * PEFS_SECTOR_SIZE) ||
+		(fsize < (pcie.pcie_nhashes - 1) * PEFS_SECTOR_SIZE)) {
+		printf("file size differs from the one in .pefs.checksum\n");
+		pn->pn_flags|= PN_WRONG_CHECKSUM;
+		return (EAUTH);
+	}
+
+	while (buf < end) {
+		if ((end - buf) >= PEFS_SECTOR_SIZE) {
+			p = (long *)buf;
+			resid = PEFS_SECTOR_SIZE / sizeof(long);
+			for (; resid > 0; resid--)
+				if (*(p++) != 0)
+					break;
+			if (resid == 0) {
+				bzero(buf, PEFS_SECTOR_SIZE);
+				offset += PEFS_SECTOR_SIZE;
+				buf += PEFS_SECTOR_SIZE;
+				continue;
+			}
+			resid = PEFS_SECTOR_SIZE;
+		}
+		else
+			resid = end - buf;
+
+		error = pefs_generate_checksum(vp, buf, resid, &digest1, &digest_len);
+		if (error != 0)
+			return (error);
+
+		error = pefs_retrieve_checksum(vp, &pcie, offset, &digest2, digest_len);
+		if (error != 0) {
+			free(digest1, M_TEMP);
+			return (error);
+		}
+
+		error = pefs_compare_checksums(digest1, digest2, digest_len);
+		free(digest1, M_TEMP);
+		free(digest2, M_TEMP);
+		if (error != 0) {
+			pn->pn_flags|= PN_WRONG_CHECKSUM;
+			return (error);
+		}
+
+		buf += resid;
+		offset += resid;
+	}
+
+	return (0);
+}

Modified: soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_checksum.h
==============================================================================
--- soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_checksum.h	Fri Jul 13 13:24:33 2012	(r239342)
+++ soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_checksum.h	Fri Jul 13 14:36:05 2012	(r239343)
@@ -27,13 +27,40 @@
  */
 
 #define PEFS_FILE_CHECKSUM		".pefs.checksum"
+
 #define PEFS_CFH_SIZE 16	/* file header of .pefs.checksum file */
 #define PEFS_HT_CELL_SIZE 16 /* hash table cell(bucket) size */
 
+#define PEFS_CHECKSUM_SUPPORTED_DIGESTS	2
+
+#define PEFS_SHA256 0
+#define PEFS_SHA512 1
+
+//#define PEFS_EMPTY_INDEX_ENTRY(a)	(((struct pefs_checksum_index_entry *)a->pcie_nhashes == 0) ? 1 : 0)
+//#define PEFS_NEEDS_CHECKING(a)		(((struct pefs_node *)a->pn_checksum_index_entry != NULL) ? 1 : 0)
+
 struct pefs_checksum_index_entry {
 	uint32_t pcie_nhashes;
 	uint32_t pcie_offset;
 	uint64_t pcie_file_id;
 };
 
-void	pefs_checksum_lookup(char *enc_name, size_t enc_name_len, struct componentname *cnp, struct vnode *vp);
+static __inline int
+PEFS_EMPTY_INDEX_ENTRY(struct pefs_checksum_index_entry *pcie)
+{
+	MPASS(pcie != NULL);
+	return ((pcie->pcie_nhashes == 0) ? 1 : 0);
+}
+
+static __inline int
+PEFS_NEEDS_CHECKING(struct pefs_node *pn)
+{
+	MPASS(pn != NULL);
+	return ((pn->pn_checksum_index_entry != NULL) ? 1 : 0);
+}
+
+void	pefs_checksum_lookup(char *enc_name, size_t enc_name_len,
+			struct componentname *cnp, struct vnode *vp);
+int		pefs_integrity_check(struct vnode *vp, off_t offset,
+			u_quad_t fsize,	struct pefs_chunk *pc);
+int		pefs_sanitize_checksum_header(struct pefs_checksum *pcs);

Modified: soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_vfsops.c
==============================================================================
--- soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_vfsops.c	Fri Jul 13 13:24:33 2012	(r239342)
+++ soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_vfsops.c	Fri Jul 13 14:36:05 2012	(r239343)
@@ -149,7 +149,7 @@
 	printf("\n+++CHECKSUM FILE HEADER INFO+++\n");
 	printf("version = %x\nreserved = %d\nhash len = %d\noffset = %d\nsize = %d\nalgo = %s\n\n",
 		pcs->pcs_version, pcs->pcs_reserved, pcs->pcs_hash_len, pcs->pcs_offset_to_hash_table,
-		pcs->pcs_hash_table_size, pcs->pcs_hash_algo);
+		pcs->pcs_hash_table_size, pcs->pcs_hash_algo_name);
 
 	/* print table1 */
 	printf("+++HASH TABLE 1+++\n\n");
@@ -252,14 +252,20 @@
 	bufp+=sizeof(pcs->pcs_reserved);
 	memcpy(&(pcs->pcs_hash_len), bufp, sizeof(pcs->pcs_hash_len));
 	bufp+=sizeof(pcs->pcs_hash_len);
-	memcpy(&(pcs->pcs_hash_algo), bufp, sizeof(pcs->pcs_hash_algo));
-	bufp+=sizeof(pcs->pcs_hash_algo);
+	memcpy(&(pcs->pcs_hash_algo_name), bufp, sizeof(pcs->pcs_hash_algo_name));
+	bufp+=sizeof(pcs->pcs_hash_algo_name);
 	memcpy(&(pcs->pcs_offset_to_hash_table), bufp, sizeof(pcs->pcs_offset_to_hash_table));
 	bufp+=sizeof(pcs->pcs_offset_to_hash_table);
 	memcpy(&(pcs->pcs_hash_table_size), bufp, sizeof(pcs->pcs_hash_table_size));
 	pcs->pcs_hash_table_size = le32toh(pcs->pcs_hash_table_size);
 
-	/* XXXgpf: [TODO] sanitize input, turn hash_algo to number */
+	error = pefs_sanitize_checksum_header(pcs);
+	if (error != 0) {
+		printf("pefs_checksum_load: sanitize error %d\n", error);
+		pefs_chunk_free(&pc, NULL);
+		vput(checksumvp);
+		return (error);
+	}
 
 	pefs_chunk_free(&pc, NULL);
 

Modified: soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_vnops.c
==============================================================================
--- soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_vnops.c	Fri Jul 13 13:24:33 2012	(r239342)
+++ soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_vnops.c	Fri Jul 13 14:36:05 2012	(r239343)
@@ -1915,6 +1915,7 @@
 		return (error);
 
 	error = pefs_read_int(vp, uio, ioflag, cred, fsize);
+
 	return (error);
 }
 
@@ -1924,6 +1925,7 @@
 {
 	struct vnode *lvp = PEFS_LOWERVP(vp);
 	struct uio *puio;
+	struct pefs_mount *pm = VFS_TO_PEFS(vp->v_mount);
 	struct pefs_node *pn = VP_TO_PN(vp);
 	struct pefs_chunk pc;
 	struct sf_buf *sf;
@@ -1980,6 +1982,11 @@
 
 		/* XXX assert full buffer is read */
 		pefs_chunk_setsize(&pc, done);
+		if ((pm->pm_flags & PM_CHECKSUM) != 0 && PEFS_NEEDS_CHECKING(pn)) {
+			error = pefs_integrity_check(vp, poffset, fsize, &pc);
+			if (error != 0)
+				break;
+		}
 		pefs_data_decrypt(&pn->pn_tkey, poffset, &pc);
 		if (nocopy == 0) {
 			error = pefs_chunk_copy(&pc, bskip, uio);


More information about the svn-soc-all mailing list