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