diff -uN a/portage-utils-0.62/qtegrity.c b/portage-utils-0.62/qtegrity.c --- a/portage-utils-0.62/qtegrity.c 2017-06-04 20:54:27.822259502 +0200 +++ b/portage-utils-0.62/qtegrity.c 2017-06-05 22:34:59.697970715 +0200 @@ -9,27 +9,81 @@ #ifdef APPLET_qtegrity -#define QTEGRITY_FLAGS "i" COMMON_FLAGS +#define QTEGRITY_FLAGS "ia:" COMMON_FLAGS static struct option const qtegrity_long_opts[] = { {"no-ima-check", no_argument, NULL, 'i'}, + {"add", a_argument, NULL, 'a'}, COMMON_LONG_OPTS }; static const char * const qtegrity_opts_help[] = { - "Don't perform integrity check against IMA file", //@TODO, also add functionality to add a single file+digest to the vardb-QTEGRITY file + "Don't perform integrity check against IMA file", + "Add file to QTEGRITY", 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; + bool add; + char* add_file; }; +#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 +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 text[29+strlen(filepath)+1]; + snprintf(text, 29+strlen(filepath)+1, "/usr/bin/%ssum \"%s\" 2>&1", algo, filepath); + + /* Open the command for reading. */ + fp = popen(text, "r"); + if (fp == NULL) { + printf("Failed to run command '%s'\n", text); + 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; +} + void get_file_from_ima(char * line, char **ret, int digest_size) { //skip first 123 chars to get to file @@ -181,18 +235,41 @@ } } +int check_file(char * filename) +{ + if (strlen(filename) > 255) + err("Filename too long"); + if (filename == NULL) { + return FILE_EMPTY; + } else 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, }; while ((i = GETOPT_LONG(QTEGRITY, qtegrity, "")) != -1) { switch (i) { COMMON_GETOPTS_CASES(qtegrity) case 'i': state.ima = false; break; + 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; } } @@ -201,6 +278,7 @@ int fd_ima; FILE *fp_ima; struct stat st; + int file_state; fd_ima = open(fn_ima, O_RDONLY|O_CLOEXEC, 0); if (fd_ima == -1) { @@ -221,6 +299,7 @@ while (getline(&line, &linelen, fp_ima) != -1) { free(buffer); buffer = xstrdup(line); + file_state = 0; if (buffer[0] != '1' || buffer[1] != '0') continue; @@ -230,10 +309,11 @@ get_file_from_ima(buffer, &targetfname, digest_size); get_digest_from_ima(buffer, &targetdigest, digest_size); - if (targetfname == NULL) { + file_state = check_file(targetfname); + if (file_state == FILE_EMPTY) { printf("Target empty: %s\n", line); continue; - } else if (targetfname[0] != '/') { + } else if (file_state == FILE_RELATIVE) { printf("Seems like a kernel process: %s\n", targetfname); continue; } else if (targetdigest == NULL) { @@ -266,8 +346,54 @@ close(fd_ima); fclose(fp_ima); + } else if (state.add) { + //Add a single executable file+digest to the vardb-QTEGRITY file + 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 = "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(fn_qtegrity_custom, fp_qtegrity_custom); + fputs("\n", fp_qtegrity_custom); + + flush_status = fflush(fp_qtegrity_custom); + if (flush_status != 0) + puts("Error flushing stream!"); } + if (state.add) + free(state.add_file); + return EXIT_SUCCESS; }