1
2#include "codesign.h"
3
4#include <stdint.h>
5#include <stdio.h>
6#include <arpa/inet.h>
7#include <assert.h>
8#include <ctype.h>
9#include <stdlib.h>
10#include <unistd.h>
11#include <fcntl.h>
12
13#include <mach-o/loader.h>
14#include <mach-o/fat.h>
15#include <CommonCrypto/CommonDigest.h>
16
17#define DEBUG_ASSERT_PRODUCTION_CODE 0
18#include <AssertMacros.h>
19
20/*
21 * Magic numbers used by Code Signing
22 */
23enum {
24	CSMAGIC_REQUIREMENT	= 0xfade0c00,		/* single Requirement blob */
25	CSMAGIC_REQUIREMENTS = 0xfade0c01,		/* Requirements vector (internal requirements) */
26	CSMAGIC_CODEDIRECTORY = 0xfade0c02,		/* CodeDirectory blob */
27	CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, /* embedded form of signature data */
28	CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, /* multi-arch collection of embedded signatures */
29
30	CSSLOT_CODEDIRECTORY = 0,				/* slot index for CodeDirectory */
31};
32
33
34/*
35 * Structure of an embedded-signature SuperBlob
36 */
37typedef struct __BlobIndex {
38	uint32_t type;					/* type of entry */
39	uint32_t offset;				/* offset of entry */
40} CS_BlobIndex;
41
42typedef struct __SuperBlob {
43	uint32_t magic;					/* magic number */
44	uint32_t length;				/* total length of SuperBlob */
45	uint32_t count;					/* number of index entries following */
46	CS_BlobIndex index[];			/* (count) entries */
47	/* followed by Blobs in no particular order as indicated by offsets in index */
48} CS_SuperBlob;
49
50
51/*
52 * C form of a CodeDirectory.
53 */
54typedef struct __CodeDirectory {
55	uint32_t magic;					/* magic number (CSMAGIC_CODEDIRECTORY) */
56	uint32_t length;				/* total length of CodeDirectory blob */
57	uint32_t version;				/* compatibility version */
58	uint32_t flags;					/* setup and mode flags */
59	uint32_t hashOffset;			/* offset of hash slot element at index zero */
60	uint32_t identOffset;			/* offset of identifier string */
61	uint32_t nSpecialSlots;			/* number of special hash slots */
62	uint32_t nCodeSlots;			/* number of ordinary (code) hash slots */
63	uint32_t codeLimit;				/* limit to main image signature range */
64	uint8_t hashSize;				/* size of each hash in bytes */
65	uint8_t hashType;				/* type of hash (cdHashType* constants) */
66	uint8_t spare1;					/* unused (must be zero) */
67	uint8_t	pageSize;				/* log2(page size in bytes); 0 => infinite */
68	uint32_t spare2;				/* unused (must be zero) */
69	/* followed by dynamic content as located by offset fields above */
70} CS_CodeDirectory;
71
72
73	//assert(page < ntohl(cd->nCodeSlots));
74	//return base + ntohl(cd->hashOffset) + page * 20;
75
76
77static CFMutableDictionaryRef lc_code_sig(uint8_t *lc_code_signature, size_t lc_code_signature_len)
78{
79    CFDataRef codedir = NULL;
80    CFDataRef message = NULL;
81    CFMutableDictionaryRef code_signature =
82        CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
83                &kCFTypeDictionaryKeyCallBacks,
84                &kCFTypeDictionaryValueCallBacks);
85    require(code_signature, out);
86
87    CS_SuperBlob *sb = (CS_SuperBlob*)lc_code_signature;
88    require(ntohl(sb->magic) == CSMAGIC_EMBEDDED_SIGNATURE, out);
89    uint32_t count;
90    for (count = 0; count < ntohl(sb->count); count++) {
91        //uint32_t type = ntohl(sb->index[count].type);
92        uint32_t offset = ntohl(sb->index[count].offset);
93        uint8_t *bytes = lc_code_signature + offset;
94        //fprintf(stderr, "blob[%d]: (type: 0x%.08x, offset: %p)\n", count, type, (void*)offset);
95        uint32_t magic = ntohl(*(uint32_t*)bytes);
96        uint32_t length = ntohl(*(uint32_t*)(bytes+4));
97        //fprintf(stderr, "    magic: 0x%.08x length: %d\n", magic, length);
98        switch(magic) {
99            case 0xfade0c01: //write_data("requirements", bytes, length);
100                break;
101            case 0xfade0c02: //write_data("codedir", bytes, length);
102                codedir = CFDataCreate(kCFAllocatorDefault, bytes, length);
103                require(codedir, out);
104                CFDictionarySetValue(code_signature, CFSTR("CodeDirectory"), codedir);
105                CFRelease(codedir);
106                uint8_t *cursor = bytes;
107                require_string(htonl(*(uint32_t*)(cursor+8)) >= 0x20001, out, "incompatible version");
108                require_string(htonl(*(uint32_t*)(cursor+8)) <= 0x2F000, out, "incompatible version");
109                uint32_t hash_offset = htonl(*(uint32_t*)(cursor+16));
110                require_string(htonl(*(uint32_t*)(cursor+24)) >= 5, out, "no entitlements slot yet");
111                require_string(*(cursor+36) == 20, out, "unexpected hash size");
112                require_string(*(cursor+37) == 1, out, "unexpected hash type");
113                message = CFDataCreate(kCFAllocatorDefault, cursor+hash_offset-5*20, 20);
114                require(message, out);
115                CFDictionarySetValue(code_signature, CFSTR("EntitlementsCDHash"), message);
116                CFRelease(message);
117                break;
118            case 0xfade0b01:  //write_data("signed", lc_code_signature, bytes-lc_code_signature);
119                if (length == 8) {
120                    fprintf(stderr, "Ad-hoc signed binary\n");
121                    goto out;
122                } else {
123                    message = CFDataCreate(kCFAllocatorDefault, bytes+8, length-8);
124                    require(message, out);
125                    CFDictionarySetValue(code_signature, CFSTR("SignedData"), message);
126                    CFRelease(message);
127               }
128                break;
129            case 0xfade7171:
130            {
131                unsigned char digest[CC_SHA1_DIGEST_LENGTH];
132                CCDigest(kCCDigestSHA1, bytes, length, digest);
133                message = CFDataCreate(kCFAllocatorDefault, digest, sizeof(digest));
134                require(message, out);
135                CFDictionarySetValue(code_signature, CFSTR("EntitlementsHash"), message);
136                CFRelease(message);
137                message = CFDataCreate(kCFAllocatorDefault, bytes+8, length-8);
138                require(message, out);
139                CFDictionarySetValue(code_signature, CFSTR("Entitlements"), message);
140                CFRelease(message);
141                break;
142            }
143            default:
144                fprintf(stderr, "Skipping block with magic: 0x%x\n", magic);
145                break;
146        }
147    }
148    return code_signature;
149out:
150    if (code_signature) CFRelease(code_signature);
151    return NULL;
152}
153
154#if 1
155static FILE *
156open_bundle(const char * path, const char * mode)
157{
158    char full_path[1024] = {};
159    CFStringRef path_cfstring = NULL;
160    CFURLRef path_url = NULL;
161    CFBundleRef bundle = NULL;
162
163    path_cfstring = CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, path);
164    require(path_cfstring, out);
165    path_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_cfstring, kCFURLPOSIXPathStyle, true);
166    require(path_url, out);
167	bundle =  CFBundleCreate(kCFAllocatorDefault, path_url);
168    require(bundle, out);
169    CFURLRef exec = CFBundleCopyExecutableURL(bundle);
170    require(exec, out);
171    require(CFURLGetFileSystemRepresentation(exec, true, (uint8_t*)full_path, sizeof(full_path)), out);
172out:
173    if (path_cfstring) CFRelease(path_cfstring);
174    if (path_url) CFRelease(path_url);
175    if (bundle) CFRelease(bundle);
176
177    return fopen(full_path, "r");
178}
179#else
180static FILE *
181open_bundle(const char *path, const char *mode)
182{
183    char full_path[1024] = {};
184    const char *slash;
185    char *dot;
186    slash = rindex(path, '/');
187    if (!slash) slash = path;
188    require(strlcpy(full_path, path, sizeof(full_path)) < sizeof(full_path), out);
189    require(strlcat(full_path, "/", sizeof(full_path)), out);
190    require(strlcat(full_path, slash, sizeof(full_path)), out);
191    require(dot = rindex(full_path, '.'), out);
192    *dot = '\0';
193    return fopen(full_path, "r");
194out:
195    return NULL;
196}
197#endif
198
199CFMutableDictionaryRef load_code_signature(FILE *binary, size_t slice_offset)
200{
201    bool signature_found = false;
202    CFMutableDictionaryRef result = NULL;
203    union {
204        struct load_command lc;
205        struct linkedit_data_command le_lc;
206    } cmd;
207    do {
208        require(1 == fread(&cmd.lc, sizeof(cmd.lc), 1, binary), out);
209        if (cmd.lc.cmd == LC_CODE_SIGNATURE) {
210            require(1 == fread((uint8_t *)&cmd.lc + sizeof(cmd.lc), sizeof(cmd.le_lc) - sizeof(cmd.lc), 1, binary), out);
211            require_noerr(fseek(binary, slice_offset+cmd.le_lc.dataoff, SEEK_SET), out);
212            size_t length = cmd.le_lc.datasize;
213            uint8_t *data = malloc(length);
214            require(length && data, out);
215            require(1 == fread(data, length, 1, binary), out);
216            signature_found = true;
217            result = lc_code_sig(data, length);
218            free(data);
219            break;
220        }
221        require_noerr(fseek(binary, cmd.lc.cmdsize-sizeof(cmd.lc), SEEK_CUR), out);
222    } while(cmd.lc.cmd || cmd.lc.cmdsize); /* count lc */
223out:
224    if (!signature_found)
225        fprintf(stderr, "No LC_CODE_SIGNATURE segment found\n");
226    return result;
227}
228
229CFArrayRef load_code_signatures(const char *path)
230{
231    bool fully_parsed_binary = false;
232    CFMutableDictionaryRef result = NULL;
233    CFMutableArrayRef results = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
234
235    FILE *binary = open_bundle(path, "r");
236    require(binary, out);
237
238    struct mach_header header;
239    require(1 == fread(&header, sizeof(header), 1, binary), out);
240    if (header.magic == 0xfeedface) {
241        result = load_code_signature(binary, 0 /*non fat*/);
242        require(result, out);
243        CFArrayAppendValue(results, result);
244    }
245    else
246    {
247        struct fat_header fat;
248        require(!fseek(binary, 0L, SEEK_SET), out);
249        require(1 == fread(&fat, sizeof(fat), 1, binary), out);
250        require(htonl(fat.magic) == FAT_MAGIC, out);
251        uint32_t slice, slices = htonl(fat.nfat_arch);
252        struct fat_arch *archs = calloc(slices, sizeof(struct fat_arch));
253        require(slices == fread(archs, sizeof(struct fat_arch), slices, binary), out);
254        for (slice = 0; slice < slices; slice++) {
255            uint32_t slice_offset = htonl(archs[slice].offset);
256            require(!fseek(binary, slice_offset, SEEK_SET), out);
257            require(1 == fread(&header, sizeof(header), 1, binary), out);
258            require(header.magic == 0xfeedface, out);
259            result = load_code_signature(binary, slice_offset);
260            require(result, out);
261            CFArrayAppendValue(results, result);
262            CFRelease(result);
263        }
264    }
265    fully_parsed_binary = true;
266out:
267    if (!fully_parsed_binary) {
268        if (results) {
269            CFRelease(results);
270            results = NULL;
271        }
272    }
273    if (binary)
274        fclose(binary);
275    return results;
276}
277