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