From 2288b178e612386e7a75471c8861c3f6d81c300d Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Sat, 21 May 2022 23:44:49 -0400 Subject: Add tc remove and append -f flags Also fix some conflicting types to hopefully prevent possible overflows. --- .gitignore | 1 + Makefile | 2 +- README.txt | 13 +++++-- append.c | 26 +++++++++---- create.c | 9 ++--- info.c | 25 ++++++------- remove.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tc.1 | 23 ++++++++++++ tc.c | 7 +++- trustcache.h | 7 ++++ 10 files changed, 200 insertions(+), 33 deletions(-) create mode 100644 remove.c diff --git a/.gitignore b/.gitignore index 4c3a795..93ac639 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ a.out *.o *.bin tc +.vscode diff --git a/Makefile b/Makefile index 364764d..fb2bd37 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ OBJS = tc.o -OBJS += append.o create.o info.o +OBJS += append.o create.o info.o remove.o OBJS += machoparse/cdhash.o cache_from_tree.o sort.o OBJS += uuid/gen_uuid.o uuid/pack.o uuid/unpack.o uuid/parse.o uuid/unparse.o uuid/copy.o diff --git a/README.txt b/README.txt index 2e55a09..295f66f 100644 --- a/README.txt +++ b/README.txt @@ -4,20 +4,22 @@ NAME tc – Create and interact with trustcaches SYNOPSIS - tc append [-u uuid | 0] infile file ... + tc append [-f flags] [-u uuid | 0] infile file ... tc create [-u uuid] [-v version] outfile file ... tc info [-c] [-h] [-e entrynum] file + tc remove [-k] file hash ... DESCRIPTION The tc utility is used to get info about and modify Apple trustcaches. The following commands are supported by tc: - append [-u uuid | 0] infile file ... + append [-f flags] [-u uuid | 0] infile file ... Modify the trustcache at infile to include each signed Mach-O in the specified path. uuid is used to specify a custom uuid to be used. If it is 0, the uuid will be left the same, otherwise, it - will be regenerated. + will be regenerated. If -f is specified, any new entries with + have the flags specified at flags. create [-u uuid] [-v version] outfile file ... Create a trustcache at outfile. Each Mach-O found in the @@ -37,6 +39,11 @@ DESCRIPTION given, only the header will be printed. If entrynum is specified, only that entry will be printed. + remove [-k] file hash ... + Remove each specified hash from file. If -k is specified, the + uuid will not be regenerated. The number of removed entries will + be printed. + EXIT STATUS The tc utility exits 0 on success, and >0 if an error occurs. diff --git a/append.c b/append.c index 475200e..a5cd031 100644 --- a/append.c +++ b/append.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -38,14 +39,13 @@ int tcappend(int argc, char **argv) { - if (argc < 3) - return -1; - int keepuuid = 0; uuid_t uuid; + const char *errstr = NULL; + uint8_t flags = 0; int ch; - while ((ch = getopt(argc, argv, "u:")) != -1) { + while ((ch = getopt(argc, argv, "u:f:")) != -1) { switch (ch) { case 'u': if (strlen(optarg) == 1 && *optarg == '0') { @@ -57,12 +57,22 @@ tcappend(int argc, char **argv) keepuuid = 2; } break; + case 'f': + flags = strtonum(optarg, 0, UINT8_MAX, &errstr); + if (errstr != NULL) { + fprintf(stderr, "flag number is %s: %s\n", errstr, optarg); + exit(1); + } + break; } } argc -= optind; argv += optind; + if (argc < 2) + return -1; + FILE *f = NULL; struct trust_cache cache = opentrustcache(argv[0]); struct trust_cache append = { @@ -76,16 +86,16 @@ tcappend(int argc, char **argv) if ((cache.hashes = realloc(cache.hashes, sizeof(trust_cache_hash0) * (cache.num_entries + append.num_entries))) == NULL) exit(1); - for (int j = 0; j < append.num_entries; j++) { + for (uint32_t j = 0; j < append.num_entries; j++) { memcpy(cache.hashes[cache.num_entries + j], append.hashes[j], CS_CDHASH_LEN); } } else if (append.version == 1) { if ((cache.entries = realloc(cache.entries, sizeof(struct trust_cache_entry1) * (cache.num_entries + append.num_entries))) == NULL) exit(1); - for (int j = 0; j < append.num_entries; j++) { + for (uint32_t j = 0; j < append.num_entries; j++) { cache.entries[cache.num_entries + j].hash_type = append.entries[j].hash_type; - cache.entries[cache.num_entries + j].flags = append.entries[j].flags; + cache.entries[cache.num_entries + j].flags = flags != 0 ? flags : append.entries[j].flags; memcpy(cache.entries[cache.num_entries + j].cdhash, append.entries[j].cdhash, CS_CDHASH_LEN); } } @@ -120,7 +130,7 @@ tcappend(int argc, char **argv) cache.version = le32toh(cache.version); cache.num_entries = le32toh(cache.num_entries); - for (int i = 0; i < cache.num_entries; i++) { + for (uint32_t i = 0; i < cache.num_entries; i++) { if (cache.version == 0) fwrite(&cache.hashes[i], sizeof(trust_cache_hash0), 1, f); else if (cache.version == 1) diff --git a/create.c b/create.c index a19d7f3..955c833 100644 --- a/create.c +++ b/create.c @@ -71,9 +71,8 @@ tccreate(int argc, char **argv) argc -= optind; argv += optind; - if (argc == 0) { + if (argc == 0) return -1; - } for (int i = 1; i < argc; i++) { append = cache_from_tree(argv[i], cache.version); @@ -81,14 +80,14 @@ tccreate(int argc, char **argv) if ((cache.hashes = realloc(cache.hashes, sizeof(trust_cache_hash0) * (cache.num_entries + append.num_entries))) == NULL) exit(1); - for (int j = 0; j < append.num_entries; j++) { + for (uint32_t j = 0; j < append.num_entries; j++) { memcpy(cache.hashes[cache.num_entries + j], append.hashes[j], CS_CDHASH_LEN); } } else if (append.version == 1) { if ((cache.entries = realloc(cache.entries, sizeof(struct trust_cache_entry1) * (cache.num_entries + append.num_entries))) == NULL) exit(1); - for (int j = 0; j < append.num_entries; j++) { + for (uint32_t j = 0; j < append.num_entries; j++) { cache.entries[cache.num_entries + j].hash_type = append.entries[j].hash_type; cache.entries[cache.num_entries + j].flags = append.entries[j].flags; memcpy(cache.entries[cache.num_entries + j].cdhash, append.entries[j].cdhash, CS_CDHASH_LEN); @@ -115,7 +114,7 @@ tccreate(int argc, char **argv) cache.version = le32toh(cache.version); cache.num_entries = le32toh(cache.num_entries); - for (int i = 0; i < cache.num_entries; i++) { + for (uint32_t i = 0; i < cache.num_entries; i++) { if (cache.version == 1) fwrite(&cache.entries[i], sizeof(struct trust_cache_entry1), 1, f); else if (cache.version == 0) diff --git a/info.c b/info.c index c676586..8de924c 100644 --- a/info.c +++ b/info.c @@ -26,24 +26,19 @@ */ #include -#include #include +#include #include #include #include "trustcache.h" -void print_header(struct trust_cache cache); -void print_hash(uint8_t cdhash[CS_CDHASH_LEN], bool newline); -void print_entry(struct trust_cache_entry1 entry); -void print_entries(struct trust_cache cache); - int tcinfo(int argc, char **argv) { struct trust_cache cache; bool headeronly = false, onlyhash = false; - int entrynum = -1; + uint32_t entrynum = -1; const char *errstr = NULL; int ch; @@ -53,7 +48,7 @@ tcinfo(int argc, char **argv) headeronly = true; break; case 'e': - entrynum = strtonum(optarg, 1, INT_MAX, &errstr); + entrynum = strtonum(optarg, 1, UINT32_MAX, &errstr); if (errstr != NULL) { fprintf(stderr, "entry number is %s: %s\n", errstr, optarg); exit(1); @@ -68,9 +63,8 @@ tcinfo(int argc, char **argv) argc -= optind; argv += optind; - if (argc == 0) { + if (argc == 0) return -1; - } cache = opentrustcache(argv[0]); @@ -78,7 +72,7 @@ tcinfo(int argc, char **argv) print_header(cache); if (!headeronly) { if (onlyhash) { - for (int i = 0; i < cache.num_entries; i++) { + for (uint32_t i = 0; i < cache.num_entries; i++) { if (cache.version == 0) print_hash(cache.hashes[i], true); else @@ -120,7 +114,7 @@ print_header(struct trust_cache cache) void print_entries(struct trust_cache cache) { - for (int i = 0; i < cache.num_entries; i++) { + for (uint32_t i = 0; i < cache.num_entries; i++) { if (cache.version == 0) print_hash(cache.hashes[i], true); else if (cache.version == 1) @@ -143,9 +137,12 @@ print_entry(struct trust_cache_entry1 entry) case CS_TRUST_CACHE_AMFID|CS_TRUST_CACHE_ANE: printf(" CS_TRUST_CACHE_AMFID|CS_TRUST_CACHE_ANE "); break; - default: + case 0: printf(" [none] "); break; + default: + printf(" [%i] ", entry.flags); + break; } printf("[%i]\n", entry.hash_type); @@ -154,7 +151,7 @@ print_entry(struct trust_cache_entry1 entry) void print_hash(uint8_t cdhash[CS_CDHASH_LEN], bool newline) { - for (int j = 0; j < CS_CDHASH_LEN; j++) { + for (size_t j = 0; j < CS_CDHASH_LEN; j++) { printf("%02x", cdhash[j]); } if (newline) diff --git a/remove.c b/remove.c new file mode 100644 index 0000000..442a9e2 --- /dev/null +++ b/remove.c @@ -0,0 +1,120 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 Cameron Katri. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY CAMERON KATRI AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CAMERON KATRI OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "trustcache.h" +#include "uuid/uuid.h" + +int +tcremove(int argc, char **argv) +{ + bool keepuuid = false; + int numremoved = 0; + + int ch; + while ((ch = getopt(argc, argv, "k")) != -1) { + switch (ch) { + case 'k': + keepuuid = true; + break; + } + } + + argc -= optind; + argv += optind; + + if (argc < 2) + return -1; + + FILE *f = NULL; + struct trust_cache cache = opentrustcache(argv[0]); + + if (!keepuuid) + uuid_generate(cache.uuid); + + uint8_t hash[CS_CDHASH_LEN] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + for (int i = 1; i < argc; i++) { + if (strlen(argv[i]) != 40) { + fprintf(stderr, "%s is not a valid CDHash\n", argv[i]); + exit(1); + } + for (size_t j = 0; j < CS_CDHASH_LEN; j++) + sscanf(argv[i] + 2 * j, "%02hhx", &hash[j]); + + uint32_t j = 0; + while (j < cache.num_entries) { + if (cache.version == 0) { + if (memcmp(cache.hashes[j], hash, CS_CDHASH_LEN) == 0) { + memmove(&cache.hashes[j], &cache.hashes[j + 1], (cache.num_entries - j - 1) * sizeof(trust_cache_hash0)); + cache.num_entries--; + numremoved++; + continue; + } + } else if (cache.version == 1) { + if (memcmp(cache.entries[j].cdhash, hash, CS_CDHASH_LEN) == 0) { + memmove(&cache.entries[j], &cache.entries[j + 1], (cache.num_entries - j - 1) * sizeof(struct trust_cache_entry1)); + cache.num_entries--; + numremoved++; + continue; + } + } + j++; + } + for (size_t j = 0; j < CS_CDHASH_LEN; j++) + hash[j] = 0; + } + + if ((f = fopen(argv[0], "wb")) == NULL) { + fprintf(stderr, "%s: %s\n", argv[0], strerror(errno)); + return 1; + } + + cache.version = htole32(cache.version); + cache.num_entries = htole32(cache.num_entries); + fwrite(&cache, sizeof(struct trust_cache) - sizeof(struct trust_cache_entry1*), 1, f); + cache.version = le32toh(cache.version); + cache.num_entries = le32toh(cache.num_entries); + + for (uint32_t i = 0; i < cache.num_entries; i++) { + if (cache.version == 0) + fwrite(&cache.hashes[i], sizeof(trust_cache_hash0), 1, f); + else if (cache.version == 1) + fwrite(&cache.entries[i], sizeof(struct trust_cache_entry1), 1, f); + } + + printf("Removed %i %s\n", numremoved, numremoved == 1 ? "entry" : "entries"); + + return 0; +} diff --git a/tc.1 b/tc.1 index 19df3de..030eaf1 100644 --- a/tc.1 +++ b/tc.1 @@ -31,6 +31,7 @@ .Sh SYNOPSIS .Nm .Cm append +.Op Fl f Ar flags .Op Fl u Ar uuid | 0 .Ar infile .Ar @@ -46,6 +47,11 @@ .Op Fl h .Op Fl e Ar entrynum .Ar file +.Nm +.Cm remove +.Op Fl k +.Ar file +.Ar hash ... .Sh DESCRIPTION The .Nm @@ -56,6 +62,7 @@ The following commands are supported by .Bl -tag -width create .It Xo .Cm append +.Op Fl f Ar flags .Op Fl u Ar uuid | 0 .Ar infile .Ar @@ -68,6 +75,10 @@ is used to specify a custom uuid to be used. If it is .Ar 0 , the uuid will be left the same, otherwise, it will be regenerated. +If +.Fl f +is specified, any new entries with have the flags specified at +.Ar flags . .It Xo .Cm create .Op Fl u Ar uuid @@ -107,6 +118,18 @@ is given, only the header will be printed. If .Ar entrynum is specified, only that entry will be printed. +.It Xo +.Cm remove +.Op Fl k +.Ar file +.Ar hash ... +.Xc +Remove each specified hash from +.Ar file . +If +.Fl k +is specified, the uuid will not be regenerated. +The number of removed entries will be printed. .El .Sh EXIT STATUS .Ex -std diff --git a/tc.c b/tc.c index f6cc759..6142057 100644 --- a/tc.c +++ b/tc.c @@ -41,9 +41,10 @@ main(int argc, char **argv) { if (argc < 2) { help: - fprintf(stderr, "Usage: tc append [-u uuid | 0] infile file ...\n" + fprintf(stderr, "Usage: tc append [-f flags] [-u uuid | 0] infile file ...\n" " tc create [-u uuid] [-v version] outfile file ...\n" - " tc info [-c] [-h] [-e entrynum] file\n\n" + " tc info [-c] [-h] [-e entrynum] file\n" + " tc remove [-k] file hash ...\n\n" "See tc(1) for more information\n"); exit(1); } @@ -56,6 +57,8 @@ help: ret = tccreate(argc - 1, argv + 1); else if (strcmp(argv[1], "append") == 0) ret = tcappend(argc - 1, argv + 1); + else if (strcmp(argv[1], "remove") == 0) + ret = tcremove(argc - 1, argv + 1); else fprintf(stderr, "Unknown subcommand %s\n", argv[1]); diff --git a/trustcache.h b/trustcache.h index 1f8a3cd..5b75459 100644 --- a/trustcache.h +++ b/trustcache.h @@ -1,6 +1,7 @@ #ifndef _TRUSTCACHE_H_ #define _TRUSTCACHE_H_ +#include #include #if __APPLE__ @@ -44,8 +45,14 @@ struct trust_cache cache_from_tree(const char *path, uint32_t version); int tcinfo(int argc, char **argv); int tccreate(int argc, char **argv); int tcappend(int argc, char **argv); +int tcremove(int argc, char **argv); int ent_cmp(const void * vp1, const void * vp2); int hash_cmp(const void * vp1, const void * vp2); +void print_header(struct trust_cache cache); +void print_hash(uint8_t cdhash[CS_CDHASH_LEN], bool newline); +void print_entry(struct trust_cache_entry1 entry); +void print_entries(struct trust_cache cache); + #endif -- cgit v1.2.3-56-ge451