From owner-svn-soc-all@FreeBSD.ORG Fri Jul 13 14:36:07 2012 Return-Path: Delivered-To: svn-soc-all@FreeBSD.org Received: from socsvn.FreeBSD.org (unknown [IPv6:2001:4f8:fff6::2f]) by hub.freebsd.org (Postfix) with SMTP id A66A11065670 for ; Fri, 13 Jul 2012 14:36:05 +0000 (UTC) (envelope-from gpf@FreeBSD.org) Received: by socsvn.FreeBSD.org (sSMTP sendmail emulation); Fri, 13 Jul 2012 14:36:05 +0000 Date: Fri, 13 Jul 2012 14:36:05 +0000 From: gpf@FreeBSD.org To: svn-soc-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Message-Id: <20120713143605.A66A11065670@hub.freebsd.org> Cc: Subject: socsvn commit: r239343 - in soc2012/gpf/pefs_kmod: sbin/pefs sys/fs/pefs X-BeenThere: svn-soc-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the entire Summer of Code repository List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 13 Jul 2012 14:36:07 -0000 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 #include +#include + #include #include +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);