Richard Haines | e40bbea | 2015-09-30 16:29:20 +0100 | [diff] [blame] | 1 | #include <stdio.h> |
| 2 | #include <stdlib.h> |
| 3 | #include <string.h> |
| 4 | #include <getopt.h> |
| 5 | #include <errno.h> |
| 6 | #include <selinux/selinux.h> |
| 7 | #include <selinux/label.h> |
| 8 | |
| 9 | static size_t digest_len; |
| 10 | |
William Roberts | e4f2bcc | 2016-11-01 14:23:10 -0700 | [diff] [blame] | 11 | static __attribute__ ((__noreturn__)) void usage(const char *progname) |
Richard Haines | e40bbea | 2015-09-30 16:29:20 +0100 | [diff] [blame] | 12 | { |
| 13 | fprintf(stderr, |
Richard Haines | e045605 | 2015-10-15 16:29:54 +0100 | [diff] [blame] | 14 | "usage: %s -b backend [-d] [-v] [-B] [-i] [-f file]\n\n" |
Richard Haines | e40bbea | 2015-09-30 16:29:20 +0100 | [diff] [blame] | 15 | "Where:\n\t" |
| 16 | "-b The backend - \"file\", \"media\", \"x\", \"db\" or " |
| 17 | "\"prop\"\n\t" |
| 18 | "-v Run \"cat <specfile_list> | openssl dgst -sha1 -hex\"\n\t" |
| 19 | " on the list of specfiles to compare the SHA1 digests.\n\t" |
| 20 | "-B Use base specfiles only (valid for \"-b file\" only).\n\t" |
Richard Haines | e045605 | 2015-10-15 16:29:54 +0100 | [diff] [blame] | 21 | "-i Do not request a digest.\n\t" |
Richard Haines | e40bbea | 2015-09-30 16:29:20 +0100 | [diff] [blame] | 22 | "-f Optional file containing the specs (defaults to\n\t" |
| 23 | " those used by loaded policy).\n\n", |
| 24 | progname); |
| 25 | exit(1); |
| 26 | } |
| 27 | |
| 28 | static int run_check_digest(char *cmd, char *selabel_digest) |
| 29 | { |
| 30 | FILE *fp; |
| 31 | char files_digest[128]; |
| 32 | char *files_ptr; |
| 33 | int rc = 0; |
| 34 | |
| 35 | fp = popen(cmd, "r"); |
| 36 | if (!fp) { |
Christian Göttsche | 0c407c3 | 2022-05-05 19:44:01 +0200 | [diff] [blame] | 37 | fprintf(stderr, "Failed to run command '%s': %s\n", cmd, strerror(errno)); |
Richard Haines | e40bbea | 2015-09-30 16:29:20 +0100 | [diff] [blame] | 38 | return -1; |
| 39 | } |
| 40 | |
| 41 | /* Only expect one line "(stdin)= x.." so read and find first space */ |
| 42 | while (fgets(files_digest, sizeof(files_digest) - 1, fp) != NULL) |
| 43 | ; |
| 44 | |
| 45 | files_ptr = strstr(files_digest, " "); |
| 46 | |
| 47 | rc = strncmp(selabel_digest, files_ptr + 1, digest_len * 2); |
| 48 | if (rc) { |
| 49 | printf("Failed validation:\n\tselabel_digest: %s\n\t" |
| 50 | "files_digest: %s\n", |
| 51 | selabel_digest, files_ptr + 1); |
| 52 | } else { |
| 53 | printf("Passed validation - digest: %s\n", selabel_digest); |
| 54 | } |
| 55 | |
| 56 | pclose(fp); |
| 57 | return rc; |
| 58 | } |
| 59 | |
| 60 | int main(int argc, char **argv) |
| 61 | { |
Nicolas Iooss | 8647a6c | 2016-09-25 14:16:07 +0200 | [diff] [blame] | 62 | int backend = 0, rc, opt, validate = 0; |
Richard Haines | e045605 | 2015-10-15 16:29:54 +0100 | [diff] [blame] | 63 | char *baseonly = NULL, *file = NULL, *digest = (char *)1; |
Richard Haines | e40bbea | 2015-09-30 16:29:20 +0100 | [diff] [blame] | 64 | char **specfiles = NULL; |
| 65 | unsigned char *sha1_digest = NULL; |
Nicolas Iooss | 8647a6c | 2016-09-25 14:16:07 +0200 | [diff] [blame] | 66 | size_t i, num_specfiles; |
Richard Haines | e40bbea | 2015-09-30 16:29:20 +0100 | [diff] [blame] | 67 | |
| 68 | char cmd_buf[4096]; |
| 69 | char *cmd_ptr; |
| 70 | char *sha1_buf; |
| 71 | |
| 72 | struct selabel_handle *hnd; |
| 73 | struct selinux_opt selabel_option[] = { |
| 74 | { SELABEL_OPT_PATH, file }, |
| 75 | { SELABEL_OPT_BASEONLY, baseonly }, |
Richard Haines | e045605 | 2015-10-15 16:29:54 +0100 | [diff] [blame] | 76 | { SELABEL_OPT_DIGEST, digest } |
Richard Haines | e40bbea | 2015-09-30 16:29:20 +0100 | [diff] [blame] | 77 | }; |
| 78 | |
| 79 | if (argc < 3) |
| 80 | usage(argv[0]); |
| 81 | |
Richard Haines | e045605 | 2015-10-15 16:29:54 +0100 | [diff] [blame] | 82 | while ((opt = getopt(argc, argv, "ib:Bvf:")) > 0) { |
Richard Haines | e40bbea | 2015-09-30 16:29:20 +0100 | [diff] [blame] | 83 | switch (opt) { |
| 84 | case 'b': |
| 85 | if (!strcasecmp(optarg, "file")) { |
| 86 | backend = SELABEL_CTX_FILE; |
| 87 | } else if (!strcmp(optarg, "media")) { |
| 88 | backend = SELABEL_CTX_MEDIA; |
| 89 | } else if (!strcmp(optarg, "x")) { |
| 90 | backend = SELABEL_CTX_X; |
| 91 | } else if (!strcmp(optarg, "db")) { |
| 92 | backend = SELABEL_CTX_DB; |
| 93 | } else if (!strcmp(optarg, "prop")) { |
| 94 | backend = SELABEL_CTX_ANDROID_PROP; |
Janis Danisevskis | 6dd85b9 | 2016-09-29 12:39:18 +0100 | [diff] [blame] | 95 | } else if (!strcmp(optarg, "service")) { |
| 96 | backend = SELABEL_CTX_ANDROID_SERVICE; |
Richard Haines | e40bbea | 2015-09-30 16:29:20 +0100 | [diff] [blame] | 97 | } else { |
| 98 | fprintf(stderr, "Unknown backend: %s\n", |
| 99 | optarg); |
| 100 | usage(argv[0]); |
| 101 | } |
| 102 | break; |
| 103 | case 'B': |
| 104 | baseonly = (char *)1; |
| 105 | break; |
| 106 | case 'v': |
| 107 | validate = 1; |
| 108 | break; |
Richard Haines | e045605 | 2015-10-15 16:29:54 +0100 | [diff] [blame] | 109 | case 'i': |
| 110 | digest = NULL; |
| 111 | break; |
Richard Haines | e40bbea | 2015-09-30 16:29:20 +0100 | [diff] [blame] | 112 | case 'f': |
| 113 | file = optarg; |
| 114 | break; |
| 115 | default: |
| 116 | usage(argv[0]); |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | memset(cmd_buf, 0, sizeof(cmd_buf)); |
| 121 | |
| 122 | selabel_option[0].value = file; |
| 123 | selabel_option[1].value = baseonly; |
Richard Haines | e045605 | 2015-10-15 16:29:54 +0100 | [diff] [blame] | 124 | selabel_option[2].value = digest; |
Richard Haines | e40bbea | 2015-09-30 16:29:20 +0100 | [diff] [blame] | 125 | |
| 126 | hnd = selabel_open(backend, selabel_option, 3); |
| 127 | if (!hnd) { |
| 128 | switch (errno) { |
| 129 | case EOVERFLOW: |
| 130 | fprintf(stderr, "ERROR Number of specfiles or specfile" |
| 131 | " buffer caused an overflow.\n"); |
| 132 | break; |
| 133 | default: |
| 134 | fprintf(stderr, "ERROR: selabel_open: %s\n", |
| 135 | strerror(errno)); |
| 136 | } |
| 137 | return -1; |
| 138 | } |
| 139 | |
| 140 | rc = selabel_digest(hnd, &sha1_digest, &digest_len, &specfiles, |
| 141 | &num_specfiles); |
| 142 | |
| 143 | if (rc) { |
| 144 | switch (errno) { |
| 145 | case EINVAL: |
| 146 | fprintf(stderr, "No digest available.\n"); |
| 147 | break; |
| 148 | default: |
| 149 | fprintf(stderr, "selabel_digest ERROR: %s\n", |
| 150 | strerror(errno)); |
| 151 | } |
| 152 | goto err; |
| 153 | } |
| 154 | |
| 155 | sha1_buf = malloc(digest_len * 2 + 1); |
| 156 | if (!sha1_buf) { |
| 157 | fprintf(stderr, "Could not malloc buffer ERROR: %s\n", |
| 158 | strerror(errno)); |
| 159 | rc = -1; |
| 160 | goto err; |
| 161 | } |
| 162 | |
| 163 | printf("SHA1 digest: "); |
| 164 | for (i = 0; i < digest_len; i++) |
| 165 | sprintf(&(sha1_buf[i * 2]), "%02x", sha1_digest[i]); |
| 166 | |
| 167 | printf("%s\n", sha1_buf); |
| 168 | printf("calculated using the following specfile(s):\n"); |
| 169 | |
| 170 | if (specfiles) { |
| 171 | cmd_ptr = &cmd_buf[0]; |
| 172 | sprintf(cmd_ptr, "/usr/bin/cat "); |
| 173 | cmd_ptr = &cmd_buf[0] + strlen(cmd_buf); |
| 174 | |
| 175 | for (i = 0; i < num_specfiles; i++) { |
| 176 | sprintf(cmd_ptr, "%s ", specfiles[i]); |
| 177 | cmd_ptr += strlen(specfiles[i]) + 1; |
| 178 | printf("%s\n", specfiles[i]); |
| 179 | } |
| 180 | sprintf(cmd_ptr, "| /usr/bin/openssl dgst -sha1 -hex"); |
| 181 | |
| 182 | if (validate) |
| 183 | rc = run_check_digest(cmd_buf, sha1_buf); |
| 184 | } |
| 185 | |
| 186 | free(sha1_buf); |
| 187 | err: |
| 188 | selabel_close(hnd); |
| 189 | return rc; |
| 190 | } |