]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/xpc-tsa/main-tsa.m
Security-57740.60.18.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 = xpc_data_create([data bytes], length);
225 xpc_connection_t peer = xpc_dictionary_get_remote_connection(event);
226 xpc_object_t reply = (tsaError)?
227 xpc_create_reply_with_format(event,
228 "{TimeStampReply: %value, TimeStampError: %value, TimeStampStatus: %value}", tsaReply, tsaError, tsaStatusCode) :
229 xpc_create_reply_with_format(event, "{TimeStampReply: %value}", tsaReply);
230 xpc_connection_send_message(peer, reply);
231 xpc_release(reply);
232
233 if (tsaReq)
234 CFRelease(tsaReq);
235 if (tsaReply)
236 xpc_release(tsaReply);
237 if (tsaError)
238 xpc_release(tsaError);
239 if (tsaStatusCode)
240 xpc_release(tsaStatusCode);
241 };
242
243 sendTSARequest(tsaReq, tsaURL, reqCompletionBlock);
244 }
245
246 void handle_request_event(struct connection_info *info, xpc_object_t event)
247 {
248 xpc_connection_t peer = xpc_dictionary_get_remote_connection(event);
249 xpc_type_t xtype = xpc_get_type(event);
250
251 if (info->done)
252 {
253 xpctsaDebug("event %p while done", event);
254 return;
255 }
256 if (xtype == XPC_TYPE_ERROR)
257 {
258 if (event == XPC_ERROR_TERMINATION_IMMINENT) {
259 // launchd would like us to die, but we have open transactions. When we finish with them xpc_service_main
260 // will exit for us, so there is nothing for us to do here.
261 return;
262 }
263
264 if (!info->done) {
265 info->done = true;
266 xpc_release(info->peer);
267 }
268 if (peer == NULL && XPC_ERROR_CONNECTION_INVALID == event && 0 != info->processed) {
269 // this is a normal shutdown on a connection that has processed at least
270 // one request. Nothing intresting to log.
271 return;
272 }
273 xpctsaDebug("listener event error (connection %p): %s", peer, xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
274 }
275 else
276 if (xtype == XPC_TYPE_DICTIONARY)
277 {
278 size_t length = 0;
279 const char *operation = xpc_dictionary_get_string(event, "operation");
280 audit_token_t auditToken = {};
281 xpc_connection_get_audit_token(peer, &auditToken);
282
283 if (operation && !strcmp(operation, "TimeStampRequest"))
284 {
285 if (callerHasNetworkEntitlement(auditToken)) {
286 xpctsaDebug("Handling TimeStampRequest event");
287 const void *requestData = xpc_dictionary_get_data(event, "TimeStampRequest", &length);
288 const char *url = xpc_dictionary_get_string(event, "ServerURL");
289
290 communicateWithTimeStampingServer(event, requestData, length, url);
291 }
292 else
293 xpctsaDebug("No network entitlement for pid %d", xpc_connection_get_pid(peer));
294 }
295 else
296 xpctsaDebug("Unknown op=%s request from pid %d", operation, xpc_connection_get_pid(peer));
297 }
298 else
299 xpctsaDebug("Unhandled request event=%p type=%p", event, xtype);
300 }
301
302 void finalize_connection(void *not_used)
303 {
304 xpc_transaction_end();
305 }
306
307 void handle_connection_event(const xpc_connection_t peer)
308 {
309 __block struct connection_info info;
310 info.peer = peer;
311 info.processed = 0;
312 info.done = false;
313
314 xpc_connection_set_event_handler(peer, ^(xpc_object_t event)
315 {
316 handle_request_event(&info, event);
317 });
318
319 // unlike dispatch objects xpc objects don't need a context set in order to run a finalizer. (we use our finalizer to
320 // end the transaction we are about to begin...this keeps xpc from idle exiting us while we have a live connection)
321 xpc_connection_set_finalizer_f(peer, finalize_connection);
322 xpc_transaction_begin();
323
324 // enable the peer connection to receive messages
325 xpc_connection_resume(peer);
326 xpc_retain(peer);
327 }
328
329 int main(int argc, const char *argv[])
330 {
331 char *wait4debugger = getenv("WAIT4DEBUGGER");
332 if (wait4debugger && !strcasecmp("YES", wait4debugger))
333 {
334 syslog(LOG_ERR, "Waiting for debugger");
335 kill(getpid(), SIGSTOP);
336 }
337
338 xpc_main(handle_connection_event);
339
340 return EX_OSERR;
341 }
342