socsvn commit: r236202 - soc2012/gpf/pefs_kmod/sbin/pefs
gpf at FreeBSD.org
gpf at FreeBSD.org
Wed May 23 14:45:42 UTC 2012
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);
+}
More information about the svn-soc-all
mailing list