]> git.saurik.com Git - apple/security.git/blob - sec/Security/Tool/codesign.c
Security-55471.14.8.tar.gz
[apple/security.git] / sec / Security / Tool / codesign.c
1 /*
2 * Copyright (c) 2008,2010 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <TargetConditionals.h>
25 #if TARGET_OS_EMBEDDED
26
27 #include "SecurityCommands.h"
28
29 #include <AssertMacros.h>
30 #include <mach-o/loader.h>
31 #include <mach-o/fat.h>
32 #include <CoreFoundation/CoreFoundation.h>
33 #include <Security/SecCMS.h>
34 #include <Security/SecPolicyPriv.h>
35 #include <CommonCrypto/CommonDigest.h>
36 #include <CommonCrypto/CommonDigestSPI.h>
37
38 /*
39 * Magic numbers used by Code Signing
40 */
41 enum {
42 CSMAGIC_REQUIREMENT = 0xfade0c00, /* single Requirement blob */
43 CSMAGIC_REQUIREMENTS = 0xfade0c01, /* Requirements vector (internal requirements) */
44 CSMAGIC_CODEDIRECTORY = 0xfade0c02, /* CodeDirectory blob */
45 CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, /* embedded form of signature data */
46 CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, /* multi-arch collection of embedded signatures */
47
48 CSSLOT_CODEDIRECTORY = 0, /* slot index for CodeDirectory */
49 };
50
51
52 /*
53 * Structure of an embedded-signature SuperBlob
54 */
55 typedef struct __BlobIndex {
56 uint32_t type; /* type of entry */
57 uint32_t offset; /* offset of entry */
58 } CS_BlobIndex;
59
60 typedef struct __SuperBlob {
61 uint32_t magic; /* magic number */
62 uint32_t length; /* total length of SuperBlob */
63 uint32_t count; /* number of index entries following */
64 CS_BlobIndex index[]; /* (count) entries */
65 /* followed by Blobs in no particular order as indicated by offsets in index */
66 } CS_SuperBlob;
67
68
69 /*
70 * C form of a CodeDirectory.
71 */
72 typedef struct __CodeDirectory {
73 uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */
74 uint32_t length; /* total length of CodeDirectory blob */
75 uint32_t version; /* compatibility version */
76 uint32_t flags; /* setup and mode flags */
77 uint32_t hashOffset; /* offset of hash slot element at index zero */
78 uint32_t identOffset; /* offset of identifier string */
79 uint32_t nSpecialSlots; /* number of special hash slots */
80 uint32_t nCodeSlots; /* number of ordinary (code) hash slots */
81 uint32_t codeLimit; /* limit to main image signature range */
82 uint8_t hashSize; /* size of each hash in bytes */
83 uint8_t hashType; /* type of hash (cdHashType* constants) */
84 uint8_t spare1; /* unused (must be zero) */
85 uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */
86 uint32_t spare2; /* unused (must be zero) */
87 /* followed by dynamic content as located by offset fields above */
88 } CS_CodeDirectory;
89
90
91 //assert(page < ntohl(cd->nCodeSlots));
92 //return base + ntohl(cd->hashOffset) + page * 20;
93
94 #if 0
95 static void debug_data(uint8_t *data, size_t length)
96 {
97 uint32_t i, j;
98 for (i = 0; i < length; i+=16) {
99 fprintf(stderr, "%p ", (void*)(data+i));
100 for (j = 0; (j < 16) && (j+i < length); j++) {
101 uint8_t byte = *(uint8_t*)(data+i+j);
102 fprintf(stderr, "%.02x %c|", byte, isprint(byte) ? byte : '?');
103 }
104 fprintf(stderr, "\n");
105 }
106 }
107
108 static void write_data(const char *path, uint8_t *data, size_t length)
109 {
110 int fd = open(path, O_RDWR|O_TRUNC|O_CREAT, 0644);
111 require(fd>0, out);
112 write(fd, data, length);
113 close(fd);
114 out:
115 return;
116 }
117 #endif
118
119 static void fprint_digest(FILE *file, unsigned char *digest, size_t length) {
120 size_t ix;
121 for (ix = 0; ix < length; ++ix) {
122 fprintf(file, "%02x", digest[ix]);
123 }
124 }
125
126 static CFMutableDictionaryRef lc_code_sig(uint8_t *lc_code_signature, size_t lc_code_signature_len)
127 {
128 CFMutableDictionaryRef code_signature =
129 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
130 &kCFTypeDictionaryKeyCallBacks,
131 &kCFTypeDictionaryValueCallBacks);
132 require(code_signature, out);
133
134 CS_SuperBlob *sb = (CS_SuperBlob*)lc_code_signature;
135 require(ntohl(sb->magic) == CSMAGIC_EMBEDDED_SIGNATURE, out);
136 uint32_t count;
137 for (count = 0; count < ntohl(sb->count); count++) {
138 //uint32_t type = ntohl(sb->index[count].type);
139 uint32_t offset = ntohl(sb->index[count].offset);
140 uint8_t *bytes = lc_code_signature + offset;
141 //fprintf(stderr, "blob[%d]: (type: 0x%.08x, offset: %p)\n", count, type, (void*)offset);
142 uint32_t magic = ntohl(*(uint32_t*)bytes);
143 uint32_t length = ntohl(*(uint32_t*)(bytes+4));
144 //fprintf(stderr, " magic: 0x%.08x length: %d\n", magic, length);
145 switch(magic) {
146 case 0xfade0c01: //write_data("requirements", bytes, length);
147 break;
148 case 0xfade0c02: //write_data("codedir", bytes, length);
149 {
150 const CS_CodeDirectory *cd = (const CS_CodeDirectory *)bytes;
151 CFDataRef codedir = CFDataCreate(kCFAllocatorDefault, bytes, length);
152 require(codedir, out);
153 CFDictionarySetValue(code_signature, CFSTR("CodeDirectory"), codedir);
154 CFRelease(codedir);
155 require_string(ntohl(cd->version) >= 0x20001, out, "incompatible version");
156 require_string(ntohl(cd->version) <= 0x2F000, out, "incompatible version");
157 require_string(cd->hashSize == 20, out, "unexpected hash size");
158 require_string(cd->hashType == 1, out, "unexpected hash type");
159
160 uint32_t hash_offset = ntohl(cd->hashOffset);
161 uint32_t entitlement_slot = 5;
162
163 if (ntohl(cd->nSpecialSlots) >= entitlement_slot) {
164 CFDataRef message = CFDataCreate(kCFAllocatorDefault, bytes+hash_offset-entitlement_slot*cd->hashSize, cd->hashSize);
165 require(message, out);
166 CFDictionarySetValue(code_signature, CFSTR("EntitlementsCDHash"), message);
167 CFRelease(message);
168 } else
169 fprintf(stderr, "no entitlements slot yet\n");
170 }
171 break;
172 case 0xfade0b01: //write_data("signed", lc_code_signature, bytes-lc_code_signature);
173 if (length != 8) {
174 CFDataRef message = CFDataCreate(kCFAllocatorDefault, bytes+8, length-8);
175 require(message, out);
176 CFDictionarySetValue(code_signature, CFSTR("SignedData"), message);
177 CFRelease(message);
178 }
179 break;
180 case 0xfade7171:
181 {
182 unsigned char digest[CC_SHA1_DIGEST_LENGTH];
183 CCDigest(kCCDigestSHA1, bytes, length, digest);
184
185 CFDataRef message = CFDataCreate(kCFAllocatorDefault, digest, sizeof(digest));
186 require(message, out);
187 CFDictionarySetValue(code_signature, CFSTR("EntitlementsHash"), message);
188 CFRelease(message);
189 message = CFDataCreate(kCFAllocatorDefault, bytes+8, length-8);
190 require(message, out);
191 CFDictionarySetValue(code_signature, CFSTR("Entitlements"), message);
192 CFRelease(message);
193 break;
194 }
195 default:
196 fprintf(stderr, "Skipping block with magic: 0x%x\n", magic);
197 break;
198 }
199 }
200 return code_signature;
201 out:
202 if (code_signature) CFRelease(code_signature);
203 return NULL;
204 }
205
206 static FILE *
207 open_bundle(const char * path, const char * mode)
208 {
209 char full_path[1024] = {};
210 CFStringRef path_cfstring = NULL;
211 CFURLRef path_url = NULL;
212 CFBundleRef bundle = NULL;
213
214 path_cfstring = CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, path);
215 require_quiet(path_cfstring, out);
216 path_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_cfstring, kCFURLPOSIXPathStyle, true);
217 require_quiet(path_url, out);
218 bundle = CFBundleCreate(kCFAllocatorDefault, path_url);
219 require_quiet(bundle, out);
220 CFURLRef exec = CFBundleCopyExecutableURL(bundle);
221 require(exec, out);
222 require(CFURLGetFileSystemRepresentation(exec, true, (uint8_t*)full_path, sizeof(full_path)), out);
223 out:
224 if (path_cfstring) CFRelease(path_cfstring);
225 if (path_url) CFRelease(path_url);
226 if (bundle) CFRelease(bundle);
227
228 return fopen(full_path, "r");
229 }
230
231 static CFMutableDictionaryRef load_code_signature(FILE *binary, size_t slice_offset)
232 {
233 bool signature_found = false;
234 CFMutableDictionaryRef result = NULL;
235 struct load_command lc;
236 do {
237 require(1 == fread(&lc, sizeof(lc), 1, binary), out);
238 if (lc.cmd == LC_CODE_SIGNATURE) {
239 struct { uint32_t offset; uint32_t size; } sig;
240 require(1 == fread(&sig, sizeof(sig), 1, binary), out);
241 require_noerr(fseek(binary, slice_offset+sig.offset, SEEK_SET), out);
242 size_t length = sig.size;
243 uint8_t *data = malloc(length);
244 require(length && data, out);
245 require(1 == fread(data, length, 1, binary), out);
246 signature_found = true;
247 result = lc_code_sig(data, length);
248 free(data);
249 break;
250 }
251 require_noerr(fseek(binary, lc.cmdsize-sizeof(lc), SEEK_CUR), out);
252 } while(lc.cmd || lc.cmdsize); /* count lc */
253 out:
254 if (!signature_found)
255 fprintf(stderr, "No LC_CODE_SIGNATURE segment found\n");
256 return result;
257 }
258
259 static CFArrayRef load_code_signatures(const char *path)
260 {
261 bool fully_parsed_binary = false;
262 CFMutableDictionaryRef result = NULL;
263 CFMutableArrayRef results = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
264
265 FILE *binary = open_bundle(path, "r");
266 if (!binary) binary = fopen(path, "r");
267 require(binary, out);
268
269 struct mach_header header;
270 require(1 == fread(&header, sizeof(header), 1, binary), out);
271 if ((header.magic == MH_MAGIC) || (header.magic == MH_MAGIC_64)) {
272 if (header.magic == MH_MAGIC_64)
273 fseek(binary, sizeof(struct mach_header_64) - sizeof(struct mach_header), SEEK_CUR);
274 result = load_code_signature(binary, 0 /*non fat*/);
275 require(result, out);
276 CFStringRef type = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("CPU type: (%d,%d)"), header.cputype, header.cpusubtype);
277 CFDictionarySetValue(result, CFSTR("ARCH"), type);
278 CFRelease(type);
279 CFArrayAppendValue(results, result);
280 }
281 else
282 {
283 struct fat_header fat;
284 require(!fseek(binary, 0L, SEEK_SET), out);
285 require(1 == fread(&fat, sizeof(fat), 1, binary), out);
286 require(ntohl(fat.magic) == FAT_MAGIC, out);
287 uint32_t slice, slices = ntohl(fat.nfat_arch);
288 struct fat_arch *archs = calloc(slices, sizeof(struct fat_arch));
289 require(slices == fread(archs, sizeof(struct fat_arch), slices, binary), out);
290 for (slice = 0; slice < slices; slice++) {
291 uint32_t slice_offset = ntohl(archs[slice].offset);
292 require(!fseek(binary, slice_offset, SEEK_SET), out);
293 require(1 == fread(&header, sizeof(header), 1, binary), out);
294 require((header.magic == MH_MAGIC) || (header.magic == MH_MAGIC_64), out);
295 if (header.magic == MH_MAGIC_64)
296 fseek(binary, sizeof(struct mach_header_64) - sizeof(struct mach_header), SEEK_CUR);
297 result = load_code_signature(binary, slice_offset);
298 require(result, out);
299 CFStringRef type = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("CPU type: (%d,%d)"), header.cputype, header.cpusubtype);
300 CFDictionarySetValue(result, CFSTR("ARCH"), type);
301 CFRelease(type);
302 CFArrayAppendValue(results, result);
303 CFRelease(result);
304 }
305 }
306 fully_parsed_binary = true;
307 out:
308 if (!fully_parsed_binary) {
309 if (results) {
310 CFRelease(results);
311 results = NULL;
312 }
313 }
314 if (binary)
315 fclose(binary);
316 return results;
317 }
318
319
320 extern int codesign_util(int argc, char * const *argv)
321 {
322 int result = 1, verbose = 0;
323 char ch;
324
325 while ((ch = getopt(argc, argv, "v")) != -1)
326 {
327 switch (ch)
328 {
329 case 'v':
330 verbose++;
331 break;
332 default:
333 return 2; /* Trigger usage message. */
334 }
335 }
336
337 argc -= optind;
338 argv += optind;
339
340 if (argc != 1)
341 return 2; /* Trigger usage message. */
342
343 CFArrayRef sigs = load_code_signatures(argv[0]);
344 require(sigs, out);
345
346 if (verbose >= 2)
347 CFShow(sigs);
348
349 CFIndex i, count = CFArrayGetCount(sigs);
350
351 for (i = 0; i < count; i++) {
352 CFDictionaryRef signature = CFArrayGetValueAtIndex(sigs, i);
353
354 CFDataRef code_dir = CFDictionaryGetValue(signature, CFSTR("CodeDirectory"));
355 const CS_CodeDirectory *cd = (CS_CodeDirectory *)CFDataGetBytePtr(code_dir);
356
357 CFDataRef signed_data = CFDictionaryGetValue(signature, CFSTR("SignedData"));
358
359 CFDataRef entitlements = CFDictionaryGetValue(signature, CFSTR("Entitlements"));
360 CFDataRef entitlements_cd_hash = CFDictionaryGetValue(signature, CFSTR("EntitlementsCDHash"));
361 CFDataRef entitlements_hash = CFDictionaryGetValue(signature, CFSTR("EntitlementsHash"));
362
363 CFStringRef arch = CFDictionaryGetValue(signature, CFSTR("ARCH"));
364
365 CFShow(arch);
366
367 SecPolicyRef policy = SecPolicyCreateiPhoneApplicationSigning();
368
369 if (signed_data) {
370 if (SecCMSVerify(signed_data, code_dir, policy, NULL, NULL)) {
371 fprintf(stderr, "Failed to verify signature\n");
372 result = -1;
373 } else
374 fprintf(stderr, "Signature ok\n");
375
376 } else
377 fprintf(stderr, "Ad-hoc signed binary\n");
378
379 if (entitlements_cd_hash) {
380 if (entitlements_hash && entitlements_cd_hash && CFEqual(entitlements_hash, entitlements_cd_hash))
381 fprintf(stderr, "Entitlements ok\n");
382 else
383 fprintf(stderr, "Entitlements modified\n");
384 }
385
386 if (verbose >= 2) {
387 fprintf(stderr, "magic: 0x%x length: %u(%lu)\n", ntohl(cd->magic), ntohl(cd->length), CFDataGetLength(code_dir));
388 fprintf(stderr, "code directory version/flags: 0x%x/0x%x special/code hash slots: %u/%u\n"
389 "codelimit: %u hash size/type: %u/%u hash/ident offset: %u/%u\n",
390 ntohl(cd->version), ntohl(cd->flags), ntohl(cd->nSpecialSlots), ntohl(cd->nCodeSlots),
391 ntohl(cd->codeLimit), cd->hashSize, cd->hashType, ntohl(cd->hashOffset), ntohl(cd->identOffset));
392 fprintf(stderr, "ident: '%s'\n", CFDataGetBytePtr(code_dir) + ntohl(cd->identOffset));
393
394 uint32_t ix;
395 uint8_t *hashes = (uint8_t *)CFDataGetBytePtr(code_dir) + ntohl(cd->hashOffset);
396 for (ix = 0; ix < ntohl(cd->nSpecialSlots); ++ix) {
397 fprint_digest(stderr, hashes, cd->hashSize);
398 fprintf(stderr, "\n");
399 hashes += cd->hashSize;
400 }
401 }
402
403 if (verbose >= 1) {
404 if (entitlements)
405 fprintf(stderr, "Entitlements\n%.*s", (int)CFDataGetLength(entitlements)-8, CFDataGetBytePtr(entitlements)+8);
406 }
407
408 if (verbose >= 2) {
409 if (entitlements_hash) {
410 fprintf(stderr, "digest: ");
411 fprint_digest(stderr, (uint8_t *)CFDataGetBytePtr(entitlements_hash), CC_SHA1_DIGEST_LENGTH);
412 fprintf(stderr, "\n");
413 }
414 }
415 }
416
417 CFRelease(sigs);
418
419 return result;
420 out:
421 return -1;
422 }
423
424 #endif // TARGET_OS_EMBEDDED