]> git.saurik.com Git - apple/security.git/blob - codesign_wrapper/codesign.c
Security-57337.50.23.tar.gz
[apple/security.git] / codesign_wrapper / codesign.c
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 */
23 enum {
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 */
37 typedef struct __BlobIndex {
38 uint32_t type; /* type of entry */
39 uint32_t offset; /* offset of entry */
40 } CS_BlobIndex;
41
42 typedef 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 */
54 typedef 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
77 static 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;
149 out:
150 if (code_signature) CFRelease(code_signature);
151 return NULL;
152 }
153
154 #if 1
155 static FILE *
156 open_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);
172 out:
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
180 static FILE *
181 open_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");
194 out:
195 return NULL;
196 }
197 #endif
198
199 CFMutableDictionaryRef 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 */
223 out:
224 if (!signature_found)
225 fprintf(stderr, "No LC_CODE_SIGNATURE segment found\n");
226 return result;
227 }
228
229 CFArrayRef 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;
266 out:
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 }