Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 530278 Details for
Bug 619988
Portage-utils: Compare digests of installed files with IMA
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
Complete c file, replacing previous patches
qtegrity.c (text/x-csrc), 11.03 KB, created by
Sam
on 2018-05-06 20:57:20 UTC
(
hide
)
Description:
Complete c file, replacing previous patches
Filename:
MIME Type:
Creator:
Sam
Created:
2018-05-06 20:57:20 UTC
Size:
11.03 KB
patch
obsolete
>/* > * Copyright 2005-2014 Gentoo Foundation > * Distributed under the terms of the GNU General Public License v2 > * > * Copyright 2005-2010 Ned Ludd - <solar@gentoo.org> > * Copyright 2005-2014 Mike Frysinger - <vapier@gentoo.org> > * Copyright 2017 Sam Besselink > */ > >#ifdef APPLET_qtegrity > >#define QTEGRITY_FLAGS "a:is" COMMON_FLAGS >static struct option const qtegrity_long_opts[] = { > {"add", a_argument, NULL, 'a'}, > {"ignore-non-existent", no_argument, NULL, 'i'}, > {"show-matches", no_argument, NULL, 's'}, >// {"convert", a_argument, NULL, 'c'}, //TODO, add this functionality > COMMON_LONG_OPTS >}; >static const char * const qtegrity_opts_help[] = { > "Add file to store of known-good digests", > "Be silent if recorded file no longer exists", > "Show recorded digests that match with known-good digests", >// "Convert known good digests to different hash function", //TODO, add this functionality > COMMON_OPTS_HELP >}; >#define qtegrity_usage(ret) usage(ret, QTEGRITY_FLAGS, qtegrity_long_opts, qtegrity_opts_help, NULL, lookup_applet_idx("qtegrity")) > >struct qtegrity_opt_state { > bool ima; > bool add; > char* add_file; > bool ignore_non_exist; > bool show_matches; >// bool convert; >}; > >#define FILE_SUCCESS 1 >#define FILE_EMPTY 2 >#define FILE_RELATIVE 3 > >#define SHA1_DIGEST_LENGTH 40 >#define SHA256_PREFIX_LENGTH 8 >#define SHA256_DIGEST_LENGTH 64 >#define SHA256_LENGTH (SHA256_PREFIX_LENGTH + SHA256_DIGEST_LENGTH) >#define SHA512_DIGEST_LENGTH 128 > >static void external_check_sha(char * ret_digest, char * filepath, char * algo) { > FILE *fp; > char digest[130]; > size_t size_digest = 1; > > if (strcmp(algo, "sha256") == 0) { > size_digest = 66; > } else if (strcmp(algo, "sha512") == 0) { > size_digest = 130; > } > > if ((strcmp(algo, "sha256") != 0) && (strcmp(algo, "sha512") != 0)) { > return; > } > > char cmd[29+strlen(filepath)+1]; > snprintf(cmd, 29+strlen(filepath)+1, "/usr/bin/%ssum \"%s\" 2>&1", algo, filepath); > > /* Open the command for reading. */ > fp = popen(cmd, "r"); //TODO, use pipe-fork-exec > if (fp == NULL) { > printf("Failed to run command '%s'\n", cmd); > exit(1); > } > > /* Read the output a line at a time - output it. */ > if (fgets(digest, size_digest, fp) == NULL) > return; > > char *pfound; > size_t dposfound; > pfound = strchr(digest, ' '); //pointer to first char in string > dposfound = pfound - digest + 1; //get relative position by subtracting pointers > if (dposfound == 65) { > memcpy(ret_digest, digest, dposfound - 1); > ret_digest[dposfound - 1] = '\0'; > } else { > printf("%zu\n", dposfound); > } > > /* Close */ > pclose(fp); > > return; >} > >static void get_fname_from_line(char * line, char **ret, int digest_size, int offset) >{ > /* Skip first 123 chars to get to file > * depends on digest_func in IMA > */ > size_t dlenstr = strlen(line); > int skip = ((digest_size == SHA256_DIGEST_LENGTH) || (digest_size == SHA512_DIGEST_LENGTH)) ? digest_size+offset+8 : digest_size+offset+6; > > char *p; > > if (dlenstr > skip) { //assume file is at least two chars long > int segment_size = dlenstr - skip - 1; > p = xmalloc(segment_size+1); > memcpy(p, line + skip, segment_size); > p[segment_size] = '\0'; > } else { > // E.g. digest used wrong hash algo, or malformed input > p = NULL; > } > > *ret = p; >} > >static void get_digest_from_line(char * line, char * ret, int digest_size, int offset) >{ > /* Skip first chars to get to digest > * depends on digest_func in IMA > */ > size_t dlenstr = strlen(line); > int skip = ((digest_size == SHA256_DIGEST_LENGTH) || (digest_size == SHA512_DIGEST_LENGTH)) ? offset+8 : offset+6; > > if (dlenstr > (digest_size+skip+1)) { > memcpy(ret, line+skip, digest_size); > ret[digest_size] = '\0'; > } >} > >static void get_known_good_digest(const char * fn_store, char * recorded_fname, char * ret, int recorded_digest_size) >{ > /* Open file with known good hashes */ > int fd_store; > FILE *fp_store; > > fd_store = open(fn_store, O_RDONLY|O_CLOEXEC, 0); > if (fd_store == -1) { > warnp("unable to open(%s)", fn_store); > exit(0); > } > if ((fp_store = fdopen(fd_store, "r")) == NULL) { > warnp("unable to fopen(%s, r)", fn_store); > close(fd_store); > exit(0); > } > > char *buffered_line, *line, *fname; > size_t linelen; > > /* Iterate over lines in known-good-hashes-file; per line: if fname matches, grab hash. */ > buffered_line = line = fname = NULL; > while (getline(&line, &linelen, fp_store) != -1) { > free(buffered_line); > buffered_line = xstrdup(line); > > get_fname_from_line(line, &fname, recorded_digest_size, 15); > > if (fname == NULL) { > //probably line without digest (e.g. symlink) > continue; > } > > if (strcmp(recorded_fname, fname) == 0) { > get_digest_from_line(line, ret, recorded_digest_size, 9); > > free(fname); > break; > } > > free(fname); > } > > free(line); > free(buffered_line); > > close(fd_store); > fclose(fp_store); >} > >static int get_size_digest(char * line) >{ > int ret = 0; > > char *pfound; > pfound = strchr(line, ':'); //find colon; it is boundary between end of hash func & begin of digest > if (pfound != NULL) { > int dpfound = pfound - line; > int cutoff_prefix = 51; > int dsegment = dpfound - cutoff_prefix; > > char *line_segment; > line_segment = xmalloc(dsegment + 1); > memcpy(line_segment, line + cutoff_prefix, dsegment); //chop off the first chars to get to the hash func > line_segment[dsegment] = '\0'; > > /* If line segment equals name of hash func, then return relevant const. */ > if (strcmp(line_segment, "sha512") == 0) { > ret = SHA512_DIGEST_LENGTH; > } else if (strcmp(line_segment, "sha256") == 0) { > ret = SHA256_DIGEST_LENGTH; > } else if (strcmp(line_segment, "sha1") == 0) { > ret = SHA1_DIGEST_LENGTH; > } > > free(line_segment); > } > > return ret; >} > >static int check_file(char * filename) >{ > if (strlen(filename) > 255) //TODO, this is 4096 too low, because this variable also holds path; for linux path is max 4096 chars > err("Filename too long"); > > if (filename[0] != '/') { > return FILE_RELATIVE; > } > > return FILE_SUCCESS; >} > >int qtegrity_main(int argc, char **argv) >{ > int i; > > struct qtegrity_opt_state state = { > .ima = true, > .add = false, > .ignore_non_exist = false, > .show_matches = false, >// .convert = false; > }; > > while ((i = GETOPT_LONG(QTEGRITY, qtegrity, "")) != -1) { > switch (i) { > COMMON_GETOPTS_CASES(qtegrity) > case 'a': > state.ima = false; > state.add = true; > if (check_file(optarg) == FILE_SUCCESS) { > state.add_file = xstrdup(optarg); > } else { > err("Expected absolute file as argument, got '%s'", optarg); > } > break; > case 'i': state.ignore_non_exist = true; break; > case 's': state.show_matches = true; break; > } > } > > if (state.ima) { > const 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); //TODO, shouldn't we explicitly remind user IMA/securityfs is needed? > exit(0); > } > if ((fp_ima = fdopen(fd_ima, "r")) == NULL) { > warnp("Unable to fopen(%s, r)", fn_ima); > close(fd_ima); > exit(0); > } > > char *buffered_line, *line, *recorded_fname; > int recorded_digest_size = 0; > size_t linelen; > > /* Iterate over IMA file, grab fname and digest, get known good digest for fname and compare */ > buffered_line = line = recorded_fname = NULL; > while (getline(&line, &linelen, fp_ima) != -1) { > free(buffered_line); > buffered_line = xstrdup(line); > > if (buffered_line[0] != '1' || buffered_line[1] != '0') > continue; > > recorded_digest_size = get_size_digest(buffered_line); > > char * recorded_digest = xmalloc(recorded_digest_size+1); > recorded_digest[0] = '\0'; > > get_fname_from_line(buffered_line, &recorded_fname, recorded_digest_size, 51); //grab fname from IMA file line > get_digest_from_line(buffered_line, recorded_digest, recorded_digest_size, 50); //grab digest from IMA file line, @TODO, check whether digest == 000etc > > if (recorded_fname == NULL || recorded_digest == NULL) { > printf("Empty recorded filename: %s\n", line); > > if (recorded_fname != NULL) > free(recorded_fname); > > if (recorded_digest != NULL) > free(recorded_digest); > > continue; > } > > if (check_file(recorded_fname) == FILE_RELATIVE) { > printf("Seems like a kernel process: %s\n", recorded_fname); > > free(recorded_fname); > free(recorded_digest); > continue; > } > > if (stat(recorded_fname, &st) < 0) { > if (!state.ignore_non_exist) > printf("Couldn't access recorded file '%s'\n", recorded_fname); > > free(recorded_fname); > free(recorded_digest); > continue; > } > > if (!(st.st_mode & S_IXUSR || st.st_mode & S_IXGRP || st.st_mode & S_IXOTH)) { > free(recorded_fname); > free(recorded_digest); > continue; > } > > char * digest = xmalloc(recorded_digest_size+1); > digest[0] = '\0'; > > get_known_good_digest("/var/db/QTEGRITY_custom", recorded_fname, digest, recorded_digest_size); //first try custom known good digests for fname > > if (digest[0] == '\0') { > digest[0] = '\0'; > get_known_good_digest("/var/db/QTEGRITY", recorded_fname, digest, recorded_digest_size); //then try from OS source > > if (digest[0] == '\0') { > printf("No digest found for: %s\n", line); > > free(recorded_fname); > free(recorded_digest); > free(digest); > continue; > } > } > > if (strcmp(recorded_digest, digest) != 0) { > printf("Digest didn't match for %s\n", recorded_fname); > printf("Known-good: '%s'...\nRecorded: '%s'\n\n", digest, recorded_digest); > } else if (state.show_matches) { > printf("Success! Digest matched for %s\n", recorded_fname); > } > > free(recorded_fname); > free(recorded_digest); > free(digest); > } > > free(line); > free(buffered_line); > > close(fd_ima); > fclose(fp_ima); > } else if (state.add) { > //Add a single executable file+digest to the custom digest store > const char *fn_qtegrity_custom = "/var/db/QTEGRITY_custom"; > int fd_qtegrity_custom; > FILE *fp_qtegrity_custom; > struct stat st; > int flush_status; > > fd_qtegrity_custom = open(fn_qtegrity_custom, O_RDWR|O_CREAT|O_APPEND|O_CLOEXEC, 0); > if (fd_qtegrity_custom == -1) { > warnp("Unable to open(%s)", fn_qtegrity_custom); > exit(0); > } > if ((fp_qtegrity_custom = fdopen(fd_qtegrity_custom, "a+")) == NULL) { > warnp("Unable to fopen(%s, r)", fn_qtegrity_custom); > close(fd_qtegrity_custom); > exit(0); > } > > printf("Adding %s to %s\n", state.add_file, fn_qtegrity_custom); > > if (stat(state.add_file, &st) < 0) > err("Couldn't access file '%s'\n", state.add_file); > > if (!(st.st_mode & S_IXUSR || st.st_mode & S_IXGRP || st.st_mode & S_IXOTH)) > err("File '%s' is not executable\n", state.add_file); > > //add digest > char *hash_algo = (char *)"sha256"; > char *file_digest; > file_digest = xmalloc(SHA256_DIGEST_LENGTH+1); > external_check_sha(file_digest, state.add_file, hash_algo); > > fputs(hash_algo, fp_qtegrity_custom); > fputs(":", fp_qtegrity_custom); > fputs(file_digest, fp_qtegrity_custom); > fputs(" file:", fp_qtegrity_custom); > fputs(state.add_file, fp_qtegrity_custom); > fputs("\n", fp_qtegrity_custom); > > flush_status = fflush(fp_qtegrity_custom); > if (flush_status != 0) > puts("Error flushing stream!"); > > free(file_digest); > } > > if (state.add) > free(state.add_file); > > return EXIT_SUCCESS; >} > >#else >DEFINE_APPLET_STUB(qtegrity) >#endif
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 619988
:
474532
|
474678
|
475232
|
475294
|
475298
|
528952
|
530276
|
530278
|
531030