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 28struct connection_info { 29 xpc_connection_t peer; 30 int processed; 31 int done; 32}; 33 34xpc_object_t keychain_prefs_path = NULL; 35xpc_object_t home = NULL; 36 37extern xpc_object_t 38xpc_create_reply_with_format(xpc_object_t original, const char * format, ...); 39 40void finalize_connection(void *not_used); 41void handle_connection_event(const xpc_connection_t peer); 42void 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// 83static 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 100static 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 134static 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 213void 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 262void finalize_connection(void *not_used) 263{ 264 xpc_transaction_end(); 265} 266 267void 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 289int 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 303