diff -uN a/portage-utils-0.62/.depend b/portage-utils-0.62/.depend --- a/portage-utils-0.62/.depend 2016-02-22 19:31:41.000000000 +0100 +++ b/portage-utils-0.62/.depend 2017-05-27 20:28:18.619333105 +0200 @@ -6,6 +6,6 @@ libq/xsystem.c libq/xarray.c libq/atom_explode.c libq/atom_compare.c \ libq/basename.c libq/scandirat.c libq/prelink.c libq/profile.c \ libq/vdb.c libq/vdb_get_next_dir.c libq/virtuals.c applets.h \ - include_applets.h q.c qcheck.c qdepends.c qfile.c qlist.c qlop.c \ - qsearch.c qsize.c qtbz2.c quse.c qxpak.c qpkg.c qgrep.c qatom.c qmerge.c \ - qcache.c qglsa.c + include_applets.h q.c qcheck.c qdepends.c qtegrity.c qfile.c qlist.c \ + qlop.c qsearch.c qsize.c qtbz2.c quse.c qxpak.c qpkg.c qgrep.c qatom.c \ + qmerge.c qcache.c qglsa.c diff -uN a/portage-utils-0.62/applets.h b/portage-utils-0.62/applets.h --- a/portage-utils-0.62/applets.h 2016-02-22 19:31:41.000000000 +0100 +++ b/portage-utils-0.62/applets.h 2017-05-27 20:40:32.606290701 +0200 @@ -30,6 +30,7 @@ DECLARE_APPLET(qatom) DECLARE_APPLET(qmerge) DECLARE_APPLET(qcache) +DECLARE_APPLET(qtegrity) DECLARE_APPLET(qglsa) /* disable */ #undef DECLARE_APPLET @@ -60,6 +61,7 @@ {"qsearch", qsearch_main, "", "search pkgname/desc"}, {"qsize", qsize_main, "", "calculate size usage"}, {"qtbz2", qtbz2_main, "", "manipulate tbz2 packages"}, + {"qtegrity", qtegrity_main, "", "perform integrity check"}, {"quse", quse_main, "", "find pkgs using useflags"}, {"qxpak", qxpak_main, "", "manipulate xpak archives"}, diff -uN a/portage-utils-0.62/qtegrity.c b/portage-utils-0.62/qtegrity.c --- a/portage-utils-0.62/qtegrity.c 1970-01-01 01:00:00.000000000 +0100 +++ b/portage-utils-0.62/qtegrity.c 2017-05-28 10:42:50.450371045 +0200 @@ -0,0 +1,231 @@ +/* + * Copyright 2005-2014 Gentoo Foundation + * Distributed under the terms of the GNU General Public License v2 + * + * Copyright 2005-2010 Ned Ludd - + * Copyright 2005-2014 Mike Frysinger - + * Copyright 2017 Sam Besselink + */ + +#ifdef APPLET_qtegrity + +#define QTEGRITY_FLAGS "i" COMMON_FLAGS +static struct option const qtegrity_long_opts[] = { + {"no-ima-check", no_argument, NULL, 'i'}, + COMMON_LONG_OPTS +}; +static const char * const qtegrity_opts_help[] = { + "Perform online check against IMA file", //@TODO, also add functionality to add a single file+digest to the vardb-QTEGRITY file + COMMON_OPTS_HELP +}; +#define qtegrity_usage(ret) usage(ret, QTEGRITY_FLAGS, qtegrity_long_opts, qtegrity_opts_help, lookup_applet_idx("qtegrity")) + +struct qtegrity_opt_state { + bool ima; +}; + +#define SHA256_PREFIX_LENGTH 8 +#define SHA256_DIGEST_LENGTH 64 +#define SHA256_LENGTH (SHA256_PREFIX_LENGTH + SHA256_DIGEST_LENGTH) + +void get_file_from_ima(char * line, char **ret) +{ + //skip first 123 chars to get to file + // depends on digest_func in IMA, expects sha256; @TODO + size_t dlenstr = strlen(line); + + char *p; + + if (dlenstr > 123) { + p = xmalloc(dlenstr - 123); + memcpy(p, line + 123, dlenstr - 124); + p[dlenstr - 124] = '\0'; + } else { + p = NULL; + } + + *ret = p; +} + +void get_digest_from_ima(char * line, char **ret) +{ + //skip first 51 chars to get to digest and stop at 122 + // depends on digest_func in IMA, expects sha256; @TODO + size_t dlenstr = strlen(line); + + char *p; + + if (dlenstr > 123) { + p = xmalloc(SHA256_DIGEST_LENGTH+1); + memcpy(p, line + 58, SHA256_DIGEST_LENGTH); + p[SHA256_DIGEST_LENGTH] = '\0'; + } else { + p = NULL; + } + + *ret = p; +} + +void get_file_from_integrity(char * line, char **ret) +{ + //skip first chars to get to file + // depends on digest_func in IMA, expects sha256; @TODO + size_t dlenstr = strlen(line); + + char *p; + + if (dlenstr > (SHA256_LENGTH+5)) { + p = xmalloc(dlenstr - (SHA256_LENGTH+5)); + memcpy(p, line + (SHA256_LENGTH+5), dlenstr - (SHA256_LENGTH+6)); + p[dlenstr - (SHA256_LENGTH+6)] = '\0'; + } else { + p = NULL; + } + + *ret = p; +} + +void get_digest_from_integrity(char * line, char **ret) +{ + // skip first chars to get to digest and don't copy remaining part + // depends on digest_func in IMA, expects sha256; @TODO + size_t dlenstr = strlen(line); + + char *p; + + if (dlenstr > (SHA256_LENGTH+5)) { + p = xmalloc(SHA256_DIGEST_LENGTH+1); + memcpy(p, line + 7, SHA256_DIGEST_LENGTH); + p[SHA256_DIGEST_LENGTH] = '\0'; + } else { + p = NULL; + } + + *ret = p; +} + +void get_digest(char * targetfname, char **ret) +{ + char *fn_qtegrity = "/var/db/QTEGRITY"; + int fd_qtegrity; + FILE *fp_qtegrity; + + fd_qtegrity = open(fn_qtegrity, O_RDONLY|O_CLOEXEC, 0); + if (fd_qtegrity == -1) { + warnp("unable to open(%s)", fn_qtegrity); + exit(0); + } + if ((fp_qtegrity = fdopen(fd_qtegrity, "r")) == NULL) { + warnp("unable to fopen(%s, r)", fn_qtegrity); + close(fd_qtegrity); + exit(0); + } + + char *buffer, *line, *fname, *digest; + size_t linelen; + + buffer = line = fname = digest = NULL; + while (getline(&line, &linelen, fp_qtegrity) != -1) { + free(buffer); + buffer = xstrdup(line); + + get_file_from_integrity(line, &fname); + + if (strcmp(targetfname, fname) == 0) { + get_digest_from_integrity(line, &digest); + *ret = digest; + break; + } + } + + free(line); + free(buffer); + + close(fd_qtegrity); + fclose(fp_qtegrity); +} + +int qtegrity_main(int argc, char **argv) +{ + int i; + + struct qtegrity_opt_state state = { + .ima = true, + }; + + while ((i = GETOPT_LONG(QTEGRITY, qtegrity, "")) != -1) { + switch (i) { + COMMON_GETOPTS_CASES(qtegrity) + case 'i': state.ima = false; break; + } + } + + if (state.ima) { + char *fn_ima = "/sys/kernel/security/ima/ascii_runtime_measurements"; + int fd_ima; + FILE *fp_ima; + struct stat st; + + fd_ima = open(fn_ima, O_RDONLY|O_CLOEXEC, 0); + if (fd_ima == -1) { + warnp("Unable to open(%s)", fn_ima); + exit(0); + } + if ((fp_ima = fdopen(fd_ima, "r")) == NULL) { + warnp("Unable to fopen(%s, r)", fn_ima); + close(fd_ima); + exit(0); + } + + char *buffer, *line, *targetfname, *targetdigest, *digest; + size_t linelen; + + buffer = line = targetfname = targetdigest = digest = NULL; + while (getline(&line, &linelen, fp_ima) != -1) { + free(buffer); + buffer = xstrdup(line); + + if (buffer[0] != '1' || buffer[1] != '0') + continue; + + get_file_from_ima(buffer, &targetfname); + get_digest_from_ima(buffer, &targetdigest); + + if (targetfname == NULL || targetdigest == NULL) { + printf("Target empty: %s\n", line); + continue; + } + + if (stat(targetfname, &st) < 0) { + printf("Couldn't access file '%s'\n", targetfname); + continue; + } + + if (!(st.st_mode & S_IXUSR || st.st_mode & S_IXGRP || st.st_mode & S_IXOTH)) + continue; + + get_digest(targetfname, &digest); + if (digest == NULL) { + printf("No digest found for: %s\n", line); + continue; + } + + if (strcmp(targetdigest, digest) != 0) { + printf("Digest didn't match for %s\n", targetfname); + printf("Got %s expected %s\n", digest, targetdigest); + } + } + + free(line); + free(buffer); + + close(fd_ima); + fclose(fp_ima); + } + + return EXIT_SUCCESS; +} + +#else +DEFINE_APPLET_STUB(qtegrity) +#endif