Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 23 May 2012 14:45:40 +0000
From:      gpf@FreeBSD.org
To:        svn-soc-all@FreeBSD.org
Subject:   socsvn commit: r236202 - soc2012/gpf/pefs_kmod/sbin/pefs
Message-ID:  <20120523144540.67E3B1065670@hub.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: gpf
Date: Wed May 23 14:45:39 2012
New Revision: 236202
URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=236202

Log:
  Fix logical error where plaintexts were hashed instead of ciphertexts, leading to
  same hashes for same messages. Requiring that pefs filesystem is not mounted,
  so as to get cipher text, creates some problems when it comes to mapping user
  supplied file list to encrypted filenames.
  
  My solution is to provide a 2nd pefs command (pefs addchecklist). This command
  takes a simple filepath list and provides another one with encrypted filenames
  so that it can be used directly by original command pefs addchecksum.
  
  Another solution, requiring only 1 command, would have been to ask the user
  to supply a list of inode numbers instead of fullpaths but that seems less
  user friendly/elegant as well as more time consuming for sbin/pefs. I like
  this method better.
  
  Refer to the 2 big comment headers above pefs_addchecksum() & pefs_addchecklist()
  in pefs_ctl.c for a man-page like description of the commands + an example.
  

Modified:
  soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c
  soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.c
  soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.h
  soc2012/gpf/pefs_kmod/sbin/pefs/pefs_subr.c

Modified: soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c
==============================================================================
--- soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c	Wed May 23 14:06:49 2012	(r236201)
+++ soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c	Wed May 23 14:45:39 2012	(r236202)
@@ -34,6 +34,7 @@
 #include <sys/stat.h>
 
 #include <ctype.h>
+#include <dirent.h>
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -82,11 +83,6 @@
 	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)
@@ -111,7 +107,7 @@
 		EVP_DigestUpdate(&mdctx, buf, bytes_read);
 
 		dprintf(("read %d bytes\n", bytes_read));
-		for (i=0; i<bytes_read; i++) dprintf(("%c", buf[i]));
+		//for (i=0; i<bytes_read; i++) dprintf(("%c", buf[i]));
 
 		csp = malloc(sizeof(struct checksum));
 		if (csp == NULL) {
@@ -271,9 +267,8 @@
 		return (PEFS_ERR_SYS);
 	}
 	/*
-	 * XXXgpf: This is only temporary since retrieving the file's inode number
+	 * XXXgpf: [TODO] 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);
@@ -295,6 +290,10 @@
 		return (PEFS_ERR_SYS);
 	}
 
+	/*
+	 * XXXgpf: [TODO] deal with other types of files
+	 */
+
 	if (S_ISREG(sb.st_mode) == 0) {
 		pefs_warn("filename: %s is not a regular file", fhp->path);
 		return (PEFS_ERR_INVALID);
@@ -327,7 +326,7 @@
 
 	if (buf[strnlen(buf, sizeof(buf)) - 1] == '\n')
 		buf[strnlen(buf, sizeof(buf)) - 1] = '\0';
-	dprintf(("\nnext buf=%s!\n", buf));
+	dprintf(("\nnext file entry=%s!\n", buf));
 
 	fhp = malloc(sizeof(struct file_header));
 	if (fhp == NULL) {
@@ -346,10 +345,9 @@
  * 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)
+ * 		B1) the file_id is retrieved.
+ * 		B2) list of checksums is computed for the file's 4k blocks.
+ * 		B3) 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,
@@ -374,9 +372,7 @@
 		return (error);
 
 	while((fhp = pefs_next_file(fpin, &error)) != NULL) {
-		error = pefs_file_semantic_checks(fhp, &fs);
-		if (error != 0)
-			return error;
+		/* XXXgpf: Semantic checks are now performed by addchecklist command */
 
 		error = pefs_get_file_id(fhp);
 		if (error != 0)
@@ -431,6 +427,7 @@
 		goto out;
 
 	/* XXXgpf: [TODO] write the in memory db to .pefs.checksum */
+	/* man byteorder(9) */
 	/* error = pefs_write_checksum_file(&checksum_hash_table, fdout, ...); */
 
 out:
@@ -441,3 +438,157 @@
 
 	return (error);
 }
+
+/*
+ * Transform a decrypted fullpath residing in fsroot to an
+ * encrypted fullpath residing in fromfsroot.
+ */
+static int
+pefs_get_enc_path(struct file_header *fhp, char *fsroot, char *fromfsroot)
+{
+	/* XXXgpf: can there be a problem with paths greater than MAXPATHLEN? */
+	char enc_path[MAXPATHLEN];
+	char dec_path[MAXPATHLEN];
+	char original_path[MAXPATHLEN];
+	char buf[MAXPATHLEN];
+	struct stat sb;
+	char *rel_path;
+	char *pch;
+	DIR *dirp;
+	struct dirent *dp;
+	uint32_t ino;
+	int found;
+
+	strlcpy(enc_path, fromfsroot, sizeof(enc_path));
+	strlcpy(dec_path, fsroot, sizeof(dec_path));
+
+	strlcpy(original_path, fhp->path, sizeof(original_path));
+	rel_path = original_path + strlen(fsroot);
+
+	dprintf(("constructing encrypted path for: %s%s\n", fsroot, rel_path));
+
+	pch = strtok (rel_path,"/");
+	while (pch != NULL) {
+		dprintf(("enc path = %s\tdec path = %s\n", enc_path, dec_path));
+		dprintf(("next element: %s", pch));
+		snprintf(buf, sizeof(buf), "%s/%s", dec_path, pch);
+		strlcpy(dec_path, buf, sizeof(dec_path));
+
+		/* grab inode from dec_path */
+		if (stat(buf, &sb) != 0) {
+			warn("cannot stat file %s", buf);
+			return (PEFS_ERR_SYS);
+		}
+		ino = sb.st_ino;
+		dprintf(("\t%d\n", ino));
+
+		/* try to find inode in dirents of enc_path */
+		dirp = opendir(enc_path);
+		if (dirp == NULL) {
+			warn("cannot open dir %s", enc_path);
+			return (PEFS_ERR_SYS);
+		}
+
+		found = 0;
+		while (dirp != NULL) {
+			if ( (dp = readdir(dirp)) != NULL) {
+				if (dp->d_fileno == ino) {
+					found = 1;
+					break;
+				}
+			}
+			else {
+				closedir(dirp);
+				break;
+			}
+		}
+
+		if (found == 0) {
+			pefs_warn("inode: %d not found in directory: %s", ino, enc_path);
+			return (PEFS_ERR_NOENT);
+		}
+
+		/* append the encrypted filename and continue */
+		snprintf(buf, sizeof(buf), "%s/%s", enc_path, dp->d_name);
+		strlcpy(enc_path, buf, sizeof(enc_path));
+		closedir(dirp);
+
+		pch = strtok (NULL, "/");
+	}
+
+	dprintf(("\nresulting enc path = %s\n", enc_path));
+	if (stat(buf, &sb) != 0) {
+		warn("cannot stat file %s", enc_path);
+		return (PEFS_ERR_SYS);
+	}
+
+	/*
+	 * XXXgpf: [TODO] deal with other types of files
+	 */
+
+	if (S_ISREG(sb.st_mode) == 0) {
+		pefs_warn("filename: %s is not a regular file", enc_path);
+		return (PEFS_ERR_INVALID);
+	}
+
+	strlcpy(fhp->path, enc_path, sizeof(fhp->path));
+	strlcat(fhp->path, "\n", sizeof(fhp->path));
+	return (0);
+}
+
+static int
+pefs_write_to_checklist(int fdout, struct file_header *fhp)
+{
+	uint32_t bytes, len;
+
+	len = strnlen(fhp->path, sizeof(fhp->path));
+	bytes = write(fdout, fhp->path, len);
+	if (bytes != len) {
+		warn("error writing '%s' to checklist file", fhp->path);
+		return (PEFS_ERR_IO);
+	}
+
+	return (0);
+}
+
+/*
+ * This function creates the checklist that will be used by pefs_addchecksum.
+ * For each file entry:
+ * 		1) semantic checks: residing in pefs filesystem & regular file type checks.
+ * 		2) the encrypted fullpath of the file is retrieved
+ * 		3) entry is written to checklist_file
+ */
+int
+pefs_create_checklist(FILE *fpin, int fdout, char *fsroot, char *fromfsroot)
+{
+	struct statfs fs;
+	struct file_header *fhp;
+	int error;
+
+	if (statfs(fsroot, &fs) == -1) {
+		pefs_warn("statfs failed: %s: %s", fsroot, strerror(errno));
+		return (PEFS_ERR_SYS);
+	}
+
+	while((fhp = pefs_next_file(fpin, &error)) != NULL) {
+		error = pefs_file_semantic_checks(fhp, &fs);
+		if (error != 0)
+			return error;
+
+		error = pefs_get_enc_path(fhp, fsroot, fromfsroot);
+		if (error != 0)
+			return error;
+
+		error = pefs_write_to_checklist(fdout, fhp);
+		if (error != 0)
+			return error;
+
+		free(fhp);
+	}
+
+	/* error during pefs_next_file() */
+	if (error != 0)
+		return error;
+
+	return (0);
+}

Modified: soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.c
==============================================================================
--- soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.c	Wed May 23 14:06:49 2012	(r236201)
+++ soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.c	Wed May 23 14:45:39 2012	(r236202)
@@ -76,6 +76,7 @@
 static int	pefs_showchains(int argc, char *argv[]);
 static int	pefs_showalgs(int argc, char *argv[]);
 static int	pefs_addchecksum(int argc, char *argv[]);
+static int	pefs_addchecklist(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,
@@ -103,6 +104,7 @@
 	{ "showchains",	pefs_showchains },
 	{ "showalgs",	pefs_showalgs },
 	{ "addchecksum", pefs_addchecksum},
+	{ "addchecklist", pefs_addchecklist},
 	{ NULL, NULL },
 };
 
@@ -142,6 +144,16 @@
 		exit(PEFS_ERR_INVALID);
 }
 
+static void
+initfsroots(int argc, char **argv, int flags, char *fsroot, char *fromfsroot, size_t size)
+{
+	if (!checkargs_fs(argc, argv))
+		pefs_usage();
+
+	if (pefs_getfsroots(argv[0], flags, fsroot, fromfsroot, size) != 0)
+		exit(PEFS_ERR_INVALID);
+}
+
 static int
 openx_rdonly(const char *path)
 {
@@ -996,6 +1008,26 @@
 	return (0);
 }
 
+/*
+ * XXXgpf: Instead of a man page entry:
+ *
+ * pefs addchecksum [-a algo] [-i inputfile] filesystem
+ *
+ * $command creates .pefs.checksum db file in root of filesystem.
+ * This file will contain all checksums necessary to check integrity
+ * of files upon access.
+ *
+ * algo is the name of the algorithm to be used as a cryptographic
+ * hash function; supported algorithms: sha256, sha512.
+ *
+ * inputfile contains list of files that need integrity checking.
+ * This should be the outputfile of `pefs addchecklist`.
+ *
+ * When $command is run, filesystem should *not* be already
+ * mounted with pefs so that hashes are calculated for ciphertexts
+ * and not plain texts.
+ *
+ */
 static int
 pefs_addchecksum(int argc, char *argv[])
 {
@@ -1008,7 +1040,7 @@
 	/* by default use sha256 */
 	algo = supported_digests[0];
 
-	while ((i = getopt(argc, argv, "a:f:")) != -1)
+	while ((i = getopt(argc, argv, "a:i:")) != -1)
 		switch(i) {
 		case 'a':
 			for (j=0; j < PEFS_SUPPORTED_DIGESTS; j++)
@@ -1022,7 +1054,7 @@
 				return (PEFS_ERR_INVALID);
 			}
 			break;
-		case 'f':
+		case 'i':
 			fpin = fopen(optarg, "r");
 			if (fpin == NULL) {
 				warn("cannot open inputfile: %s", optarg);
@@ -1036,11 +1068,15 @@
 	argv += optind;
 
 	if (fpin == NULL) {
-		pefs_warn("please supply an input file [-f]");
+		pefs_warn("please supply an input file [-i]");
 		return (PEFS_ERR_USAGE);
 	}
 
-	initfsroot(argc, argv, 0, fsroot, sizeof(fsroot));
+	/* XXXgpf: [TODO] probably check that fsroot is not mounted */
+	if (!checkargs_fs(argc, argv))
+		pefs_usage();
+
+	strlcpy(fsroot, argv[0], sizeof(fsroot));
 
 	error = pefs_create_checksum_file(fpin, fsroot, algo);
 
@@ -1049,6 +1085,101 @@
 	return (error);
 }
 
+/*
+ * XXXgpf: Instead of a man page entry
+ *
+ * pefs addchecklist [-i inputfile] [-o outputfile] filesystem
+ *
+ * $command creates an outputfile that may be supplied to
+ * `pefs addchecksum`.
+ *
+ * inputfile contains list of files that need integrity checking.
+ * Entries of this file list are just filepaths. Only one entry per line
+ * is allowed.
+ * e.g. "/mnt/my_file.txt\n"
+ *
+ * outputfile will be created and it will contain the same list of files,
+ * but encrypted filenames will be used instead.
+ *
+ * filesystem should be already mounted and key already supplied, so that
+ * filenames are decrypted. However, it must *not* be mounted on the same
+ * directory so that both decrypted and encrypted filenames exist at the
+ * same time in the system.
+ *
+ * A proper way of ensuring integrity checks for a pefs filesystem would be:
+ *
+ * pefs mount /usr/home/paul/priv.enc /mnt
+ * pefs addkey -c /mnt
+ * ./my_script > filelist.txt
+ * pefs addchecklist -i filelist.txt pefs_filelist /mnt
+ * pefs unmount /mnt
+ * pefs addchecksum -i pefs_filelist /usr/home/paul/p.enc
+ * pefs mount -o checksum=yes /usr/home/paul/priv.enc /any/path
+ *
+ */
+static int
+pefs_addchecklist(int argc, char *argv[])
+{
+	char fsroot[MAXPATHLEN], fromfsroot[MAXPATHLEN];
+	char output_file[MAXPATHLEN];
+	FILE *fpin;
+	int error, fdout, i;
+
+	fpin = NULL;
+	fdout = -1;
+
+	while ((i = getopt(argc, argv, "i:o:")) != -1)
+		switch(i) {
+		case 'i':
+			fpin = fopen(optarg, "r");
+			if (fpin == NULL) {
+				warn("cannot open inputfile: %s", optarg);
+				return (PEFS_ERR_INVALID);
+			}
+			break;
+		case 'o':
+			strlcpy(output_file, optarg, sizeof(output_file));
+			fdout = open(output_file, O_WRONLY | O_CREAT | O_EXCL,  S_IRUSR | S_IWUSR);
+			if (fdout == -1) {
+				warn("cannot open %s", optarg);
+				return (PEFS_ERR_IO);
+			}
+			break;
+		default:
+			pefs_usage();
+		}
+	argc -= optind;
+	argv += optind;
+
+	if (fpin == NULL) {
+		pefs_warn("please supply an input file [-i]");
+		return (PEFS_ERR_USAGE);
+	}
+
+	if (fdout == -1) {
+		pefs_warn("please supply an output file [-o]");
+		return (PEFS_ERR_USAGE);
+	}
+
+	initfsroots(argc, argv, 0, fsroot, fromfsroot, sizeof(fsroot));
+
+	if (strcmp(fsroot, fromfsroot) == 0) {
+		pefs_warn("filesystem: %s must not be mounted upon itself!\n", fromfsroot);
+		unlink(output_file);
+		return (PEFS_ERR_USAGE);
+	}
+
+	error = pefs_create_checklist(fpin, fdout, fsroot, fromfsroot);
+
+	fclose(fpin);
+	close(fdout);
+
+	if (error != 0)
+		unlink(output_file);
+
+	return (error);
+}
+
 static void
 pefs_usage_alg(void)
 {
@@ -1074,7 +1205,8 @@
 "	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"
+"	pefs addchecksum [-a algo] [-i inputfile] filesystem\n"
+"	pefs addchecklist [-i inputfile] [-o outputfile[ 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	Wed May 23 14:06:49 2012	(r236201)
+++ soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.h	Wed May 23 14:45:39 2012	(r236202)
@@ -84,6 +84,8 @@
 void	pefs_warn(const char *, ...) __printf0like(1, 2);
 
 int	pefs_getfsroot(const char *path, int flags, char *fsroot, size_t size);
+int pefs_getfsroots(const char *path, int flags, char *fsroot,
+		char * fromfsroot, size_t size);
 
 int	pefs_key_generate(struct pefs_xkey *xk, const char *passphrase,
 	    struct pefs_keyparam *kp);
@@ -93,6 +95,7 @@
 	    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);
+int pefs_create_checklist(FILE *fpin, int fdout, char *fsroot, char *fromfsroot);
 
 const char *	pefs_alg_name(struct pefs_xkey *xk);
 void	pefs_alg_list(FILE *stream);

Modified: soc2012/gpf/pefs_kmod/sbin/pefs/pefs_subr.c
==============================================================================
--- soc2012/gpf/pefs_kmod/sbin/pefs/pefs_subr.c	Wed May 23 14:06:49 2012	(r236201)
+++ soc2012/gpf/pefs_kmod/sbin/pefs/pefs_subr.c	Wed May 23 14:45:39 2012	(r236202)
@@ -73,3 +73,34 @@
 
 	return (0);
 }
+
+int
+pefs_getfsroots(const char *path, int flags, char *fsroot, char * fromfsroot, size_t size)
+{
+	struct statfs fs;
+	const char *realfsroot, *realfromfsroot;
+
+	if (statfs(path, &fs) == -1) {
+		pefs_warn("statfs failed: %s: %s", path, strerror(errno));
+		return (PEFS_ERR_SYS);
+	}
+
+	realfsroot = fs.f_mntonname;
+	if (strcmp(PEFS_FSTYPE, fs.f_fstypename) != 0) {
+		if ((flags & PEFS_FS_IGNORE_TYPE) != 0)
+			realfsroot = path;
+		else {
+			pefs_warn("invalid file system type: %s", path);
+			return (PEFS_ERR_INVALID);
+		}
+	}
+
+	realfromfsroot = fs.f_mntfromname;
+	if (fromfsroot != NULL)
+		strlcpy(fromfsroot, realfromfsroot, size);
+
+	if (fsroot != NULL)
+		strlcpy(fsroot, realfsroot, size);
+
+	return (0);
+}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120523144540.67E3B1065670>