1 2#include "codesign.h" 3 4#include <stdint.h> 5#include <stdio.h> 6#include <arpa/inet.h> 7#include <assert.h> 8#include <ctype.h> 9#include <stdlib.h> 10#include <unistd.h> 11#include <fcntl.h> 12 13#include <mach-o/loader.h> 14#include <mach-o/fat.h> 15#include <CommonCrypto/CommonDigest.h> 16 17#define DEBUG_ASSERT_PRODUCTION_CODE 0 18#include <AssertMacros.h> 19 20/* 21 * Magic numbers used by Code Signing 22 */ 23enum { 24 CSMAGIC_REQUIREMENT = 0xfade0c00, /* single Requirement blob */ 25 CSMAGIC_REQUIREMENTS = 0xfade0c01, /* Requirements vector (internal requirements) */ 26 CSMAGIC_CODEDIRECTORY = 0xfade0c02, /* CodeDirectory blob */ 27 CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, /* embedded form of signature data */ 28 CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, /* multi-arch collection of embedded signatures */ 29 30 CSSLOT_CODEDIRECTORY = 0, /* slot index for CodeDirectory */ 31}; 32 33 34/* 35 * Structure of an embedded-signature SuperBlob 36 */ 37typedef struct __BlobIndex { 38 uint32_t type; /* type of entry */ 39 uint32_t offset; /* offset of entry */ 40} CS_BlobIndex; 41 42typedef struct __SuperBlob { 43 uint32_t magic; /* magic number */ 44 uint32_t length; /* total length of SuperBlob */ 45 uint32_t count; /* number of index entries following */ 46 CS_BlobIndex index[]; /* (count) entries */ 47 /* followed by Blobs in no particular order as indicated by offsets in index */ 48} CS_SuperBlob; 49 50 51/* 52 * C form of a CodeDirectory. 53 */ 54typedef struct __CodeDirectory { 55 uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */ 56 uint32_t length; /* total length of CodeDirectory blob */ 57 uint32_t version; /* compatibility version */ 58 uint32_t flags; /* setup and mode flags */ 59 uint32_t hashOffset; /* offset of hash slot element at index zero */ 60 uint32_t identOffset; /* offset of identifier string */ 61 uint32_t nSpecialSlots; /* number of special hash slots */ 62 uint32_t nCodeSlots; /* number of ordinary (code) hash slots */ 63 uint32_t codeLimit; /* limit to main image signature range */ 64 uint8_t hashSize; /* size of each hash in bytes */ 65 uint8_t hashType; /* type of hash (cdHashType* constants) */ 66 uint8_t spare1; /* unused (must be zero) */ 67 uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */ 68 uint32_t spare2; /* unused (must be zero) */ 69 /* followed by dynamic content as located by offset fields above */ 70} CS_CodeDirectory; 71 72 73 //assert(page < ntohl(cd->nCodeSlots)); 74 //return base + ntohl(cd->hashOffset) + page * 20; 75 76 77static CFMutableDictionaryRef lc_code_sig(uint8_t *lc_code_signature, size_t lc_code_signature_len) 78{ 79 CFDataRef codedir = NULL; 80 CFDataRef message = NULL; 81 CFMutableDictionaryRef code_signature = 82 CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 83 &kCFTypeDictionaryKeyCallBacks, 84 &kCFTypeDictionaryValueCallBacks); 85 require(code_signature, out); 86 87 CS_SuperBlob *sb = (CS_SuperBlob*)lc_code_signature; 88 require(ntohl(sb->magic) == CSMAGIC_EMBEDDED_SIGNATURE, out); 89 uint32_t count; 90 for (count = 0; count < ntohl(sb->count); count++) { 91 //uint32_t type = ntohl(sb->index[count].type); 92 uint32_t offset = ntohl(sb->index[count].offset); 93 uint8_t *bytes = lc_code_signature + offset; 94 //fprintf(stderr, "blob[%d]: (type: 0x%.08x, offset: %p)\n", count, type, (void*)offset); 95 uint32_t magic = ntohl(*(uint32_t*)bytes); 96 uint32_t length = ntohl(*(uint32_t*)(bytes+4)); 97 //fprintf(stderr, " magic: 0x%.08x length: %d\n", magic, length); 98 switch(magic) { 99 case 0xfade0c01: //write_data("requirements", bytes, length); 100 break; 101 case 0xfade0c02: //write_data("codedir", bytes, length); 102 codedir = CFDataCreate(kCFAllocatorDefault, bytes, length); 103 require(codedir, out); 104 CFDictionarySetValue(code_signature, CFSTR("CodeDirectory"), codedir); 105 CFRelease(codedir); 106 uint8_t *cursor = bytes; 107 require_string(htonl(*(uint32_t*)(cursor+8)) >= 0x20001, out, "incompatible version"); 108 require_string(htonl(*(uint32_t*)(cursor+8)) <= 0x2F000, out, "incompatible version"); 109 uint32_t hash_offset = htonl(*(uint32_t*)(cursor+16)); 110 require_string(htonl(*(uint32_t*)(cursor+24)) >= 5, out, "no entitlements slot yet"); 111 require_string(*(cursor+36) == 20, out, "unexpected hash size"); 112 require_string(*(cursor+37) == 1, out, "unexpected hash type"); 113 message = CFDataCreate(kCFAllocatorDefault, cursor+hash_offset-5*20, 20); 114 require(message, out); 115 CFDictionarySetValue(code_signature, CFSTR("EntitlementsCDHash"), message); 116 CFRelease(message); 117 break; 118 case 0xfade0b01: //write_data("signed", lc_code_signature, bytes-lc_code_signature); 119 if (length == 8) { 120 fprintf(stderr, "Ad-hoc signed binary\n"); 121 goto out; 122 } else { 123 message = CFDataCreate(kCFAllocatorDefault, bytes+8, length-8); 124 require(message, out); 125 CFDictionarySetValue(code_signature, CFSTR("SignedData"), message); 126 CFRelease(message); 127 } 128 break; 129 case 0xfade7171: 130 { 131 unsigned char digest[CC_SHA1_DIGEST_LENGTH]; 132 CCDigest(kCCDigestSHA1, bytes, length, digest); 133 message = CFDataCreate(kCFAllocatorDefault, digest, sizeof(digest)); 134 require(message, out); 135 CFDictionarySetValue(code_signature, CFSTR("EntitlementsHash"), message); 136 CFRelease(message); 137 message = CFDataCreate(kCFAllocatorDefault, bytes+8, length-8); 138 require(message, out); 139 CFDictionarySetValue(code_signature, CFSTR("Entitlements"), message); 140 CFRelease(message); 141 break; 142 } 143 default: 144 fprintf(stderr, "Skipping block with magic: 0x%x\n", magic); 145 break; 146 } 147 } 148 return code_signature; 149out: 150 if (code_signature) CFRelease(code_signature); 151 return NULL; 152} 153 154#if 1 155static FILE * 156open_bundle(const char * path, const char * mode) 157{ 158 char full_path[1024] = {}; 159 CFStringRef path_cfstring = NULL; 160 CFURLRef path_url = NULL; 161 CFBundleRef bundle = NULL; 162 163 path_cfstring = CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, path); 164 require(path_cfstring, out); 165 path_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_cfstring, kCFURLPOSIXPathStyle, true); 166 require(path_url, out); 167 bundle = CFBundleCreate(kCFAllocatorDefault, path_url); 168 require(bundle, out); 169 CFURLRef exec = CFBundleCopyExecutableURL(bundle); 170 require(exec, out); 171 require(CFURLGetFileSystemRepresentation(exec, true, (uint8_t*)full_path, sizeof(full_path)), out); 172out: 173 if (path_cfstring) CFRelease(path_cfstring); 174 if (path_url) CFRelease(path_url); 175 if (bundle) CFRelease(bundle); 176 177 return fopen(full_path, "r"); 178} 179#else 180static FILE * 181open_bundle(const char *path, const char *mode) 182{ 183 char full_path[1024] = {}; 184 const char *slash; 185 char *dot; 186 slash = rindex(path, '/'); 187 if (!slash) slash = path; 188 require(strlcpy(full_path, path, sizeof(full_path)) < sizeof(full_path), out); 189 require(strlcat(full_path, "/", sizeof(full_path)), out); 190 require(strlcat(full_path, slash, sizeof(full_path)), out); 191 require(dot = rindex(full_path, '.'), out); 192 *dot = '\0'; 193 return fopen(full_path, "r"); 194out: 195 return NULL; 196} 197#endif 198 199CFMutableDictionaryRef load_code_signature(FILE *binary, size_t slice_offset) 200{ 201 bool signature_found = false; 202 CFMutableDictionaryRef result = NULL; 203 union { 204 struct load_command lc; 205 struct linkedit_data_command le_lc; 206 } cmd; 207 do { 208 require(1 == fread(&cmd.lc, sizeof(cmd.lc), 1, binary), out); 209 if (cmd.lc.cmd == LC_CODE_SIGNATURE) { 210 require(1 == fread((uint8_t *)&cmd.lc + sizeof(cmd.lc), sizeof(cmd.le_lc) - sizeof(cmd.lc), 1, binary), out); 211 require_noerr(fseek(binary, slice_offset+cmd.le_lc.dataoff, SEEK_SET), out); 212 size_t length = cmd.le_lc.datasize; 213 uint8_t *data = malloc(length); 214 require(length && data, out); 215 require(1 == fread(data, length, 1, binary), out); 216 signature_found = true; 217 result = lc_code_sig(data, length); 218 free(data); 219 break; 220 } 221 require_noerr(fseek(binary, cmd.lc.cmdsize-sizeof(cmd.lc), SEEK_CUR), out); 222 } while(cmd.lc.cmd || cmd.lc.cmdsize); /* count lc */ 223out: 224 if (!signature_found) 225 fprintf(stderr, "No LC_CODE_SIGNATURE segment found\n"); 226 return result; 227} 228 229CFArrayRef load_code_signatures(const char *path) 230{ 231 bool fully_parsed_binary = false; 232 CFMutableDictionaryRef result = NULL; 233 CFMutableArrayRef results = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 234 235 FILE *binary = open_bundle(path, "r"); 236 require(binary, out); 237 238 struct mach_header header; 239 require(1 == fread(&header, sizeof(header), 1, binary), out); 240 if (header.magic == 0xfeedface) { 241 result = load_code_signature(binary, 0 /*non fat*/); 242 require(result, out); 243 CFArrayAppendValue(results, result); 244 } 245 else 246 { 247 struct fat_header fat; 248 require(!fseek(binary, 0L, SEEK_SET), out); 249 require(1 == fread(&fat, sizeof(fat), 1, binary), out); 250 require(htonl(fat.magic) == FAT_MAGIC, out); 251 uint32_t slice, slices = htonl(fat.nfat_arch); 252 struct fat_arch *archs = calloc(slices, sizeof(struct fat_arch)); 253 require(slices == fread(archs, sizeof(struct fat_arch), slices, binary), out); 254 for (slice = 0; slice < slices; slice++) { 255 uint32_t slice_offset = htonl(archs[slice].offset); 256 require(!fseek(binary, slice_offset, SEEK_SET), out); 257 require(1 == fread(&header, sizeof(header), 1, binary), out); 258 require(header.magic == 0xfeedface, out); 259 result = load_code_signature(binary, slice_offset); 260 require(result, out); 261 CFArrayAppendValue(results, result); 262 CFRelease(result); 263 } 264 } 265 fully_parsed_binary = true; 266out: 267 if (!fully_parsed_binary) { 268 if (results) { 269 CFRelease(results); 270 results = NULL; 271 } 272 } 273 if (binary) 274 fclose(binary); 275 return results; 276} 277