2 * Copyright (c) 2008,2010,2013-2014 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>
37 #include <utilities/SecCFRelease.h>
40 * Magic numbers used by Code Signing
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 */
49 CSSLOT_CODEDIRECTORY
= 0, /* slot index for CodeDirectory */
54 * Structure of an embedded-signature SuperBlob
56 typedef struct __BlobIndex
{
57 uint32_t type
; /* type of entry */
58 uint32_t offset
; /* offset of entry */
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 */
71 * C form of a CodeDirectory.
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 */
92 //assert(page < ntohl(cd->nCodeSlots));
93 //return base + ntohl(cd->hashOffset) + page * 20;
96 static void debug_data(uint8_t *data
, size_t length
)
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
: '?');
105 fprintf(stderr
, "\n");
109 static void write_data(const char *path
, uint8_t *data
, size_t length
)
111 int fd
= open(path
, O_RDWR
|O_TRUNC
|O_CREAT
, 0644);
113 write(fd
, data
, length
);
120 static void fprint_digest(FILE *file
, unsigned char *digest
, size_t length
) {
122 for (ix
= 0; ix
< length
; ++ix
) {
123 fprintf(file
, "%02x", digest
[ix
]);
127 static CFMutableDictionaryRef
lc_code_sig(uint8_t *lc_code_signature
, size_t lc_code_signature_len
)
129 CFMutableDictionaryRef code_signature
=
130 CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
131 &kCFTypeDictionaryKeyCallBacks
,
132 &kCFTypeDictionaryValueCallBacks
);
133 require(code_signature
, out
);
135 CS_SuperBlob
*sb
= (CS_SuperBlob
*)lc_code_signature
;
136 require(ntohl(sb
->magic
) == CSMAGIC_EMBEDDED_SIGNATURE
, out
);
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);
147 case 0xfade0c01: //write_data("requirements", bytes, length);
149 case 0xfade0c02: //write_data("codedir", bytes, length);
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
);
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");
161 uint32_t hash_offset
= ntohl(cd
->hashOffset
);
162 uint32_t entitlement_slot
= 5;
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
);
170 fprintf(stderr
, "no entitlements slot yet\n");
173 case 0xfade0b01: //write_data("signed", lc_code_signature, bytes-lc_code_signature);
175 CFDataRef message
= CFDataCreate(kCFAllocatorDefault
, bytes
+8, length
-8);
176 require(message
, out
);
177 CFDictionarySetValue(code_signature
, CFSTR("SignedData"), message
);
183 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
184 CCDigest(kCCDigestSHA1
, bytes
, length
, digest
);
186 CFDataRef message
= CFDataCreate(kCFAllocatorDefault
, digest
, sizeof(digest
));
187 require(message
, out
);
188 CFDictionarySetValue(code_signature
, CFSTR("EntitlementsHash"), message
);
190 message
= CFDataCreate(kCFAllocatorDefault
, bytes
+8, length
-8);
191 require(message
, out
);
192 CFDictionarySetValue(code_signature
, CFSTR("Entitlements"), message
);
197 fprintf(stderr
, "Skipping block with magic: 0x%x\n", magic
);
201 return code_signature
;
203 if (code_signature
) CFRelease(code_signature
);
208 open_bundle(const char * path
, const char * mode
)
210 char full_path
[1024] = {};
211 CFStringRef path_cfstring
= NULL
;
212 CFURLRef path_url
= NULL
;
213 CFBundleRef bundle
= NULL
;
214 CFURLRef exec
= NULL
;
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
);
224 require(CFURLGetFileSystemRepresentation(exec
, true, (uint8_t*)full_path
, sizeof(full_path
)), out
);
226 CFReleaseSafe(path_cfstring
);
227 CFReleaseSafe(path_url
);
228 CFReleaseSafe(bundle
);
231 return fopen(full_path
, "r");
234 static CFMutableDictionaryRef
load_code_signature(FILE *binary
, size_t slice_offset
)
236 bool signature_found
= false;
237 CFMutableDictionaryRef result
= NULL
;
238 struct load_command lc
;
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
);
254 require_noerr(fseek(binary
, lc
.cmdsize
-sizeof(lc
), SEEK_CUR
), out
);
255 } while(lc
.cmd
|| lc
.cmdsize
); /* count lc */
257 if (!signature_found
)
258 fprintf(stderr
, "No LC_CODE_SIGNATURE segment found\n");
262 static CF_RETURNS_RETAINED CFArrayRef
load_code_signatures(const char *path
)
264 bool fully_parsed_binary
= false;
265 CFMutableDictionaryRef result
= NULL
;
266 CFMutableArrayRef results
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
268 FILE *binary
= open_bundle(path
, "r");
269 if (!binary
) binary
= fopen(path
, "r");
270 require(binary
, out
);
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
);
282 CFArrayAppendValue(results
, result
);
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
);
305 CFArrayAppendValue(results
, result
);
309 fully_parsed_binary
= true;
311 if (!fully_parsed_binary
) {
323 extern int codesign_util(int argc
, char * const *argv
)
325 int result
= 1, verbose
= 0;
328 while ((ch
= getopt(argc
, argv
, "v")) != -1)
336 return SHOW_USAGE_MESSAGE
;
344 return SHOW_USAGE_MESSAGE
;
346 CFArrayRef sigs
= load_code_signatures(argv
[0]);
352 CFIndex i
, count
= CFArrayGetCount(sigs
);
354 for (i
= 0; i
< count
; i
++) {
355 CFDictionaryRef signature
= CFArrayGetValueAtIndex(sigs
, i
);
357 CFDataRef code_dir
= CFDictionaryGetValue(signature
, CFSTR("CodeDirectory"));
358 const CS_CodeDirectory
*cd
= (CS_CodeDirectory
*)CFDataGetBytePtr(code_dir
);
360 CFDataRef signed_data
= CFDictionaryGetValue(signature
, CFSTR("SignedData"));
362 CFDataRef entitlements
= CFDictionaryGetValue(signature
, CFSTR("Entitlements"));
363 CFDataRef entitlements_cd_hash
= CFDictionaryGetValue(signature
, CFSTR("EntitlementsCDHash"));
364 CFDataRef entitlements_hash
= CFDictionaryGetValue(signature
, CFSTR("EntitlementsHash"));
366 CFStringRef arch
= CFDictionaryGetValue(signature
, CFSTR("ARCH"));
370 SecPolicyRef policy
= SecPolicyCreateiPhoneApplicationSigning();
373 if (SecCMSVerify(signed_data
, code_dir
, policy
, NULL
, NULL
)) {
374 fprintf(stderr
, "Failed to verify signature\n");
377 fprintf(stderr
, "Signature ok\n");
380 fprintf(stderr
, "Ad-hoc signed binary\n");
382 if (entitlements_cd_hash
) {
383 if (entitlements_hash
&& entitlements_cd_hash
&& CFEqual(entitlements_hash
, entitlements_cd_hash
))
384 fprintf(stderr
, "Entitlements ok\n");
386 fprintf(stderr
, "Entitlements modified\n");
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
));
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
;
408 fprintf(stderr
, "Entitlements\n%.*s", (int)CFDataGetLength(entitlements
)-8, CFDataGetBytePtr(entitlements
)+8);
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");
418 CFReleaseNull(policy
);
428 #endif // TARGET_OS_EMBEDDED