]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/xpc-tsa/main-tsa.m
Security-58286.251.4.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / xpc-tsa / main-tsa.m
1 /*
2 * Copyright (c) 2012,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 <sysexits.h>
25 #include "timestampclient.h"
26 #include <syslog.h>
27 #include <Security/SecTask.h>
28 #include <xpc/private.h>
29
30 struct connection_info {
31 xpc_connection_t peer;
32 int processed;
33 int done;
34 };
35
36 xpc_object_t keychain_prefs_path = NULL;
37 xpc_object_t home = NULL;
38
39 extern xpc_object_t
40 xpc_create_reply_with_format(xpc_object_t original, const char * format, ...);
41
42 void finalize_connection(void *not_used);
43 void handle_connection_event(const xpc_connection_t peer);
44 void handle_request_event(struct connection_info *info, xpc_object_t event);
45
46 #ifndef NDEBUG
47 #define xpctsa_secinfo(format...) \
48 do { \
49 syslog(LOG_WARNING, format); \
50 } while (0)
51 #define xpctsaNSLog(format...) \
52 do { \
53 NSLog(format); \
54 } while (0)
55
56 #else
57 //empty
58 #define xpctsa_secinfo(format...)
59 #define xpctsaNSLog(format...)
60 #endif
61 #define xpctsaDebug(args...) xpctsa_secinfo(args)
62
63 /*
64 These came from:
65
66 #include <OSServices/NetworkUtilities.h>
67
68 I have no idea why they aren't more accessible.
69 */
70
71 #define kHTTPResponseCodeContinue 100
72 #define kHTTPResponseCodeOK 200
73 #define kHTTPResponseCodeNoContent 204
74 #define kHTTPResponseCodeBadRequest 400
75 #define kHTTPResponseCodeUnauthorized 401
76 #define kHTTPResponseCodeForbidden 403
77 #define kHTTPResponseCodeConflict 409
78 #define kHTTPResponseCodeExpectationFailed 417
79 #define kHTTPResponseCodeServFail 500
80 #define kHTTPResponseCodeInsufficientStorage 507
81
82 //
83 // Turn a CFString into a UTF8-encoded C string.
84 //
85 static char *cfStringToCString(CFStringRef inStr)
86 {
87 if (!inStr)
88 return "";
89 CFRetain(inStr); // compensate for release on exit
90
91 // need to extract into buffer
92 CFIndex length = CFStringGetLength(inStr); // in 16-bit character units
93 size_t len = 6 * length + 1;
94 char *buffer = malloc(len); // pessimistic
95 if (!CFStringGetCString(inStr, buffer, len, kCFStringEncodingUTF8))
96 buffer[0]=0;
97
98 CFRelease(inStr);
99 return buffer;
100 }
101
102 static void debugShowTSAResponseInfo(NSURLResponse *response, NSData *data, NSError *err)
103 {
104 #ifndef NDEBUG
105 if (response)
106 {
107 NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
108 NSInteger statusCode = [httpResponse statusCode];
109 NSDictionary *headers = [httpResponse allHeaderFields];
110 NSString *errStr2 = [NSHTTPURLResponse localizedStringForStatusCode:(NSInteger)statusCode];
111
112 xpctsaNSLog(@"TSA Response: %d, %@, %@", (int)statusCode, errStr2, headers);
113 }
114
115 if (err)
116 { xpctsaNSLog(@"TSARequestCompletionBlock error: %@", err); }
117
118 if (data)
119 {
120 xpctsaDebug("TSARequestCompletionBlock: Data (%lu bytes)",(unsigned long)[data length]);
121 xpctsaNSLog(@"TSARequestCompletionBlock: Data (%lu bytes)",(unsigned long)[data length]);
122
123 NSString *path = @"/tmp/tsaresp.rsp";
124 NSDataWritingOptions writeOptionsMask = 0;
125 NSError *errorPtr = NULL;
126 [data writeToFile:path options:writeOptionsMask error:&errorPtr];
127 if (errorPtr)
128 {
129 xpctsaNSLog(@"TSA Response error dumping response: %@", errorPtr);
130 [errorPtr release];
131 }
132 }
133 #endif
134 }
135
136 /*
137 * Check whether the caller can access the network. Currently, this applies
138 * only to applications running under App Sandbox.
139 */
140 static bool callerHasNetworkEntitlement(audit_token_t auditToken)
141 {
142 bool result = true; /* until proven otherwise */
143 SecTaskRef task = SecTaskCreateWithAuditToken(NULL, auditToken);
144 if(task != NULL) {
145 CFTypeRef appSandboxValue = SecTaskCopyValueForEntitlement(task,
146 CFSTR("com.apple.security.app-sandbox"),
147 NULL);
148 if(appSandboxValue != NULL) {
149 if(!CFEqual(kCFBooleanFalse, appSandboxValue)) {
150 CFTypeRef networkClientValue = SecTaskCopyValueForEntitlement(task,
151 CFSTR("com.apple.security.network.client"),
152 NULL);
153 if(networkClientValue != NULL) {
154 result = (!CFEqual(kCFBooleanFalse, networkClientValue));
155 CFRelease(networkClientValue);
156 } else {
157 result = false;
158 }
159 }
160 CFRelease(appSandboxValue);
161 }
162 CFRelease(task);
163 }
164 return result;
165 }
166
167 static void communicateWithTimeStampingServer(xpc_object_t event, const char *requestData, size_t requestLength, const char *tsaURL)
168 {
169 if ((requestLength==0) || !tsaURL)
170 return;
171
172 xpctsaDebug("Request Length: %ld, URL: %s", requestLength, tsaURL);
173
174 __block CFDataRef tsaReq = CFDataCreate(kCFAllocatorDefault, (const unsigned char *)requestData, requestLength);
175
176 // The completion block is called when we have a response
177 TSARequestCompletionBlock reqCompletionBlock =
178 ^(NSURLResponse *response, NSData *data, NSError *err)
179 {
180 xpc_object_t tsaError = NULL;
181 xpc_object_t tsaStatusCode = NULL;
182 NSString *errStr = NULL;
183
184 debugShowTSAResponseInfo(response, data, err);
185
186 /*
187 Handle errors first. The bad responses seen so far tend to
188 return a bad HTTP status code rather than setting the "err"
189 parameter. In this case, the "data" parameter contains the
190 HTML of the error response, which we do not want to try to
191 parse as ASN.1 data.
192 */
193
194 if (response)
195 {
196 NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
197 NSInteger statusCode = [httpResponse statusCode];
198 if (statusCode != kHTTPResponseCodeOK)
199 {
200 errStr = [NSHTTPURLResponse localizedStringForStatusCode:(NSInteger)statusCode];
201 tsaStatusCode = xpc_int64_create((int64_t)statusCode);
202 xpctsaNSLog(@"TSA Response-b: %d, %@", (int)statusCode, errStr);
203 }
204 }
205
206 if (err && !errStr)
207 {
208 errStr = [err description];
209 if (!tsaStatusCode)
210 {
211 NSInteger statusCode = [err code];
212 tsaStatusCode = xpc_int64_create((int64_t)statusCode);
213 }
214 }
215
216 if (errStr)
217 {
218 const char *cerrstr = cfStringToCString((CFStringRef)errStr);
219 tsaError = xpc_string_create(cerrstr);
220 xpctsaNSLog(@"TSA Response-c: %@", errStr);
221 }
222
223 size_t length = (errStr || !data) ? 0 : [data length];
224 xpc_object_t tsaReply = data ? xpc_data_create([data bytes], length) : NULL;
225 xpc_connection_t peer = xpc_dictionary_get_remote_connection(event);
226 xpc_object_t reply = NULL;
227 if (tsaError && tsaReply && tsaStatusCode) {
228 reply = xpc_create_reply_with_format(event, "{TimeStampReply: %value, TimeStampError: %value, TimeStampStatus: %value}",
229 tsaReply, tsaError, tsaStatusCode);
230 } else if (tsaReply) {
231 reply = xpc_create_reply_with_format(event, "{TimeStampReply: %value}", tsaReply);
232 } else {
233 reply = xpc_create_reply_with_format(event, "{}");
234 }
235 xpc_connection_send_message(peer, reply);
236 xpc_release(reply);
237
238 if (tsaReq)
239 CFRelease(tsaReq);
240 if (tsaReply)
241 xpc_release(tsaReply);
242 if (tsaError)
243 xpc_release(tsaError);
244 if (tsaStatusCode)
245 xpc_release(tsaStatusCode);
246 };
247
248 sendTSARequest(tsaReq, tsaURL, reqCompletionBlock);
249 }
250
251 void handle_request_event(struct connection_info *info, xpc_object_t event)
252 {
253 xpc_connection_t peer = xpc_dictionary_get_remote_connection(event);
254 xpc_type_t xtype = xpc_get_type(event);
255
256 if (info->done)
257 {
258 xpctsaDebug("event %p while done", event);
259 return;
260 }
261 if (xtype == XPC_TYPE_ERROR)
262 {
263 if (event == XPC_ERROR_TERMINATION_IMMINENT) {
264 // launchd would like us to die, but we have open transactions. When we finish with them xpc_service_main
265 // will exit for us, so there is nothing for us to do here.
266 return;
267 }
268
269 if (!info->done) {
270 info->done = true;
271 xpc_release(info->peer);
272 }
273 if (peer == NULL && XPC_ERROR_CONNECTION_INVALID == event && 0 != info->processed) {
274 // this is a normal shutdown on a connection that has processed at least
275 // one request. Nothing intresting to log.
276 return;
277 }
278 xpctsaDebug("listener event error (connection %p): %s", peer, xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
279 }
280 else
281 if (xtype == XPC_TYPE_DICTIONARY)
282 {
283 size_t length = 0;
284 const char *operation = xpc_dictionary_get_string(event, "operation");
285 audit_token_t auditToken = {};
286 xpc_connection_get_audit_token(peer, &auditToken);
287
288 if (operation && !strcmp(operation, "TimeStampRequest"))
289 {
290 if (callerHasNetworkEntitlement(auditToken)) {
291 xpctsaDebug("Handling TimeStampRequest event");
292 const void *requestData = xpc_dictionary_get_data(event, "TimeStampRequest", &length);
293 const char *url = xpc_dictionary_get_string(event, "ServerURL");
294
295 communicateWithTimeStampingServer(event, requestData, length, url);
296 }
297 else
298 xpctsaDebug("No network entitlement for pid %d", xpc_connection_get_pid(peer));
299 }
300 else
301 xpctsaDebug("Unknown op=%s request from pid %d", operation, xpc_connection_get_pid(peer));
302 }
303 else
304 xpctsaDebug("Unhandled request event=%p type=%p", event, xtype);
305 }
306
307 void finalize_connection(void *not_used)
308 {
309 xpc_transaction_end();
310 }
311
312 void handle_connection_event(const xpc_connection_t peer)
313 {
314 __block struct connection_info info;
315 info.peer = peer;
316 info.processed = 0;
317 info.done = false;
318
319 xpc_connection_set_event_handler(peer, ^(xpc_object_t event)
320 {
321 handle_request_event(&info, event);
322 });
323
324 // unlike dispatch objects xpc objects don't need a context set in order to run a finalizer. (we use our finalizer to
325 // end the transaction we are about to begin...this keeps xpc from idle exiting us while we have a live connection)
326 xpc_connection_set_finalizer_f(peer, finalize_connection);
327 xpc_transaction_begin();
328
329 // enable the peer connection to receive messages
330 xpc_connection_resume(peer);
331 xpc_retain(peer);
332 }
333
334 int main(int argc, const char *argv[])
335 {
336 char *wait4debugger = getenv("WAIT4DEBUGGER");
337 if (wait4debugger && !strcasecmp("YES", wait4debugger))
338 {
339 syslog(LOG_ERR, "Waiting for debugger");
340 kill(getpid(), SIGSTOP);
341 }
342
343 xpc_main(handle_connection_event);
344
345 return EX_OSERR;
346 }
347