1/* 2 * Copyright (c) 2008,2010 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 38/* 39 * Magic numbers used by Code Signing 40 */ 41enum { 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 */ 47 48 CSSLOT_CODEDIRECTORY = 0, /* slot index for CodeDirectory */ 49}; 50 51 52/* 53 * Structure of an embedded-signature SuperBlob 54 */ 55typedef struct __BlobIndex { 56 uint32_t type; /* type of entry */ 57 uint32_t offset; /* offset of entry */ 58} CS_BlobIndex; 59 60typedef 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 */ 66} CS_SuperBlob; 67 68 69/* 70 * C form of a CodeDirectory. 71 */ 72typedef 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 */ 88} CS_CodeDirectory; 89 90 91 //assert(page < ntohl(cd->nCodeSlots)); 92 //return base + ntohl(cd->hashOffset) + page * 20; 93 94#if 0 95static void debug_data(uint8_t *data, size_t length) 96{ 97 uint32_t i, j; 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 : '?'); 103 } 104 fprintf(stderr, "\n"); 105 } 106} 107 108static void write_data(const char *path, uint8_t *data, size_t length) 109{ 110 int fd = open(path, O_RDWR|O_TRUNC|O_CREAT, 0644); 111 require(fd>0, out); 112 write(fd, data, length); 113 close(fd); 114out: 115 return; 116} 117#endif 118 119static void fprint_digest(FILE *file, unsigned char *digest, size_t length) { 120 size_t ix; 121 for (ix = 0; ix < length; ++ix) { 122 fprintf(file, "%02x", digest[ix]); 123 } 124} 125 126static CFMutableDictionaryRef lc_code_sig(uint8_t *lc_code_signature, size_t lc_code_signature_len) 127{ 128 CFMutableDictionaryRef code_signature = 129 CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 130 &kCFTypeDictionaryKeyCallBacks, 131 &kCFTypeDictionaryValueCallBacks); 132 require(code_signature, out); 133 134 CS_SuperBlob *sb = (CS_SuperBlob*)lc_code_signature; 135 require(ntohl(sb->magic) == CSMAGIC_EMBEDDED_SIGNATURE, out); 136 uint32_t count; 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); 145 switch(magic) { 146 case 0xfade0c01: //write_data("requirements", bytes, length); 147 break; 148 case 0xfade0c02: //write_data("codedir", bytes, length); 149 { 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); 154 CFRelease(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"); 159 160 uint32_t hash_offset = ntohl(cd->hashOffset); 161 uint32_t entitlement_slot = 5; 162 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); 167 CFRelease(message); 168 } else 169 fprintf(stderr, "no entitlements slot yet\n"); 170 } 171 break; 172 case 0xfade0b01: //write_data("signed", lc_code_signature, bytes-lc_code_signature); 173 if (length != 8) { 174 CFDataRef message = CFDataCreate(kCFAllocatorDefault, bytes+8, length-8); 175 require(message, out); 176 CFDictionarySetValue(code_signature, CFSTR("SignedData"), message); 177 CFRelease(message); 178 } 179 break; 180 case 0xfade7171: 181 { 182 unsigned char digest[CC_SHA1_DIGEST_LENGTH]; 183 CCDigest(kCCDigestSHA1, bytes, length, digest); 184 185 CFDataRef message = CFDataCreate(kCFAllocatorDefault, digest, sizeof(digest)); 186 require(message, out); 187 CFDictionarySetValue(code_signature, CFSTR("EntitlementsHash"), message); 188 CFRelease(message); 189 message = CFDataCreate(kCFAllocatorDefault, bytes+8, length-8); 190 require(message, out); 191 CFDictionarySetValue(code_signature, CFSTR("Entitlements"), message); 192 CFRelease(message); 193 break; 194 } 195 default: 196 fprintf(stderr, "Skipping block with magic: 0x%x\n", magic); 197 break; 198 } 199 } 200 return code_signature; 201out: 202 if (code_signature) CFRelease(code_signature); 203 return NULL; 204} 205 206static FILE * 207open_bundle(const char * path, const char * mode) 208{ 209 char full_path[1024] = {}; 210 CFStringRef path_cfstring = NULL; 211 CFURLRef path_url = NULL; 212 CFBundleRef bundle = NULL; 213 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); 221 require(exec, out); 222 require(CFURLGetFileSystemRepresentation(exec, true, (uint8_t*)full_path, sizeof(full_path)), out); 223out: 224 if (path_cfstring) CFRelease(path_cfstring); 225 if (path_url) CFRelease(path_url); 226 if (bundle) CFRelease(bundle); 227 228 return fopen(full_path, "r"); 229} 230 231static CFMutableDictionaryRef load_code_signature(FILE *binary, size_t slice_offset) 232{ 233 bool signature_found = false; 234 CFMutableDictionaryRef result = NULL; 235 struct load_command lc; 236 do { 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); 248 free(data); 249 break; 250 } 251 require_noerr(fseek(binary, lc.cmdsize-sizeof(lc), SEEK_CUR), out); 252 } while(lc.cmd || lc.cmdsize); /* count lc */ 253out: 254 if (!signature_found) 255 fprintf(stderr, "No LC_CODE_SIGNATURE segment found\n"); 256 return result; 257} 258 259static CFArrayRef load_code_signatures(const char *path) 260{ 261 bool fully_parsed_binary = false; 262 CFMutableDictionaryRef result = NULL; 263 CFMutableArrayRef results = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 264 265 FILE *binary = open_bundle(path, "r"); 266 if (!binary) binary = fopen(path, "r"); 267 require(binary, out); 268 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); 278 CFRelease(type); 279 CFArrayAppendValue(results, result); 280 } 281 else 282 { 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); 301 CFRelease(type); 302 CFArrayAppendValue(results, result); 303 CFRelease(result); 304 } 305 } 306 fully_parsed_binary = true; 307out: 308 if (!fully_parsed_binary) { 309 if (results) { 310 CFRelease(results); 311 results = NULL; 312 } 313 } 314 if (binary) 315 fclose(binary); 316 return results; 317} 318 319 320extern int codesign_util(int argc, char * const *argv) 321{ 322 int result = 1, verbose = 0; 323 char ch; 324 325 while ((ch = getopt(argc, argv, "v")) != -1) 326 { 327 switch (ch) 328 { 329 case 'v': 330 verbose++; 331 break; 332 default: 333 return 2; /* Trigger usage message. */ 334 } 335 } 336 337 argc -= optind; 338 argv += optind; 339 340 if (argc != 1) 341 return 2; /* Trigger usage message. */ 342 343 CFArrayRef sigs = load_code_signatures(argv[0]); 344 require(sigs, out); 345 346 if (verbose >= 2) 347 CFShow(sigs); 348 349 CFIndex i, count = CFArrayGetCount(sigs); 350 351 for (i = 0; i < count; i++) { 352 CFDictionaryRef signature = CFArrayGetValueAtIndex(sigs, i); 353 354 CFDataRef code_dir = CFDictionaryGetValue(signature, CFSTR("CodeDirectory")); 355 const CS_CodeDirectory *cd = (CS_CodeDirectory *)CFDataGetBytePtr(code_dir); 356 357 CFDataRef signed_data = CFDictionaryGetValue(signature, CFSTR("SignedData")); 358 359 CFDataRef entitlements = CFDictionaryGetValue(signature, CFSTR("Entitlements")); 360 CFDataRef entitlements_cd_hash = CFDictionaryGetValue(signature, CFSTR("EntitlementsCDHash")); 361 CFDataRef entitlements_hash = CFDictionaryGetValue(signature, CFSTR("EntitlementsHash")); 362 363 CFStringRef arch = CFDictionaryGetValue(signature, CFSTR("ARCH")); 364 365 CFShow(arch); 366 367 SecPolicyRef policy = SecPolicyCreateiPhoneApplicationSigning(); 368 369 if (signed_data) { 370 if (SecCMSVerify(signed_data, code_dir, policy, NULL, NULL)) { 371 fprintf(stderr, "Failed to verify signature\n"); 372 result = -1; 373 } else 374 fprintf(stderr, "Signature ok\n"); 375 376 } else 377 fprintf(stderr, "Ad-hoc signed binary\n"); 378 379 if (entitlements_cd_hash) { 380 if (entitlements_hash && entitlements_cd_hash && CFEqual(entitlements_hash, entitlements_cd_hash)) 381 fprintf(stderr, "Entitlements ok\n"); 382 else 383 fprintf(stderr, "Entitlements modified\n"); 384 } 385 386 if (verbose >= 2) { 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)); 393 394 uint32_t ix; 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; 400 } 401 } 402 403 if (verbose >= 1) { 404 if (entitlements) 405 fprintf(stderr, "Entitlements\n%.*s", (int)CFDataGetLength(entitlements)-8, CFDataGetBytePtr(entitlements)+8); 406 } 407 408 if (verbose >= 2) { 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"); 413 } 414 } 415 } 416 417 CFRelease(sigs); 418 419 return result; 420out: 421 return -1; 422} 423 424#endif // TARGET_OS_EMBEDDED 425