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>