Attachment 'pefs.diff'
Download 1 Index: pefs_kmod/sbin/pefs/pefs_checksum.c
2 ===================================================================
3 --- pefs_kmod/sbin/pefs/pefs_checksum.c (revision 0)
4 +++ pefs_kmod/sbin/pefs/pefs_checksum.c (revision 240588)
5 @@ -0,0 +1,2348 @@
6 +/*-
7 + * Copyright (c) 2012 Efstratios Karatzas
8 + * All rights reserved.
9 + *
10 + * Redistribution and use in source and binary forms, with or without
11 + * modification, are permitted provided that the following conditions
12 + * are met:
13 + * 1. Redistributions of source code must retain the above copyright
14 + * notice, this list of conditions and the following disclaimer.
15 + * 2. Redistributions in binary form must reproduce the above copyright
16 + * notice, this list of conditions and the following disclaimer in the
17 + * documentation and/or other materials provided with the distribution.
18 + *
19 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 + * SUCH DAMAGE.
30 + *
31 + */
32 +
33 +#include <sys/cdefs.h>
34 +__FBSDID("$FreeBSD$");
35 +
36 +#include <sys/dirent.h>
37 +#include <sys/endian.h>
38 +#include <sys/ioctl.h>
39 +#include <sys/mount.h>
40 +#include <sys/queue.h>
41 +#include <sys/tree.h>
42 +#include <sys/types.h>
43 +#include <sys/stat.h>
44 +#include <sys/fnv_hash.h>
45 +
46 +#include <ctype.h>
47 +#include <dirent.h>
48 +#include <inttypes.h>
49 +#include <libgen.h>
50 +#include <math.h>
51 +#include <stdio.h>
52 +#include <stdlib.h>
53 +#include <string.h>
54 +#include <errno.h>
55 +#include <unistd.h>
56 +#include <fcntl.h>
57 +#include <err.h>
58 +
59 +#include <fs/pefs/pefs.h>
60 +
61 +#include <openssl/evp.h>
62 +#include <openssl/pem.h>
63 +
64 +#include "pefs_ctl.h"
65 +
66 +//#define PEFS_INTEGRITY_DEBUG
67 +#if defined (PEFS_INTEGRITY_DEBUG)
68 +#define dprintf(a) printf a
69 +#else
70 +#define dprintf(a) (void)0
71 +#endif
72 +
73 +#define PEFS_EXTEND 1
74 +#define PEFS_NOEXTEND 2
75 +#define PEFS_REALLOC 3
76 +
77 +#define PEFS_SIGNATURE_MAX_LENGTH 512
78 +#define PEFS_CHECKSUM_FILE_VERSION 0xDD
79 +#define PEFS_HASH_BYTE_ALIGNMENT 512
80 +#define PEFS_EXTRA_TABLE_SIZE 15
81 +
82 +#define PEFS_PLEN 1024
83 +#define PEFS_SEED_LEN 20
84 +
85 +#define PEFS_BUFISZE 512
86 +
87 +/* tail that contains a single file's checksums */
88 +TAILQ_HEAD(checksum_head, checksum);
89 +/* tail that contains all file headers that require integrity checking */
90 +TAILQ_HEAD(file_header_head, file_header);
91 +/* tail for all the file_headers that refer to the same inode */
92 +TAILQ_HEAD(hardlink_fh_head, file_header);
93 +
94 +/* RB tree for hardlink counters */
95 +RB_HEAD(hardlink_head, hardlink_counter);
96 +RB_PROTOTYPE(hardlink_head, hardlink_counter, hardlink_entries, pefs_rb_cmp);
97 +
98 +/* on disk size of .pefs.checksum's unique file header */
99 +#define PEFS_CFH_SIZE 16
100 +/* on disk size of a single file header (also a bucket in cuckoo hashing) */
101 +#define PEFS_FH_SIZE 16
102 +
103 +/*
104 + * This struct is used to check if all hardlinks for a given inode are
105 + * supplied by the user.
106 + */
107 +struct hardlink_counter {
108 + /* inode number for the file in question */
109 + ino_t inode;
110 + /* total hardlinks of the file */
111 + uint32_t total_links;
112 + /* how many links are found in user supplied list */
113 + uint32_t links_found;
114 + /* file headers of the links we have found */
115 + struct hardlink_fh_head file_headers;
116 + /* entry in hardlink RB tree */
117 + RB_ENTRY(hardlink_counter) hardlink_entries;
118 +};
119 +
120 +/*
121 + * This is the unique file header of the .pefs.checksum file, found in
122 + * the beginning of the file.
123 + */
124 +struct checksum_file_header {
125 + uint8_t version;
126 + uint8_t reserved;
127 + uint8_t hash_len;
128 + uint8_t hash_algo[8];
129 + uint8_t offset_to_hash_table;
130 + uint32_t hash_table_size;
131 +};
132 +
133 +struct checksum {
134 + unsigned char *hash;
135 + TAILQ_ENTRY(checksum) checksum_entries;
136 +};
137 +
138 +union file_id {
139 + uint64_t fid_num;
140 + uint8_t fid_str[8];
141 +};
142 +
143 +/* XXXgpf: [TODO] turns offsets to uint64_t? */
144 +struct file_header {
145 + /*
146 + * on disk information
147 + */
148 +
149 + /* the number of hashes for the file */
150 + uint32_t nhashes;
151 + /* in file offset to start of checksums */
152 + uint32_t offset_to_checksums;
153 + /* id is MAC tweak from filename (first 64 bits) */
154 + union file_id fid;
155 +
156 + /*
157 + * in memory information
158 + */
159 +
160 + /* fullpath for this file */
161 + char path[MAXPATHLEN + 1];
162 + /* fullpath for this file's parent dir */
163 + char dirpath[MAXPATHLEN + 1];
164 + /* filename */
165 + char filename[MAXNAMLEN + 1];
166 + /*fullpath to this symlink's immediate next target */
167 + char *target_path;
168 + /* file descriptors for the file and its parent dir */
169 + int fd, pfd;
170 + /* mark that this entry was found during "verify" action */
171 + int found;
172 + /* this file's checksums */
173 + struct checksum_head checksums;
174 + /* entry in global file header tail */
175 + TAILQ_ENTRY(file_header) file_header_entries;
176 + /* entry in hardlink counter */
177 + TAILQ_ENTRY(file_header) fh_hardlink_entries;
178 +};
179 +
180 +struct bucket {
181 + struct file_header * fhp;
182 +};
183 +
184 +/*
185 + * This cuckoo hashing implementation requires 2 tables, each
186 + * with his own hash function: pefs_hash1() & pefs_hash2()
187 + */
188 +struct cuckoo_hash_table {
189 + struct bucket *buckets1; /* table1 */
190 + struct bucket *buckets2; /* table2 */
191 + uint32_t size; /* how many buckets in each table */
192 + uint32_t nelements; /* total number of elements <= size */
193 +};
194 +
195 +static int
196 +pefs_is_prime(uint32_t num)
197 +{
198 + uint32_t i, sq;
199 +
200 + if ((num % 2 == 0) || (num % 3 == 0))
201 + return 0;
202 +
203 + sq = sqrt(num);
204 + /* All other primes are in the form of 6k+-1 */
205 + for (i = 5; i <= sq; i+=6) {
206 + if ((num % i == 0) || (num % (i+2) == 0))
207 + return 0;
208 + }
209 +
210 + return 1;
211 +}
212 +
213 +static int
214 +pefs_next_prime(uint32_t num)
215 +{
216 + uint32_t i;
217 +
218 + if (num == 2 || num == 3)
219 + return num;
220 +
221 + if (num % 2 == 0)
222 + num+=1;
223 +
224 + for (i = num;; i+=2) {
225 + if (pefs_is_prime(i))
226 + return i;
227 + }
228 +
229 + return 0;
230 +}
231 +
232 +static struct file_header *
233 +pefs_allocate_file_header(void)
234 +{
235 + struct file_header *fhp;
236 +
237 + fhp = malloc(sizeof(struct file_header));
238 + if (fhp == NULL) {
239 + pefs_warn("memory allocation error");
240 + return (NULL);
241 + }
242 +
243 + fhp->nhashes = 0;
244 + fhp->offset_to_checksums = 0;
245 + fhp->fid.fid_num = 0;
246 + fhp->fd = -1;
247 + fhp->pfd = -1;
248 + fhp->found = 0;
249 +
250 + fhp->target_path = NULL;
251 +
252 + fhp->path[0] = '\0';
253 + fhp->dirpath[0] = '\0';
254 + fhp->filename[0] = '\0';
255 +
256 + TAILQ_INIT(&(fhp->checksums));
257 +
258 + return (fhp);
259 +}
260 +
261 +static void
262 +pefs_close_files(struct file_header *fhp)
263 +{
264 + if (fhp->fd >= 0) {
265 + close(fhp->fd);
266 + fhp->fd = -1;
267 + }
268 + if (fhp->pfd >= 0) {
269 + close(fhp->pfd);
270 + fhp->pfd = -1;
271 + }
272 +}
273 +
274 +static void
275 +pefs_free_file_header(struct file_header *fhp)
276 +{
277 + struct checksum *csp, *tcsp;
278 + if (fhp != NULL) {
279 + pefs_close_files(fhp);
280 + TAILQ_FOREACH_SAFE(csp, &(fhp->checksums), checksum_entries, tcsp) {
281 + TAILQ_REMOVE(&(fhp->checksums), csp, checksum_entries);
282 + if (csp->hash != NULL)
283 + free(csp->hash);
284 + free(csp);
285 + }
286 + if (fhp->target_path != NULL)
287 + free(fhp->target_path);
288 + free(fhp);
289 + }
290 +}
291 +
292 +static int
293 +pefs_compute_symlink_checksum(struct file_header *fhp, const EVP_MD *md,
294 + uint8_t hash_len, int flags)
295 +{
296 + struct pefs_xslink_ctext xsl;
297 + EVP_MD_CTX mdctx;
298 + char *buf;
299 + int error, i, md_len;
300 + size_t buf_len;
301 + struct checksum *csp;
302 +
303 + if ((flags & PEFS_UNMOUNTED) != 0 || (flags & PEFS_NOKEY) != 0) {
304 + buf = fhp->target_path;
305 + buf_len = strnlen(fhp->target_path, MAXPATHLEN + 1);
306 + }
307 + else {
308 + /* feed parent directory to ioctl() */
309 + strlcpy(xsl.pxsl_filename, fhp->filename, sizeof(xsl.pxsl_filename));
310 + xsl.pxsl_namelen = strnlen(xsl.pxsl_filename,
311 + sizeof(xsl.pxsl_filename));
312 +
313 + error = ioctl(fhp->pfd, PEFS_GETSLINKCTEXT, &xsl);
314 + if (error != 0) {
315 + pefs_warn("error retrieving symlink's ciphertext of %s", fhp->path);
316 + return (PEFS_ERR_IO);
317 + }
318 +
319 + dprintf(("read %d bytes from kernel\n\n", xsl.pxsl_target_len));
320 + dprintf(("printing contents of buf:"));
321 + for (i=0; i < (int)xsl.pxsl_target_len; i++)
322 + dprintf(("%c", xsl.pxsl_target[i]));
323 + dprintf(("!\n"));
324 + buf = xsl.pxsl_target;
325 + buf_len = xsl.pxsl_target_len;
326 + }
327 + EVP_MD_CTX_init(&mdctx);
328 + EVP_DigestInit_ex(&mdctx, md, NULL);
329 + EVP_DigestUpdate(&mdctx, buf, buf_len);
330 +
331 + csp = malloc(sizeof(struct checksum));
332 + if (csp == NULL) {
333 + pefs_warn("memory allocation error");
334 + return (PEFS_ERR_SYS);
335 + }
336 + csp->hash = malloc(hash_len);
337 + if (csp->hash == NULL) {
338 + pefs_warn("memory allocation error");
339 + free(csp);
340 + return (PEFS_ERR_SYS);
341 + }
342 +
343 + EVP_DigestFinal_ex(&mdctx, csp->hash, &md_len);
344 +
345 + dprintf(("Digest is: "));
346 + for (i = 0; i < md_len; i++) dprintf(("%02x", csp->hash[i]));
347 + dprintf(("\n"));
348 +
349 + EVP_MD_CTX_cleanup(&mdctx);
350 + TAILQ_INSERT_TAIL(&(fhp->checksums), csp, checksum_entries);
351 + fhp->nhashes++;
352 +
353 + return (0);
354 +}
355 +
356 +static int
357 +pefs_compute_file_checksums(struct file_header *fhp, const EVP_MD *md,
358 + uint8_t hash_len, int flags)
359 +{
360 + struct pefs_xsector_ctext xsct;
361 + char sector_buf[PEFS_SECTOR_SIZE];
362 + EVP_MD_CTX mdctx;
363 + struct stat sb;
364 + off_t offset, resid;
365 + FILE * fp;
366 + char *buf;
367 + uint32_t bytes_to_read;
368 + size_t buf_len;
369 + int error, i, md_len;
370 + struct checksum *csp;
371 +
372 + if (fhp->target_path != NULL)
373 + return (pefs_compute_symlink_checksum(fhp, md, hash_len, flags));
374 +
375 + /* XXXgpf: what happens if file size > 2^64? */
376 + if (fstat(fhp->fd, &sb) != 0) {
377 + warn("cannot stat file %s", fhp->path);
378 + return (PEFS_ERR_SYS);
379 + }
380 +
381 + resid = sb.st_size;
382 + if (resid == 0) {
383 + pefs_warn("empty files are not allowed: %s", fhp->path);
384 + return (PEFS_ERR_GENERIC);
385 + }
386 +
387 + fp = NULL;
388 + if ((flags & PEFS_UNMOUNTED) != 0 || (flags & PEFS_NOKEY) != 0) {
389 + fp = fdopen(fhp->fd, "r");
390 + if (fp == NULL) {
391 + pefs_warn("could not open file for reading: %s", fhp->path);
392 + return (PEFS_ERR_IO);
393 + }
394 + }
395 +
396 + offset = 0;
397 + xsct.pxsct_offset = 0;
398 + while (resid > 0) {
399 + if (resid > PEFS_SECTOR_SIZE)
400 + bytes_to_read = PEFS_SECTOR_SIZE;
401 + else
402 + bytes_to_read = resid;
403 +
404 + if ((flags & PEFS_UNMOUNTED) != 0 || (flags & PEFS_NOKEY) != 0) {
405 + fread(sector_buf, bytes_to_read, sizeof(char), fp);
406 + if (ferror(fp)) {
407 + pefs_warn("error reading from file: %s", fhp->path);
408 + return (PEFS_ERR_IO);
409 + }
410 + buf = sector_buf;
411 + buf_len = bytes_to_read;
412 + resid-=bytes_to_read;
413 + }
414 + else {
415 + resid-=bytes_to_read;
416 + xsct.pxsct_ctext_len = bytes_to_read;
417 + error = ioctl(fhp->fd, PEFS_GETSECTORCTEXT, &xsct);
418 + if (error != 0) {
419 + pefs_warn("error retrieving ciphertext of %s", fhp->path);
420 + return (PEFS_ERR_IO);
421 + }
422 + xsct.pxsct_offset+= xsct.pxsct_ctext_len;
423 + buf = xsct.pxsct_ctext;
424 + buf_len = xsct.pxsct_ctext_len;
425 + }
426 +
427 + EVP_MD_CTX_init(&mdctx);
428 + EVP_DigestInit_ex(&mdctx, md, NULL);
429 + EVP_DigestUpdate(&mdctx, buf, buf_len);
430 +
431 + dprintf(("read %d bytes\n\n", buf_len));
432 + dprintf(("printing contents of buffer:"));
433 + for (i=0; i < (int)buf_len; i++) dprintf(("%c", buf[i]));
434 + dprintf(("!\n"));
435 +
436 + csp = malloc(sizeof(struct checksum));
437 + if (csp == NULL) {
438 + pefs_warn("memory allocation error");
439 + EVP_MD_CTX_cleanup(&mdctx);
440 + return (PEFS_ERR_SYS);
441 + }
442 + csp->hash = malloc(hash_len);
443 + if (csp->hash == NULL) {
444 + pefs_warn("memory allocation error");
445 + free(csp);
446 + EVP_MD_CTX_cleanup(&mdctx);
447 + return (PEFS_ERR_SYS);
448 + }
449 +
450 + EVP_DigestFinal_ex(&mdctx, csp->hash, &md_len);
451 +
452 + dprintf(("Digest is: "));
453 + for (i = 0; i < md_len; i++) dprintf(("%02x", csp->hash[i]));
454 + dprintf(("\n"));
455 +
456 + EVP_MD_CTX_cleanup(&mdctx);
457 +
458 + TAILQ_INSERT_TAIL(&(fhp->checksums), csp, checksum_entries);
459 + fhp->nhashes++;
460 + }
461 +
462 + /*
463 + * XXXgpf: [TODO] better move the fp into file_header struct and deal with
464 + * closing it during pefs_file_close.
465 + */
466 + if (fp != NULL) {
467 + fclose(fp);
468 + fhp->fd = -1;
469 + }
470 +
471 + return (0);
472 +}
473 +
474 +static void
475 +pefs_init_hash_table(struct cuckoo_hash_table *chtp)
476 +{
477 + chtp->size = 0;
478 + chtp->nelements = 0;
479 + chtp->buckets1 = NULL;
480 + chtp->buckets2 = NULL;
481 +}
482 +
483 +static int
484 +pefs_allocate_hash_table(struct cuckoo_hash_table *chtp, uint32_t nelements,
485 + int alloc_flag)
486 +{
487 + uint32_t i;
488 +
489 + if (alloc_flag == PEFS_EXTEND) {
490 + /*
491 + * spending 15% more space for each table lowers the chance to fall into
492 + * an infinite loop during cuckoo insertion to about 1.5%.
493 + */
494 + chtp->size = pefs_next_prime(nelements +
495 + ((nelements * PEFS_EXTRA_TABLE_SIZE)/100));
496 + chtp->nelements = nelements;
497 + if (chtp->size < chtp->nelements) {
498 + pefs_warn("numeric overflow while computing hash table size");
499 + return (PEFS_ERR_GENERIC);
500 + }
501 + }
502 + else if (alloc_flag == PEFS_NOEXTEND) {
503 + chtp->size = nelements;
504 + chtp->nelements = nelements;
505 + }
506 + /* re-alloc the hash tables in case of infinite loop during cuckoo insert */
507 + else if (alloc_flag == PEFS_REALLOC) {
508 + chtp->size = pefs_next_prime(chtp->size + 1);
509 + if (chtp->size < chtp->nelements) {
510 + pefs_warn("numeric overflow while computing new hash table size");
511 + return (PEFS_ERR_GENERIC);
512 + }
513 + free(chtp->buckets1);
514 + free(chtp->buckets2);
515 + }
516 +
517 + dprintf(("hash table elem:%u\tsize: %u\n", chtp->nelements, chtp->size));
518 +
519 + chtp->buckets1 = malloc (chtp->size * sizeof(struct bucket));
520 + if (chtp->buckets1 == NULL) {
521 + pefs_warn("memory allocation error");
522 + return (PEFS_ERR_SYS);
523 + }
524 +
525 + for (i = 0; i < chtp->size; i++)
526 + chtp->buckets1[i].fhp = NULL;
527 +
528 + chtp->buckets2 = malloc (chtp->size * sizeof(struct bucket));
529 + if (chtp->buckets2 == NULL) {
530 + pefs_warn("memory allocation error");
531 + return (PEFS_ERR_SYS);
532 + }
533 +
534 + for (i = 0; i < chtp->size; i++)
535 + chtp->buckets2[i].fhp = NULL;
536 +
537 + return (0);
538 +}
539 +
540 +static void
541 +pefs_free_hash_table(struct cuckoo_hash_table *chtp)
542 +{
543 + struct bucket *bp;
544 + uint32_t i;
545 +
546 + if (chtp->buckets1 != NULL) {
547 + for (i = 0; i < chtp->size; i++) {
548 + bp = &chtp->buckets1[i];
549 + pefs_free_file_header(bp->fhp);
550 + }
551 + free(chtp->buckets1);
552 + }
553 +
554 + if (chtp->buckets2 != NULL) {
555 + for (i = 0; i < chtp->size; i++) {
556 + bp = &chtp->buckets2[i];
557 + pefs_free_file_header(bp->fhp);
558 + }
559 + free(chtp->buckets2);
560 + }
561 +}
562 +
563 +static void
564 +pefs_free_file_header_tail(struct file_header_head *fh_headp)
565 +{
566 + struct file_header *fhp, *tfhp;
567 +
568 + TAILQ_FOREACH_SAFE(fhp, fh_headp, file_header_entries, tfhp) {
569 + TAILQ_REMOVE(fh_headp, fhp, file_header_entries);
570 + pefs_free_file_header(fhp);
571 + }
572 +}
573 +
574 +static uint32_t
575 +pefs_hash1(struct cuckoo_hash_table *chtp, struct file_header *fhp)
576 +{
577 + uint32_t nbucket;
578 +
579 + nbucket = fhp->fid.fid_num % chtp->size;
580 + dprintf(("hash1: goto bucket %d\n", nbucket));
581 + return (nbucket);
582 +}
583 +
584 +static uint32_t
585 +pefs_hash2(struct cuckoo_hash_table *chtp, struct file_header *fhp)
586 +{
587 + uint32_t nbucket;
588 +
589 + nbucket = fnv_64_buf(&(fhp->fid.fid_num), sizeof(fhp->fid.fid_num),
590 + FNV1_64_INIT) % chtp->size;
591 + dprintf(("hash2: goto bucket %d\n", nbucket));
592 +
593 + return (nbucket);
594 +}
595 +
596 +static struct file_header *
597 +pefs_cuckoo_lookup(struct cuckoo_hash_table *chtp, struct file_header *fhp)
598 +{
599 + struct file_header *elem, *elem1, *elem2;
600 + int pos1, pos2;
601 +
602 + elem = fhp;
603 + pos1 = pefs_hash1(chtp, elem);
604 + elem1 = chtp->buckets1[pos1].fhp;
605 + if (elem1 != NULL) {
606 + if (elem1->fid.fid_num == elem->fid.fid_num) {
607 + return (elem1);
608 + }
609 + }
610 +
611 + pos2 = pefs_hash2(chtp, elem);
612 + elem2 = chtp->buckets2[pos2].fhp;
613 + if (elem2 != NULL) {
614 + if (elem2->fid.fid_num == elem->fid.fid_num) {
615 + return (elem2);
616 + }
617 + }
618 +
619 + return (NULL);
620 +}
621 +
622 +static int
623 +pefs_cuckoo_insert(struct cuckoo_hash_table *chtp,
624 + struct file_header *fhp)
625 +{
626 + struct file_header *elem, *elem1, *elem2, *res;
627 + uint32_t i, max_tries, pos1, pos2;
628 +
629 + max_tries = chtp->size;
630 + elem = fhp;
631 +
632 + /* file_id collision check */
633 + res = pefs_cuckoo_lookup(chtp, elem);
634 + if (res != NULL) {
635 + pefs_warn("file identifier collision detected between files: %s & %s",
636 + res->path, elem->path);
637 + return (PEFS_ERR_EXIST);
638 + }
639 +
640 + for (i = 0; i < max_tries; i++) {
641 + pos1 = pefs_hash1(chtp, elem);
642 + elem1 = chtp->buckets1[pos1].fhp;
643 + /* do the cuckoo! */
644 + chtp->buckets1[pos1].fhp = elem;
645 + if (elem1 == NULL)
646 + return 0;
647 + pos2 = pefs_hash2(chtp, elem1);
648 + elem2 = chtp->buckets2[pos2].fhp;
649 + /* do the cuckoo! */
650 + chtp->buckets2[pos2].fhp = elem1;
651 + if (elem2 == NULL)
652 + return 0;
653 + else
654 + elem = elem2;
655 + }
656 +
657 + /* XXXgpf: should be left as a warning during development phase */
658 + pefs_warn("cuckoo_insert resulted in infinite loop!");
659 + return (PEFS_ERR_GENERIC);
660 +}
661 +
662 +static int
663 +pefs_add_to_hash_table(struct cuckoo_hash_table *chtp,
664 + struct file_header *fhp)
665 +{
666 + return (pefs_cuckoo_insert(chtp, fhp));
667 +}
668 +
669 +/* XXXgpf: for debugging purposes */
670 +static void
671 +pefs_print_hash_tables(struct cuckoo_hash_table *chtp, uint8_t hash_len)
672 +{
673 + struct file_header *fhp;
674 + struct checksum *csp;
675 + uint32_t i,j;
676 +
677 + dprintf(("\n+++Printing Hash Table 1+++\n\n"));
678 + for (i = 0; i < chtp->size; i++) {
679 + fhp = chtp->buckets1[i].fhp;
680 + dprintf(("\nbucket %d with element: %d\n", i, fhp == NULL ? 0 : 1));
681 + if (fhp != NULL) {
682 + dprintf(("\tid = %llu\tnhashes = %d\n", fhp->fid.fid_num,
683 + fhp->nhashes));
684 + if (fhp->path[0] == '/')
685 + dprintf(("\tpath = %s\n", fhp->path));
686 + TAILQ_FOREACH(csp, &(fhp->checksums), checksum_entries) {
687 + dprintf(("\t\tdigest="));
688 + for (j = 0; j < hash_len; j++)
689 + dprintf(("%02x", csp->hash[j]));
690 + dprintf(("\n"));
691 + }
692 + }
693 + }
694 +
695 + dprintf(("\n+++Printing Hash Table 2+++\n\n"));
696 + for (i = 0; i < chtp->size; i++) {
697 + fhp = chtp->buckets2[i].fhp;
698 + dprintf(("\nbucket %d with element: %d\n", i, fhp == NULL ? 0 : 1));
699 + if (fhp != NULL) {
700 + dprintf(("\tid = %llu\tnhashes = %d\n", fhp->fid.fid_num,
701 + fhp->nhashes));
702 + if (fhp->path[0] == '/')
703 + dprintf(("\tpath = %s\n", fhp->path));
704 + TAILQ_FOREACH(csp, &(fhp->checksums), checksum_entries) {
705 + dprintf(("\t\tdigest="));
706 + for (j = 0; j < hash_len; j++)
707 + dprintf(("%02x", csp->hash[j]));
708 + dprintf(("\n"));
709 + }
710 + }
711 + }
712 +}
713 +
714 +/*
715 + * pefs encrypted filename = XBase64(checksum || E(tweak || filename))
716 + * We use filename mac (checksum) as file_id. This way, should a filesystem
717 + * be dump/restored, there will be no need to recreate .pefs.checksum because
718 + * filenames remain the same.
719 + *
720 + * file id used is checksum = VMAC(E(tweak || filename))
721 + */
722 +static int
723 +pefs_get_file_id(struct file_header *fhp, int flags)
724 +{
725 + struct pefs_xnamecsum xncs;
726 + uint64_t temp;
727 + char *enc, *buf;
728 + int error, r;
729 + size_t buf_len, enc_len;
730 +
731 + if ((flags & PEFS_NOKEY) != 0 || (flags & PEFS_UNMOUNTED) != 0) {
732 + /* in this case, we already have the encrypted filename */
733 + enc = fhp->filename;
734 + enc_len = strnlen(fhp->filename, sizeof(fhp->filename));
735 + enc++;
736 + enc_len--;
737 + buf_len = MAXNAMLEN + 1;
738 + buf = malloc(buf_len);
739 + if (buf == NULL) {
740 + pefs_warn("memory allocation error");
741 + return (PEFS_ERR_SYS);
742 + }
743 +
744 + r = pefs_name_pton(enc, enc_len, buf, buf_len);
745 + if (r <= 0) {
746 + pefs_warn("failed to extract file id from encrypted filename: %s",
747 + fhp->filename);
748 + error = PEFS_ERR_GENERIC;
749 + }
750 + else {
751 + memcpy(fhp->fid.fid_str, buf, sizeof(temp));
752 + error = 0;
753 + }
754 +
755 + free(buf);
756 + return (error);
757 + }
758 +
759 + strlcpy(xncs.pxnc_filename, fhp->filename, sizeof(xncs.pxnc_filename));
760 + xncs.pxnc_namelen = strnlen(xncs.pxnc_filename, sizeof(xncs.pxnc_filename));
761 +
762 + /* feed parent directory to ioctl() */
763 + error = ioctl(fhp->pfd, PEFS_GETNAMECSUM, &xncs);
764 +
765 + if (error == 0)
766 + memcpy(fhp->fid.fid_str, xncs.pxnc_csum, sizeof(xncs.pxnc_csum));
767 + else
768 + pefs_warn("failed to fetch file id from kernel for filename: %s",
769 + fhp->filename);
770 +
771 + return (error);
772 +}
773 +
774 +static void
775 +pefs_symlink_warn(struct cuckoo_hash_table *chtp, struct file_header_head *fhhp)
776 +{
777 + struct stat sb;
778 + struct file_header targetfh;
779 + char dirbuf[MAXPATHLEN + 1], namebuf[MAXNAMLEN + 1];
780 + struct file_header *fhp, *res;
781 + char *dirnamep, *namep;
782 + int error;
783 +
784 + TAILQ_FOREACH(fhp, fhhp, file_header_entries) {
785 + /*
786 + * If fhp == symlink and target file == regular file || symlink,
787 + * then grab target's filename MAC and look it up in our hash table.
788 + * Print a warning message if it is not found.
789 + * symlink target referes to the file that is immediately pointed to by
790 + * our symlink. therefore in a syml1->syml2->file example, we only check
791 + * syml2 if we are supplied syml1. This is by choise so that user will
792 + * receive warning for intermediate parts of a symlink chain.
793 + *
794 + * XXXgpf: perhaps relax restrictions on warning messages
795 + */
796 + if (fhp->target_path != NULL) {
797 + if (lstat(fhp->target_path, &sb) != 0) {
798 + warn("cannot stat file %s", fhp->target_path);
799 + continue;
800 + }
801 +
802 + if (S_ISLNK(sb.st_mode) == 0 && S_ISREG(sb.st_mode) == 0)
803 + continue;
804 +
805 + /* retrieve dirpath & filaname */
806 + strlcpy(targetfh.path, fhp->target_path, sizeof(targetfh.path));
807 + strlcpy(dirbuf, targetfh.path, sizeof(dirbuf));
808 + strlcpy(namebuf, targetfh.path, sizeof(namebuf));
809 +
810 + dirnamep = dirname(dirbuf);
811 + if (dirnamep == NULL) {
812 + pefs_warn("failed to extract dirname of %s", targetfh.path);
813 + continue;
814 + }
815 + strlcpy(targetfh.dirpath, dirnamep, sizeof(targetfh.dirpath));
816 +
817 + namep = basename(namebuf);
818 + if (namep == NULL) {
819 + pefs_warn("failed to extract filename of %s", targetfh.path);
820 + continue;
821 + }
822 + strlcpy(targetfh.filename, namep, sizeof(targetfh.filename));
823 +
824 + targetfh.pfd = -1;
825 + targetfh.pfd = open(targetfh.dirpath, O_RDONLY);
826 + if (targetfh.pfd < 0) {
827 + warn("cannot open %s", targetfh.dirpath);
828 + continue;
829 + }
830 +
831 + error = pefs_get_file_id(&targetfh, 0);
832 + if (error == 0) {
833 + res = pefs_cuckoo_lookup(chtp, &targetfh);
834 + if (res == NULL)
835 + pefs_warn("target file %s of symlink %s was not "
836 + "found in inputlist", targetfh.path, fhp->path);
837 + }
838 + pefs_close_files(&targetfh);
839 + }
840 + }
841 +}
842 +
843 +/* XXXgpf: for debugging purposes */
844 +static void
845 +pefs_hardlink_print(struct hardlink_head *hlc_headp)
846 +{
847 + struct hardlink_counter *hlcp;
848 + struct file_header *fhp;
849 +
850 + dprintf(("\n+++Printing RB tree+++\n\n"));
851 + RB_FOREACH(hlcp, hardlink_head, hlc_headp) {
852 + dprintf(("inode %d\ttotal links %d\tlinks found %d\n",
853 + hlcp->inode, hlcp->total_links, hlcp->links_found));
854 + TAILQ_FOREACH(fhp, &(hlcp->file_headers), fh_hardlink_entries) {
855 + dprintf(("\tpath: %s\n", fhp->path));
856 + }
857 + }
858 +}
859 +
860 +static void
861 +pefs_hardlink_warn(struct hardlink_head *hlc_headp)
862 +{
863 + struct hardlink_counter *hlcp;
864 + struct file_header *fhp;
865 + int i;
866 +
867 + RB_FOREACH(hlcp, hardlink_head, hlc_headp) {
868 + if (hlcp->total_links > hlcp->links_found) {
869 + pefs_warn("%d hard link(s) of total %d were found in input list "
870 + "for file with inode: %d",
871 + hlcp->links_found, hlcp->total_links, hlcp->inode);
872 + i = 1;
873 + TAILQ_FOREACH(fhp, &(hlcp->file_headers), fh_hardlink_entries) {
874 + pefs_warn("link %d: %s", i++, fhp->path);
875 + }
876 + }
877 + }
878 +}
879 +
880 +static int
881 +pefs_hardlink_insert(struct hardlink_head *hlc_headp, struct file_header *fhp,
882 + struct stat *sbp)
883 +{
884 + struct hardlink_counter find, *res, *new_hlcp;
885 +
886 + find.inode = sbp->st_ino;
887 + res = RB_FIND(hardlink_head, hlc_headp, &find);
888 +
889 + if (res != NULL) {
890 + res->links_found++;
891 + TAILQ_INSERT_TAIL(&(res->file_headers), fhp, fh_hardlink_entries);
892 + }
893 + else {
894 + new_hlcp = malloc(sizeof(struct hardlink_counter));
895 + if (new_hlcp == NULL) {
896 + warn("memory allocation error");
897 + return (PEFS_ERR_SYS);
898 + }
899 +
900 + new_hlcp->inode = sbp->st_ino;
901 + new_hlcp->total_links = sbp->st_nlink;
902 + new_hlcp->links_found = 1;
903 + TAILQ_INIT(&(new_hlcp->file_headers));
904 + TAILQ_INSERT_TAIL(&(new_hlcp->file_headers), fhp, fh_hardlink_entries);
905 +
906 + RB_INSERT(hardlink_head, hlc_headp, new_hlcp);
907 + }
908 +
909 + return (0);
910 +}
911 +
912 +static int
913 +pefs_hardlink_cmp(struct hardlink_counter *hlcp1, struct hardlink_counter *hlcp2)
914 +{
915 + if (hlcp1->inode < hlcp2->inode)
916 + return -1;
917 + else if (hlcp1->inode > hlcp2->inode)
918 + return 1;
919 + else
920 + return 0;
921 +}
922 +
923 +static void
924 +pefs_hardlink_free(struct hardlink_head *hlc_headp)
925 +{
926 + struct hardlink_counter *cur, *next;
927 +
928 + for (cur = RB_MIN(hardlink_head, hlc_headp); cur != NULL; cur = next) {
929 + next = RB_NEXT(hardlink_head, hlc_headp, cur);
930 + RB_REMOVE(hardlink_head, hlc_headp, cur);
931 + free(cur);
932 + }
933 +}
934 +
935 +/* open a file and perform various semantic checks on it */
936 +static int
937 +pefs_open_semantic_checks(struct file_header *fhp, struct statfs *fsp,
938 + struct hardlink_head *hlc_headp, int flags)
939 +{
940 + char dirbuf[MAXPATHLEN + 1], namebuf[MAXNAMLEN + 1];
941 + char sbuf[MAXPATHLEN + 1];
942 + struct stat sb;
943 + struct statfs this_fs;
944 + char *dirnamep, *namep;
945 + size_t target_path_size;
946 + int error, nchars;
947 +
948 + /* retrieve dirpath & filename */
949 + strlcpy(dirbuf, fhp->path, sizeof(dirbuf));
950 + strlcpy(namebuf, fhp->path, sizeof(namebuf));
951 +
952 + dirnamep = dirname(dirbuf);
953 + if (dirnamep == NULL) {
954 + pefs_warn("failed to extract dirname of %s", fhp->path);
955 + return (PEFS_ERR_GENERIC);
956 + }
957 + strlcpy(fhp->dirpath, dirnamep, sizeof(fhp->dirpath));
958 +
959 + namep = basename(namebuf);
960 + if (namep == NULL) {
961 + pefs_warn("failed to extract filename of %s", fhp->path);
962 + return (PEFS_ERR_GENERIC);
963 + }
964 + strlcpy(fhp->filename, namep, sizeof(fhp->filename));
965 +
966 + dprintf(("path = %s!\ndirname = %s!\nbasename = %s!\n", fhp->path,
967 + fhp->dirpath, fhp->filename));
968 +
969 + if (lstat(fhp->path, &sb) != 0) {
970 + warn("cannot stat file %s", fhp->path);
971 + return (PEFS_ERR_SYS);
972 + }
973 +
974 + if ((sb.st_flags & SF_IMMUTABLE) == 0 &&
975 + (flags & PEFS_SETIMMUTABLE) == 0 &&
976 + (flags & PEFS_VERIFY) == 0 &&
977 + (flags & PEFS_GETID) == 0) {
978 + pefs_warn("file %s does not have schg flag", fhp->path);
979 + return (PEFS_ERR_SYS);
980 + }
981 +
982 + if (S_ISLNK(sb.st_mode) != 0) {
983 + fhp->pfd = open(fhp->dirpath, O_RDONLY);
984 + if (fhp->pfd < 0) {
985 + warn("cannot open %s", fhp->dirpath);
986 + return (PEFS_ERR_IO);
987 + }
988 +
989 + if ((sb.st_flags & SF_IMMUTABLE) == 0 &&
990 + (flags & PEFS_SETIMMUTABLE) != 0) {
991 + dprintf(("setting immutable flag %s\n", fhp->path));
992 + error = lchflags(fhp->path, SF_IMMUTABLE);
993 + if (error != 0) {
994 + warn("cannot set schg flag to file %s", fhp->path);
995 + return (PEFS_ERR_SYS);
996 + }
997 + }
998 +
999 + nchars = readlink(fhp->path, sbuf, sizeof(sbuf));
1000 + if (nchars == -1) {
1001 + warn("readlink failed: %s", fhp->path);
1002 + return (PEFS_ERR_SYS);
1003 + }
1004 +
1005 + /*
1006 + * Target_path can be used to tell if user has supplied target_file
1007 + * in input file-list, since symlinks are not traversed. User will have
1008 + * to provide fullpaths for both symlink & target file if he wants
1009 + * integrity checking for both. However, we will print warning messages
1010 + * in case target file is not provided in user supplied input list.
1011 + *
1012 + * Target referes to the file immediately pointed to by our symlink, not
1013 + * the final target of a possible symlink chain.
1014 + */
1015 + target_path_size = MAXPATHLEN + 1;
1016 + fhp->target_path = malloc(target_path_size);
1017 + if (fhp->target_path == NULL) {
1018 + warn("memory allocation error");
1019 + return (PEFS_ERR_SYS);
1020 + }
1021 + sbuf[nchars] = '\0';
1022 +
1023 + /* turn relative paths to absolute paths */
1024 + if (sbuf[0] != '/' && (flags & PEFS_UNMOUNTED) == 0 &&
1025 + (flags & PEFS_NOKEY) == 0)
1026 + snprintf(fhp->target_path, target_path_size, "%s/%s",
1027 + fhp->dirpath, sbuf);
1028 + else
1029 + strlcpy(fhp->target_path, sbuf, target_path_size);
1030 +
1031 + /*
1032 + * The only semantic check that's performed on target file is an attempt
1033 + * to lstat() the file, in order to make sure the file exists. This is
1034 + * intentional since target file is allowed to reside on a different
1035 + * filesystem or in the same filesystem, but not be a regular file or a
1036 + * symlink.
1037 + * e.g. a directory
1038 + */
1039 + if ((flags & PEFS_NOKEY) == 0 && (flags & PEFS_UNMOUNTED) == 0) {
1040 + /*
1041 + * If target_path is encrypted due to no key or unmounted fs,
1042 + * lstat will always fail.
1043 + */
1044 + if (lstat(fhp->target_path, &sb) != 0) {
1045 + warn("cannot stat symlink's target file %s", fhp->target_path);
1046 + return (PEFS_ERR_SYS);
1047 + }
1048 + }
1049 + return (0);
1050 + }
1051 +
1052 + fhp->fd = open(fhp->path, O_RDONLY | O_NOFOLLOW);
1053 + if (fhp->fd < 0) {
1054 + warn("cannot open %s", fhp->path);
1055 + return (PEFS_ERR_IO);
1056 + }
1057 +
1058 + fhp->pfd = open(fhp->dirpath, O_RDONLY);
1059 + if (fhp->pfd < 0) {
1060 + warn("cannot open %s", fhp->dirpath);
1061 + return (PEFS_ERR_IO);
1062 + }
1063 +
1064 + if (fstat(fhp->fd, &sb) != 0) {
1065 + warn("cannot stat file %s", fhp->path);
1066 + return (PEFS_ERR_SYS);
1067 + }
1068 +
1069 + if (S_ISREG(sb.st_mode) == 0 && (flags & PEFS_GETID) == 0) {
1070 + pefs_warn("filename: %s is not a regular file", fhp->path);
1071 + return (PEFS_ERR_INVALID);
1072 + }
1073 +
1074 + if ((sb.st_flags & SF_IMMUTABLE) == 0 &&
1075 + (flags & PEFS_SETIMMUTABLE) != 0) {
1076 + dprintf(("setting immutable flag %s\n", fhp->path));
1077 + error = fchflags(fhp->fd, SF_IMMUTABLE);
1078 + if (error != 0) {
1079 + warn("cannot set schg flag to file %s", fhp->path);
1080 + return (PEFS_ERR_SYS);
1081 + }
1082 + }
1083 +
1084 + if ((flags & PEFS_UNMOUNTED) == 0 && (fsp != NULL)) {
1085 + if (fstatfs(fhp->fd, &this_fs) == -1) {
1086 + pefs_warn("statfs failed: %s: %s", fhp->path, strerror(errno));
1087 + return (PEFS_ERR_SYS);
1088 + }
1089 +
1090 + if ((fsp->f_fsid.val[0] != this_fs.f_fsid.val[0]) ||
1091 + (fsp->f_fsid.val[1] != this_fs.f_fsid.val[1])) {
1092 + pefs_warn("filename: %s does not reside in filesystem %s",
1093 + fhp->path, fsp->f_mntonname);
1094 + return (PEFS_ERR_INVALID);
1095 + }
1096 + }
1097 +
1098 + /* Keep all hardlink file headers in a rb tree */
1099 + if (sb.st_nlink > 1 && hlc_headp != NULL)
1100 + return (pefs_hardlink_insert(hlc_headp, fhp, &sb));
1101 +
1102 + return (0);
1103 +}
1104 +
1105 +static struct file_header *
1106 +pefs_next_file(FILE *fpin, int *error, int *nfiles)
1107 +{
1108 + char buf[MAXPATHLEN + 1];
1109 + struct file_header *fhp;
1110 +
1111 + if (fgets(buf, sizeof(buf), fpin) == NULL) {
1112 + if (feof(fpin))
1113 + *error = 0;
1114 + else {
1115 + *error = PEFS_ERR_IO;
1116 + warn("error reading input");
1117 + }
1118 + return (NULL);
1119 + }
1120 +
1121 + if (buf[strnlen(buf, sizeof(buf)) - 1] == '\n')
1122 + buf[strnlen(buf, sizeof(buf)) - 1] = '\0';
1123 + dprintf(("\nnext file entry=%s!\n", buf));
1124 +
1125 + fhp = pefs_allocate_file_header();
1126 + if (fhp == NULL) {
1127 + *error = PEFS_ERR_SYS;
1128 + return (NULL);
1129 + }
1130 +
1131 + strlcpy(fhp->path, buf, sizeof(fhp->path));
1132 + (*nfiles)++;
1133 +
1134 + return (fhp);
1135 +}
1136 +
1137 +/*
1138 + * This function creates the in memory database that will be later written to
1139 + * the checksum file.
1140 + * A) For each file entry:
1141 + * A1) semantic checks:
1142 + * A1a) file should reside in pefs filesystem & file should be
1143 + * regular file or symbolic link.
1144 + * A1b) if symlink, acquire and save the absolute path of the symlink's
1145 + * target. Try to lstat() the target but don't do anything else.
1146 + * A1c) If hardlink, save a reference to this file entry in our
1147 + * rb tree that uses i-node numbers as keys and is used in
1148 + * part 'C' to print warnings.
1149 + * A1d) Open and store file descriptors to file & parent_directory.
1150 + * A2) the file_id is retrieved. (filename MAC)
1151 + * A3) list of checksums is computed for the file's 4k blocks.
1152 + * A4) file entry is added to the universal fh_head.
1153 + * B) Print warnings for hardlinks if the number of links found in inputlist
1154 + * isn't equal to the number of total inode links.
1155 + * C) Hash tables are allocated.
1156 + * D) Cuckoo insertion:
1157 + * We try to populate our hash tables using the cuckoo algorithm. Should we fall
1158 + * into an infinite loop during insertion, we re-allocate larger hash tables
1159 + * and try again until we succeed. The possibility to fail twice in a row is
1160 + * 1.5% * 1.5% = 0.0225%
1161 + * E) For each symlink found in input list, print warnings if its target file
1162 + * was not found in input list since symlinks are not traversed.
1163 + */
1164 +static int
1165 +pefs_create_in_memory_db(FILE *fpin, const EVP_MD *md, uint8_t hash_len,
1166 + struct cuckoo_hash_table *chtp, char *fsroot, int flags)
1167 +{
1168 + struct statfs fs;
1169 + struct file_header_head fh_head;
1170 + struct hardlink_head hlc_head;
1171 + struct file_header *fhp;
1172 + int error;
1173 + uint32_t nfiles;
1174 +
1175 + nfiles = 0;
1176 + if (statfs(fsroot, &fs) == -1) {
1177 + pefs_warn("statfs failed: %s: %s", fsroot, strerror(errno));
1178 + return (PEFS_ERR_SYS);
1179 + }
1180 +
1181 + TAILQ_INIT(&fh_head);
1182 + RB_INIT(&hlc_head);
1183 + while((fhp = pefs_next_file(fpin, &error, &nfiles)) != NULL) {
1184 + error = pefs_open_semantic_checks(fhp, &fs, &hlc_head, flags);
1185 + if (error != 0) {
1186 + pefs_free_file_header(fhp);
1187 + return (error);
1188 + }
1189 +
1190 + error = pefs_get_file_id(fhp, flags);
1191 + if (error != 0) {
1192 + pefs_free_file_header(fhp);
1193 + return (error);
1194 + }
1195 +
1196 + error = pefs_compute_file_checksums(fhp, md, hash_len, flags);
1197 + if (error != 0) {
1198 + pefs_free_file_header(fhp);
1199 + return (error);
1200 + }
1201 +
1202 + TAILQ_INSERT_TAIL(&fh_head, fhp, file_header_entries);
1203 + pefs_close_files(fhp);
1204 + }
1205 +
1206 + /* checking I/O error from pefs_next_file() */
1207 + if (error != 0)
1208 + return (error);
1209 +
1210 + pefs_hardlink_print(&hlc_head);
1211 + pefs_hardlink_warn(&hlc_head);
1212 + pefs_hardlink_free(&hlc_head);
1213 +
1214 + error = pefs_allocate_hash_table(chtp, nfiles, PEFS_EXTEND);
1215 + if (error != 0)
1216 + return (error);
1217 +
1218 +cuckoo_insert:
1219 + TAILQ_FOREACH(fhp, &fh_head, file_header_entries) {
1220 + error = pefs_add_to_hash_table(chtp, fhp);
1221 + /* collision error */
1222 + if (error == PEFS_ERR_EXIST)
1223 + return (error);
1224 + /*
1225 + * cuckoo insertion algorithm fell into an infinite loop!
1226 + * Create new, larger hash tables where size = next_prime(old_size)
1227 + * and try again.
1228 + */
1229 + else if (error != 0) {
1230 + dprintf(("fell into an infinite loop!\n"));
1231 + error = pefs_allocate_hash_table(chtp, nfiles, PEFS_REALLOC);
1232 + if (error != 0)
1233 + return (error);
1234 + goto cuckoo_insert;
1235 + }
1236 + }
1237 + pefs_print_hash_tables(chtp, hash_len);
1238 + pefs_symlink_warn(chtp, &fh_head);
1239 +
1240 + return (error);
1241 +}
1242 +
1243 +static int
1244 +pefs_write_checksum_file_header(int fdout, struct checksum_file_header *cfhp)
1245 +{
1246 + int rval;
1247 + uint32_t bytes, hash_table_size;
1248 +
1249 + rval = lseek(fdout, PEFS_SIGNATURE_MAX_LENGTH, SEEK_SET);
1250 + if (rval == -1) {
1251 + warn("lseek error while writing to .pefs.checksum");
1252 + return (PEFS_ERR_SYS);
1253 + }
1254 +
1255 + bytes = write(fdout, &(cfhp->version), sizeof(cfhp->version));
1256 + if (bytes != sizeof(cfhp->version)) {
1257 + warn("error writing to .pefs.checksum");
1258 + return (PEFS_ERR_IO);
1259 + }
1260 +
1261 + bytes = write(fdout, &(cfhp->reserved), sizeof(cfhp->reserved));
1262 + if (bytes != sizeof(cfhp->reserved)) {
1263 + warn("error writing to .pefs.checksum");
1264 + return (PEFS_ERR_IO);
1265 + }
1266 +
1267 + bytes = write(fdout, &(cfhp->hash_len), sizeof(cfhp->hash_len));
1268 + if (bytes != sizeof(cfhp->hash_len)) {
1269 + warn("error writing to .pefs.checksum");
1270 + return (PEFS_ERR_IO);
1271 + }
1272 +
1273 + bytes = write(fdout, cfhp->hash_algo, sizeof(cfhp->hash_algo));
1274 + if (bytes != sizeof(cfhp->hash_algo)) {
1275 + warn("error writing to .pefs.checksum");
1276 + return (PEFS_ERR_IO);
1277 + }
1278 +
1279 + bytes = write(fdout, &(cfhp->offset_to_hash_table),
1280 + sizeof(cfhp->offset_to_hash_table));
1281 + if (bytes != sizeof(cfhp->offset_to_hash_table)) {
1282 + warn("error writing to .pefs.checksum");
1283 + return (PEFS_ERR_IO);
1284 + }
1285 +
1286 + hash_table_size = htole32(cfhp->hash_table_size);
1287 + bytes = write(fdout, &hash_table_size, sizeof(hash_table_size));
1288 + if (bytes != sizeof(hash_table_size)) {
1289 + warn("error writing to .pefs.checksum");
1290 + return (PEFS_ERR_IO);
1291 + }
1292 +
1293 + return (0);
1294 +}
1295 +
1296 +static int
1297 +pefs_write_file_header(int fdout, struct file_header *fhp,
1298 + uint32_t *buckets_offset)
1299 +{
1300 + uint32_t nhashes, offset_to_checksums;
1301 + int bytes;
1302 +
1303 + nhashes = htole32(fhp->nhashes);
1304 + bytes = pwrite(fdout, &nhashes, sizeof(nhashes), *buckets_offset);
1305 + if (bytes != sizeof(nhashes)) {
1306 + warn("error writing to .pefs.checksum");
1307 + return (PEFS_ERR_IO);
1308 + }
1309 + (*buckets_offset)+= sizeof(nhashes);
1310 +
1311 + offset_to_checksums = htole32(fhp->offset_to_checksums);
1312 + bytes = pwrite(fdout, &offset_to_checksums,
1313 + sizeof(offset_to_checksums), *buckets_offset);
1314 + if (bytes != sizeof(offset_to_checksums)) {
1315 + warn("error writing to .pefs.checksum");
1316 + return (PEFS_ERR_IO);
1317 + }
1318 + (*buckets_offset)+= sizeof(offset_to_checksums);
1319 +
1320 + bytes = pwrite(fdout, fhp->fid.fid_str, sizeof(fhp->fid.fid_str),
1321 + *buckets_offset);
1322 + if (bytes != sizeof(fhp->fid.fid_str)) {
1323 + warn("error writing to .pefs.checksum");
1324 + return (PEFS_ERR_IO);
1325 + }
1326 + (*buckets_offset)+= sizeof(fhp->fid.fid_str);
1327 +
1328 + return (0);
1329 +}
1330 +
1331 +static int
1332 +pefs_write_bucket(int fdout, struct bucket *bp, uint32_t *buckets_offset)
1333 +{
1334 + struct file_header emptyfh;
1335 + struct file_header *fhp;
1336 +
1337 + fhp = bp->fhp;
1338 + /* Empty files aren't allowed so nhashes == 0 symbolizes an empty bucket */
1339 + if (fhp == NULL) {
1340 + emptyfh.nhashes = 0;
1341 + emptyfh.fid.fid_num = 0;
1342 + emptyfh.offset_to_checksums = 0;
1343 + fhp = &emptyfh;
1344 + }
1345 +
1346 + return (pefs_write_file_header(fdout, fhp, buckets_offset));
1347 +}
1348 +
1349 +static int
1350 +pefs_write_hash(int fdout, struct checksum *csp, uint32_t *hashes_offset,
1351 + uint8_t hash_len)
1352 +{
1353 + int bytes;
1354 +
1355 + bytes = pwrite(fdout, csp->hash, hash_len, *hashes_offset);
1356 + if (bytes != hash_len) {
1357 + warn("error writing to .pefs.checksum");
1358 + return (PEFS_ERR_IO);
1359 + }
1360 + (*hashes_offset)+= hash_len;
1361 +
1362 + return (0);
1363 +}
1364 +
1365 +/*
1366 + * All data member writes are done separately so as to avoid alignment problems.
1367 + * Writes are always in little endian byte order, except file_id which is
1368 + * always treated as big endian.
1369 + *
1370 + * First 512 bytes of .pefs.checksum are reserved for the file's digital
1371 + * signature.
1372 + *
1373 + * After that, the next 16 bytes of .pefs.checksum are filled with
1374 + * .pefs.checksum's global file header. Right after this header lies the
1375 + * 'index' part of our database.
1376 + * This index is later kept in kernel memory.
1377 + *
1378 + * Index:
1379 + * Both hash tables of cuckoo algorithm are written to the file sequentially.
1380 + * The first hash table corresponds to hash1() and the second hash table to
1381 + * hash2(). Each bucket (cell) of a hash table contains at most one
1382 + * entry(=file_header).
1383 + * The size of an entry is 16 bytes.
1384 + *
1385 + * hash table entries end at the following offset:
1386 + * 16 + hash_table_size * 2 * 16
1387 + *
1388 + * Checksums:
1389 + * The last part of .pefs.checksum is filled with the actual checksums.
1390 + * The offset where the first checksum starts is a 512 aligned address.
1391 + * Each hash table file header entry contains an offset that points to the
1392 + * beginning of a chain of checksums for that particular file's 4k blocks.
1393 + */
1394 +static int
1395 +pefs_write_checksum_file(int fdout, struct checksum_file_header *cfhp,
1396 + struct cuckoo_hash_table *chtp)
1397 +{
1398 + struct bucket *bp;
1399 + struct checksum *csp;
1400 + struct file_header *fhp;
1401 + uint32_t i, buckets_offset, hashes_offset;
1402 + int error;
1403 +
1404 + error = pefs_write_checksum_file_header(fdout, cfhp);
1405 + if (error != 0)
1406 + return (error);
1407 +
1408 + /* this points to where the buckets start */
1409 + buckets_offset = PEFS_SIGNATURE_MAX_LENGTH + cfhp->offset_to_hash_table;
1410 +
1411 + /* this points to where the buckets stop and the checksums start */
1412 + hashes_offset = buckets_offset;
1413 + hashes_offset+= chtp->size * PEFS_FH_SIZE * 2;
1414 + if (hashes_offset % PEFS_HASH_BYTE_ALIGNMENT != 0)
1415 + hashes_offset+= PEFS_HASH_BYTE_ALIGNMENT - (hashes_offset %
1416 + PEFS_HASH_BYTE_ALIGNMENT);
1417 +
1418 + for (i = 0; i < chtp->size; i++) {
1419 + bp = &chtp->buckets1[i];
1420 + if (bp->fhp != NULL)
1421 + bp->fhp->offset_to_checksums = hashes_offset;
1422 + error = pefs_write_bucket(fdout, bp, &buckets_offset);
1423 + if (error != 0)
1424 + return (error);
1425 +
1426 + fhp = bp->fhp;
1427 + if (fhp != NULL) {
1428 + TAILQ_FOREACH(csp, &(fhp->checksums), checksum_entries) {
1429 + error = pefs_write_hash(fdout, csp, &hashes_offset,
1430 + cfhp->hash_len);
1431 + if (error != 0)
1432 + return (error);
1433 + }
1434 + }
1435 + }
1436 +
1437 + for (i = 0; i < chtp->size; i++) {
1438 + bp = &chtp->buckets2[i];
1439 + if (bp->fhp != NULL)
1440 + bp->fhp->offset_to_checksums = hashes_offset;
1441 + error = pefs_write_bucket(fdout, bp, &buckets_offset);
1442 + if (error != 0)
1443 + return (error);
1444 +
1445 + fhp = bp->fhp;
1446 + if (fhp != NULL) {
1447 + TAILQ_FOREACH(csp, &(fhp->checksums), checksum_entries) {
1448 + error = pefs_write_hash(fdout, csp, &hashes_offset,
1449 + cfhp->hash_len);
1450 + if (error != 0)
1451 + return (error);
1452 + }
1453 + }
1454 + }
1455 +
1456 + return (0);
1457 +}
1458 +
1459 +static void
1460 +pefs_init_checksum_file_header(struct checksum_file_header *cfhp,
1461 + const char *algo, uint8_t hash_len, struct cuckoo_hash_table *chtp)
1462 +{
1463 + cfhp->hash_len = hash_len;
1464 + cfhp->hash_table_size = chtp->size;
1465 + cfhp->version = PEFS_CHECKSUM_FILE_VERSION;
1466 + strlcpy(cfhp->hash_algo, algo, sizeof(cfhp->hash_algo));
1467 + cfhp->offset_to_hash_table = PEFS_CFH_SIZE;
1468 +}
1469 +
1470 +/* read dsa privkey from file */
1471 +static EVP_PKEY *
1472 +pefs_read_dsa_privkey(FILE *pk_fp)
1473 +{
1474 + DSA *dsa;
1475 + EVP_PKEY *pkey;
1476 + int rval;
1477 +
1478 + dsa = PEM_read_DSAPrivateKey(pk_fp, NULL, NULL, NULL);
1479 + if (dsa == NULL) {
1480 + pefs_warn("error reading dsa private key");
1481 + return (NULL);
1482 + }
1483 +
1484 + pkey = EVP_PKEY_new();
1485 + if (pkey == NULL) {
1486 + pefs_warn("error allocating a pkey");
1487 + DSA_free(dsa);
1488 + return (NULL);
1489 + }
1490 +
1491 + rval = EVP_PKEY_assign_DSA(pkey, dsa);
1492 + if (rval != 1) {
1493 + pefs_warn("error assigning dsa key");
1494 + EVP_PKEY_free(pkey);
1495 + DSA_free(dsa);
1496 + return (NULL);
1497 + }
1498 +
1499 + return (pkey);
1500 +}
1501 +
1502 +/* Sign .pefs.checksum. Signature is placed at the beginning of the file. */
1503 +static int
1504 +pefs_sign_file(int fd, FILE *pkfp)
1505 +{
1506 + unsigned char buf[PEFS_BUFISZE];
1507 + EVP_MD_CTX ctx;
1508 + const EVP_MD *md;
1509 + EVP_PKEY *pkey;
1510 + unsigned char *sign;
1511 + unsigned int sign_len;
1512 + int bytes, error, rval;
1513 +
1514 + pkey = pefs_read_dsa_privkey(pkfp);
1515 + if (pkey == NULL)
1516 + return (PEFS_ERR_SYS);
1517 +
1518 + md = EVP_dss1();
1519 + if (md == NULL) {
1520 + pefs_warn("error acquiring digest type");
1521 + EVP_PKEY_free(pkey);
1522 + return (PEFS_ERR_GENERIC);
1523 + }
1524 +
1525 + /* generate digital signature */
1526 + EVP_SignInit(&ctx, md);
1527 +
1528 + error = lseek(fd, PEFS_SIGNATURE_MAX_LENGTH, SEEK_SET);
1529 + if (error == -1) {
1530 + warn("lseek error while signing file");
1531 + EVP_PKEY_free(pkey);
1532 + return (PEFS_ERR_SYS);
1533 + }
1534 +
1535 + while ((bytes = read(fd, buf, sizeof(buf))) > 0) {
1536 + rval = EVP_SignUpdate(&ctx, buf, bytes);
1537 + if (rval != 1) {
1538 + pefs_warn("error updating sign data");
1539 + EVP_PKEY_free(pkey);
1540 + return (PEFS_ERR_SYS);
1541 + }
1542 + }
1543 +
1544 + if (bytes == -1) {
1545 + warn("read error");
1546 + EVP_PKEY_free(pkey);
1547 + return (PEFS_ERR_IO);
1548 + }
1549 +
1550 + sign = malloc(EVP_PKEY_size(pkey));
1551 + if (sign == NULL) {
1552 + pefs_warn("memory allocation error");
1553 + EVP_PKEY_free(pkey);
1554 + return (PEFS_ERR_SYS);
1555 + }
1556 +
1557 + rval = EVP_SignFinal(&ctx, sign, &sign_len, pkey);
1558 + if (rval != 1) {
1559 + pefs_warn("error generating signature");
1560 + free(sign);
1561 + EVP_PKEY_free(pkey);
1562 + return (PEFS_ERR_SYS);
1563 + }
1564 +
1565 + /* write digital signature to beginning of .pefs.checksum */
1566 + bytes = pwrite(fd, sign, sign_len, 0);
1567 + if (bytes == -1) {
1568 + pefs_warn("error writing signature");
1569 + free(sign);
1570 + EVP_PKEY_free(pkey);
1571 + return (PEFS_ERR_IO);
1572 + }
1573 +
1574 + free(sign);
1575 + EVP_PKEY_free(pkey);
1576 +
1577 + return (0);
1578 +}
1579 +
1580 +/* read dsa pubkey from file */
1581 +static EVP_PKEY *
1582 +pefs_read_dsa_pubkey(FILE *pk_fp)
1583 +{
1584 + DSA *dsa;
1585 + EVP_PKEY *pkey;
1586 + int rval;
1587 +
1588 + dsa = PEM_read_DSA_PUBKEY(pk_fp, NULL, NULL, NULL);
1589 + if (dsa == NULL) {
1590 + pefs_warn("error reading dsa pubkey");
1591 + return (NULL);
1592 + }
1593 +
1594 + pkey = EVP_PKEY_new();
1595 + if (pkey == NULL) {
1596 + pefs_warn("error allocating a pkey");
1597 + DSA_free(dsa);
1598 + return (NULL);
1599 + }
1600 +
1601 + rval = EVP_PKEY_assign_DSA(pkey, dsa);
1602 + if (rval != 1) {
1603 + pefs_warn("error assigning dsa key");
1604 + EVP_PKEY_free(pkey);
1605 + DSA_free(dsa);
1606 + return (NULL);
1607 + }
1608 +
1609 + return (pkey);
1610 +}
1611 +
1612 +/* verify digital signature of .pefs.checksum */
1613 +static int
1614 +pefs_verify_signature(int fd, FILE *pk_fp)
1615 +{
1616 + unsigned char buf[PEFS_BUFISZE];
1617 + EVP_MD_CTX ctx;
1618 + const EVP_MD *md;
1619 + EVP_PKEY *pkey;
1620 + unsigned char *sign;
1621 + int bytes, error, rval, sign_len;
1622 +
1623 + pkey = pefs_read_dsa_pubkey(pk_fp);
1624 + if (pkey == NULL)
1625 + return (PEFS_ERR_SYS);
1626 +
1627 + sign = malloc(EVP_PKEY_size(pkey));
1628 + if (sign == NULL) {
1629 + pefs_warn("memory allocation error");
1630 + EVP_PKEY_free(pkey);
1631 + return (PEFS_ERR_SYS);
1632 + }
1633 +
1634 + /* read signature from .pefs.checksum */
1635 + sign_len = pread(fd, sign, EVP_PKEY_size(pkey), 0);
1636 + if (sign_len == -1) {
1637 + warn("error reading signature");
1638 + free(sign);
1639 + return (PEFS_ERR_IO);
1640 + }
1641 +
1642 + md = EVP_dss1();
1643 + if (md == NULL) {
1644 + pefs_warn("error acquiring digest type");
1645 + free(sign);
1646 + EVP_PKEY_free(pkey);
1647 + return (PEFS_ERR_GENERIC);
1648 + }
1649 +
1650 + /* process .pefs.checksum & verify the signature */
1651 + EVP_VerifyInit(&ctx, md);
1652 +
1653 + error = lseek(fd, PEFS_SIGNATURE_MAX_LENGTH, SEEK_SET);
1654 + if (error == -1) {
1655 + warn("lseek error while verifying file");
1656 + free(sign);
1657 + EVP_PKEY_free(pkey);
1658 + return (PEFS_ERR_SYS);
1659 + }
1660 +
1661 + while ((bytes = read(fd, buf, sizeof(buf))) > 0) {
1662 + rval = EVP_VerifyUpdate(&ctx, buf, bytes);
1663 + if (rval != 1) {
1664 + pefs_warn("error updating sign data");
1665 + free(sign);
1666 + EVP_PKEY_free(pkey);
1667 + return (PEFS_ERR_SYS);
1668 + }
1669 + }
1670 +
1671 + if (bytes == -1) {
1672 + warn("read error");
1673 + free(sign);
1674 + EVP_PKEY_free(pkey);
1675 + return (PEFS_ERR_IO);
1676 + }
1677 +
1678 + rval = EVP_VerifyFinal(&ctx, sign, sign_len, pkey);
1679 + if (rval == 0) {
1680 + pefs_warn("file signature is not verified by public key");
1681 + free(sign);
1682 + EVP_PKEY_free(pkey);
1683 + return (PEFS_ERR_GENERIC);
1684 + }
1685 + else if (rval == -1) {
1686 + pefs_warn("error verifying signature");
1687 + free(sign);
1688 + EVP_PKEY_free(pkey);
1689 + return (PEFS_ERR_GENERIC);
1690 + }
1691 +
1692 + free(sign);
1693 + EVP_PKEY_free(pkey);
1694 +
1695 + return (0);
1696 +}
1697 +
1698 +/*
1699 + * If .pefs.checksum is created inside pefs mounted fs, then it will obtain an
1700 + * encrypted filename & encrypted data, which is unacceptable. User should
1701 + * create checksum file outside of filesystem and then copy it by hand.
1702 + */
1703 +static int
1704 +pefs_open_checksum_file(int *fdp, char *fsroot, char *csm_path)
1705 +{
1706 + struct statfs pefs_fs, checksum_fs;
1707 + int fd;
1708 +
1709 + *fdp = -1;
1710 +
1711 + /* create checksum file */
1712 + fd = open(csm_path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
1713 + if (fd == -1) {
1714 + warn("cannot open %s", csm_path);
1715 + return (PEFS_ERR_IO);
1716 + }
1717 +
1718 + *fdp = fd;
1719 +
1720 + if (statfs(fsroot, &pefs_fs) == -1) {
1721 + pefs_warn("statfs failed: %s: %s", fsroot, strerror(errno));
1722 + return (PEFS_ERR_SYS);
1723 + }
1724 +
1725 + if (statfs(csm_path, &checksum_fs) == -1) {
1726 + pefs_warn("statfs failed: %s: %s", csm_path, strerror(errno));
1727 + return (PEFS_ERR_SYS);
1728 + }
1729 +
1730 + if ((pefs_fs.f_fsid.val[0] == checksum_fs.f_fsid.val[0]) &&
1731 + (pefs_fs.f_fsid.val[1] == checksum_fs.f_fsid.val[1])) {
1732 + pefs_warn("%s must be created outside of pefs mounted filesystem %s",
1733 + csm_path, pefs_fs.f_mntonname);
1734 + return (PEFS_ERR_INVALID);
1735 + }
1736 +
1737 + return (0);
1738 +}
1739 +
1740 +/*
1741 + * An in memory database is created from entries in fpin. This database is
1742 + * later written to file ".pefs.checksum" which is created under csm_path.
1743 + * algo is used as a cryptographic hash function that produces checksums
1744 + * for 4k blocks of each file. When we are done with .pefs.checksum, we
1745 + * sign it and place the signature at the beginning of .pefs.checksum.
1746 + */
1747 +int
1748 +pefs_create_checksum_file(FILE *fpin, char *fsroot, char *csm_path,
1749 + FILE *pk_fp, const char *algo, int flags)
1750 +{
1751 + struct cuckoo_hash_table checksum_hash_table;
1752 + struct checksum_file_header cfh;
1753 + const EVP_MD *md;
1754 + int error, fdout;
1755 + uint8_t hash_len;
1756 +
1757 + OpenSSL_add_all_digests();
1758 + md = EVP_get_digestbyname(algo);
1759 +
1760 + if(md == NULL) {
1761 + pefs_warn("Unknown message digest %s\n", algo);
1762 + return (PEFS_ERR_INVALID);
1763 + }
1764 + hash_len = EVP_MD_size(md);
1765 +
1766 + pefs_init_hash_table(&checksum_hash_table);
1767 +
1768 + error = pefs_open_checksum_file(&fdout, fsroot, csm_path);
1769 + if (error != 0)
1770 + goto out;
1771 +
1772 + error = pefs_create_in_memory_db(fpin, md, hash_len,
1773 + &checksum_hash_table, fsroot, flags);
1774 + if (error != 0)
1775 + goto out;
1776 +
1777 + pefs_init_checksum_file_header(&cfh, algo, hash_len, &checksum_hash_table);
1778 +
1779 + error = pefs_write_checksum_file(fdout, &cfh, &checksum_hash_table);
1780 + if (error != 0)
1781 + goto out;
1782 +
1783 + error = pefs_sign_file(fdout, pk_fp);
1784 +
1785 +out:
1786 + if (fdout >= 0) {
1787 + close(fdout);
1788 + if (error != 0)
1789 + unlink(csm_path);
1790 + }
1791 + pefs_free_hash_table(&checksum_hash_table);
1792 +
1793 + return (error);
1794 +}
1795 +
1796 +static int
1797 +pefs_read_checksum_file_header(int fdin, struct checksum_file_header *cfhp)
1798 +{
1799 + int rval;
1800 + uint32_t bytes, hash_table_size;
1801 +
1802 + rval = lseek(fdin, PEFS_SIGNATURE_MAX_LENGTH, SEEK_SET);
1803 + if (rval == -1) {
1804 + warn("lseek error while reading checksum file header");
1805 + return (PEFS_ERR_SYS);
1806 + }
1807 +
1808 + bytes = read(fdin, &(cfhp->version), sizeof(cfhp->version));
1809 + if (bytes != sizeof(cfhp->version)) {
1810 + warn("error reading from .pefs.checksum");
1811 + return (PEFS_ERR_IO);
1812 + }
1813 +
1814 + bytes = read(fdin, &(cfhp->reserved), sizeof(cfhp->reserved));
1815 + if (bytes != sizeof(cfhp->reserved)) {
1816 + warn("error reading from .pefs.checksum");
1817 + return (PEFS_ERR_IO);
1818 + }
1819 +
1820 + bytes = read(fdin, &(cfhp->hash_len), sizeof(cfhp->hash_len));
1821 + if (bytes != sizeof(cfhp->hash_len)) {
1822 + warn("error reading from .pefs.checksum");
1823 + return (PEFS_ERR_IO);
1824 + }
1825 +
1826 + bytes = read(fdin, cfhp->hash_algo, sizeof(cfhp->hash_algo));
1827 + if (bytes != sizeof(cfhp->hash_algo)) {
1828 + warn("error reading from .pefs.checksum");
1829 + return (PEFS_ERR_IO);
1830 + }
1831 +
1832 + bytes = read(fdin, &(cfhp->offset_to_hash_table),
1833 + sizeof(cfhp->offset_to_hash_table));
1834 + if (bytes != sizeof(cfhp->offset_to_hash_table)) {
1835 + warn("error reading from .pefs.checksum");
1836 + return (PEFS_ERR_IO);
1837 + }
1838 +
1839 + bytes = read(fdin, &hash_table_size, sizeof(hash_table_size));
1840 + if (bytes != sizeof(hash_table_size)) {
1841 + warn("error reading from .pefs.checksum");
1842 + return (PEFS_ERR_IO);
1843 + }
1844 + cfhp->hash_table_size = le32toh(hash_table_size);
1845 +
1846 + dprintf(("+++printing checksum file header info+++\n"));
1847 + dprintf(("version %X\nhash_len %d\nhash_algo %s\n",
1848 + cfhp->version, cfhp->hash_len, cfhp->hash_algo));
1849 + dprintf(("offset to hash table %d\nhash table size %d\n",
1850 + cfhp->offset_to_hash_table, cfhp->hash_table_size));
1851 +
1852 + return (0);
1853 +}
1854 +
1855 +static int
1856 +pefs_read_file_header(int fdin, struct file_header *fhp,
1857 + uint32_t *buckets_offset)
1858 +{
1859 + uint32_t nhashes, offset_to_checksums;
1860 + int bytes;
1861 +
1862 + bytes = pread(fdin, &nhashes, sizeof(nhashes), *buckets_offset);
1863 + if (bytes != sizeof(nhashes)) {
1864 + warn("error reading from .pefs.checksum");
1865 + return (PEFS_ERR_IO);
1866 + }
1867 + fhp->nhashes = le32toh(nhashes);
1868 + (*buckets_offset)+= sizeof(nhashes);
1869 +
1870 + bytes = pread(fdin, &offset_to_checksums, sizeof(offset_to_checksums),
1871 + *buckets_offset);
1872 + if (bytes != sizeof(offset_to_checksums)) {
1873 + warn("error reading from .pefs.checksum");
1874 + return (PEFS_ERR_IO);
1875 + }
1876 + fhp->offset_to_checksums = le32toh(offset_to_checksums);
1877 + (*buckets_offset)+= sizeof(offset_to_checksums);
1878 +
1879 + bytes = pread(fdin, fhp->fid.fid_str, sizeof(fhp->fid.fid_str),
1880 + *buckets_offset);
1881 + if (bytes != sizeof(fhp->fid.fid_str)) {
1882 + warn("error reading from .pefs.checksum");
1883 + return (PEFS_ERR_IO);
1884 + }
1885 + (*buckets_offset)+= sizeof(fhp->fid.fid_str);
1886 +
1887 + dprintf(("\nfile header offset = %d\n", *fh_offset));
1888 + dprintf(("\n++priting file header info++\n"));
1889 + dprintf(("nhashes %d\noffset_to_checksums %u\n",
1890 + fhp->nhashes, fhp->offset_to_checksums));
1891 + dprintf(("file id %llu\n", fhp->file_id));
1892 +
1893 + return (0);
1894 +}
1895 +
1896 +static int
1897 +pefs_read_bucket(int fdin, struct bucket *bp, uint32_t *buckets_offset)
1898 +{
1899 + struct file_header *fhp;
1900 + int error;
1901 +
1902 + dprintf(("bucket offset = %d\n", *buckets_offset));
1903 + fhp = pefs_allocate_file_header();
1904 + if (fhp == NULL)
1905 + return (PEFS_ERR_SYS);
1906 +
1907 + error = pefs_read_file_header(fdin, fhp, buckets_offset);
1908 + if (error != 0)
1909 + return (error);
1910 +
1911 + if (fhp->nhashes == 0) {
1912 + pefs_free_file_header(fhp);
1913 + fhp = NULL;
1914 + }
1915 + bp->fhp = fhp;
1916 +
1917 + dprintf(("\n++priting bucket info++\n"));
1918 +
1919 + return (0);
1920 +}
1921 +
1922 +static int
1923 +pefs_read_hash(int fdin, struct checksum *csp, uint32_t *hashes_offset,
1924 + uint8_t hash_len)
1925 +{
1926 + int bytes;
1927 +
1928 + bytes = pread(fdin, csp->hash, hash_len, *hashes_offset);
1929 + if (bytes != hash_len) {
1930 + warn("error reading from .pefs.checksum");
1931 + return (PEFS_ERR_IO);
1932 + }
1933 + (*hashes_offset)+= hash_len;
1934 +
1935 + dprintf(("hashes offset = %d\n", *hashes_offset));
1936 + dprintf(("hash %s\n", csp->hash));
1937 +
1938 + return (0);
1939 +}
1940 +
1941 +static void
1942 +pefs_add_to_file_header(struct file_header *fhp, struct checksum *csp)
1943 +{
1944 + TAILQ_INSERT_TAIL(&(fhp->checksums), csp, checksum_entries);
1945 +}
1946 +
1947 +/* Create in memory checksum database by reading .pefs.checksum file. */
1948 +static int
1949 +pefs_read_checksum_file(int fdin, struct checksum_file_header *cfhp,
1950 + struct cuckoo_hash_table *chtp)
1951 +{
1952 + struct bucket *bp;
1953 + struct checksum *csp;
1954 + struct file_header *fhp;
1955 + uint32_t i, k, buckets_offset, hashes_offset;
1956 + int error;
1957 +
1958 + /* this points to where the buckets start */
1959 + buckets_offset = PEFS_SIGNATURE_MAX_LENGTH + cfhp->offset_to_hash_table;
1960 +
1961 + for (i = 0; i < chtp->size; i++) {
1962 + bp = &chtp->buckets1[i];
1963 + error = pefs_read_bucket(fdin, bp, &buckets_offset);
1964 + if (error != 0)
1965 + return (error);
1966 +
1967 + fhp = bp->fhp;
1968 + if (fhp != NULL) {
1969 + hashes_offset = fhp->offset_to_checksums;
1970 +
1971 + for (k = 0; k < fhp->nhashes; k++) {
1972 + csp = malloc(sizeof(struct checksum));
1973 + if (csp == NULL) {
1974 + pefs_warn("memory allocation error");
1975 + return (PEFS_ERR_SYS);
1976 + }
1977 + csp->hash = malloc(cfhp->hash_len);
1978 + if (csp->hash == NULL) {
1979 + pefs_warn("memory allocation error");
1980 + return (PEFS_ERR_SYS);
1981 + }
1982 +
1983 + error = pefs_read_hash(fdin, csp, &hashes_offset,
1984 + cfhp->hash_len);
1985 + if (error != 0)
1986 + return (error);
1987 +
1988 + pefs_add_to_file_header(fhp, csp);
1989 + }
1990 + }
1991 + }
1992 +
1993 + for (i = 0; i < chtp->size; i++) {
1994 + bp = &chtp->buckets2[i];
1995 + error = pefs_read_bucket(fdin, bp, &buckets_offset);
1996 + if (error != 0)
1997 + return (error);
1998 +
1999 + fhp = bp->fhp;
2000 + if (fhp != NULL) {
2001 + hashes_offset = fhp->offset_to_checksums;
2002 +
2003 + for (k = 0; k < fhp->nhashes; k++) {
2004 + csp = malloc(sizeof(struct checksum));
2005 + if (csp == NULL) {
2006 + pefs_warn("memory allocation error");
2007 + return (PEFS_ERR_SYS);
2008 + }
2009 + csp->hash = malloc(cfhp->hash_len);
2010 + if (csp->hash == NULL) {
2011 + pefs_warn("memory allocation error");
2012 + return (PEFS_ERR_SYS);
2013 + }
2014 +
2015 + error = pefs_read_hash(fdin, csp, &hashes_offset,
2016 + cfhp->hash_len);
2017 + if (error != 0)
2018 + return (error);
2019 +
2020 + pefs_add_to_file_header(fhp, csp);
2021 + }
2022 + }
2023 + }
2024 +
2025 + return (0);
2026 +}
2027 +
2028 +static int
2029 +pefs_compare_checksums(struct file_header *fhp, struct file_header *indexfhp,
2030 + uint8_t hash_len)
2031 +{
2032 + struct checksum *csp1, *csp2;
2033 + uint32_t i;
2034 + int error, cmp;
2035 +
2036 + dprintf(("comparing hashes for file with fid: %llu\n",
2037 + fhp->fid.fid_num));
2038 +
2039 + error = 0;
2040 + if (fhp->nhashes != indexfhp->nhashes) {
2041 + pefs_warn("number of hashes differ between on disk file and %s values "
2042 + "for file %s: %u vs %u",
2043 + PEFS_FILE_CHECKSUM, fhp->path, fhp->nhashes, indexfhp->nhashes);
2044 + error = PEFS_ERR_CHECKSUM;
2045 + }
2046 +
2047 + csp1 = TAILQ_FIRST(&fhp->checksums);
2048 + csp2 = TAILQ_FIRST(&indexfhp->checksums);
2049 + i = 1;
2050 + while (csp1 != NULL && csp2 != NULL) {
2051 + cmp = memcmp(csp1->hash, csp2->hash, hash_len);
2052 + if (cmp != 0) {
2053 + pefs_warn("checksum no: %u differs between on disk file and %s "
2054 + "values for file %s",
2055 + i, PEFS_FILE_CHECKSUM, fhp->path);
2056 + error = PEFS_ERR_CHECKSUM;
2057 + }
2058 + csp1 = TAILQ_NEXT(csp1, checksum_entries);
2059 + csp2 = TAILQ_NEXT(csp2, checksum_entries);
2060 + i++;
2061 + }
2062 +
2063 + return (error);
2064 +}
2065 +
2066 +/*
2067 + * Traverse the entire filesystem and for every regular file or symbolic link,
2068 + * look it up in .pefs.checksum index and verify its checksums.
2069 + *
2070 + * This function will try to avoid returning due to errors encountered when
2071 + * checksums mismatch or immutable flags are missing so as to print as many
2072 + * warnings as possible.
2073 + */
2074 +static int
2075 +pefs_traverse_fs(struct cuckoo_hash_table *chtp, const EVP_MD *md,
2076 + uint8_t hash_len, DIR *dirp, char *path, struct statfs *fsp,
2077 + struct hardlink_head *hlc_headp, struct file_header_head *fh_headp,
2078 + int flags, int *checksum_error)
2079 +{
2080 + char tmpath[MAXPATHLEN];
2081 + struct stat sb;
2082 + DIR *dirtmp;
2083 + struct dirent *sdp;
2084 + struct file_header *fhp, *indexfhp;
2085 + int error;
2086 +
2087 + while (dirp) {
2088 + sdp = readdir(dirp);
2089 + if (sdp != NULL) {
2090 + if (strcmp(sdp->d_name, "..") == 0 ||
2091 + strcmp(sdp->d_name, ".") == 0 ||
2092 + strcmp(sdp->d_name, ".pefs.db") == 0 ||
2093 + strcmp(sdp->d_name, ".pefs.conf") == 0 ||
2094 + strcmp(sdp->d_name, ".pefs.checksum") == 0)
2095 + continue;
2096 +
2097 + dprintf(("dirent: %s\n", sdp->d_name));
2098 + snprintf(tmpath, sizeof(tmpath), "%s/%s", path, sdp->d_name);
2099 + switch (sdp->d_type) {
2100 + case DT_DIR:
2101 + dirtmp = opendir(tmpath);
2102 + if (dirtmp == NULL) {
2103 + pefs_warn("failed to open dir: %s", tmpath);
2104 + closedir(dirp);
2105 + return (PEFS_ERR_SYS);
2106 + }
2107 + error = pefs_traverse_fs(chtp, md, hash_len, dirtmp, tmpath,
2108 + fsp, hlc_headp, fh_headp, flags, checksum_error);
2109 + if (error != 0) {
2110 + closedir(dirp);
2111 + return (PEFS_ERR_SYS);
2112 + }
2113 + break;
2114 + /*
2115 + * Look up the file and verify its checksums.
2116 + * Total number of checksums should be the same and checksums
2117 + * should match.
2118 + * Also, take care of hardlinks & symlink warnings.
2119 + * After the traversal is done, we should have found all of the
2120 + * entries in the checksum file.
2121 + */
2122 + /* FALLTHROUGH */
2123 + case DT_REG:
2124 + case DT_LNK:
2125 + fhp = pefs_allocate_file_header();
2126 + if (fhp == NULL) {
2127 + closedir(dirp);
2128 + return (PEFS_ERR_SYS);
2129 + }
2130 + strlcpy(fhp->path, tmpath, sizeof(fhp->path));
2131 +
2132 + error = pefs_open_semantic_checks(fhp, fsp, NULL, flags);
2133 + if (error != 0) {
2134 + closedir(dirp);
2135 + pefs_free_file_header(fhp);
2136 + return (error);
2137 + }
2138 +
2139 + error = pefs_get_file_id(fhp, flags);
2140 + if (error != 0) {
2141 + closedir(dirp);
2142 + pefs_free_file_header(fhp);
2143 + return (error);
2144 + }
2145 +
2146 + indexfhp = pefs_cuckoo_lookup(chtp, fhp);
2147 + if (indexfhp == NULL) {
2148 + pefs_free_file_header(fhp);
2149 + break;
2150 + }
2151 + indexfhp->found = 1;
2152 +
2153 + error = pefs_compute_file_checksums(fhp, md, hash_len, flags);
2154 + if (error != 0) {
2155 + closedir(dirp);
2156 + pefs_free_file_header(fhp);
2157 + return (error);
2158 + }
2159 +
2160 + error = lstat(fhp->path, &sb);
2161 + if (error != 0) {
2162 + warn("cannot stat file %s", fhp->path);
2163 + closedir(dirp);
2164 + pefs_free_file_header(fhp);
2165 + return (PEFS_ERR_SYS);
2166 + }
2167 +
2168 + if ((sb.st_flags & SF_IMMUTABLE) == 0) {
2169 + pefs_warn("file %s does not have schg flag", fhp->path);
2170 + *checksum_error = PEFS_ERR_CHECKSUM;
2171 + }
2172 +
2173 + error = pefs_hardlink_insert(hlc_headp, fhp, &sb);
2174 + if (error != 0) {
2175 + closedir(dirp);
2176 + pefs_free_file_header(fhp);
2177 + return (error);
2178 + }
2179 +
2180 + /*
2181 + * if error encountered during pefs_compare_checksums,
2182 + * keep on traversing the fs to find other errors as well.
2183 + */
2184 + error = pefs_compare_checksums(fhp, indexfhp, hash_len);
2185 + if (error != 0)
2186 + *checksum_error = error;
2187 +
2188 + TAILQ_INSERT_TAIL(fh_headp, fhp, file_header_entries);
2189 + pefs_close_files(fhp);
2190 + break;
2191 + default:
2192 + break;
2193 + }
2194 + }
2195 + else {
2196 + closedir(dirp);
2197 + return (0);
2198 + }
2199 + }
2200 +
2201 + /* unreachable, just to silence compiler */
2202 + pefs_warn("invalid dirp argument for dir: %s", path);
2203 + return (PEFS_ERR_SYS);
2204 +}
2205 +
2206 +static int
2207 +pefs_found_all_entries(struct cuckoo_hash_table *chtp)
2208 +{
2209 + struct file_header *fhp;
2210 + uint32_t i;
2211 + int error;
2212 +
2213 + error = 0;
2214 + for (i = 0; i < chtp->size; i++) {
2215 + fhp = chtp->buckets1[i].fhp;
2216 + if (fhp != NULL)
2217 + if (fhp->found != 1) {
2218 + pefs_warn("file with file id %llu was not found in "
2219 + "filesystem but exists in %s",
2220 + fhp->fid.fid_num, PEFS_FILE_CHECKSUM);
2221 + error = PEFS_ERR_NOENT;
2222 + }
2223 + }
2224 +
2225 + for (i = 0; i < chtp->size; i++) {
2226 + fhp = chtp->buckets2[i].fhp;
2227 + if (fhp != NULL)
2228 + if (fhp->found != 1) {
2229 + pefs_warn("file with file id %llu was not found in "
2230 + "filesystem but exists in %s",
2231 + fhp->fid.fid_num, PEFS_FILE_CHECKSUM);
2232 + error = PEFS_ERR_NOENT;
2233 + }
2234 + }
2235 +
2236 + return (error);
2237 +}
2238 +
2239 +/*
2240 + * Verify the contents of a .pefs.checksum file.
2241 + * A) .pefs.checksum is read into memory.
2242 + * B) The entire filesystem is traversed in order to check each and every file.
2243 + * C) warning messages are produced for hardlinks and symbolic links.
2244 + * D) check that every file in .pefs.checksum was actually found in filesystem.
2245 + * E) verify the file's signature with the user supplied public key
2246 + */
2247 +int
2248 +pefs_verify_checksum(int fdin, FILE *pk_fp, char *fsroot, int flags)
2249 +{
2250 + struct statfs fs;
2251 + struct checksum_file_header cfh;
2252 + struct cuckoo_hash_table cht;
2253 + struct file_header_head fh_head;
2254 + struct hardlink_head hlc_head;
2255 + const EVP_MD *md;
2256 + DIR *dirp;
2257 + int error, checksum_error;
2258 + uint8_t hash_len;
2259 +
2260 + RB_INIT(&hlc_head);
2261 + TAILQ_INIT(&fh_head);
2262 + checksum_error = 0;
2263 +
2264 + if (statfs(fsroot, &fs) == -1) {
2265 + pefs_warn("statfs failed: %s: %s", fsroot, strerror(errno));
2266 + return (PEFS_ERR_SYS);
2267 + }
2268 +
2269 + error = pefs_read_checksum_file_header(fdin, &cfh);
2270 + if (error != 0)
2271 + return (error);
2272 +
2273 + OpenSSL_add_all_digests();
2274 + md = EVP_get_digestbyname(cfh.hash_algo);
2275 +
2276 + if(md == NULL) {
2277 + pefs_warn("Unknown message digest %s", cfh.hash_algo);
2278 + return (PEFS_ERR_INVALID);
2279 + }
2280 + hash_len = EVP_MD_size(md);
2281 +
2282 + error = pefs_allocate_hash_table(&cht, cfh.hash_table_size, PEFS_NOEXTEND);
2283 + if (error != 0)
2284 + return (error);
2285 +
2286 + error = pefs_read_checksum_file(fdin, &cfh, &cht);
2287 + if (error != 0)
2288 + goto out;
2289 +
2290 + /* pefs_print_hash_tables(&cht, hash_len); */
2291 +
2292 + dirp = opendir(fsroot);
2293 + if (dirp == NULL) {
2294 + pefs_warn("failed to open dir %s", fsroot);
2295 + goto out;
2296 + }
2297 +
2298 + error = pefs_traverse_fs(&cht, md, hash_len, dirp, fsroot, &fs, &hlc_head,
2299 + &fh_head, flags, &checksum_error);
2300 + if (error != 0)
2301 + goto out;
2302 +
2303 + /* pefs_hardlink_print(&hlc_head); */
2304 + pefs_hardlink_warn(&hlc_head);
2305 + if ((flags & PEFS_UNMOUNTED) == 0 && (flags & PEFS_NOKEY) == 0)
2306 + pefs_symlink_warn(&cht, &fh_head);
2307 +
2308 + error = pefs_found_all_entries(&cht);
2309 + if (error == 0 && checksum_error != 0)
2310 + error = checksum_error;
2311 +
2312 + error = pefs_verify_signature(fdin, pk_fp);
2313 +
2314 +out:
2315 + pefs_free_hash_table(&cht);
2316 + pefs_hardlink_free(&hlc_head);
2317 + pefs_free_file_header_tail(&fh_head);
2318 +
2319 + return (error);
2320 +}
2321 +
2322 +/* retrieve and then print the name checksum ID for a given filename */
2323 +int
2324 +pefs_filename_to_id(char *file_path, int flags)
2325 +{
2326 + struct file_header *fhp;
2327 + int error;
2328 +
2329 + fhp = pefs_allocate_file_header();
2330 + if (fhp == NULL) {
2331 + error = PEFS_ERR_SYS;
2332 + goto out;
2333 + }
2334 +
2335 + strlcpy(fhp->path, file_path, sizeof(fhp->path));
2336 +
2337 + error = pefs_open_semantic_checks(fhp, NULL, NULL, flags);
2338 + if (error != 0)
2339 + goto out;
2340 +
2341 + error = pefs_get_file_id(fhp, flags);
2342 + if (error != 0)
2343 + goto out;
2344 +
2345 + printf("id: %llu\n", fhp->fid.fid_num);
2346 +
2347 +out:
2348 + pefs_free_file_header(fhp);
2349 + return (error);
2350 +}
2351 +
2352 +RB_GENERATE(hardlink_head, hardlink_counter, hardlink_entries,
2353 + pefs_hardlink_cmp);
2354 Index: pefs_kmod/sbin/pefs/pefs_ctl.c
2355 ===================================================================
2356 --- pefs_kmod/sbin/pefs/pefs_ctl.c (revision 235719)
2357 +++ pefs_kmod/sbin/pefs/pefs_ctl.c (revision 240588)
2358 @@ -32,6 +32,8 @@
2359 #include <sys/ioccom.h>
2360 #include <sys/module.h>
2361 #include <sys/mount.h>
2362 +#include <sys/stat.h>
2363 +#include <sys/dirent.h>
2364
2365 #include <assert.h>
2366 #include <ctype.h>
2367 @@ -74,6 +76,9 @@
2368 static int pefs_getkey(int argc, char *argv[]);
2369 static int pefs_showchains(int argc, char *argv[]);
2370 static int pefs_showalgs(int argc, char *argv[]);
2371 +static int pefs_addchecksum(int argc, char *argv[]);
2372 +static int pefs_verify(int argc, char *argv[]);
2373 +static int pefs_nameid(int argc, char *argv[]);
2374
2375 typedef int (*command_func_t)(int argc, char **argv);
2376 typedef int (*keyop_func_t)(struct pefs_keychain_head *kch, int fd,
2377 @@ -100,9 +105,16 @@
2378 { "delchain", pefs_delchain },
2379 { "showchains", pefs_showchains },
2380 { "showalgs", pefs_showalgs },
2381 + { "addchecksum", pefs_addchecksum},
2382 + { "verify", pefs_verify},
2383 + { "nameid", pefs_nameid},
2384 { NULL, NULL },
2385 };
2386
2387 +
2388 +/* XXXgpf: [TODO] should probably add more at a later point */
2389 +const char *supported_digests[] = {"sha256","sha512"};
2390 +
2391 void
2392 pefs_warn(const char *fmt, ...)
2393 {
2394 @@ -991,6 +1003,334 @@
2395 return (0);
2396 }
2397
2398 +/*
2399 + * XXXgpf: Instead of a man page entry:
2400 + *
2401 + * pefs addchecksum [-s] [-a alg] [-i inputfile] [-k pkey_file] [-d dirpath] \
2402 + * filesystem
2403 + *
2404 + * $command creates .pefs.checksum db file for filesystem.
2405 + * This file will contain all checksums necessary to check integrity
2406 + * of files upon access.
2407 + *
2408 + * alg is the name of the algorithm to be used as a cryptographic
2409 + * hash function; supported algorithms: sha256, sha512. sha256 is
2410 + * used by default.
2411 + *
2412 + * inputfile contains list of files that need integrity checking. If
2413 + * the argument is not supplied, input is read from stdin by default.
2414 + * These files should be either regular files or symbolic links.
2415 + * Symlinks are not traversed.
2416 + *
2417 + * dirpath defines where .pefs.checksum should be created. By default,
2418 + * .pefs.checksum is created under $PWD. dirpath should be a directory,
2419 + * outside of target pefs filesystem.
2420 + *
2421 + * pkey_file is the file that contains the private key that will be used
2422 + * by the DSA signing algorithm. Key should be in PEM format.
2423 + *
2424 + * -f symbolizes that $command should set immutable flag schg for every file
2425 + * in inputlist if the flag is not already set.
2426 + *
2427 + * When $command is run, filesystem must be mounted with pefs, and
2428 + * user must have supplied the necessary pefs key(s).
2429 + *
2430 + */
2431 +static int
2432 +pefs_addchecksum(int argc, char *argv[])
2433 +{
2434 + char fsroot[MAXPATHLEN + 1];
2435 + char csm_path[MAXPATHLEN + 1];
2436 + struct stat sb;
2437 + FILE *fpin, *pk_fp;
2438 + int error, flags, i, j;
2439 + const char *algo;
2440 +
2441 + flags = 0;
2442 + fpin = stdin;
2443 + pk_fp = NULL;
2444 + /* by default use sha256 */
2445 + algo = supported_digests[0];
2446 + /* by default create checksum file under $PWD */
2447 + snprintf(csm_path, sizeof(csm_path), "./%s", PEFS_FILE_CHECKSUM);
2448 +
2449 + while ((i = getopt(argc, argv, "sa:i:k:d:")) != -1)
2450 + switch(i) {
2451 + case 'a':
2452 + for (j=0; j < PEFS_SUPPORTED_DIGESTS; j++)
2453 + if (strcmp(supported_digests[j], optarg) == 0) {
2454 + algo = supported_digests[j];
2455 + break;
2456 + }
2457 +
2458 + if (j == PEFS_SUPPORTED_DIGESTS) {
2459 + pefs_warn("invalid digestname: %s", optarg);
2460 + error = PEFS_ERR_INVALID;
2461 + goto out;
2462 + }
2463 + break;
2464 + case 's':
2465 + flags|= PEFS_SETIMMUTABLE;
2466 + break;
2467 + case 'i':
2468 + fpin = fopen(optarg, "r");
2469 + if (fpin == NULL) {
2470 + warn("cannot open inputfile: %s", optarg);
2471 + error = PEFS_ERR_INVALID;
2472 + goto out;
2473 + }
2474 + break;
2475 + case 'k':
2476 + pk_fp = fopen(optarg, "r");
2477 + if (pk_fp == NULL) {
2478 + warn("error opening privkey file %s", optarg);
2479 + error = PEFS_ERR_SYS;
2480 + goto out;
2481 + }
2482 + break;
2483 + case 'd':
2484 + if (stat(optarg, &sb) != 0) {
2485 + warn("cannot stat file %s", optarg);
2486 + error = PEFS_ERR_INVALID;
2487 + goto out;
2488 + }
2489 +
2490 + if (S_ISDIR(sb.st_mode) == 0) {
2491 + pefs_warn("filename: %s is not a directory", optarg);
2492 + error = PEFS_ERR_INVALID;
2493 + goto out;
2494 + }
2495 +
2496 + snprintf(csm_path, sizeof(csm_path), "%s/%s", optarg,
2497 + PEFS_FILE_CHECKSUM);
2498 + break;
2499 + default:
2500 + if (fpin != NULL)
2501 + fclose(fpin);
2502 + pefs_usage();
2503 + }
2504 + argc -= optind;
2505 + argv += optind;
2506 +
2507 + if (pk_fp == NULL) {
2508 + pefs_warn("user must provide a file containing the private key");
2509 + return (PEFS_ERR_INVALID);
2510 + }
2511 +
2512 + initfsroot(argc, argv, 0, fsroot, sizeof(fsroot));
2513 +
2514 + error = pefs_create_checksum_file(fpin, fsroot, csm_path, pk_fp,
2515 + algo, flags);
2516 +
2517 +out:
2518 + if (fpin != NULL)
2519 + fclose(fpin);
2520 + if (pk_fp != NULL)
2521 + fclose(pk_fp);
2522 +
2523 + return (error);
2524 +}
2525 +
2526 +/*
2527 + * XXXgpf: Instead of a man page entry:
2528 + *
2529 + * pefs verify [-u/-n] [-k pkey_file] checksumpath filesystem
2530 + *
2531 + * $command verifies the contents of a .pefs.checksum file. It scans the
2532 + * entire filesystem and checks that every entry in .pefs.checksum is
2533 + * found in the filesystem with the same checksums.
2534 + *
2535 + * $command will try to produce the same warning messages as addchecksum
2536 + * concerning hardlinks and symbolic links.
2537 + *
2538 + * -n flag should be used if filesystem is mounted but key has not
2539 + * been provided yet.
2540 + *
2541 + * -u flag should be used if filesystem is unmounted.
2542 + *
2543 + * flags -u and -n are mutually exclusive.
2544 + *
2545 + * pkey_file is the file containing the public key that is used to verify
2546 + * .pefs.checksum's signature by the DSA algorithm.
2547 + *
2548 + * By default, pefs will assume that filesystem is mounted and user
2549 + * has provided key.
2550 + */
2551 +static int
2552 +pefs_verify(int argc, char *argv[])
2553 +{
2554 + struct stat sb;
2555 + char fsroot[MAXPATHLEN + 1];
2556 + FILE *pk_fp;
2557 + int error, fdin, flags, i;
2558 +
2559 + fdin = -1;
2560 + flags = PEFS_VERIFY;
2561 + pk_fp = NULL;
2562 + while ((i = getopt(argc, argv, "k:nu")) != -1)
2563 + switch(i) {
2564 + case 'k':
2565 + pk_fp = fopen(optarg, "r");
2566 + if (pk_fp == NULL) {
2567 + warn("error opening pubkey file %s", optarg);
2568 + error = PEFS_ERR_SYS;
2569 + goto out;
2570 + }
2571 + break;
2572 + case 'n':
2573 + flags|= PEFS_NOKEY;
2574 + if ((flags & PEFS_UNMOUNTED) != 0) {
2575 + pefs_warn("flags -u and -n are mutually exclusive");
2576 + return (PEFS_ERR_INVALID);
2577 + }
2578 + break;
2579 + case 'u':
2580 + flags|= PEFS_UNMOUNTED;
2581 + if ((flags & PEFS_NOKEY) != 0) {
2582 + pefs_warn("flags -u and -n are mutually exclusive");
2583 + return (PEFS_ERR_INVALID);
2584 + }
2585 + break;
2586 + default:
2587 + pefs_usage();
2588 + }
2589 + argc -= optind;
2590 + argv += optind;
2591 +
2592 + if (pk_fp == NULL) {
2593 + pefs_warn("user must provide a file containing the public key");
2594 + return (PEFS_ERR_INVALID);
2595 + }
2596 +
2597 + if (argc != 2) {
2598 + if (argc < 2)
2599 + warnx("too few arguments");
2600 + else
2601 + warnx("too many arguments");
2602 + pefs_usage();
2603 + }
2604 +
2605 + fdin = open(argv[0], O_RDONLY);
2606 + if (fdin == -1) {
2607 + warn("cannot open %s file: %s", PEFS_FILE_CHECKSUM, argv[0]);
2608 + error = PEFS_ERR_INVALID;
2609 + goto out;
2610 + }
2611 +
2612 + argc -=1;
2613 + argv +=1;
2614 +
2615 + if ((flags & PEFS_UNMOUNTED) == 0)
2616 + initfsroot(argc, argv, 0, fsroot, sizeof(fsroot));
2617 + else {
2618 + /*
2619 + * XXXgpf: should i also check that fsroot path belongs to
2620 + * a non pefs filesystem?
2621 + */
2622 + strlcpy(fsroot, argv[0], sizeof(fsroot));
2623 + if (stat(fsroot, &sb) != 0) {
2624 + warn("cannot stat fs root: %s", fsroot);
2625 + error = PEFS_ERR_NOENT;
2626 + goto out;
2627 + }
2628 +
2629 + if (S_ISDIR(sb.st_mode) == 0) {
2630 + pefs_warn("fs root is not a directory: %s", fsroot);
2631 + error = PEFS_ERR_SYS;
2632 + goto out;
2633 + }
2634 + }
2635 +
2636 + error = pefs_verify_checksum(fdin, pk_fp, fsroot, flags);
2637 + if (error == 0)
2638 + printf("integrity verification ok!\n");
2639 + else
2640 + pefs_warn("integrity verification encountered error(s)");
2641 +
2642 +out:
2643 + if (fdin >= 0)
2644 + close(fdin);
2645 + if (pk_fp != NULL)
2646 + fclose(pk_fp);
2647 + return (error);
2648 +}
2649 +
2650 +/*
2651 + * XXXgpf: Instead of a man page entry:
2652 + *
2653 + * pefs nameid [-u/-n] filepath
2654 + *
2655 + * $command prints out the identifier for an encrypted pefs filename where
2656 + * pefs encrypted filename = XBase64(checksum || E(tweak || filename)).
2657 + *
2658 + * The id is the name checksum, meaning VMAC(E(tweak || filename)).
2659 + *
2660 + * This identifier is used as a primary key when a specific filename is handled
2661 + * by pefs for integrity checking purposes.
2662 + *
2663 + * Some warning messages produced by /sbin/pefs refer to files by their internal
2664 + * ID and not their unencrypted fullpath; e.g. when verifying an unmounted pefs
2665 + * filesystem. Therefore this command can be used to map fullpaths to internal
2666 + * IDs.
2667 + *
2668 + * -n flag should be used if filesystem is mounted but key has not been
2669 + * provided yet.
2670 + *
2671 + * -u flag should be used if filesystem is unmounted.
2672 + *
2673 + * In both of these scenarios the "filepath" that is provided by the user should
2674 + * be the encrypted filepath.
2675 + *
2676 + * flags -u and -n are mutually exclusive.
2677 + *
2678 + * filepath may refer to any kind of file that is encrypted by pefs filesystem,
2679 + * such as directories, regular files, symlinks, etc.
2680 + *
2681 + * Symlinks are not traversed.
2682 + */
2683 +static int
2684 +pefs_nameid(int argc, char *argv[])
2685 +{
2686 + char file_path[MAXPATHLEN + 1];
2687 + int error, flags, i;
2688 +
2689 + flags = PEFS_GETID;
2690 + while ((i = getopt(argc, argv, "nu")) != -1)
2691 + switch(i) {
2692 + case 'n':
2693 + flags|= PEFS_NOKEY;
2694 + if ((flags & PEFS_UNMOUNTED) != 0) {
2695 + pefs_warn("flags -u and -n are mutually exclusive");
2696 + return (PEFS_ERR_INVALID);
2697 + }
2698 + break;
2699 + case 'u':
2700 + flags|= PEFS_UNMOUNTED;
2701 + if ((flags & PEFS_NOKEY) != 0) {
2702 + pefs_warn("flags -u and -n are mutually exclusive");
2703 + return (PEFS_ERR_INVALID);
2704 + }
2705 + break;
2706 + default:
2707 + pefs_usage();
2708 + }
2709 + argc -= optind;
2710 + argv += optind;
2711 +
2712 + if (argc != 1) {
2713 + if (argc < 1)
2714 + warnx("too few arguments");
2715 + else
2716 + warnx("too many arguments");
2717 + pefs_usage();
2718 + }
2719 +
2720 + strlcpy(file_path, argv[0], sizeof(file_path));
2721 + error = pefs_filename_to_id(file_path, flags);
2722 +
2723 + return (error);
2724 +}
2725 +
2726 static void
2727 pefs_usage_alg(void)
2728 {
2729 @@ -1016,6 +1356,9 @@
2730 " pefs randomchain [-fv] [-n min] [-N max] filesystem\n"
2731 " pefs showchains [-fp] [-i iterations] [-k keyfile] filesystem\n"
2732 " pefs showalgs\n"
2733 +" pefs addchecksum [-s] [-a algo] [-i inputfile] [-k pkey_file] [-d dirpath] filesystem\n"
2734 +" pefs verify [-n/u] [-k pkey_file] [checksumpath filesystem]\n"
2735 +" pefs nameid [-u/-n] [filepath]"
2736 );
2737 exit(PEFS_ERR_USAGE);
2738 }
2739 Index: pefs_kmod/sbin/pefs/pefs.8
2740 ===================================================================
2741 --- pefs_kmod/sbin/pefs/pefs.8 (revision 235719)
2742 +++ pefs_kmod/sbin/pefs/pefs.8 (revision 240588)
2743 @@ -104,6 +104,25 @@
2744 .Pp
2745 .Nm
2746 .Cm showalgs
2747 +.Pp
2748 +.Nm
2749 +.Cm addchecksum
2750 +.Op Fl s
2751 +.Op Fl a Ar alg
2752 +.Op Fl i Ar input_file
2753 +.Op Fl k Ar privatekey_file
2754 +.Op Fl d Ar dirpath
2755 +.Ar filesystem
2756 +.Nm
2757 +.Cm verify
2758 +.Op Fl u|n
2759 +.Op Fl k Ar publickey_file
2760 +.Ar checksum_file
2761 +.Ar filesystem
2762 +.Nm
2763 +.Cm nameid
2764 +.Op Fl u|n
2765 +.Ar filepath
2766 .Sh DESCRIPTION
2767 The
2768 .Nm
2769 @@ -227,6 +246,76 @@
2770 Print all elements of the key chain staring with given parent key.
2771 .It Cm showalgs
2772 Print list of all supported algorithms.
2773 +.It Cm addchecksum Ar filesystem
2774 +Create
2775 +.Em .pefs.checksum
2776 +db file for
2777 +.Ar filesystem.
2778 +The algorithm that will be used as a hash function (sha256 by default) is
2779 +set by
2780 +.Fl a Ar alg .
2781 +The file that contains the private key in PEM format for the DSA signing
2782 +algorithm must be provided using
2783 +.Fl k Ar privatekey_file .
2784 +The list of files is read from stdin unless
2785 +.Fl i Ar input_file
2786 +is used. Files should be either regular files or symbolic links. Symlinks
2787 +are not traversed.
2788 +All files that need integrity checking must have the immutable flag (schg) set;
2789 +.Fl s
2790 +can be used to let
2791 +.Nm
2792 +turn it on for files that do not.
2793 +.Fl d Ar dirpath
2794 +can be used to specify under which directory the resulting
2795 +.Em .pefs.checksum
2796 +file should be placed. Otherwise, it is created under $PWD.
2797 +.It Cm verify Ar checksumpath filesystem
2798 +Verify the contents of a
2799 +.Em .pefs.checksum
2800 +file. This command scans the entire
2801 +.Ar filesystem
2802 +and checks that every entry in
2803 +.Em .pefs.checkum
2804 +is found and produces the same checksums. The command will try to produce
2805 +the same warning messages as
2806 +.Cm addchecksum
2807 +concerning hardlinks and symbolic links. It will also try to produce as many
2808 +warning messages as possible before failing. If
2809 +.Ar filesystem
2810 +is mounted but the key has not been supplied yet,
2811 +.Fl n
2812 +flag should be used. If the pefs
2813 +.Ar filesystem
2814 +is unmounted, the
2815 +.Fl u
2816 +flag should be used instead. By default,
2817 +.Nm
2818 +will assume that the filesystem is mounted and user has provided the
2819 +necessary key(s) using
2820 +.Cm addkey .
2821 +The file that contains the public key in PEM format must be provided using
2822 +.Fl k Ar privatekey_file .
2823 +.It Cm nameid Ar filepath
2824 +Print the identifier for an encrypted pefs filename where filename =
2825 +XBase64(checksum || E(tweak || filename)). The id is the name checksum,
2826 +meaning VMAC(E(tweak || filename)). This identifier is used as a primary key
2827 +when a filename is handled by
2828 +.Nm
2829 +for integrity checking purposes. Some warning messages produced by
2830 +.Nm
2831 +refer to files by their internal ID and not their decrypted fullpath; e.g.
2832 +when verifying an unmounted pefs filesystem. Therefore, this command can be
2833 +used to map fullpaths to internal IDs. If the pefs
2834 +.Ar filesystem
2835 +is unmounted, the
2836 +.Fl u
2837 +flag should be used instead. By default,
2838 +.Nm
2839 +will assume that the filesystem is mounted and user has provided the
2840 +necessary key(s) using
2841 +.Cm addkey .
2842 +Symlinks are not traversed.
2843 .El
2844 .Pp
2845 .Ss COMMAND OPTIONS
2846 @@ -248,11 +337,18 @@
2847 .It Fl C
2848 Disables key chain lookup.
2849 By default if chain is found, keys it consists of are also used for operation.
2850 +.It Fl d Ar dirpath
2851 +specifies under which directory the resulting
2852 +.Em .pefs.checksum
2853 +file should be placed.
2854 .It Fl i Ar iterations
2855 Number of
2856 .Ar iterations
2857 to use with PKCS#5v2.
2858 -If this option is not specified default value of 50000 is used.
2859 +If this option is not specified default value of 50000 is used. In case of
2860 +.Cm addchecksum
2861 +, it may be used to specify the file that contains the list of full filenames
2862 +that require integrity checking.
2863 .It Fl I Ar iterations
2864 Specifies number of
2865 .Ar iterations
2866 @@ -270,9 +366,15 @@
2867 Specifies a file which contains part of the key.
2868 If
2869 .Ar keyfile
2870 -is given as -, standard input will be used.
2871 +is given as -, standard input will be used. In case of integrity
2872 +checking actions, this specifies either the public or the private key
2873 +that is used by the signing algorithm.
2874 .It Fl K Ar keyfile
2875 Specifies a file which contains part of the secondary/child key.
2876 +.It Fl n
2877 +Specifies that the pefs
2878 +.Ar filesystem
2879 +is mounted but user has not provided the necessary key(s) yet.
2880 .It Fl o Ar options
2881 Mount options passed to
2882 .Xr mount 8
2883 @@ -281,10 +383,19 @@
2884 Do not ask for passphrase.
2885 .It Fl P
2886 Do not ask for passphrase for secondary/child key.
2887 +.It Fl s
2888 +Is used to let
2889 +.Cm addchecksum
2890 +turn on the schg immutable flag for files that need integrity checking but
2891 +lack the schg flag.
2892 .It Fl t
2893 Test-only mode.
2894 Do not perform actual operation but check if it can be performed.
2895 Usable for scripting.
2896 +.It Fl u
2897 +Specifies that the pefs
2898 +.Ar filesystem
2899 +is unmounted.
2900 .It Fl v
2901 Verbose mode.
2902 .It Fl x
2903 @@ -369,6 +480,14 @@
2904 before loading
2905 .Nm
2906 kernel module.
2907 +.It Va vfs.pefs.exec.enable
2908 +If this flag is set to 1, the system allows execution of code that derives
2909 +solely from files with the immutable flag (schg) set. This flag is temporary
2910 +as this functionality should be controlled by securelevel.
2911 +.It Va vfs.pefs.exec.enable.noscript
2912 +Same as the above except for when user is trying to execute a script. In
2913 +that case, only the interpreter will be checked for the schg flag, not the
2914 +script file.
2915 .El
2916 .Sh EXAMPLES
2917 Encrypting a directory:
2918 Index: pefs_kmod/sbin/pefs/pefs_ctl.h
2919 ===================================================================
2920 --- pefs_kmod/sbin/pefs/pefs_ctl.h (revision 235719)
2921 +++ pefs_kmod/sbin/pefs/pefs_ctl.h (revision 240588)
2922 @@ -28,6 +28,8 @@
2923
2924 #include <inttypes.h>
2925
2926 +struct EVP_MD;
2927 +
2928 #define PEFS_FSTYPE "pefs"
2929 #define PEFS_KLD PEFS_FSTYPE
2930
2931 @@ -36,9 +38,18 @@
2932
2933 #define PEFS_KDF_ITERATIONS 50000
2934
2935 +#define PEFS_BLOCKSIZE 4096
2936 +
2937 #define PEFS_FILE_KEYCHAIN ".pefs.db"
2938 #define PEFS_FILE_KEYCONF ".pefs.conf"
2939 +#define PEFS_FILE_CHECKSUM ".pefs.checksum"
2940
2941 +#define PEFS_NOKEY 0x0001
2942 +#define PEFS_UNMOUNTED 0x0002
2943 +#define PEFS_SETIMMUTABLE 0x0004
2944 +#define PEFS_VERIFY 0x0010
2945 +#define PEFS_GETID 0x0020
2946 +
2947 #define PEFS_KEYCONF_ALG_IND 0
2948 #define PEFS_KEYCONF_ITERATIONS_IND 1
2949
2950 @@ -47,6 +58,8 @@
2951
2952 #define PEFS_KEYENC_MAC_SIZE (PEFS_KEY_SIZE / 2)
2953
2954 +#define PEFS_SUPPORTED_DIGESTS 2
2955 +
2956 #define PEFS_ERR_GENERIC 1
2957 #define PEFS_ERR_USAGE 2
2958 #define PEFS_ERR_IO 3
2959 @@ -54,6 +67,7 @@
2960 #define PEFS_ERR_NOENT 5
2961 #define PEFS_ERR_EXIST 6
2962 #define PEFS_ERR_INVALID 7
2963 +#define PEFS_ERR_CHECKSUM 8
2964
2965 #define PEFS_FS_IGNORE_TYPE 0x0001
2966
2967 @@ -77,6 +91,8 @@
2968 void pefs_warn(const char *, ...) __printf0like(1, 2);
2969
2970 int pefs_getfsroot(const char *path, int flags, char *fsroot, size_t size);
2971 +int pefs_getfsroots(const char *path, int flags, char *fsroot,
2972 + char * fromfsroot, size_t size);
2973
2974 int pefs_key_generate(struct pefs_xkey *xk, const char *passphrase,
2975 struct pefs_keyparam *kp);
2976 @@ -85,7 +101,14 @@
2977 int pefs_key_decrypt(struct pefs_xkeyenc *xe,
2978 const struct pefs_xkey *xk_parent);
2979 uintmax_t pefs_keyid_as_int(char *keyid);
2980 +int pefs_create_checksum_file(FILE *fpin, char *fsroot, char *csm_path,
2981 + FILE *pk_fp, const char *algo, int flags);
2982 +int pefs_verify_checksum(int fdin, FILE *pk_fp, char *fsroot, int flags);
2983 +int pefs_filename_to_id(char *file_path, int flags);
2984
2985 +int pefs_name_pton(char const *src, size_t srclen, u_char *target,
2986 + size_t targsize);
2987 +
2988 const char * pefs_alg_name(struct pefs_xkey *xk);
2989 void pefs_alg_list(FILE *stream);
2990
2991 Index: pefs_kmod/sbin/pefs/pefs_key.c
2992 ===================================================================
2993 --- pefs_kmod/sbin/pefs/pefs_key.c (revision 235719)
2994 +++ pefs_kmod/sbin/pefs/pefs_key.c (revision 240588)
2995 @@ -31,6 +31,7 @@
2996 #include <sys/param.h>
2997 #include <sys/types.h>
2998 #include <sys/errno.h>
2999 +#include <sys/dirent.h>
3000 #include <assert.h>
3001 #include <inttypes.h>
3002 #include <stdio.h>
3003 Index: pefs_kmod/sbin/pefs/pefs_keychain.c
3004 ===================================================================
3005 --- pefs_kmod/sbin/pefs/pefs_keychain.c (revision 235719)
3006 +++ pefs_kmod/sbin/pefs/pefs_keychain.c (revision 240588)
3007 @@ -30,6 +30,7 @@
3008 #include <sys/param.h>
3009 #include <sys/endian.h>
3010 #include <sys/stat.h>
3011 +#include <sys/dirent.h>
3012 #include <assert.h>
3013 #include <inttypes.h>
3014 #include <stdio.h>
3015 Index: pefs_kmod/sbin/pefs/pefs_subr.c
3016 ===================================================================
3017 --- pefs_kmod/sbin/pefs/pefs_subr.c (revision 235719)
3018 +++ pefs_kmod/sbin/pefs/pefs_subr.c (revision 240588)
3019 @@ -32,6 +32,7 @@
3020 #include <sys/ioccom.h>
3021 #include <sys/module.h>
3022 #include <sys/mount.h>
3023 +#include <sys/dirent.h>
3024
3025 #include <assert.h>
3026 #include <ctype.h>
3027 @@ -73,3 +74,73 @@
3028
3029 return (0);
3030 }
3031 +
3032 +#define Assert(Cond) (void)0
3033 +
3034 +/*
3035 + * Algorithm is standard base64 with few exceptions:
3036 + * - file system friendly alphabet
3037 + * - no paddings and whitespace skip
3038 + */
3039 +static const char Base64[] =
3040 + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_";
3041 +int
3042 +pefs_name_pton(char const *src, size_t srclen, u_char *target, size_t targsize)
3043 +{
3044 + int tarindex, state, ch;
3045 + char *pos;
3046 +
3047 + state = 0;
3048 + tarindex = 0;
3049 +
3050 + while ((ch = *src++) != '\0' && srclen-- > 0) {
3051 + if (target && (size_t)tarindex >= targsize)
3052 + return (-1);
3053 +
3054 + pos = strchr(Base64, ch);
3055 + if (pos == 0) /* A non-base64 character. */
3056 + return (-1);
3057 +
3058 + switch (state) {
3059 + case 0:
3060 + if (target) {
3061 + target[tarindex] = (pos - Base64) << 2;
3062 + }
3063 + state = 1;
3064 + break;
3065 + case 1:
3066 + if (target) {
3067 + target[tarindex] |= (pos - Base64) >> 4;
3068 + if ((size_t)tarindex + 1 < targsize)
3069 + target[tarindex+1] =
3070 + ((pos - Base64) & 0x0f) << 4 ;
3071 + }
3072 + tarindex++;
3073 + state = 2;
3074 + break;
3075 + case 2:
3076 + if (target) {
3077 + target[tarindex] |= (pos - Base64) >> 2;
3078 + if ((size_t)tarindex + 1 < targsize)
3079 + target[tarindex+1] =
3080 + ((pos - Base64) & 0x03) << 6;
3081 + }
3082 + tarindex++;
3083 + state = 3;
3084 + break;
3085 + case 3:
3086 + if (target) {
3087 + target[tarindex] |= (pos - Base64);
3088 + }
3089 + tarindex++;
3090 + state = 0;
3091 + break;
3092 + default:
3093 + return (-1);
3094 + }
3095 + }
3096 +
3097 + if (tarindex == 0)
3098 + return (-1);
3099 + return (tarindex);
3100 +}
3101 Index: pefs_kmod/sbin/pefs/Makefile
3102 ===================================================================
3103 --- pefs_kmod/sbin/pefs/Makefile (revision 235719)
3104 +++ pefs_kmod/sbin/pefs/Makefile (revision 240588)
3105 @@ -5,7 +5,7 @@
3106 .PATH: ${SYS}/crypto/hmac ${SYS}/crypto/rijndael ${SYS}/crypto/sha2
3107
3108 PROG= pefs
3109 -SRCS= pefs_ctl.c pefs_key.c pefs_keychain.c pefs_subr.c
3110 +SRCS= pefs_ctl.c pefs_key.c pefs_keychain.c pefs_subr.c pefs_checksum.c
3111 SRCS+= hmac_sha512.c sha2.c
3112 SRCS+= rijndael-api.c rijndael-api-fst.c rijndael-alg-fst.c
3113 SRCS+= pkcs5v2.c
3114 @@ -17,7 +17,7 @@
3115 DEBUG_FLAGS+= -g
3116
3117 DPADD= ${LIBUTIL}
3118 -LDADD= -lutil
3119 +LDADD= -lutil -lcrypto
3120
3121 BINDIR?= /sbin
3122
3123 Index: pefs_kmod/sys/fs/pefs/pefs.h
3124 ===================================================================
3125 --- pefs_kmod/sys/fs/pefs/pefs.h (revision 235719)
3126 +++ pefs_kmod/sys/fs/pefs/pefs.h (revision 240588)
3127 @@ -48,6 +48,25 @@
3128 char pxk_key[PEFS_KEY_SIZE];
3129 };
3130
3131 +struct pefs_xnamecsum {
3132 + uint32_t pxnc_namelen;
3133 + char pxnc_csum[PEFS_NAME_CSUM_SIZE];
3134 + char pxnc_filename[MAXNAMLEN + 1];
3135 +};
3136 +
3137 +struct pefs_xsector_ctext {
3138 + off_t pxsct_offset;
3139 + uint32_t pxsct_ctext_len;
3140 + char pxsct_ctext[PEFS_SECTOR_SIZE];
3141 +};
3142 +
3143 +struct pefs_xslink_ctext {
3144 + uint32_t pxsl_namelen;
3145 + uint32_t pxsl_target_len;
3146 + char pxsl_filename[MAXPATHLEN + 1];
3147 + char pxsl_target[PEFS_SECTOR_SIZE];
3148 +};
3149 +
3150 #ifdef _IO
3151 #define PEFS_GETKEY _IOWR('p', 0, struct pefs_xkey)
3152 #define PEFS_ADDKEY _IOWR('p', 1, struct pefs_xkey)
3153 @@ -55,6 +74,9 @@
3154 #define PEFS_DELKEY _IOWR('p', 3, struct pefs_xkey)
3155 #define PEFS_FLUSHKEYS _IO('p', 4)
3156 #define PEFS_GETNODEKEY _IOWR('p', 5, struct pefs_xkey)
3157 +#define PEFS_GETNAMECSUM _IOWR('p', 6, struct pefs_xnamecsum)
3158 +#define PEFS_GETSECTORCTEXT _IOWR('p', 7, struct pefs_xsector_ctext)
3159 +#define PEFS_GETSLINKCTEXT _IOWR('p', 8, struct pefs_xslink_ctext)
3160 #endif
3161
3162 #ifdef _KERNEL
3163 @@ -98,6 +120,8 @@
3164 #define PN_WANTRECYCLE 0x000100
3165 #define PN_LOCKBUF_SMALL 0x001000
3166 #define PN_LOCKBUF_LARGE 0x002000
3167 +#define PN_NO_CHECKSUM 0x000010
3168 +#define PN_WRONG_CHECKSUM 0x000020
3169
3170 struct pefs_node {
3171 LIST_ENTRY(pefs_node) pn_listentry;
3172 @@ -109,17 +133,32 @@
3173 void *pn_buf_large;
3174 int pn_flags;
3175 struct pefs_tkey pn_tkey;
3176 + char *pn_checksum_index_entry;
3177 };
3178
3179 #define PM_ROOT_CANRECURSE 0x01
3180 #define PM_DIRCACHE 0x02
3181 #define PM_ASYNCRECLAIM 0x04
3182 +#define PM_CHECKSUM 0x08
3183
3184 +struct pefs_checksum {
3185 + uint8_t pcs_version;
3186 + uint8_t pcs_reserved;
3187 + uint8_t pcs_hash_len;
3188 + uint8_t pcs_hash_algo;
3189 + uint8_t pcs_hash_algo_name[8];
3190 + uint8_t pcs_offset_to_hash_table;
3191 + uint32_t pcs_hash_table_size;
3192 + char *pcs_table1, *pcs_table2;
3193 + struct vnode *pcs_checksumvp;
3194 +};
3195 +
3196 struct pefs_mount {
3197 struct mount *pm_lowervfs;
3198 struct vnode *pm_rootvp;
3199 struct mtx pm_keys_lock;
3200 struct pefs_key_head pm_keys;
3201 + struct pefs_checksum pm_checksum;
3202 int pm_flags;
3203 };
3204
3205 Index: pefs_kmod/sys/fs/pefs/pefs_mac.c
3206 ===================================================================
3207 --- pefs_kmod/sys/fs/pefs/pefs_mac.c (revision 0)
3208 +++ pefs_kmod/sys/fs/pefs/pefs_mac.c (revision 240588)
3209 @@ -0,0 +1,218 @@
3210 +/*-
3211 + * Copyright (c) 2012 Efstratios Karatzas
3212 + * All rights reserved.
3213 + *
3214 + * Redistribution and use in source and binary forms, with or without
3215 + * modification, are permitted provided that the following conditions
3216 + * are met:
3217 + * 1. Redistributions of source code must retain the above copyright
3218 + * notice, this list of conditions and the following disclaimer.
3219 + * 2. Redistributions in binary form must reproduce the above copyright
3220 + * notice, this list of conditions and the following disclaimer in the
3221 + * documentation and/or other materials provided with the distribution.
3222 + *
3223 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
3224 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3225 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3226 + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
3227 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3228 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3229 + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3230 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3231 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3232 + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3233 + * SUCH DAMAGE.
3234 + *
3235 + * $FreeBSD$
3236 + */
3237 +
3238 +#include <sys/cdefs.h>
3239 +__FBSDID("$FreeBSD$");
3240 +
3241 +#include <sys/param.h>
3242 +#include <sys/systm.h>
3243 +#include <sys/kernel.h>
3244 +#include <sys/dirent.h>
3245 +#include <sys/mman.h>
3246 +#include <sys/module.h>
3247 +#include <sys/mount.h>
3248 +#include <sys/sysctl.h>
3249 +#include <sys/stat.h>
3250 +#include <sys/vnode.h>
3251 +#include <sys/imgact.h>
3252 +
3253 +#include <vm/vm.h>
3254 +
3255 +#include <fs/pefs/pefs.h>
3256 +
3257 +#include <security/mac/mac_policy.h>
3258 +
3259 +/*
3260 + * XXXgpf:
3261 + * The problem with this MAC hook is that the hook is called before
3262 + * do_execve() checks if our executable requires an interpreter.
3263 + * Therefore, the script file will itself be checked for the schg flag.
3264 + *
3265 + * We could:
3266 + *
3267 + * a) allow this because it's a feature! During development of a script,
3268 + * user will have to pass it as an argument to the interpreter and when it's
3269 + * complete, continue calling it like that or add the schg flag.
3270 + *
3271 + * b) add a brand new MAC hook that will be called at the precise point
3272 + * in do_execve() where only the interpreter or the regular executable
3273 + * will be checked for the schg flag. [don't seem the other devs will go
3274 + * for us modifying MAC framework though]
3275 + *
3276 + * c) duplicate code from do_execve() and perform the check ourselves. It
3277 + * could be done I guess but I'm not sure since image activators seem to have
3278 + * their own custom functions that are called in order to figure out whether
3279 + * the interpreted flag should be turned on. Don't know how much they are
3280 + * allowed to tamper with imgp, besides that flag.
3281 + *
3282 + */
3283 +static int
3284 +pefs_vnode_check_exec(struct ucred *cred, struct vnode *vp,
3285 + struct label *vplabel, struct image_params *imgp,
3286 + struct label *execlabel)
3287 +{
3288 + int enabled, rval;
3289 + size_t enabled_len;
3290 +
3291 + rval = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable",
3292 + &enabled, &enabled_len, NULL, 0, NULL, 0);
3293 +
3294 + if (rval == 0 && enabled != 0) {
3295 + if ((imgp->attr->va_flags & SF_IMMUTABLE) == 0) {
3296 + return (EPERM);
3297 + }
3298 + }
3299 +
3300 + return (0);
3301 +}
3302 +
3303 +/*
3304 + * XXXgpf:
3305 + *
3306 + * This new hook is placed in do_execve(), found in sys/kern/kern_exec.c.
3307 + * Its purpose is to only check the interpreter for the schg flag in case
3308 + * there's an executable that requires an interpreter.
3309 + *
3310 + * It is placed after exec_check_permissions() and it will be called after
3311 + * we've looped back for the interpreter. Therefore, only either the interpeter
3312 + * or the regular executable itself will ever be checked by this hook; we'll
3313 + * never check the script file itself.
3314 + *
3315 + * It uses a different dbg sysctl var than the above hook for obvious reasons.
3316 + */
3317 +static int
3318 +pefs_vnode_check_exec_noscript(struct ucred *cred, struct vnode *vp,
3319 + struct label *vplabel, struct image_params *imgp,
3320 + struct label *execlabel)
3321 +{
3322 + int enabled, rval;
3323 + size_t enabled_len;
3324 +
3325 + rval = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable_noscript",
3326 + &enabled, &enabled_len, NULL, 0, NULL, 0);
3327 +
3328 + if (rval == 0 && enabled != 0) {
3329 + if ((imgp->attr->va_flags & SF_IMMUTABLE) == 0)
3330 + return (EPERM);
3331 + }
3332 +
3333 + return (0);
3334 +}
3335 +
3336 +
3337 +/*
3338 + * XXXgpf: Check if schg is turned on when we mmap() a vnode with
3339 + * the PROT_EXEC flag.
3340 + */
3341 +static int
3342 +pefs_vnode_check_mmap(struct ucred *cred, struct vnode *vp,
3343 + struct label *vplabel, int prot, int flags)
3344 +{
3345 + struct vattr va;
3346 + int enabled1, enabled2, error, rval1, rval2;
3347 + size_t enabled_len;
3348 +
3349 + if ((prot & PROT_EXEC) == 0)
3350 + return (0);
3351 +
3352 + rval1 = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable",
3353 + &enabled1, &enabled_len, NULL, 0, NULL, 0);
3354 +
3355 + rval2 = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable_noscript",
3356 + &enabled2, &enabled_len, NULL, 0, NULL, 0);
3357 +
3358 + if ((rval1 == 0 && enabled1 != 0) || (rval2 == 0 && enabled2 != 0)) {
3359 + error = VOP_GETATTR(vp, &va, cred);
3360 + if (error != 0)
3361 + return (EACCES);
3362 +
3363 + if ((va.va_flags & SF_IMMUTABLE) == 0)
3364 + return (EACCES);
3365 + }
3366 +
3367 + return (0);
3368 +}
3369 +
3370 +/*
3371 + * XXXgpf:
3372 + *
3373 + * Checking mmap(2) with the above MAC hook is not enough if we wish to
3374 + * prevent user from mmaping a file and then executing those pages due to
3375 + * mprotect(2).
3376 + *
3377 + * I did notice the existance of mac_vnode_check_mprotect(), but unfortunately
3378 + * it's not used anywhere in the kernel for some reason(?)! If it ever comes
3379 + * back into action, I believe it would be preferable to the following solution.
3380 + *
3381 + * My alternative solution was to set the MAXPROT flag of the mapped area
3382 + * during mmap(). If we are mapping a file and we need schg protection, we
3383 + * remove VM_PROT_EXECUTE from MAXPROT which in turn causes following attempts
3384 + * to mprotect() with PROT_EXEC to fail.
3385 + */
3386 +static void
3387 +pefs_vnode_set_mmap_maxprot(struct ucred *cred, struct vnode *vp,
3388 + struct label *vplabel, vm_prot_t *maxprotp, int flags)
3389 +{
3390 + int enabled1, enabled2, rval1, rval2;
3391 + size_t enabled_len;
3392 +
3393 + if ((*maxprotp & VM_PROT_EXECUTE) == 0)
3394 + return;
3395 +
3396 + rval1 = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable",
3397 + &enabled1, &enabled_len, NULL, 0, NULL, 0);
3398 +
3399 + rval2 = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable_noscript",
3400 + &enabled2, &enabled_len, NULL, 0, NULL, 0);
3401 +
3402 + if ((rval1 == 0 && enabled1 != 0) || (rval2 == 0 && enabled2 != 0))
3403 + *maxprotp &= ~VM_PROT_EXECUTE;
3404 +}
3405 +
3406 +static struct mac_policy_ops pefs_ops =
3407 +{
3408 + .mpo_vnode_check_exec = pefs_vnode_check_exec,
3409 + .mpo_vnode_check_exec_noscript = pefs_vnode_check_exec_noscript,
3410 + .mpo_vnode_check_mmap = pefs_vnode_check_mmap,
3411 + .mpo_vnode_set_mmap_maxprot = pefs_vnode_set_mmap_maxprot,
3412 +};
3413 +
3414 +MAC_POLICY_SET(&pefs_ops, mac_pefs, "pefs exec protection",
3415 + MPC_LOADTIME_FLAG_UNLOADOK, NULL);
3416 +
3417 +/* XXXgpf: declare our debugging sysctls for schg execution control */
3418 +SYSCTL_NODE(_vfs_pefs, OID_AUTO, exec, CTLFLAG_RW, 0,
3419 + "PEFS kern_exec.c stuff");
3420 +
3421 +int pefs_exec_enable = 0;
3422 +SYSCTL_INT(_vfs_pefs_exec, OID_AUTO, enable, CTLFLAG_RW,
3423 + &pefs_exec_enable, 0, "Enable exec protection");
3424 +
3425 +int pefs_exec_enable_noscript = 0;
3426 +SYSCTL_INT(_vfs_pefs_exec, OID_AUTO, enable_noscript, CTLFLAG_RW,
3427 + &pefs_exec_enable_noscript, 0, "Enable exec protection [no scripts]");
3428 Index: pefs_kmod/sys/fs/pefs/pefs_checksum.c
3429 ===================================================================
3430 --- pefs_kmod/sys/fs/pefs/pefs_checksum.c (revision 0)
3431 +++ pefs_kmod/sys/fs/pefs/pefs_checksum.c (revision 240588)
3432 @@ -0,0 +1,409 @@
3433 +/*-
3434 + * Copyright (c) 2012 Efstratios Karatzas
3435 + * All rights reserved.
3436 + *
3437 + * Redistribution and use in source and binary forms, with or without
3438 + * modification, are permitted provided that the following conditions
3439 + * are met:
3440 + * 1. Redistributions of source code must retain the above copyright
3441 + * notice, this list of conditions and the following disclaimer.
3442 + * 2. Redistributions in binary form must reproduce the above copyright
3443 + * notice, this list of conditions and the following disclaimer in the
3444 + * documentation and/or other materials provided with the distribution.
3445 + *
3446 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
3447 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3448 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3449 + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
3450 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3451 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3452 + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3453 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3454 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3455 + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3456 + * SUCH DAMAGE.
3457 + *
3458 + * $FreeBSD$
3459 + */
3460 +
3461 +#include <sys/cdefs.h>
3462 +__FBSDID("$FreeBSD$");
3463 +
3464 +#include <sys/param.h>
3465 +#include <sys/systm.h>
3466 +#include <sys/malloc.h>
3467 +#include <sys/mount.h>
3468 +#include <sys/namei.h>
3469 +#include <sys/stat.h>
3470 +#include <sys/vnode.h>
3471 +#include <sys/dirent.h>
3472 +#include <sys/endian.h>
3473 +#include <sys/fnv_hash.h>
3474 +
3475 +#include <crypto/sha2/sha2.h>
3476 +
3477 +#include <fs/pefs/pefs.h>
3478 +#include <fs/pefs/pefs_checksum.h>
3479 +
3480 +const char *pefs_checksum_supported_digests[] = {"sha256","sha512"};
3481 +uint8_t pefs_checksum_supported_hash_lengths[] = {32, 64};
3482 +
3483 +/* sanitize .pefs.checkum's global file header that's read during VFS_MOUNT() */
3484 +int
3485 +pefs_sanitize_checksum_header(struct pefs_checksum *pcs)
3486 +{
3487 + int error, i;
3488 +
3489 + error = 0;
3490 + for (i=0; i < PEFS_CHECKSUM_SUPPORTED_DIGESTS; i++)
3491 + if (strncmp(pefs_checksum_supported_digests[i], pcs->pcs_hash_algo_name,
3492 + sizeof(pcs->pcs_hash_algo_name)) == 0)
3493 + break;
3494 +
3495 + pcs->pcs_hash_algo = i;
3496 + switch(pcs->pcs_hash_algo) {
3497 + /* FALLTHROUGH */
3498 + case (PEFS_SHA256):
3499 + case (PEFS_SHA512):
3500 + dprintf(("digest: %s\n", pcs->pcs_hash_algo_name));
3501 + if (pcs->pcs_hash_len != pefs_checksum_supported_hash_lengths[i]) {
3502 + dprintf(("pefs_sanitize invalid algo len %u\n", pcs->pcs_hash_len));
3503 + error = EINVAL;
3504 + }
3505 + break;
3506 + default:
3507 + dprintf(("pefs_sanitize invalid algo %s\n", pcs->pcs_hash_algo_name));
3508 + error = ENODEV;
3509 + break;
3510 + }
3511 +
3512 + return (error);
3513 +}
3514 +
3515 +static uint32_t
3516 +pefs_checksum_hash1(struct pefs_checksum *pc,
3517 + struct pefs_checksum_index_entry *pcie)
3518 +{
3519 + uint32_t nbucket;
3520 +
3521 + nbucket = pcie->pcie_fid.pcfi_num % pc->pcs_hash_table_size;
3522 + dprintf(("hash1: goto bucket %d\n", nbucket));
3523 + return (nbucket);
3524 +}
3525 +
3526 +static uint32_t
3527 +pefs_checksum_hash2(struct pefs_checksum *pc,
3528 + struct pefs_checksum_index_entry *pcie)
3529 +{
3530 + uint32_t nbucket;
3531 +
3532 + nbucket = fnv_64_buf(&(pcie->pcie_fid.pcfi_num),
3533 + sizeof(pcie->pcie_fid.pcfi_num), FNV1_64_INIT) %
3534 + pc->pcs_hash_table_size;
3535 + dprintf(("hash2: goto bucket %d\n", nbucket));
3536 +
3537 + return (nbucket);
3538 +}
3539 +
3540 +/* fill out pcie from data pointed to by p */
3541 +static void
3542 +pefs_get_index_entry(char *p, struct pefs_checksum_index_entry *pcie)
3543 +{
3544 + MPASS(p != NULL);
3545 +
3546 + memcpy(&(pcie->pcie_nhashes), p, sizeof(pcie->pcie_nhashes));
3547 + pcie->pcie_nhashes = le32toh(pcie->pcie_nhashes);
3548 + if (pcie->pcie_nhashes != 0) {
3549 + p+=sizeof(pcie->pcie_nhashes);
3550 +
3551 + memcpy(&(pcie->pcie_offset), p, sizeof(pcie->pcie_offset));
3552 + pcie->pcie_offset = le32toh(pcie->pcie_offset);
3553 + p+=sizeof(pcie->pcie_offset);
3554 +
3555 + memcpy(pcie->pcie_fid.pcfi_str, p, sizeof(pcie->pcie_fid.pcfi_str));
3556 + }
3557 +}
3558 +
3559 +static int
3560 +pefs_checksum_index_lookup(struct pefs_checksum_index_entry *pcie,
3561 + struct vnode *vp)
3562 +{
3563 + struct pefs_checksum_index_entry target_pcie;
3564 + struct pefs_mount *pm = VFS_TO_PEFS(vp->v_mount);
3565 + struct pefs_checksum *pcs = &(pm->pm_checksum);
3566 + struct pefs_node *pn = VP_TO_PN(vp);
3567 + char *start;
3568 + uint32_t pos;
3569 +
3570 + pos = pefs_checksum_hash1(pcs, pcie);
3571 + start = &(pcs->pcs_table1[pos * PEFS_HT_CELL_SIZE]);
3572 + pefs_get_index_entry(start, &target_pcie);
3573 +
3574 + if (!PEFS_EMPTY_INDEX_ENTRY(&target_pcie)) {
3575 + dprintf(("cell %d:\n", pos));
3576 + dprintf(("\thashes = %d\n\toffset = %d\n\tfile id = %llu\n",
3577 + target_pcie.pcie_nhashes, target_pcie.pcie_offset,
3578 + target_pcie.pcie_fid.pcfi_num));
3579 +
3580 + if (target_pcie.pcie_fid.pcfi_num == pcie->pcie_fid.pcfi_num) {
3581 + pn->pn_checksum_index_entry = start;
3582 + dprintf(("checksum lookup: found1!\n"));
3583 + return (0);
3584 + }
3585 + }
3586 +
3587 + pos = pefs_checksum_hash2(pcs, pcie);
3588 + start = &(pcs->pcs_table2[pos * PEFS_HT_CELL_SIZE]);
3589 + pefs_get_index_entry(start, &target_pcie);
3590 +
3591 + if (!PEFS_EMPTY_INDEX_ENTRY(&target_pcie)) {
3592 + dprintf(("cell %d:\n", pos));
3593 + dprintf(("\thashes = %d\n\toffset = %d\n\tfile id = %llu\n",
3594 + target_pcie.pcie_nhashes, target_pcie.pcie_offset,
3595 + target_pcie.pcie_fid.pcfi_num));
3596 +
3597 + if (target_pcie.pcie_fid.pcfi_num == pcie->pcie_fid.pcfi_num) {
3598 + pn->pn_checksum_index_entry = start;
3599 + dprintf(("checksum lookup: found2!\n"));
3600 + return (0);
3601 + }
3602 + }
3603 +
3604 + pn->pn_checksum_index_entry = NULL;
3605 + dprintf(("checksum lookup: not found!\n"));
3606 + return (ENOENT);
3607 +}
3608 +
3609 +void
3610 +pefs_checksum_lookup(char *enc_name, size_t enc_name_len,
3611 + struct componentname *cnp, struct vnode *vp)
3612 +{
3613 + struct vattr va;
3614 + struct pefs_checksum_index_entry pcie;
3615 + struct pefs_node *pn = VP_TO_PN(vp);
3616 + struct ucred *cred = vp->v_mount->mnt_cred;
3617 + char *buf;
3618 + size_t buf_len;
3619 + int error, r;
3620 +
3621 + dprintf(("gpf: checksum code @ lookup\n"));
3622 + if ((cnp != NULL && cnp->cn_nameiop != LOOKUP) || (vp->v_type != VREG &&
3623 + vp->v_type != VLNK)
3624 + || ((pn->pn_flags & PN_NO_CHECKSUM) != 0))
3625 + goto not_found;
3626 +
3627 + if (strncmp(enc_name, ".pefs.db", enc_name_len) == 0 ||
3628 + strncmp(enc_name, ".pefs.conf", enc_name_len) == 0 ||
3629 + strncmp(enc_name, ".pefs.checksum", enc_name_len) == 0)
3630 + goto not_found;
3631 +
3632 + enc_name++;
3633 + enc_name_len--;
3634 + buf_len = MAXNAMLEN + 1;
3635 + buf = malloc(buf_len, M_TEMP, M_WAITOK);
3636 +
3637 + r = pefs_name_pton(enc_name, enc_name_len, buf, buf_len);
3638 + if (r <= 0) {
3639 + /* XXXgpf: I sincerely doubt an error can occur here */
3640 + error = EINVAL;
3641 + dprintf(("name_pton error: %d\n", error));
3642 + }
3643 + else {
3644 + memcpy(pcie.pcie_fid.pcfi_str, buf, sizeof(pcie.pcie_fid.pcfi_str));
3645 + dprintf(("id to lookup: %llu\n", pcie.pcie_fid.pcfi_num));
3646 + error = pefs_checksum_index_lookup(&pcie, vp);
3647 + if (error != 0) {
3648 + free(buf, M_TEMP);
3649 + goto not_found;
3650 + }
3651 + }
3652 + /*
3653 + * Check to see if schg flag is set, if not mark the vnode so that all
3654 + * read access is denied.
3655 + *
3656 + * XXXgpf: [TODO] I should make sure that the PN_WRONG_CHECKSUM flag is
3657 + * used in other parts of the pefs codebase to deny access to the file
3658 + * data.
3659 + */
3660 + error = VOP_GETATTR(vp, &va, cred);
3661 + if (error != 0) {
3662 + dprintf(("unable to retrieve attributes of %llu\n",
3663 + pcie.pcie_fid.pcfi_num));
3664 + pn->pn_flags|= PN_WRONG_CHECKSUM;
3665 + }
3666 + else {
3667 + if ((va.va_flags & SF_IMMUTABLE) == 0) {
3668 + dprintf(("schg not set for %llu\n", pcie.pcie_fid.pcfi_num));
3669 + pn->pn_flags|= PN_WRONG_CHECKSUM;
3670 + }
3671 + }
3672 +
3673 + free(buf, M_TEMP);
3674 + return;
3675 +
3676 +not_found:
3677 + pn->pn_flags|= PN_NO_CHECKSUM;
3678 +}
3679 +
3680 +static int
3681 +pefs_generate_checksum(struct vnode *vp, char *data, ssize_t data_len,
3682 + unsigned char **digest, ssize_t *digest_len)
3683 +{
3684 + struct pefs_mount *pm = VFS_TO_PEFS(vp->v_mount);
3685 + struct pefs_checksum *pcs = &(pm->pm_checksum);
3686 + unsigned char *dig;
3687 +
3688 + switch(pcs->pcs_hash_algo) {
3689 + case (PEFS_SHA256):
3690 + *digest_len = SHA256_DIGEST_STRING_LENGTH;
3691 + dig = malloc(*digest_len, M_TEMP, M_WAITOK);
3692 + /*
3693 + * XXXgpf: Does this interface work for any length input?
3694 + * [TODO] Also, I should either use a different interface or store the
3695 + * checksums in hex during .pefs.checksum creation because turning
3696 + * them to hex at this point every single time we have a read is
3697 + * just silly.
3698 + */
3699 + SHA256_Data(data, data_len, dig);
3700 + break;
3701 + case (PEFS_SHA512):
3702 + *digest_len = SHA512_DIGEST_STRING_LENGTH;
3703 + dig = malloc(*digest_len, M_TEMP, M_WAITOK);
3704 + SHA512_Data(data, data_len, dig);
3705 + break;
3706 + default:
3707 + dprintf(("pefs_sanitize invalid algo %s\n", pcs->pcs_hash_algo_name));
3708 + return (ENODEV);
3709 + }
3710 +
3711 + *digest = dig;
3712 + return (0);
3713 +}
3714 +
3715 +static int
3716 +pefs_retrieve_checksum(struct vnode *vp, struct pefs_checksum_index_entry *pcie,
3717 + off_t block_offset, unsigned char **digest, ssize_t digest_len)
3718 +{
3719 + struct pefs_mount *pm = VFS_TO_PEFS(vp->v_mount);
3720 + struct pefs_checksum *pcs = &(pm->pm_checksum);
3721 + struct ucred *cred = vp->v_mount->mnt_cred;
3722 + struct uio *puio;
3723 + struct pefs_chunk pc;
3724 + unsigned char *dig;
3725 + off_t checksum_offset;
3726 + int error, i;
3727 +
3728 + pefs_chunk_create(&pc, NULL, pcs->pcs_hash_len);
3729 + checksum_offset = pcie->pcie_offset + pcs->pcs_hash_len *
3730 + (block_offset / PEFS_SECTOR_SIZE);
3731 + puio = pefs_chunk_uio(&pc, checksum_offset, UIO_READ);
3732 +
3733 + /* XXXgpf: gleb says I should use vn_rdwr instead of VOP_READ */
3734 + error = VOP_READ(pcs->pcs_checksumvp, puio, IO_UNIT, cred);
3735 + if (error != 0) {
3736 + dprintf(("pefs_retrieve_checksum read error %d\n", error));
3737 + return (error);
3738 + }
3739 +
3740 + dig = malloc(digest_len, M_TEMP, M_WAITOK);
3741 + for (i=0; i < pcs->pcs_hash_len; i++)
3742 + sprintf(&dig[i*2],"%02x", ((unsigned char *)pc.pc_base)[i]);
3743 + dig[i*2] = '\0';
3744 +
3745 + pefs_chunk_free(&pc, NULL);
3746 + *digest = dig;
3747 +
3748 + return (0);
3749 +}
3750 +
3751 +static int
3752 +pefs_compare_checksums(unsigned char *digest1, unsigned char *digest2,
3753 + ssize_t digest_len)
3754 +{
3755 + int error;
3756 +
3757 + dprintf(("compare dig1: %s\n", digest1));
3758 + dprintf(("compare dig2: %s\n", digest2));
3759 +
3760 + error = memcmp(digest1, digest2, digest_len);
3761 + if (error != 0) {
3762 + dprintf(("checksum mismatch!\n"));
3763 + error = EAUTH;
3764 + }
3765 +
3766 + return (error);
3767 +}
3768 +
3769 +int
3770 +pefs_integrity_check(struct vnode *vp, off_t offset, u_quad_t fsize,
3771 + struct pefs_chunk *pc)
3772 +{
3773 + struct pefs_checksum_index_entry pcie;
3774 + struct pefs_node *pn = VP_TO_PN(vp);
3775 + ssize_t digest_len, resid;
3776 + unsigned char *digest1, *digest2;
3777 + char *buf, *end;
3778 + long *p;
3779 + int error;
3780 +
3781 + dprintf(("integrity checking!\noffset %llu\n", offset));
3782 +
3783 + if ((pn->pn_flags & PN_WRONG_CHECKSUM) != 0)
3784 + return (EAUTH);
3785 +
3786 + pefs_get_index_entry(pn->pn_checksum_index_entry, &pcie);
3787 +
3788 + dprintf(("id: %llu\n", pcie.pcie_fid.pcfi_num));
3789 +
3790 + buf = (char *)pc->pc_base;
3791 + end = buf + pc->pc_size;
3792 +
3793 + if ((fsize > pcie.pcie_nhashes * PEFS_SECTOR_SIZE) ||
3794 + (fsize < (pcie.pcie_nhashes - 1) * PEFS_SECTOR_SIZE)) {
3795 + dprintf(("file size differs from the one in .pefs.checksum\n"));
3796 + pn->pn_flags|= PN_WRONG_CHECKSUM;
3797 + return (EAUTH);
3798 + }
3799 +
3800 + while (buf < end) {
3801 + if ((end - buf) >= PEFS_SECTOR_SIZE) {
3802 + p = (long *)buf;
3803 + resid = PEFS_SECTOR_SIZE / sizeof(long);
3804 + for (; resid > 0; resid--)
3805 + if (*(p++) != 0)
3806 + break;
3807 + if (resid == 0) {
3808 + bzero(buf, PEFS_SECTOR_SIZE);
3809 + offset += PEFS_SECTOR_SIZE;
3810 + buf += PEFS_SECTOR_SIZE;
3811 + continue;
3812 + }
3813 + resid = PEFS_SECTOR_SIZE;
3814 + }
3815 + else
3816 + resid = end - buf;
3817 +
3818 + error = pefs_generate_checksum(vp, buf, resid, &digest1, &digest_len);
3819 + if (error != 0)
3820 + return (error);
3821 +
3822 + error = pefs_retrieve_checksum(vp, &pcie, offset, &digest2, digest_len);
3823 + if (error != 0) {
3824 + free(digest1, M_TEMP);
3825 + return (error);
3826 + }
3827 +
3828 + error = pefs_compare_checksums(digest1, digest2, digest_len);
3829 + free(digest1, M_TEMP);
3830 + free(digest2, M_TEMP);
3831 + if (error != 0) {
3832 + pn->pn_flags|= PN_WRONG_CHECKSUM;
3833 + return (error);
3834 + }
3835 +
3836 + buf += resid;
3837 + offset += resid;
3838 + }
3839 +
3840 + return (0);
3841 +}
3842 Index: pefs_kmod/sys/fs/pefs/pefs_vnops.c
3843 ===================================================================
3844 --- pefs_kmod/sys/fs/pefs/pefs_vnops.c (revision 235719)
3845 +++ pefs_kmod/sys/fs/pefs/pefs_vnops.c (revision 240588)
3846 @@ -64,6 +64,7 @@
3847 #include <vm/vnode_pager.h>
3848
3849 #include <fs/pefs/pefs.h>
3850 +#include <fs/pefs/pefs_checksum.h>
3851 #include <fs/pefs/pefs_dircache.h>
3852
3853 #define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN + 1))
3854 @@ -474,6 +475,7 @@
3855 pefs_lookup(struct vop_cachedlookup_args *ap)
3856 {
3857 struct componentname *cnp = ap->a_cnp;
3858 + struct pefs_mount *pm;
3859 struct vnode *vp = NULL;
3860 struct vnode *lvp = NULL;
3861 struct vnode *dvp = ap->a_dvp;
3862 @@ -484,8 +486,8 @@
3863 int nokey_lookup, skip_lookup;
3864 int error;
3865
3866 - PEFSDEBUG("pefs_lookup: op=%lx, name=%.*s\n",
3867 - cnp->cn_nameiop, (int)cnp->cn_namelen, cnp->cn_nameptr);
3868 + dprintf(("pefs_lookup: op=%lx, name=%.*s\n",
3869 + cnp->cn_nameiop, (int)cnp->cn_namelen, cnp->cn_nameptr));
3870
3871 pefs_enccn_init(&enccn);
3872
3873 @@ -560,6 +562,22 @@
3874 if (error != 0) {
3875 vput(lvp);
3876 } else {
3877 + pm = VFS_TO_PEFS(vp->v_mount);
3878 + if ((pm->pm_flags & PM_CHECKSUM) != 0) {
3879 + if (nokey_lookup) {
3880 + dprintf(("cnp name=%.*s\n",(int)cnp->cn_namelen,
3881 + cnp->cn_nameptr));
3882 + pefs_checksum_lookup(cnp->cn_nameptr,
3883 + cnp->cn_namelen, cnp, vp);
3884 + }
3885 + else {
3886 + dprintf(("enccnp name=%.*s\n",
3887 + (int)enccn.pec_cn.cn_namelen,
3888 + enccn.pec_cn.cn_nameptr));
3889 + pefs_checksum_lookup(enccn.pec_cn.cn_nameptr,
3890 + enccn.pec_cn.cn_namelen, cnp, vp);
3891 + }
3892 + }
3893 *ap->a_vpp = vp;
3894 if ((cnp->cn_flags & MAKEENTRY) &&
3895 cnp->cn_nameiop != CREATE)
3896 @@ -1901,6 +1919,7 @@
3897 return (error);
3898
3899 error = pefs_read_int(vp, uio, ioflag, cred, fsize);
3900 +
3901 return (error);
3902 }
3903
3904 @@ -1910,6 +1929,7 @@
3905 {
3906 struct vnode *lvp = PEFS_LOWERVP(vp);
3907 struct uio *puio;
3908 + struct pefs_mount *pm = VFS_TO_PEFS(vp->v_mount);
3909 struct pefs_node *pn = VP_TO_PN(vp);
3910 struct pefs_chunk pc;
3911 struct sf_buf *sf;
3912 @@ -1966,6 +1986,11 @@
3913
3914 /* XXX assert full buffer is read */
3915 pefs_chunk_setsize(&pc, done);
3916 + if ((pm->pm_flags & PM_CHECKSUM) != 0 && PEFS_NEEDS_CHECKING(pn)) {
3917 + error = pefs_integrity_check(vp, poffset, fsize, &pc);
3918 + if (error != 0)
3919 + break;
3920 + }
3921 pefs_data_decrypt(&pn->pn_tkey, poffset, &pc);
3922 if (nocopy == 0) {
3923 error = pefs_chunk_copy(&pc, bskip, uio);
3924 @@ -2355,15 +2380,28 @@
3925 static int
3926 pefs_ioctl(struct vop_ioctl_args *ap)
3927 {
3928 + struct pefs_enccn enccn;
3929 + struct componentname cn;
3930 + struct pefs_chunk pc;
3931 + struct nameidata nd, *ndp = &nd;
3932 + u_quad_t fsize;
3933 struct vnode *vp = ap->a_vp;
3934 + struct vnode *lvp = PEFS_LOWERVP(vp);
3935 + struct vnode *svp, *slvp;
3936 struct pefs_xkey *xk = ap->a_data;
3937 + struct pefs_xnamecsum *xncs = ap->a_data;
3938 + struct pefs_xsector_ctext *xsct = ap->a_data;
3939 + struct pefs_xslink_ctext *xsl = ap->a_data;
3940 struct ucred *cred = ap->a_cred;
3941 struct thread *td = ap->a_td;
3942 struct mount *mp = vp->v_mount;
3943 struct pefs_mount *pm = VFS_TO_PEFS(mp);
3944 struct pefs_node *pn;
3945 struct pefs_key *pk;
3946 - int error = 0, i;
3947 + struct uio *puio;
3948 + char *enc, *buf;
3949 + size_t enc_len, buf_len;
3950 + int error = 0, i, r;
3951
3952 if (mp->mnt_cred->cr_uid != cred->cr_uid) {
3953 error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0);
3954 @@ -2465,6 +2503,179 @@
3955 if (pefs_key_remove_all(pm))
3956 pefs_flushkey(mp, td, PEFS_FLUSHKEY_ALL, NULL);
3957 break;
3958 + case PEFS_GETSECTORCTEXT:
3959 + vn_lock(vp, LK_EXCLUSIVE);
3960 +
3961 + if (vp->v_type != VREG) {
3962 + dprintf(("pefs_ioctl: PEFS_GETSECTORCTEXT vp is not a reg file\n"));
3963 + VOP_UNLOCK(vp, 0);
3964 + return (EOPNOTSUPP);
3965 + }
3966 +
3967 + error = pefs_getsize(vp, &fsize, cred);
3968 + if (error != 0) {
3969 + VOP_UNLOCK(vp, 0);
3970 + return (error);
3971 + }
3972 +
3973 + if (xsct->pxsct_ctext_len > PEFS_SECTOR_SIZE ||
3974 + xsct->pxsct_ctext_len == 0 ||
3975 + xsct->pxsct_ctext_len > fsize) {
3976 + dprintf(("pefs_ioctl: PEFS_GETSECTORCTEXT invalid len: %d\n",
3977 + xsct->pxsct_ctext_len));
3978 + VOP_UNLOCK(vp, 0);
3979 + return (EINVAL);
3980 + }
3981 +
3982 + if (xsct->pxsct_offset > (fsize - xsct->pxsct_ctext_len)) {
3983 + dprintf(("pefs_ioctl: PEFS_GETSECTORCTEXT invalid offset: %llu\n",
3984 + xsct->pxsct_offset));
3985 + VOP_UNLOCK(vp, 0);
3986 + return (EINVAL);
3987 + }
3988 +
3989 + pn = VP_TO_PN(vp);
3990 + pefs_chunk_create(&pc, pn, xsct->pxsct_ctext_len);
3991 + puio = pefs_chunk_uio(&pc, xsct->pxsct_offset, UIO_READ);
3992 +
3993 + error = VOP_READ(lvp, puio, IO_UNIT, cred);
3994 +
3995 + if (error == 0)
3996 + memcpy(xsct->pxsct_ctext, pc.pc_base, xsct->pxsct_ctext_len);
3997 +
3998 + pefs_chunk_free(&pc, pn);
3999 + VOP_UNLOCK(vp, 0);
4000 + break;
4001 + case PEFS_GETNAMECSUM:
4002 + vn_lock(vp, LK_EXCLUSIVE);
4003 + if (vp->v_type != VDIR) {
4004 + dprintf(("pefs_ioctl: PEFS_GETNAMEMAC vp is not a directory\n"));
4005 + VOP_UNLOCK(vp, 0);
4006 + return (ENOTDIR);
4007 + }
4008 +
4009 + if (strnlen(xncs->pxnc_filename, sizeof(xncs->pxnc_filename)) !=
4010 + xncs->pxnc_namelen) {
4011 + dprintf(("pefs_ioctl: PEFS_GETNAMEMAC incorrect pxnc_namelen %d\n",
4012 + xncs->pxnc_namelen));
4013 + VOP_UNLOCK(vp, 0);
4014 + return (EINVAL);
4015 + }
4016 +
4017 + if (strchr(xncs->pxnc_filename, '/') != NULL) {
4018 + dprintf(("pefs_ioctl: PEFS_GETNAMEMAC pxnc_filename contains '/'\n");
4019 + VOP_UNLOCK(vp, 0));
4020 + return (EINVAL);
4021 + }
4022 +
4023 + pefs_enccn_init(&enccn);
4024 + if (pefs_no_keys(vp)) {
4025 + enc = xncs->pxnc_filename;
4026 + enc_len = xncs->pxnc_namelen;
4027 + error = 0;
4028 + }
4029 + else {
4030 + cn.cn_nameiop = LOOKUP;
4031 + cn.cn_thread = td;
4032 + cn.cn_cred = cred;
4033 + cn.cn_lkflags = 0;
4034 + cn.cn_flags = 0;
4035 + cn.cn_nameptr = xncs->pxnc_filename;
4036 + cn.cn_namelen = xncs->pxnc_namelen;
4037 +
4038 + /* XXXgpf: does this lookup rely solely on present cache data? */
4039 + error = pefs_enccn_lookup(&enccn, vp, &cn);
4040 + if (error == 0) {
4041 + enc = enccn.pec_cn.cn_nameptr;
4042 + enc_len = enccn.pec_cn.cn_namelen;
4043 + }
4044 + }
4045 + VOP_UNLOCK(vp, 0);
4046 +
4047 + if (error == 0) {
4048 + if (enc[0] != '.' || enc_len <= 1) {
4049 + pefs_enccn_free(&enccn);
4050 + error = EINVAL;
4051 + break;
4052 + }
4053 + enc++;
4054 + enc_len--;
4055 + buf_len = MAXNAMLEN + 1;
4056 + buf = malloc(buf_len, M_TEMP, M_WAITOK);
4057 +
4058 + r = pefs_name_pton(enc, enc_len, buf, buf_len);
4059 + if (r <= 0)
4060 + error = EINVAL;
4061 + else
4062 + memcpy(xncs->pxnc_csum, buf, sizeof(xncs->pxnc_csum));
4063 +
4064 + pefs_enccn_free(&enccn);
4065 + free(buf, M_TEMP);
4066 + }
4067 + break;
4068 + case PEFS_GETSLINKCTEXT:
4069 + if (vp->v_type != VDIR) {
4070 + dprintf(("pefs_ioctl: PEFS_GETSLINKCTEXT vp is not a directory\n"));
4071 + VOP_UNLOCK(vp, 0);
4072 + return (ENOTDIR);
4073 + }
4074 +
4075 + if (strnlen(xsl->pxsl_filename, sizeof(xsl->pxsl_filename)) !=
4076 + xsl->pxsl_namelen) {
4077 + dprintf(("pefs_ioctl: PEFS_GETSLINKCTEXT incorrect namelen %d\n",
4078 + xsl->pxsl_namelen));
4079 + VOP_UNLOCK(vp, 0);
4080 + return (EINVAL);
4081 + }
4082 +
4083 + if (strchr(xsl->pxsl_filename, '/') != NULL) {
4084 + dprintf(("pefs_ioctl: PEFS_GETSLINKCTEXT filename contains '/'\n"));
4085 + VOP_UNLOCK(vp, 0);
4086 + return (EINVAL);
4087 + }
4088 +
4089 + /* grab a vnodep for our symlink */
4090 + NDINIT_ATVP(ndp, LOOKUP, MPSAFE | LOCKPARENT | LOCKLEAF | NOFOLLOW,
4091 + UIO_SYSSPACE, xsl->pxsl_filename, vp, td);
4092 + error = namei(ndp);
4093 + if (error != 0) {
4094 + dprintf(("pefs_ioctl: PEFS_GETSLINKCTEXT namei error %d\n", error));
4095 + return (error);
4096 + }
4097 + NDFREE(ndp, NDF_ONLY_PNBUF);
4098 + svp = ndp->ni_vp;
4099 +
4100 + if (ndp->ni_dvp != vp) {
4101 + dprintf(("pefs_ioctl: PEFS_GETSLINKCTEXT namei wrong parent\n"));
4102 + vput(svp);
4103 + VOP_UNLOCK(vp, 0);
4104 + return (ENOENT);
4105 + }
4106 +
4107 + if (svp->v_type != VLNK) {
4108 + dprintf(("pefs_ioctl: PEFS_GETSLINKCTEXT svp is not a symlink\n"));
4109 + vput(svp);
4110 + VOP_UNLOCK(vp, 0);
4111 + return (EFTYPE);
4112 + }
4113 +
4114 + /* VOP_READLINK our slvp */
4115 + slvp = PEFS_LOWERVP(svp);
4116 +
4117 + pn = VP_TO_PN(svp);
4118 + pefs_chunk_create(&pc, pn, MAXPATHLEN + 1);
4119 + puio = pefs_chunk_uio(&pc, 0, UIO_READ);
4120 +
4121 + error = VOP_READLINK(slvp, puio, cred);
4122 +
4123 + xsl->pxsl_target_len = pc.pc_size - pc.pc_uio.uio_resid;
4124 + if (error == 0)
4125 + memcpy(xsl->pxsl_target, pc.pc_base, xsl->pxsl_target_len);
4126 +
4127 + pefs_chunk_free(&pc, pn);
4128 + vput(svp);
4129 + VOP_UNLOCK(vp, 0);
4130 + break;
4131 default:
4132 error = ENOTTY;
4133 break;
4134 Index: pefs_kmod/sys/fs/pefs/pefs_checksum.h
4135 ===================================================================
4136 --- pefs_kmod/sys/fs/pefs/pefs_checksum.h (revision 0)
4137 +++ pefs_kmod/sys/fs/pefs/pefs_checksum.h (revision 240588)
4138 @@ -0,0 +1,81 @@
4139 +/*-
4140 + * Copyright (c) 2012 Efstratios Karatzas
4141 + * All rights reserved.
4142 + *
4143 + * Redistribution and use in source and binary forms, with or without
4144 + * modification, are permitted provided that the following conditions
4145 + * are met:
4146 + * 1. Redistributions of source code must retain the above copyright
4147 + * notice, this list of conditions and the following disclaimer.
4148 + * 2. Redistributions in binary form must reproduce the above copyright
4149 + * notice, this list of conditions and the following disclaimer in the
4150 + * documentation and/or other materials provided with the distribution.
4151 + *
4152 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
4153 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4154 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4155 + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
4156 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4157 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4158 + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4159 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4160 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
4161 + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
4162 + * SUCH DAMAGE.
4163 + *
4164 + * $FreeBSD$
4165 + */
4166 +
4167 +#define PEFS_FILE_CHECKSUM ".pefs.checksum"
4168 +
4169 +#define PEFS_CFH_SIZE 16 /* file header of .pefs.checksum file */
4170 +#define PEFS_HT_CELL_SIZE 16 /* hash table cell(bucket) size */
4171 +
4172 +#define PEFS_SIGNATURE_MAX_LENGTH 512
4173 +
4174 +#define PEFS_CHECKSUM_SUPPORTED_DIGESTS 2
4175 +
4176 +#define PEFS_SHA256 0
4177 +#define PEFS_SHA512 1
4178 +
4179 +/*
4180 + * XXXgpf: use this instead of PEFS_DEBUG so as to lower the number of debugging
4181 + * messages during code development.
4182 + */
4183 +//#define PEFS_INTEGRITY_DEBUG
4184 +#if defined (PEFS_INTEGRITY_DEBUG)
4185 +#define dprintf(a) printf a
4186 +#else
4187 +#define dprintf(a) (void)0
4188 +#endif
4189 +
4190 +union pefs_checksum_file_id {
4191 + uint64_t pcfi_num;
4192 + uint8_t pcfi_str[8];
4193 +};
4194 +
4195 +struct pefs_checksum_index_entry {
4196 + uint32_t pcie_nhashes;
4197 + uint32_t pcie_offset;
4198 + union pefs_checksum_file_id pcie_fid;
4199 +};
4200 +
4201 +static __inline int
4202 +PEFS_EMPTY_INDEX_ENTRY(struct pefs_checksum_index_entry *pcie)
4203 +{
4204 + MPASS(pcie != NULL);
4205 + return ((pcie->pcie_nhashes == 0) ? 1 : 0);
4206 +}
4207 +
4208 +static __inline int
4209 +PEFS_NEEDS_CHECKING(struct pefs_node *pn)
4210 +{
4211 + MPASS(pn != NULL);
4212 + return ((pn->pn_checksum_index_entry != NULL) ? 1 : 0);
4213 +}
4214 +
4215 +void pefs_checksum_lookup(char *enc_name, size_t enc_name_len,
4216 + struct componentname *cnp, struct vnode *vp);
4217 +int pefs_integrity_check(struct vnode *vp, off_t offset,
4218 + u_quad_t fsize, struct pefs_chunk *pc);
4219 +int pefs_sanitize_checksum_header(struct pefs_checksum *pcs);
4220 Index: pefs_kmod/sys/fs/pefs/pefs_subr.c
4221 ===================================================================
4222 --- pefs_kmod/sys/fs/pefs/pefs_subr.c (revision 235719)
4223 +++ pefs_kmod/sys/fs/pefs/pefs_subr.c (revision 240588)
4224 @@ -52,6 +52,7 @@
4225 #include <sys/vnode.h>
4226
4227 #include <fs/pefs/pefs.h>
4228 +#include <fs/pefs/pefs_checksum.h>
4229 #include <fs/pefs/pefs_dircache.h>
4230
4231 typedef int pefs_node_init_fn(struct mount *mp, struct pefs_node *pn,
4232 @@ -400,6 +401,7 @@
4233 PEFSDEBUG("pefs_node_get: creating node without key: %p\n", pn);
4234
4235 pn->pn_vnode = vp;
4236 + pn->pn_checksum_index_entry = NULL;
4237 vp->v_type = lvp->v_type;
4238 vp->v_data = pn;
4239 vp->v_vnlock = lvp->v_vnlock;
4240 @@ -454,8 +456,24 @@
4241 pefs_node_get_lookupkey(struct mount *mp, struct vnode *lvp, struct vnode **vpp,
4242 struct ucred *cred)
4243 {
4244 + struct pefs_mount *pm = VFS_TO_PEFS(mp);
4245 + char *encname;
4246 + int encname_len, error;
4247 +
4248 MPASS(cred != NULL);
4249 - return (pefs_node_get(mp, lvp, vpp, pefs_node_init_lookupkey, cred));
4250 + error = pefs_node_get(mp, lvp, vpp, pefs_node_init_lookupkey, cred);
4251 + if ((error == 0) && (pm->pm_flags & PM_CHECKSUM) != 0) {
4252 + encname_len = MAXNAMLEN + 1;
4253 + encname = malloc(encname_len, M_TEMP, M_WAITOK);
4254 +
4255 + error = pefs_node_lookup_name(lvp, NULL, cred, encname, &encname_len);
4256 + if (error == 0) {
4257 + printf("lookupkey encname=%.*s\n",(int)encname_len, encname);
4258 + pefs_checksum_lookup(encname, encname_len, NULL, *vpp);
4259 + }
4260 + free(encname, M_TEMP);
4261 + }
4262 + return (error);
4263 }
4264
4265 static __inline void
4266 Index: pefs_kmod/sys/fs/pefs/pefs_xbase64.c
4267 ===================================================================
4268 --- pefs_kmod/sys/fs/pefs/pefs_xbase64.c (revision 235719)
4269 +++ pefs_kmod/sys/fs/pefs/pefs_xbase64.c (revision 240588)
4270 @@ -48,6 +48,7 @@
4271 #include <sys/libkern.h>
4272 #include <sys/mount.h>
4273 #include <sys/vnode.h>
4274 +#include <sys/dirent.h>
4275
4276 #include <fs/pefs/pefs.h>
4277
4278 Index: pefs_kmod/sys/fs/pefs/pefs_vfsops.c
4279 ===================================================================
4280 --- pefs_kmod/sys/fs/pefs/pefs_vfsops.c (revision 235719)
4281 +++ pefs_kmod/sys/fs/pefs/pefs_vfsops.c (revision 240588)
4282 @@ -34,6 +34,7 @@
4283 #include <sys/cdefs.h>
4284 __FBSDID("$FreeBSD$");
4285
4286 +#include <sys/endian.h>
4287 #include <sys/param.h>
4288 #include <sys/systm.h>
4289 #include <sys/fcntl.h>
4290 @@ -44,8 +45,10 @@
4291 #include <sys/namei.h>
4292 #include <sys/proc.h>
4293 #include <sys/vnode.h>
4294 +#include <sys/dirent.h>
4295
4296 #include <fs/pefs/pefs.h>
4297 +#include <fs/pefs/pefs_checksum.h>
4298
4299 struct pefs_opt_descr {
4300 char *fs;
4301 @@ -72,10 +75,12 @@
4302 "dircache",
4303 "nodircache",
4304 "asyncreclaim",
4305 + "checksum",
4306 NULL
4307 };
4308
4309 static MALLOC_DEFINE(M_PEFSMNT, "pefs_mount", "PEFS mount structure");
4310 +static MALLOC_DEFINE(M_PEFSCSTABLE, "pefs_checksum_table", "PEFS checksum table");
4311
4312 static void
4313 pefs_opt_set(struct mount *mp, int opt, struct pefs_mount *pm,
4314 @@ -119,6 +124,136 @@
4315 return (0);
4316 }
4317
4318 +/* XXXgpf: tmp 4 dbg purposes */
4319 +static void
4320 +pefs_dbg_vnode(struct vnode *vp, char *str)
4321 +{
4322 + if (vp == NULL)
4323 + return;
4324 + dprintf(("%s is locked: %d\n", str, VOP_ISLOCKED(vp)));
4325 + dprintf(("%s usecount: %d\n", str, vp->v_usecount));
4326 + dprintf(("%s holdcnt: %d\n", str, vp->v_holdcnt));
4327 + dprintf(("%s writecount: %d\n", str, vp->v_writecount));
4328 +}
4329 +
4330 +static int
4331 +pefs_checksum_load(struct mount *mp)
4332 +{
4333 + char *bufp, *path;
4334 + struct nameidata nd, *ndp = &nd;
4335 + struct pefs_chunk pc;
4336 + struct pefs_mount *pm = VFS_TO_PEFS(mp);
4337 + struct pefs_checksum *pcs = &(pm->pm_checksum);
4338 + struct ucred *cred = mp->mnt_cred;
4339 + struct uio *puio;
4340 + struct vnode *checksumvp;
4341 + uint32_t buflen, pathlen;
4342 + int error;
4343 +
4344 + pcs->pcs_checksumvp = NULL;
4345 + pcs->pcs_table1 = NULL;
4346 + pcs->pcs_table2 = NULL;
4347 +
4348 + pathlen = MAXPATHLEN + 1;
4349 + path = malloc(pathlen, M_TEMP, M_WAITOK);
4350 + snprintf(path, pathlen, "%s/%s", mp->mnt_stat.f_mntfromname,
4351 + PEFS_FILE_CHECKSUM);
4352 +
4353 + /* grab a vp for our checksum file */
4354 + NDINIT(ndp, LOOKUP, LOCKLEAF, UIO_SYSSPACE, path, curthread);
4355 + error = namei(ndp);
4356 + free(path, M_TEMP);
4357 + if (error != 0) {
4358 + dprintf(("pefs_checksum_load: namei error %d\n", error));
4359 + return (error);
4360 + }
4361 +
4362 + checksumvp = ndp->ni_vp;
4363 + NDFREE(ndp, NDF_NO_VP_PUT);
4364 +
4365 + /* read checksum file header info */
4366 + buflen = PEFS_CFH_SIZE;
4367 + pefs_chunk_create(&pc, NULL, buflen);
4368 + puio = pefs_chunk_uio(&pc, PEFS_SIGNATURE_MAX_LENGTH, UIO_READ);
4369 +
4370 + /* XXXgpf: gleb says I should use vn_rdwr instead of VOP_READ */
4371 + error = VOP_READ(checksumvp, puio, IO_UNIT, cred);
4372 + if (error != 0) {
4373 + dprintf(("pefs_checksum_load: vop_read1 error %d\n", error));
4374 + pefs_chunk_free(&pc, NULL);
4375 + vput(checksumvp);
4376 + return (error);
4377 + }
4378 +
4379 + bufp = pc.pc_base;
4380 + memcpy(&(pcs->pcs_version), bufp, sizeof(pcs->pcs_version));
4381 + bufp+=sizeof(pcs->pcs_version);
4382 + memcpy(&(pcs->pcs_reserved), bufp, sizeof(pcs->pcs_reserved));
4383 + bufp+=sizeof(pcs->pcs_reserved);
4384 + memcpy(&(pcs->pcs_hash_len), bufp, sizeof(pcs->pcs_hash_len));
4385 + bufp+=sizeof(pcs->pcs_hash_len);
4386 + memcpy(&(pcs->pcs_hash_algo_name), bufp, sizeof(pcs->pcs_hash_algo_name));
4387 + bufp+=sizeof(pcs->pcs_hash_algo_name);
4388 + memcpy(&(pcs->pcs_offset_to_hash_table), bufp,
4389 + sizeof(pcs->pcs_offset_to_hash_table));
4390 + bufp+=sizeof(pcs->pcs_offset_to_hash_table);
4391 + memcpy(&(pcs->pcs_hash_table_size), bufp, sizeof(pcs->pcs_hash_table_size));
4392 + pcs->pcs_hash_table_size = le32toh(pcs->pcs_hash_table_size);
4393 +
4394 + error = pefs_sanitize_checksum_header(pcs);
4395 + if (error != 0) {
4396 + dprintf(("pefs_checksum_load: sanitize error %d\n", error));
4397 + pefs_chunk_free(&pc, NULL);
4398 + vput(checksumvp);
4399 + return (error);
4400 + }
4401 +
4402 + pefs_chunk_free(&pc, NULL);
4403 +
4404 + /* load and keep the 2 hash tables in kernel heap */
4405 + buflen = pcs->pcs_hash_table_size * PEFS_HT_CELL_SIZE;
4406 + pefs_chunk_create(&pc, NULL, buflen);
4407 + puio = pefs_chunk_uio(&pc, PEFS_SIGNATURE_MAX_LENGTH +
4408 + pcs->pcs_offset_to_hash_table, UIO_READ);
4409 +
4410 + error = VOP_READ(checksumvp, puio, IO_UNIT, cred);
4411 + if (error != 0) {
4412 + dprintf(("pefs_checksum_load: vop_read2 error %d\n", error));
4413 + pefs_chunk_free(&pc, NULL);
4414 + vput(checksumvp);
4415 + return (error);
4416 + }
4417 +
4418 + pcs->pcs_table1 = malloc(buflen, M_PEFSCSTABLE, M_WAITOK);
4419 + memcpy(pcs->pcs_table1, pc.pc_base, buflen);
4420 + pefs_chunk_free(&pc, NULL);
4421 +
4422 + pefs_chunk_create(&pc, NULL, buflen);
4423 + puio = pefs_chunk_uio(&pc, PEFS_SIGNATURE_MAX_LENGTH +
4424 + pcs->pcs_offset_to_hash_table + buflen, UIO_READ);
4425 +
4426 + error = VOP_READ(checksumvp, puio, IO_UNIT, cred);
4427 + if (error != 0) {
4428 + dprintf(("pefs_checksum_load: vop_read3 error %d\n", error));
4429 + pefs_chunk_free(&pc, NULL);
4430 + free(pcs->pcs_table1, M_PEFSCSTABLE);
4431 + vput(checksumvp);
4432 + return (error);
4433 + }
4434 +
4435 + pcs->pcs_table2 = malloc(buflen, M_PEFSCSTABLE, M_WAITOK);
4436 + memcpy(pcs->pcs_table2, pc.pc_base, buflen);
4437 + pefs_chunk_free(&pc, NULL);
4438 +
4439 + pefs_dbg_vnode(checksumvp, "before VOP_UNLOCK checksumvp");
4440 + /* keep the reference for checksumvp */
4441 + VOP_UNLOCK(checksumvp, 0);
4442 + pefs_dbg_vnode(checksumvp, "after VOP_UNLOCK checksumvp");
4443 + pcs->pcs_checksumvp = checksumvp;
4444 +
4445 + return (0);
4446 +}
4447 +
4448 /*
4449 * Mount null layer
4450 */
4451 @@ -131,7 +266,7 @@
4452 struct pefs_mount *pm;
4453 char *from, *from_free;
4454 int isvnunlocked = 0, len;
4455 - int opt_dircache, opt_asyncreclaim;
4456 + int opt_checksum, opt_dircache, opt_asyncreclaim;
4457 int error = 0;
4458
4459 PEFSDEBUG("pefs_mount(mp = %p)\n", (void *)mp);
4460 @@ -155,6 +290,12 @@
4461 vfs_deleteopt(mp->mnt_optnew, "asyncreclaim");
4462 opt_asyncreclaim = 1;
4463 }
4464 + opt_checksum = -1;
4465 + if (vfs_flagopt(mp->mnt_optnew, "checksum", NULL, 0)) {
4466 + vfs_deleteopt(mp->mnt_optnew, "checksum");
4467 + dprintf(("checksum!\n"));
4468 + opt_checksum = 1;
4469 + }
4470
4471 if (mp->mnt_flag & MNT_UPDATE) {
4472 error = EOPNOTSUPP;
4473 @@ -170,6 +311,9 @@
4474 PM_ASYNCRECLAIM, "asyncreclaim");
4475 error = 0;
4476 }
4477 + /*
4478 + * XXXgpf: should we allow opt_checksum with MNT_UPDATE?
4479 + */
4480 return (error);
4481 }
4482
4483 @@ -262,6 +406,7 @@
4484 pm->pm_flags |= PM_ROOT_CANRECURSE;
4485 pefs_opt_set(mp, opt_dircache, pm, PM_DIRCACHE, "dircache");
4486 pefs_opt_set(mp, opt_asyncreclaim, pm, PM_ASYNCRECLAIM, "asyncreclaim");
4487 + pefs_opt_set(mp, opt_checksum, pm, PM_CHECKSUM, "checksum");
4488
4489 /*
4490 * Save reference. Each mount also holds
4491 @@ -302,6 +447,17 @@
4492 mp->mnt_data = pm;
4493 vfs_getnewfsid(mp);
4494
4495 + if ((pm->pm_flags & PM_CHECKSUM) != 0) {
4496 + pefs_dbg_vnode(lowerrootvp, "BEFORE lowerrootvp");
4497 + error = pefs_checksum_load(mp);
4498 + pefs_dbg_vnode(lowerrootvp, "AFTER lowerrootvp");
4499 + if (error != 0) {
4500 + vrele(vp);
4501 + free(pm, M_PEFSMNT);
4502 + return (error);
4503 + }
4504 + }
4505 +
4506 PEFSDEBUG("pefs_mount: lower %s, alias at %s\n",
4507 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
4508 return (0);
4509 @@ -331,6 +487,16 @@
4510 * Finally, throw away the pefs_mount structure
4511 */
4512 pm = VFS_TO_PEFS(mp);
4513 + if ((pm->pm_flags & PM_CHECKSUM) != 0) {
4514 + /* free the hash tables from the heap */
4515 + if (pm->pm_checksum.pcs_table1 != NULL)
4516 + free(pm->pm_checksum.pcs_table1, M_PEFSCSTABLE);
4517 + if (pm->pm_checksum.pcs_table2 != NULL)
4518 + free(pm->pm_checksum.pcs_table2, M_PEFSCSTABLE);
4519 + /* vrele the checksum file's vnode */
4520 + if (pm->pm_checksum.pcs_checksumvp != NULL)
4521 + vrele(pm->pm_checksum.pcs_checksumvp);
4522 + }
4523 mp->mnt_data = 0;
4524 pefs_key_remove_all(pm);
4525 mtx_destroy(&pm->pm_keys_lock);
4526 Index: pefs_kmod/sys/modules/pefs/Makefile
4527 ===================================================================
4528 --- pefs_kmod/sys/modules/pefs/Makefile (revision 235719)
4529 +++ pefs_kmod/sys/modules/pefs/Makefile (revision 240588)
4530 @@ -6,7 +6,9 @@
4531 SRCS= vnode_if.h \
4532 pefs_subr.c pefs_vfsops.c pefs_vnops.c pefs_xbase64.c pefs_crypto.c \
4533 pefs_dircache.c \
4534 - pefs_xts.c vmac.c
4535 + pefs_xts.c vmac.c \
4536 + pefs_checksum.c \
4537 + pefs_mac.c
4538
4539 .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64"
4540 SRCS+= pefs_aesni.c
4541 @@ -17,12 +19,9 @@
4542 #DEBUG_FLAGS+= -DPEFS_DEBUG
4543 #DEBUG_FLAGS+= -DPEFS_DEBUG_EXTRA
4544
4545 -CFLAGS+= -I${.CURDIR}/../../
4546 -
4547 -# Temporally build crypto/hmac into pefs module
4548 +CFLAGS+= -I${.CURDIR}/../../# Temporally build crypto/hmac into pefs module
4549 .PATH: ${.CURDIR}/../../crypto/hmac
4550
4551 SRCS+= hmac_sha512.c
4552
4553 .include <bsd.kmod.mk>
4554 -
4555
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.