Also fix some conflicting types to hopefully prevent possible overflows.
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
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
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.
#include <errno.h>
#include <getopt.h>
#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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') {
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 = {
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);
}
}
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)
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);
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);
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)
*/
#include <getopt.h>
-#include <limits.h>
#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#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;
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);
argc -= optind;
argv += optind;
- if (argc == 0) {
+ if (argc == 0)
return -1;
- }
cache = opentrustcache(argv[0]);
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
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)
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);
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)
--- /dev/null
+/*-
+ * 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 <errno.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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;
+}
.Sh SYNOPSIS
.Nm
.Cm append
+.Op Fl f Ar flags
.Op Fl u Ar uuid | 0
.Ar infile
.Ar
.Op Fl h
.Op Fl e Ar entrynum
.Ar file
+.Nm
+.Cm remove
+.Op Fl k
+.Ar file
+.Ar hash ...
.Sh DESCRIPTION
The
.Nm
.Bl -tag -width create
.It Xo
.Cm append
+.Op Fl f Ar flags
.Op Fl u Ar uuid | 0
.Ar infile
.Ar
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
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
{
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);
}
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]);
#ifndef _TRUSTCACHE_H_
#define _TRUSTCACHE_H_
+#include <stdbool.h>
#include <sys/types.h>
#if __APPLE__
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