Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 427 lines (336 sloc) 12.263 kb
bddbcd1b »
2010-10-23 initial commit
1 //
2 // validatereceipt.m
3 //
4 // Created by Ruotger Skupin on 23.10.10.
fcd6d2c5 »
2010-11-03 cleaned up, added anlumo to copyright notice and README
5 // Copyright 2010 Matthew Stevens, Ruotger Skupin, Apple, Dave Carlton, Fraser Hess, anlumo. All rights reserved.
bddbcd1b »
2010-10-23 initial commit
6 //
7
eb16b8f5 »
2010-12-24 added BSD-style license; tweaked build settings; corrected pointer co…
8 /*
9 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
10
11 Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
12
13 Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15
16 Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
20 BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
21 SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
24 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
bddbcd1b »
2010-10-23 initial commit
27 #import "validatereceipt.h"
28
fcd6d2c5 »
2010-11-03 cleaned up, added anlumo to copyright notice and README
29 // link with Foundation.framework, IOKit.framework, Security.framework and libCrypto (via -lcrypto in Other Linker Flags)
bddbcd1b »
2010-10-23 initial commit
30
31 #import <IOKit/IOKitLib.h>
32 #import <Foundation/Foundation.h>
33
9fef3207 »
2010-11-03 working certificate check
34 #import <Security/Security.h>
35
bddbcd1b »
2010-10-23 initial commit
36 #include <openssl/pkcs7.h>
37 #include <openssl/objects.h>
38 #include <openssl/sha.h>
9fef3207 »
2010-11-03 working certificate check
39 #include <openssl/x509.h>
40 #include <openssl/err.h>
bddbcd1b »
2010-10-23 initial commit
41
f22472bd »
2010-11-09 cosmetic changes
42 //#define USE_SAMPLE_RECEIPT // also defined in the debug build settings
9d599b33 »
2010-10-30 added proto for copy_mac_address(); added warnings for when it is com…
43
44 #ifdef USE_SAMPLE_RECEIPT
45 #warning ************************************
46 #warning ******* USES SAMPLE RECEIPT! *******
47 #warning ************************************
48 #endif
49
bddbcd1b »
2010-10-23 initial commit
50
eb16b8f5 »
2010-12-24 added BSD-style license; tweaked build settings; corrected pointer co…
51 #ifndef YES_I_HAVE_READ_THE_WARNING_AND_I_ACCEPT_THE_RISK
52
53 #warning --- DON'T USE THIS CODE AS IS! IF EVERYONE USES THE SAME CODE
54 #warning --- IT IS PRETTY EASY TO BUILD AN AUTOMATIC CRACKING TOOL
55 #warning --- FOR APPS USING THIS CODE!
56 #warning --- BY USING THIS CODE YOU ACCEPT TAKING THE RESPONSIBILITY FOR
57 #warning --- ANY DAMAGE!
58 #warning ---
59 #warning --- YOU HAVE BEEN WARNED!
60
61 // if you want to take that risk, add "-DYES_I_HAVE_READ_THE_WARNING_AND_I_ACCEPT_THE_RISK" in the build settings at "Other C Flags"
62
63 #endif // YES_I_HAVE_READ_THE_WARNING_AND_I_ACCEPT_THE_RISK
ec42914e »
2011-01-05 Moving VRCFRelease to a better place
64
10510755 »
2011-01-05 Adding CFRelease macro
65 #define VRCFRelease(object) if(object) CFRelease(object)
eb16b8f5 »
2010-12-24 added BSD-style license; tweaked build settings; corrected pointer co…
66
bddbcd1b »
2010-10-23 initial commit
67 NSString *kReceiptBundleIdentifer = @"BundleIdentifier";
68 NSString *kReceiptBundleIdentiferData = @"BundleIdentifierData";
69 NSString *kReceiptVersion = @"Version";
70 NSString *kReceiptOpaqueValue = @"OpaqueValue";
71 NSString *kReceiptHash = @"Hash";
72
9fef3207 »
2010-11-03 working certificate check
73
f22472bd »
2010-11-09 cosmetic changes
74 NSData * appleRootCert(void)
9fef3207 »
2010-11-03 working certificate check
75 {
76 OSStatus status;
77
78 SecKeychainRef keychain = nil;
79 status = SecKeychainOpen("/System/Library/Keychains/SystemRootCertificates.keychain", &keychain);
10510755 »
2011-01-05 Adding CFRelease macro
80 if(status){
81 VRCFRelease(keychain);
9fef3207 »
2010-11-03 working certificate check
82 return nil;
83 }
84
85 CFArrayRef searchList = CFArrayCreate(kCFAllocatorDefault, (const void**)&keychain, 1, &kCFTypeArrayCallBacks);
86
10510755 »
2011-01-05 Adding CFRelease macro
87 VRCFRelease(keychain);
9fef3207 »
2010-11-03 working certificate check
88
89 SecKeychainSearchRef searchRef = nil;
90 status = SecKeychainSearchCreateFromAttributes(searchList, kSecCertificateItemClass, NULL, &searchRef);
10510755 »
2011-01-05 Adding CFRelease macro
91 if(status){
9e9f7d65 »
2011-01-05 Spaces to tabs
92 VRCFRelease(searchRef);
93 VRCFRelease(searchList);
9fef3207 »
2010-11-03 working certificate check
94 return nil;
95 }
96
97 SecKeychainItemRef itemRef = nil;
98 NSData * resultData = nil;
99
100 while(SecKeychainSearchCopyNext(searchRef, &itemRef) == noErr && resultData == nil) {
101 // Grab the name of the certificate
102 SecKeychainAttributeList list;
103 SecKeychainAttribute attributes[1];
104
105 attributes[0].tag = kSecLabelItemAttr;
106
107 list.count = 1;
108 list.attr = attributes;
109
110 SecKeychainItemCopyContent(itemRef, nil, &list, nil, nil);
111 NSData *nameData = [NSData dataWithBytesNoCopy:attributes[0].data length:attributes[0].length freeWhenDone:NO];
112 NSString *name = [[NSString alloc] initWithData:nameData encoding:NSUTF8StringEncoding];
113
114 if([name isEqualToString:@"Apple Root CA"]) {
115 CSSM_DATA certData;
e615ec83 »
2011-01-05 Removing un-needed release and status variable.
116 SecCertificateGetData((SecCertificateRef)itemRef, &certData);
9fef3207 »
2010-11-03 working certificate check
117 resultData = [NSData dataWithBytes:certData.Data length:certData.Length];
118 SecKeychainItemFreeContent(&list, NULL);
9e9f7d65 »
2011-01-05 Spaces to tabs
119 VRCFRelease(itemRef);
9fef3207 »
2010-11-03 working certificate check
120 }
e615ec83 »
2011-01-05 Removing un-needed release and status variable.
121
9e9f7d65 »
2011-01-05 Spaces to tabs
122 [name release];
9fef3207 »
2010-11-03 working certificate check
123 }
e615ec83 »
2011-01-05 Removing un-needed release and status variable.
124
9e9f7d65 »
2011-01-05 Spaces to tabs
125 VRCFRelease(searchList);
126 VRCFRelease(searchRef);
9fef3207 »
2010-11-03 working certificate check
127
128 return resultData;
129 }
130
131
bddbcd1b »
2010-10-23 initial commit
132 NSDictionary * dictionaryWithAppStoreReceipt(NSString * path)
133 {
9fef3207 »
2010-11-03 working certificate check
134 NSData * rootCertData = appleRootCert();
135
9e9f7d65 »
2011-01-05 Spaces to tabs
136 enum ATTRIBUTES
bddbcd1b »
2010-10-23 initial commit
137 {
9e9f7d65 »
2011-01-05 Spaces to tabs
138 ATTR_START = 1,
139 BUNDLE_ID,
140 VERSION,
141 OPAQUE_VALUE,
142 HASH,
143 ATTR_END
144 };
145
9fef3207 »
2010-11-03 working certificate check
146 ERR_load_PKCS7_strings();
147 ERR_load_X509_strings();
148 OpenSSL_add_all_digests();
149
9e9f7d65 »
2011-01-05 Spaces to tabs
150 // Expected input is a PKCS7 container with signed data containing
151 // an ASN.1 SET of SEQUENCE structures. Each SEQUENCE contains
152 // two INTEGERS and an OCTET STRING.
153
bddbcd1b »
2010-10-23 initial commit
154 const char * receiptPath = [[path stringByStandardizingPath] fileSystemRepresentation];
9e9f7d65 »
2011-01-05 Spaces to tabs
155 FILE *fp = fopen(receiptPath, "rb");
156 if (fp == NULL)
157 return nil;
158
159 PKCS7 *p7 = d2i_PKCS7_fp(fp, NULL);
160 fclose(fp);
8d5c75d6 »
2010-11-26 Fixed crash when the receipt file is invalid.
161
162 // Check if the receipt file was invalid (otherwise we go crashing and burning)
163 if (p7 == NULL) {
164 return nil;
165 }
9e9f7d65 »
2011-01-05 Spaces to tabs
166
167 if (!PKCS7_type_is_signed(p7)) {
168 PKCS7_free(p7);
169 return nil;
170 }
171
172 if (!PKCS7_type_is_data(p7->d.sign->contents)) {
173 PKCS7_free(p7);
174 return nil;
175 }
176
91a8697c »
2010-11-18 Proper allocation checks and cleanup for bio and X509.
177 int verifyReturnValue = 0;
9fef3207 »
2010-11-03 working certificate check
178 X509_STORE *store = X509_STORE_new();
91a8697c »
2010-11-18 Proper allocation checks and cleanup for bio and X509.
179 if (store)
180 {
75f7b199 »
2011-01-05 Changes from yene's fork, this fixes my issue with warnings: issue 7
181 const unsigned char *data = (unsigned char *)(rootCertData.bytes);
eb16b8f5 »
2010-12-24 added BSD-style license; tweaked build settings; corrected pointer co…
182 X509 *appleCA = d2i_X509(NULL, &data, (long)rootCertData.length);
91a8697c »
2010-11-18 Proper allocation checks and cleanup for bio and X509.
183 if (appleCA)
184 {
185 BIO *payload = BIO_new(BIO_s_mem());
186 X509_STORE_add_cert(store, appleCA);
187
188 if (payload)
189 {
190 verifyReturnValue = PKCS7_verify(p7,NULL,store,NULL,payload,0);
191 BIO_free(payload);
192 }
9fef3207 »
2010-11-03 working certificate check
193
91a8697c »
2010-11-18 Proper allocation checks and cleanup for bio and X509.
194 // this code will come handy when the first real receipts arrive
9fef3207 »
2010-11-03 working certificate check
195 #if 0
91a8697c »
2010-11-18 Proper allocation checks and cleanup for bio and X509.
196 unsigned long err = ERR_get_error();
197 if(err)
198 printf("%lu: %s\n",err,ERR_error_string(err,NULL));
199 else {
200 STACK_OF(X509) *stack = PKCS7_get0_signers(p7, NULL, 0);
201 for(NSUInteger i = 0; i < sk_num(stack); i++) {
202 const X509 *signer = (X509*)sk_value(stack, i);
203 NSLog(@"name = %s", signer->name);
204 }
205 }
206 #endif
207
208 X509_free(appleCA);
9fef3207 »
2010-11-03 working certificate check
209 }
91a8697c »
2010-11-18 Proper allocation checks and cleanup for bio and X509.
210 X509_STORE_free(store);
9fef3207 »
2010-11-03 working certificate check
211 }
212 EVP_cleanup();
213
214 if (verifyReturnValue != 1)
fcd6d2c5 »
2010-11-03 cleaned up, added anlumo to copyright notice and README
215 {
9e9f7d65 »
2011-01-05 Spaces to tabs
216 PKCS7_free(p7);
9fef3207 »
2010-11-03 working certificate check
217 return nil;
fcd6d2c5 »
2010-11-03 cleaned up, added anlumo to copyright notice and README
218 }
9fef3207 »
2010-11-03 working certificate check
219
9e9f7d65 »
2011-01-05 Spaces to tabs
220 ASN1_OCTET_STRING *octets = p7->d.sign->contents->d.data;
75f7b199 »
2011-01-05 Changes from yene's fork, this fixes my issue with warnings: issue 7
221 const unsigned char *p = octets->data;
9e9f7d65 »
2011-01-05 Spaces to tabs
222 const unsigned char *end = p + octets->length;
223
224 int type = 0;
225 int xclass = 0;
226 long length = 0;
227
228 ASN1_get_object(&p, &length, &type, &xclass, end - p);
229 if (type != V_ASN1_SET) {
230 PKCS7_free(p7);
231 return nil;
232 }
233
234 NSMutableDictionary *info = [NSMutableDictionary dictionary];
235
236 while (p < end) {
237 ASN1_get_object(&p, &length, &type, &xclass, end - p);
238 if (type != V_ASN1_SEQUENCE)
239 break;
240
241 const unsigned char *seq_end = p + length;
242
243 int attr_type = 0;
244 int attr_version = 0;
245
246 // Attribute type
247 ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
248 if (type == V_ASN1_INTEGER && length == 1) {
249 attr_type = p[0];
250 }
251 p += length;
252
253 // Attribute version
254 ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
255 if (type == V_ASN1_INTEGER && length == 1) {
256 attr_version = p[0];
8cce890a »
2010-10-24 Added new static library target validateAS.
257 attr_version = attr_version;
9e9f7d65 »
2011-01-05 Spaces to tabs
258 }
259 p += length;
260
261 // Only parse attributes we're interested in
262 if (attr_type > ATTR_START && attr_type < ATTR_END) {
263 NSString *key;
264
265 ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
266 if (type == V_ASN1_OCTET_STRING) {
267
268 // Bytes
269 if (attr_type == BUNDLE_ID || attr_type == OPAQUE_VALUE || attr_type == HASH) {
270 NSData *data = [NSData dataWithBytes:p length:(NSUInteger)length];
271
272 switch (attr_type) {
273 case BUNDLE_ID:
274 // This is included for hash generation
275 key = kReceiptBundleIdentiferData;
276 break;
277 case OPAQUE_VALUE:
278 key = kReceiptOpaqueValue;
279 break;
280 case HASH:
281 key = kReceiptHash;
282 break;
283 }
284
285 [info setObject:data forKey:key];
286 }
287
288 // Strings
289 if (attr_type == BUNDLE_ID || attr_type == VERSION) {
290 int str_type = 0;
291 long str_length = 0;
75f7b199 »
2011-01-05 Changes from yene's fork, this fixes my issue with warnings: issue 7
292 const unsigned char *str_p = p;
9e9f7d65 »
2011-01-05 Spaces to tabs
293 ASN1_get_object(&str_p, &str_length, &str_type, &xclass, seq_end - str_p);
294 if (str_type == V_ASN1_UTF8STRING) {
295 NSString *string = [[[NSString alloc] initWithBytes:str_p
296 length:(NSUInteger)str_length
297 encoding:NSUTF8StringEncoding] autorelease];
bddbcd1b »
2010-10-23 initial commit
298
9e9f7d65 »
2011-01-05 Spaces to tabs
299 switch (attr_type) {
300 case BUNDLE_ID:
301 key = kReceiptBundleIdentifer;
302 break;
303 case VERSION:
304 key = kReceiptVersion;
305 break;
306 }
307
308 [info setObject:string forKey:key];
309 }
310 }
311 }
312 p += length;
313 }
314
315 // Skip any remaining fields in this SEQUENCE
316 while (p < seq_end) {
317 ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
318 p += length;
319 }
320 }
321
322 PKCS7_free(p7);
323
324 return info;
bddbcd1b »
2010-10-23 initial commit
325 }
326
327
328
329 // Returns a CFData object, containing the machine's GUID.
330 CFDataRef copy_mac_address(void)
331 {
9e9f7d65 »
2011-01-05 Spaces to tabs
332 kern_return_t kernResult;
333 mach_port_t master_port;
334 CFMutableDictionaryRef matchingDict;
335 io_iterator_t iterator;
336 io_object_t service;
337 CFDataRef macAddress = nil;
bddbcd1b »
2010-10-23 initial commit
338
9e9f7d65 »
2011-01-05 Spaces to tabs
339 kernResult = IOMasterPort(MACH_PORT_NULL, &master_port);
340 if (kernResult != KERN_SUCCESS) {
341 printf("IOMasterPort returned %d\n", kernResult);
342 return nil;
343 }
bddbcd1b »
2010-10-23 initial commit
344
9e9f7d65 »
2011-01-05 Spaces to tabs
345 matchingDict = IOBSDNameMatching(master_port, 0, "en0");
346 if(!matchingDict) {
347 printf("IOBSDNameMatching returned empty dictionary\n");
348 return nil;
349 }
bddbcd1b »
2010-10-23 initial commit
350
9e9f7d65 »
2011-01-05 Spaces to tabs
351 kernResult = IOServiceGetMatchingServices(master_port, matchingDict, &iterator);
352 if (kernResult != KERN_SUCCESS) {
353 printf("IOServiceGetMatchingServices returned %d\n", kernResult);
354 return nil;
355 }
bddbcd1b »
2010-10-23 initial commit
356
9e9f7d65 »
2011-01-05 Spaces to tabs
357 while((service = IOIteratorNext(iterator)) != 0)
358 {
359 io_object_t parentService;
bddbcd1b »
2010-10-23 initial commit
360
9e9f7d65 »
2011-01-05 Spaces to tabs
361 kernResult = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parentService);
362 if(kernResult == KERN_SUCCESS)
363 {
364 VRCFRelease(macAddress);
365 macAddress = IORegistryEntryCreateCFProperty(parentService, CFSTR("IOMACAddress"), kCFAllocatorDefault, 0);
366 IOObjectRelease(parentService);
367 }
368 else {
369 printf("IORegistryEntryGetParentEntry returned %d\n", kernResult);
370 }
bddbcd1b »
2010-10-23 initial commit
371
9e9f7d65 »
2011-01-05 Spaces to tabs
372 IOObjectRelease(service);
373 }
bddbcd1b »
2010-10-23 initial commit
374
9e9f7d65 »
2011-01-05 Spaces to tabs
375 return macAddress;
bddbcd1b »
2010-10-23 initial commit
376 }
377
378 BOOL validateReceiptAtPath(NSString * path)
379 {
380 NSDictionary * receipt = dictionaryWithAppStoreReceipt(path);
adf2ab54 »
2010-10-31 Validate bundle identifier and bundle version
381
bddbcd1b »
2010-10-23 initial commit
382 if (!receipt)
383 return NO;
384
2939033e »
2010-11-01 initializing pointers to nil
385 NSData * guidData = nil;
386 NSString *bundleVersion = nil;
387 NSString *bundleIdentifer = nil;
adf2ab54 »
2010-10-31 Validate bundle identifier and bundle version
388 #ifndef USE_SAMPLE_RECEIPT
389 guidData = (NSData*)copy_mac_address();
bddbcd1b »
2010-10-23 initial commit
390
391 if ([NSGarbageCollector defaultCollector])
392 [[NSGarbageCollector defaultCollector] enableCollectorForPointer:guidData];
393 else
394 [guidData autorelease];
395
396 if (!guidData)
397 return NO;
adf2ab54 »
2010-10-31 Validate bundle identifier and bundle version
398
399 bundleVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
400 bundleIdentifer = [[NSBundle mainBundle] bundleIdentifier];
bddbcd1b »
2010-10-23 initial commit
401
adf2ab54 »
2010-10-31 Validate bundle identifier and bundle version
402 #else
403 // Overwrite with example GUID for use with example receipt
404 unsigned char guid[] = { 0x00, 0x17, 0xf2, 0xc4, 0xbc, 0xc0 };
405 guidData = [NSData dataWithBytes:guid length:sizeof(guid)];
406 bundleVersion = @"1.0.2";
407 bundleIdentifer = @"com.example.SampleApp";
8cce890a »
2010-10-24 Added new static library target validateAS.
408 #endif
bddbcd1b »
2010-10-23 initial commit
409
410 NSMutableData *input = [NSMutableData data];
411 [input appendData:guidData];
412 [input appendData:[receipt objectForKey:kReceiptOpaqueValue]];
413 [input appendData:[receipt objectForKey:kReceiptBundleIdentiferData]];
414
415 NSMutableData *hash = [NSMutableData dataWithLength:SHA_DIGEST_LENGTH];
416 SHA1([input bytes], [input length], [hash mutableBytes]);
adf2ab54 »
2010-10-31 Validate bundle identifier and bundle version
417
418 if ([bundleIdentifer isEqualToString:[receipt objectForKey:kReceiptBundleIdentifer]] &&
419 [bundleVersion isEqualToString:[receipt objectForKey:kReceiptVersion]] &&
420 [hash isEqualToData:[receipt objectForKey:kReceiptHash]])
bddbcd1b »
2010-10-23 initial commit
421 {
422 return YES;
423 }
424
425 return NO;
426 }
Something went wrong with that request. Please try again.