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 */
42enum {
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 */
56typedef struct __BlobIndex {
57	uint32_t type;					/* type of entry */
58	uint32_t offset;				/* offset of entry */
59} CS_BlobIndex;
60
61typedef 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 */
73typedef 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
96static 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
109static 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);
115out:
116    return;
117}
118#endif
119
120static 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
127static 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;
202out:
203    if (code_signature) CFRelease(code_signature);
204    return NULL;
205}
206
207static FILE *
208open_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);
225out:
226    CFReleaseSafe(path_cfstring);
227    CFReleaseSafe(path_url);
228    CFReleaseSafe(bundle);
229    CFReleaseSafe(exec);
230
231    return fopen(full_path, "r");
232}
233
234static 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 */
256out:
257    if (!signature_found)
258        fprintf(stderr, "No LC_CODE_SIGNATURE segment found\n");
259    return result;
260}
261
262static 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;
310out:
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
323extern 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    }
419
420    CFReleaseSafe(sigs);
421
422    return result;
423out:
424    return -1;
425}
426
427#endif // TARGET_OS_EMBEDDED
428