1/*
2 * Copyright (c) 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#import <Foundation/Foundation.h>
24#import <CrashReporterSupport/CrashReporterSupportPrivate.h>
25#import <sys/utsname.h>
26
27static bool useInternalServer() {
28	struct utsname name;
29	return ((uname(&name) == 0 && strstr(name.version, "DEVELOPMENT") != NULL)
30	        && access("/var/db/gkoverride_use_internal", F_OK) == 0);
31}
32
33@interface OverrideClient : NSObject<NSURLConnectionDelegate> {
34	BOOL allow;
35	NSMutableData *buffer;
36	NSURLConnection *connection;
37	int state;
38}
39@property NSString *currentHash;
40@property NSString *opaqueHash;
41@property NSString *bundleID;
42@property NSString *bundleVersion;
43- (NSString *)serverHost;
44- (void)sendQuery;
45- (void)sendReport;
46- (void)abort;
47@end
48
49@implementation OverrideClient
50
51- (NSString *)serverHost {
52	if (useInternalServer())
53		return @"gkq-stg.siri.apple.com";
54	else
55		return @"gkq.apple.com";
56}
57
58- (id)init {
59	self = [super init];
60	allow = NO;
61	buffer = [NSMutableData new];
62	return self;
63}
64
65- (void)sendQuery {
66	state = 1;
67	NSMutableURLRequest *request = [NSMutableURLRequest new];
68	NSString *url = [NSString stringWithFormat:@"https://%@/q/%@/%@", [self serverHost], self.currentHash, self.opaqueHash];
69	[request setURL:[NSURL URLWithString:url]];
70	[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
71	[request setTimeoutInterval:5];
72	connection = [NSURLConnection connectionWithRequest:request delegate:self];
73}
74
75- (void)sendReport {
76	state = 2;
77	NSMutableURLRequest *request = [NSMutableURLRequest new];
78	NSString *body = [NSString stringWithFormat:@"current=%@&bundleid=%@&version=%@",
79		[self.currentHash stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
80		[self.bundleID stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
81		[self.bundleVersion stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
82	NSString *url = [NSString stringWithFormat:@"https://%@/report", [self serverHost]];
83	[request setURL:[NSURL URLWithString:url]];
84	[request setTimeoutInterval:5];
85	[request setHTTPMethod:@"POST"];
86	[request setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]];
87	connection = [NSURLConnection connectionWithRequest:request delegate:self];
88}
89
90- (BOOL)connection:(NSURLConnection *)conn canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
91	SecTrustRef trust = [protectionSpace serverTrust];
92	// abort if untrusted
93	SecTrustResultType trustResult;
94	SecTrustEvaluate(trust, &trustResult);
95	if (trustResult != kSecTrustResultProceed && trustResult != kSecTrustResultUnspecified) {
96		[conn cancel];
97		[self abort];
98		return NO;
99	}
100	// allow if server presented an EV cert
101	NSDictionary *result = CFBridgingRelease(SecTrustCopyResult(trust));
102	if (result) {
103		NSNumber *ev = [result objectForKey:(__bridge id)kSecTrustExtendedValidation];
104		if (ev != NULL && [ev boolValue] == YES) {
105			return NO;
106		}
107	}
108	// allow if using internal server (EV not required)
109	if (useInternalServer())
110		return NO;
111	// otherwise abort
112	[conn cancel];
113	[self abort];
114	return NO;
115}
116
117- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
118	[buffer appendData:data];
119	if ([buffer length] > 100)
120		[self abort];
121}
122
123- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
124	if (state == 1) {
125		NSString *verdict = [[NSString alloc] initWithData:buffer encoding:NSUTF8StringEncoding];
126		if ([verdict isEqualToString:@"allow"]) {
127			allow = YES;
128			[self abort];
129		} else if (CRIsAutoSubmitEnabled()) {	// "Send diagnostic & usage data to Apple" checked
130			[self sendReport];
131		}
132	} else {
133		[self abort];	// report sent
134	}
135}
136
137- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
138	[self abort];
139}
140
141- (void)abort {
142	if (allow) {
143		printf("allow\n");
144		exit(42);
145	} else {
146		printf("deny\n");
147		exit(1);
148	}
149}
150
151@end
152
153int main(int argc, const char * argv[]) {
154	if (argc != 5) {
155		fprintf(stderr, "usage: %s <current> <opaque> <bundleid> <version>\n", argv[0]);
156		return 1;
157	}
158	@autoreleasepool {
159		OverrideClient *client = [OverrideClient new];
160		client.currentHash = [NSString stringWithUTF8String:argv[1]];
161		client.opaqueHash = [NSString stringWithUTF8String:argv[2]];
162		client.bundleID = [NSString stringWithUTF8String:argv[3]];
163		client.bundleVersion = [NSString stringWithUTF8String:argv[4]];
164		[client sendQuery];
165		[[NSRunLoop currentRunLoop] run];
166	}
167	return 0;
168}
169