From owner-svn-soc-all@FreeBSD.ORG Tue May 22 18:20:41 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 3200E106564A for ; Tue, 22 May 2012 18:20:39 +0000 (UTC) (envelope-from gpf@FreeBSD.org) Received: by socsvn.FreeBSD.org (sSMTP sendmail emulation); Tue, 22 May 2012 18:20:39 +0000 Date: Tue, 22 May 2012 18:20:39 +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: <20120522182039.3200E106564A@hub.freebsd.org> Cc: Subject: socsvn commit: r236147 - soc2012/gpf/pefs_kmod/sbin/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: Tue, 22 May 2012 18:20:41 -0000 Author: gpf Date: Tue May 22 18:20:38 2012 New Revision: 236147 URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=236147 Log: Create in memory database filled with file headers & checksums. Next step is to write database to .pefs.checksum. Note: Temporarily, inode numbers are used as file identifiers and size of hash table equals to number of elements. Added: soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c Modified: soc2012/gpf/pefs_kmod/sbin/pefs/Makefile soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.c soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.h Modified: soc2012/gpf/pefs_kmod/sbin/pefs/Makefile ============================================================================== --- soc2012/gpf/pefs_kmod/sbin/pefs/Makefile Tue May 22 17:11:18 2012 (r236146) +++ soc2012/gpf/pefs_kmod/sbin/pefs/Makefile Tue May 22 18:20:38 2012 (r236147) @@ -5,7 +5,7 @@ .PATH: ${SYS}/crypto/hmac ${SYS}/crypto/rijndael ${SYS}/crypto/sha2 PROG= pefs -SRCS= pefs_ctl.c pefs_key.c pefs_keychain.c pefs_subr.c +SRCS= pefs_ctl.c pefs_key.c pefs_keychain.c pefs_subr.c pefs_checksum.c SRCS+= hmac_sha512.c sha2.c SRCS+= rijndael-api.c rijndael-api-fst.c rijndael-alg-fst.c SRCS+= pkcs5v2.c @@ -17,7 +17,7 @@ DEBUG_FLAGS+= -g DPADD= ${LIBUTIL} -LDADD= -lutil +LDADD= -lutil -lcrypto BINDIR?= /sbin Added: soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c Tue May 22 18:20:38 2012 (r236147) @@ -0,0 +1,443 @@ +/*- + * Copyright (c) 2012 Efstratios Karatzas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "pefs_ctl.h" + +#define PEFS_INTEGRITY_DEBUG +#if defined (PEFS_INTEGRITY_DEBUG) +#define dprintf(a) printf a +#else +#define dprintf(a) (void)0 +#endif + +LIST_HEAD(file_header_head, file_header); +TAILQ_HEAD(checksum_head, checksum); + +struct checksum { + unsigned char *hash; + TAILQ_ENTRY(checksum) checksum_entries; +}; + +struct file_header { + uint32_t nhashes; + uint64_t file_id; + char path[MAXPATHLEN]; + LIST_ENTRY(file_header) bucket_entries; + struct checksum_head checksums; +}; + +struct bucket { + struct file_header_head file_headers; + uint32_t nelements; +}; + +struct hash_table { + struct bucket *buckets; + uint32_t size; +}; + +/* + * XXXgpf: dbg info: + * man page example(sha256) 318b20b83a6730b928c46163a2a1cefee4466132731c95c39613acb547ccb715 + * "Test Message\n" + "Hello World\n" + */ +static int +pefs_compute_file_checksums(struct file_header *fhp, const EVP_MD *md, + uint8_t hash_len) +{ + char buf[PEFS_SECTOR_SIZE]; + EVP_MD_CTX mdctx; + int md_len, i, fd, bytes_read; + struct checksum *csp; + + TAILQ_INIT(&(fhp->checksums)); + + fd = open(fhp->path, O_RDONLY); + if (fd < 0) { + warn("failed to open file: %s", fhp->path); + return (PEFS_ERR_IO); + } + + fhp->nhashes = 0; + while ((bytes_read = read(fd, buf, sizeof(buf))) > 0) { + EVP_MD_CTX_init(&mdctx); + EVP_DigestInit_ex(&mdctx, md, NULL); + EVP_DigestUpdate(&mdctx, buf, bytes_read); + + dprintf(("read %d bytes\n", bytes_read)); + for (i=0; ihash = malloc(hash_len); + if (csp->hash == NULL) { + pefs_warn("memory allocation error"); + close(fd); + return (PEFS_ERR_SYS); + } + + EVP_DigestFinal_ex(&mdctx, csp->hash, &md_len); + + dprintf(("Digest is: ")); + for (i = 0; i < md_len; i++) dprintf(("%02x", csp->hash[i])); + dprintf(("\n")); + + EVP_MD_CTX_cleanup(&mdctx); + + TAILQ_INSERT_TAIL(&(fhp->checksums), csp, checksum_entries); + fhp->nhashes++; + } + + close(fd); + return (0); +} + +static int +pefs_count_file_entries(FILE *fpin, uint32_t *nelementsp) +{ + char buf[MAXPATHLEN + 1]; + uint32_t nfiles; + + nfiles = 0; + + while (fgets(buf, sizeof(buf), fpin) != NULL) { + nfiles++; + } + + if (feof(fpin) == 0) { + warn("error reading input"); + return (PEFS_ERR_IO); + } + + fseek(fpin, 0, SEEK_SET); + *nelementsp = nfiles; + + return (0); +} + +static int +pefs_allocate_hash_table(struct hash_table *checksum_hash_tablep, uint32_t nelements) +{ + uint32_t i; + + /* + * XXXgpf: needs optimization + */ + checksum_hash_tablep->size = nelements; + checksum_hash_tablep->buckets = malloc (nelements * sizeof(struct bucket)); + + if (checksum_hash_tablep->buckets == NULL) { + pefs_warn("memory allocation error"); + return (PEFS_ERR_SYS); + } + + for (i = 0; i < checksum_hash_tablep->size; i++) { + checksum_hash_tablep->buckets[i].nelements = 0; + LIST_INIT(&(checksum_hash_tablep->buckets[i].file_headers)); + } + + return (0); +} + +static int +pefs_add_to_bucket(struct bucket *bucketp, struct file_header *fhp) +{ + struct file_header *elementp; + uint32_t i; + + i = 1; + + if (bucketp->nelements == 0) + LIST_INSERT_HEAD(&(bucketp->file_headers), fhp, bucket_entries); + else + LIST_FOREACH(elementp, &(bucketp->file_headers), bucket_entries) { + if (elementp->file_id == fhp->file_id) { + warn("file identifier collision detected between files: %s & %s", + fhp->path, elementp->path); + return (PEFS_ERR_EXIST); + } + + if (fhp->file_id < elementp->file_id) { + LIST_INSERT_BEFORE(elementp, fhp, bucket_entries); + break; + } + else if (i++ == bucketp->nelements) { + LIST_INSERT_AFTER(elementp, fhp, bucket_entries); + break; + } + } + + bucketp->nelements++; + return (0); +} + +static struct bucket * +pefs_find_bucket(struct hash_table *checksum_hash_tablep, struct file_header *fhp) +{ + uint32_t nbucket; + + nbucket = fhp->file_id % checksum_hash_tablep->size; + dprintf(("goto bucket %d\n", nbucket)); + return (&(checksum_hash_tablep->buckets[nbucket])); +} + +static int +pefs_add_to_hash_table(struct hash_table *checksum_hash_tablep, + struct file_header *fhp) +{ + return (pefs_add_to_bucket(pefs_find_bucket(checksum_hash_tablep, fhp), fhp)); +} + +/* for debugging purposes */ +static void +pefs_print_hash_table(struct hash_table *checksum_hash_tablep, uint8_t hash_len) +{ + struct file_header *fhp; + struct checksum *csp; + uint32_t i,j; + + dprintf(("\n+++Printing Hash Table+++\n\n")); + for (i = 0; i < checksum_hash_tablep->size; i++) { + dprintf(("\nbucket %d with elements: %u\n", i, checksum_hash_tablep->buckets[i].nelements)); + LIST_FOREACH(fhp, &(checksum_hash_tablep->buckets[i].file_headers), bucket_entries) { + dprintf(("\tpath=%s!\t id = %d!\tnhashes = %d\n", fhp->path, (int)fhp->file_id, fhp->nhashes)); + TAILQ_FOREACH(csp, &(fhp->checksums), checksum_entries) { + dprintf(("\t\tdigest=")); + for (j = 0; j < hash_len; j++) + dprintf(("%02x", csp->hash[j])); + dprintf(("\n")); + } + } + } +} + +static int +pefs_get_file_id(struct file_header *fhp) +{ + struct stat sb; + + if (stat(fhp->path, &sb) != 0) { + warn("cannot stat file %s", fhp->path); + return (PEFS_ERR_SYS); + } + /* + * XXXgpf: This is only temporary since retrieving the file's inode number + * is way simpler than retrieving the checksum value from encrypted filename. + * I'm thinking of an ioctl(), we'll see. + */ + fhp->file_id = sb.st_ino; + return (0); +} + +static int +pefs_file_semantic_checks(struct file_header *fhp, struct statfs *fsp) +{ + struct stat sb; + struct statfs this_fs; + + if (stat(fhp->path, &sb) != 0) { + warn("cannot stat file %s", fhp->path); + return (PEFS_ERR_SYS); + } + + if (statfs(fhp->path, &this_fs) == -1) { + pefs_warn("statfs failed: %s: %s", fhp->path, strerror(errno)); + return (PEFS_ERR_SYS); + } + + if (S_ISREG(sb.st_mode) == 0) { + pefs_warn("filename: %s is not a regular file", fhp->path); + return (PEFS_ERR_INVALID); + } + + if ((fsp->f_fsid.val[0] != this_fs.f_fsid.val[0]) || + (fsp->f_fsid.val[1] != this_fs.f_fsid.val[1])) { + pefs_warn("filename: %s does not reside in filesystem %s", + fhp->path, fsp->f_mntonname); + return (PEFS_ERR_INVALID); + } + return (0); +} + +static struct file_header * +pefs_next_file(FILE *fpin, int *error) +{ + char buf[MAXPATHLEN + 1]; + struct file_header *fhp; + + if (fgets(buf, sizeof(buf), fpin) == NULL) { + if (feof(fpin)) + *error = 0; + else { + *error = PEFS_ERR_IO; + warn("error reading input"); + } + return (NULL); + } + + if (buf[strnlen(buf, sizeof(buf)) - 1] == '\n') + buf[strnlen(buf, sizeof(buf)) - 1] = '\0'; + dprintf(("\nnext buf=%s!\n", buf)); + + fhp = malloc(sizeof(struct file_header)); + if (fhp == NULL) { + warn("memory allocation error"); + *error = PEFS_ERR_SYS; + return (NULL); + } + + strlcpy(fhp->path, buf, sizeof(fhp->path)); + + return (fhp); +} + +/* + * This function creates the in memory database that will be later written to + * the checksum file. + * A) The total sum of entries is gathered so that a hash table is allocated. + * B) For each file entry: + * B1) semantic checks: residing in pefs filesystem & regular file type checks. + * B2) the file_id is retrieved. + * B3) list of checksums is computed for the file's 4k blocks. + * B4) file entry is added to hash table. (separate chaining is used) + */ +static int +pefs_create_in_memory_db(FILE *fpin, char *fsroot, const EVP_MD *md, uint8_t hash_len, + struct hash_table *checksum_hash_tablep) +{ + struct statfs fs; + struct file_header *fhp; + int error; + uint32_t nfiles; + + if (statfs(fsroot, &fs) == -1) { + pefs_warn("statfs failed: %s: %s", fsroot, strerror(errno)); + return (PEFS_ERR_SYS); + } + + error = pefs_count_file_entries(fpin, &nfiles); + if (error != 0) + return (error); + + error = pefs_allocate_hash_table(checksum_hash_tablep, nfiles); + if (error != 0) + return (error); + + while((fhp = pefs_next_file(fpin, &error)) != NULL) { + error = pefs_file_semantic_checks(fhp, &fs); + if (error != 0) + return error; + + error = pefs_get_file_id(fhp); + if (error != 0) + return error; + + error = pefs_compute_file_checksums(fhp, md, hash_len); + if (error != 0) + return error; + + error = pefs_add_to_hash_table(checksum_hash_tablep, fhp); + if (error != 0) + return error; + } + + /* error during pefs_next_file() */ + if (error != 0) + return error; + + pefs_print_hash_table(checksum_hash_tablep, hash_len); + + return (0); +} + +int +pefs_create_checksum_file(FILE *fpin, char *fsroot, const char *algo) +{ + char checksum_path[MAXPATHLEN]; + struct hash_table checksum_hash_table; + const EVP_MD *md; + int error, fdout; + uint8_t hash_len; + + OpenSSL_add_all_digests(); + md = EVP_get_digestbyname(algo); + + if(md == NULL) { + pefs_warn("Unknown message digest %s\n", algo); + return (PEFS_ERR_INVALID); + } + hash_len = EVP_MD_size(md); + + snprintf(checksum_path, sizeof(checksum_path), "%s/%s", fsroot, PEFS_FILE_CHECKSUM); + fdout = open(checksum_path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + if (fdout == -1) { + warn("cannot open %s", checksum_path); + return (PEFS_ERR_IO); + } + + error = pefs_create_in_memory_db(fpin, fsroot, md, hash_len, + &checksum_hash_table); + if (error != 0) + goto out; + + /* XXXgpf: [TODO] write the in memory db to .pefs.checksum */ + /* error = pefs_write_checksum_file(&checksum_hash_table, fdout, ...); */ + +out: + close(fdout); + if (error != 0) + unlink(checksum_path); + /* XXXgpf: [TODO] free dynamic memory */ + + return (error); +} Modified: soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.c ============================================================================== --- soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.c Tue May 22 17:11:18 2012 (r236146) +++ soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.c Tue May 22 18:20:38 2012 (r236147) @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -74,6 +75,7 @@ static int pefs_getkey(int argc, char *argv[]); static int pefs_showchains(int argc, char *argv[]); static int pefs_showalgs(int argc, char *argv[]); +static int pefs_addchecksum(int argc, char *argv[]); typedef int (*command_func_t)(int argc, char **argv); typedef int (*keyop_func_t)(struct pefs_keychain_head *kch, int fd, @@ -100,9 +102,12 @@ { "delchain", pefs_delchain }, { "showchains", pefs_showchains }, { "showalgs", pefs_showalgs }, + { "addchecksum", pefs_addchecksum}, { NULL, NULL }, }; +const char *supported_digests[] = {"sha256","sha512"}; + void pefs_warn(const char *fmt, ...) { @@ -991,6 +996,59 @@ return (0); } +static int +pefs_addchecksum(int argc, char *argv[]) +{ + char fsroot[MAXPATHLEN]; + FILE *fpin; + int error, i, j; + const char *algo; + + fpin = NULL; + /* by default use sha256 */ + algo = supported_digests[0]; + + while ((i = getopt(argc, argv, "a:f:")) != -1) + switch(i) { + case 'a': + for (j=0; j < PEFS_SUPPORTED_DIGESTS; j++) + if (strcmp(supported_digests[j], optarg) == 0) { + algo = supported_digests[j]; + break; + } + + if (j == PEFS_SUPPORTED_DIGESTS) { + pefs_warn("invalid digestname: %s", optarg); + return (PEFS_ERR_INVALID); + } + break; + case 'f': + fpin = fopen(optarg, "r"); + if (fpin == NULL) { + warn("cannot open inputfile: %s", optarg); + return (PEFS_ERR_INVALID); + } + break; + default: + pefs_usage(); + } + argc -= optind; + argv += optind; + + if (fpin == NULL) { + pefs_warn("please supply an input file [-f]"); + return (PEFS_ERR_USAGE); + } + + initfsroot(argc, argv, 0, fsroot, sizeof(fsroot)); + + error = pefs_create_checksum_file(fpin, fsroot, algo); + + fclose(fpin); + + return (error); +} + static void pefs_usage_alg(void) { @@ -1016,6 +1074,7 @@ " pefs randomchain [-fv] [-n min] [-N max] filesystem\n" " pefs showchains [-fp] [-i iterations] [-k keyfile] filesystem\n" " pefs showalgs\n" +" pefs addchecksum [-a algo] [-f inputfile] filesystem\n" ); exit(PEFS_ERR_USAGE); } Modified: soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.h ============================================================================== --- soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.h Tue May 22 17:11:18 2012 (r236146) +++ soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.h Tue May 22 18:20:38 2012 (r236147) @@ -28,6 +28,8 @@ #include +struct EVP_MD; + #define PEFS_FSTYPE "pefs" #define PEFS_KLD PEFS_FSTYPE @@ -36,8 +38,11 @@ #define PEFS_KDF_ITERATIONS 50000 +#define PEFS_BLOCKSIZE 4096 + #define PEFS_FILE_KEYCHAIN ".pefs.db" #define PEFS_FILE_KEYCONF ".pefs.conf" +#define PEFS_FILE_CHECKSUM ".pefs.checksum" #define PEFS_KEYCONF_ALG_IND 0 #define PEFS_KEYCONF_ITERATIONS_IND 1 @@ -47,6 +52,8 @@ #define PEFS_KEYENC_MAC_SIZE (PEFS_KEY_SIZE / 2) +#define PEFS_SUPPORTED_DIGESTS 2 + #define PEFS_ERR_GENERIC 1 #define PEFS_ERR_USAGE 2 #define PEFS_ERR_IO 3 @@ -85,6 +92,7 @@ int pefs_key_decrypt(struct pefs_xkeyenc *xe, const struct pefs_xkey *xk_parent); uintmax_t pefs_keyid_as_int(char *keyid); +int pefs_create_checksum_file(FILE *fpin, char *fsroot, const char *algo); const char * pefs_alg_name(struct pefs_xkey *xk); void pefs_alg_list(FILE *stream);