2 * Copyright (c) 2008,2010 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <TargetConditionals.h>
25 #if TARGET_OS_EMBEDDED
27 #include "SecurityCommands.h"
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>
39 * Magic numbers used by Code Signing
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 */
48 CSSLOT_CODEDIRECTORY
= 0, /* slot index for CodeDirectory */
53 * Structure of an embedded-signature SuperBlob
55 typedef struct __BlobIndex
{
56 uint32_t type
; /* type of entry */
57 uint32_t offset
; /* offset of entry */
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 */
70 * C form of a CodeDirectory.
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 */
91 //assert(page < ntohl(cd->nCodeSlots));
92 //return base + ntohl(cd->hashOffset) + page * 20;
95 static void debug_data(uint8_t *data
, size_t length
)
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
: '?');
104 fprintf(stderr
, "\n");
108 static void write_data(const char *path
, uint8_t *data
, size_t length
)
110 int fd
= open(path
, O_RDWR
|O_TRUNC
|O_CREAT
, 0644);
112 write(fd
, data
, length
);
119 static void fprint_digest(FILE *file
, unsigned char *digest
, size_t length
) {
121 for (ix
= 0; ix
< length
; ++ix
) {
122 fprintf(file
, "%02x", digest
[ix
]);
126 static CFMutableDictionaryRef
lc_code_sig(uint8_t *lc_code_signature
, size_t lc_code_signature_len
)
128 CFMutableDictionaryRef code_signature
=
129 CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
130 &kCFTypeDictionaryKeyCallBacks
,
131 &kCFTypeDictionaryValueCallBacks
);
132 require(code_signature
, out
);
134 CS_SuperBlob
*sb
= (CS_SuperBlob
*)lc_code_signature
;
135 require(ntohl(sb
->magic
) == CSMAGIC_EMBEDDED_SIGNATURE
, out
);
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);
146 case 0xfade0c01: //write_data("requirements", bytes, length);
148 case 0xfade0c02: //write_data("codedir", bytes, length);
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
);
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");
160 uint32_t hash_offset
= ntohl(cd
->hashOffset
);
161 uint32_t entitlement_slot
= 5;
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
);
169 fprintf(stderr
, "no entitlements slot yet\n");
172 case 0xfade0b01: //write_data("signed", lc_code_signature, bytes-lc_code_signature);
174 CFDataRef message
= CFDataCreate(kCFAllocatorDefault
, bytes
+8, length
-8);
175 require(message
, out
);
176 CFDictionarySetValue(code_signature
, CFSTR("SignedData"), message
);
182 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
183 CCDigest(kCCDigestSHA1
, bytes
, length
, digest
);
185 CFDataRef message
= CFDataCreate(kCFAllocatorDefault
, digest
, sizeof(digest
));
186 require(message
, out
);
187 CFDictionarySetValue(code_signature
, CFSTR("EntitlementsHash"), message
);
189 message
= CFDataCreate(kCFAllocatorDefault
, bytes
+8, length
-8);
190 require(message
, out
);
191 CFDictionarySetValue(code_signature
, CFSTR("Entitlements"), message
);
196 fprintf(stderr
, "Skipping block with magic: 0x%x\n", magic
);
200 return code_signature
;
202 if (code_signature
) CFRelease(code_signature
);
207 open_bundle(const char * path
, const char * mode
)
209 char full_path
[1024] = {};
210 CFStringRef path_cfstring
= NULL
;
211 CFURLRef path_url
= NULL
;
212 CFBundleRef bundle
= NULL
;
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
);
222 require(CFURLGetFileSystemRepresentation(exec
, true, (uint8_t*)full_path
, sizeof(full_path
)), out
);
224 if (path_cfstring
) CFRelease(path_cfstring
);
225 if (path_url
) CFRelease(path_url
);
226 if (bundle
) CFRelease(bundle
);
228 return fopen(full_path
, "r");
231 static CFMutableDictionaryRef
load_code_signature(FILE *binary
, size_t slice_offset
)
233 bool signature_found
= false;
234 CFMutableDictionaryRef result
= NULL
;
235 struct load_command lc
;
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
);
251 require_noerr(fseek(binary
, lc
.cmdsize
-sizeof(lc
), SEEK_CUR
), out
);
252 } while(lc
.cmd
|| lc
.cmdsize
); /* count lc */
254 if (!signature_found
)
255 fprintf(stderr
, "No LC_CODE_SIGNATURE segment found\n");
259 static CFArrayRef
load_code_signatures(const char *path
)
261 bool fully_parsed_binary
= false;
262 CFMutableDictionaryRef result
= NULL
;
263 CFMutableArrayRef results
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
265 FILE *binary
= open_bundle(path
, "r");
266 if (!binary
) binary
= fopen(path
, "r");
267 require(binary
, out
);
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
);
279 CFArrayAppendValue(results
, result
);
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
);
302 CFArrayAppendValue(results
, result
);
306 fully_parsed_binary
= true;
308 if (!fully_parsed_binary
) {
320 extern int codesign_util(int argc
, char * const *argv
)
322 int result
= 1, verbose
= 0;
325 while ((ch
= getopt(argc
, argv
, "v")) != -1)
333 return 2; /* Trigger usage message. */
341 return 2; /* Trigger usage message. */
343 CFArrayRef sigs
= load_code_signatures(argv
[0]);
349 CFIndex i
, count
= CFArrayGetCount(sigs
);
351 for (i
= 0; i
< count
; i
++) {
352 CFDictionaryRef signature
= CFArrayGetValueAtIndex(sigs
, i
);
354 CFDataRef code_dir
= CFDictionaryGetValue(signature
, CFSTR("CodeDirectory"));
355 const CS_CodeDirectory
*cd
= (CS_CodeDirectory
*)CFDataGetBytePtr(code_dir
);
357 CFDataRef signed_data
= CFDictionaryGetValue(signature
, CFSTR("SignedData"));
359 CFDataRef entitlements
= CFDictionaryGetValue(signature
, CFSTR("Entitlements"));
360 CFDataRef entitlements_cd_hash
= CFDictionaryGetValue(signature
, CFSTR("EntitlementsCDHash"));
361 CFDataRef entitlements_hash
= CFDictionaryGetValue(signature
, CFSTR("EntitlementsHash"));
363 CFStringRef arch
= CFDictionaryGetValue(signature
, CFSTR("ARCH"));
367 SecPolicyRef policy
= SecPolicyCreateiPhoneApplicationSigning();
370 if (SecCMSVerify(signed_data
, code_dir
, policy
, NULL
, NULL
)) {
371 fprintf(stderr
, "Failed to verify signature\n");
374 fprintf(stderr
, "Signature ok\n");
377 fprintf(stderr
, "Ad-hoc signed binary\n");
379 if (entitlements_cd_hash
) {
380 if (entitlements_hash
&& entitlements_cd_hash
&& CFEqual(entitlements_hash
, entitlements_cd_hash
))
381 fprintf(stderr
, "Entitlements ok\n");
383 fprintf(stderr
, "Entitlements modified\n");
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
));
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
;
405 fprintf(stderr
, "Entitlements\n%.*s", (int)CFDataGetLength(entitlements
)-8, CFDataGetBytePtr(entitlements
)+8);
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");
424 #endif // TARGET_OS_EMBEDDED