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