]> git.saurik.com Git - apple/security.git/blob - utilities/src/SecCertificateTrace.c
Security-55471.14.18.tar.gz
[apple/security.git] / utilities / src / SecCertificateTrace.c
1 //
2 // SecCertificateTrace.c
3 // utilities
4 //
5 // Created on 10/18/13.
6 // Copyright (c) 2013 Apple Inc. All rights reserved.
7 //
8
9 #include "SecCertificateTrace.h"
10 #include <TargetConditionals.h>
11 #include <inttypes.h>
12 #include "SecCFWrappers.h"
13 #include <sys/time.h>
14 #include <CoreFoundation/CoreFoundation.h>
15 #include "debugging.h"
16
17 #define SEC_CONST_DECL(k,v) CFTypeRef k = (CFTypeRef)(CFSTR(v));
18
19 SEC_CONST_DECL (kCertStatsPrefix, "com.apple.certstats");
20 SEC_CONST_DECL (kCertStatsCert, "cert");
21 SEC_CONST_DECL (kCertStatsPolicy, "id");
22 SEC_CONST_DECL (kCertStatsNotBefore, "nb");
23 SEC_CONST_DECL (kCertStatsNotAfter, "na");
24 SEC_CONST_DECL (kCertStatsSerialNumber, "sn");
25 SEC_CONST_DECL (kCertStatsSubjectSummary, "s");
26 SEC_CONST_DECL (kCertStatsIssuerSummary, "i");
27
28 static const CFStringRef kCertStatsFormat = CFSTR("%@/%@=%@/%@=%@/%@=%@/%@=%@/%@=%@/%@=%@");
29 static const CFStringRef kCertStatsDelimiters = CFSTR("/");
30
31 bool SetCertificateTraceValueForKey(CFStringRef key, int64_t value);
32 void* BeginCertificateLoggingTransaction(void);
33 bool AddKeyValuePairToCertificateLoggingTransaction(void* token, CFStringRef key, int64_t value);
34 void CloseCertificateLoggingTransaction(void* token);
35 CFStringRef SecCFStringCopyEncodingDelimiters(CFStringRef str, CFStringRef delimiters);
36
37 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
38 #include <asl.h>
39
40 static const char* gTopLevelKeyForCertificateTracing = "com.apple.certstats";
41
42 static const char* gMessageTracerSetPrefix = "com.apple.message.";
43
44 static const char* gMessageTracerDomainField = "com.apple.message.domain";
45
46 /* --------------------------------------------------------------------------
47 Function: OSX_BeginCertificateLoggingTransaction
48
49 Description: For OSX, the message tracer back end wants its logging
50 done in "bunches". This function allows for beginning
51 a 'transaction' of logging which will allow for putting
52 all of the transaction's items into a single log making
53 the message tracer folks happy.
54
55 The work of this function is to create the aslmsg context
56 and set the domain field and then return the aslmsg
57 context as a void* result.
58 -------------------------------------------------------------------------- */
59 static void* OSX_BeginCertificateLoggingTransaction()
60 {
61 void* result = NULL;
62 aslmsg mAsl = NULL;
63 mAsl = asl_new(ASL_TYPE_MSG);
64 if (NULL == mAsl)
65 {
66 return result;
67 }
68
69 asl_set(mAsl, gMessageTracerDomainField, gTopLevelKeyForCertificateTracing);
70
71 result = (void *)mAsl;
72 return result;
73 }
74
75 /* --------------------------------------------------------------------------
76 Function: OSX_AddKeyValuePairToCertificateLoggingTransaction
77
78 Description: Once a call to OSX_BeginCertificateLoggingTransaction
79 is done, this call will allow for adding items to the
80 "bunch" of items being logged.
81
82 NOTE: The key should be a simple key such as
83 "numberOfPeers". This is because this function will
84 append the required prefix of "com.apple.message."
85 -------------------------------------------------------------------------- */
86 static bool OSX_AddKeyValuePairToCertificateLoggingTransaction(void* token, CFStringRef key, int64_t value)
87 {
88 if (NULL == token || NULL == key)
89 {
90 return false;
91 }
92
93 aslmsg mAsl = (aslmsg)token;
94
95 // Fix up the key
96 CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%@"), gMessageTracerSetPrefix, key);
97 if (NULL == real_key)
98 {
99 return false;
100 }
101
102 CFIndex key_length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(real_key), kCFStringEncodingUTF8);
103 key_length += 1; // For null
104 char key_buffer[key_length];
105 memset(key_buffer, 0,key_length);
106 if (!CFStringGetCString(real_key, key_buffer, key_length, kCFStringEncodingUTF8))
107 {
108 return false;
109 }
110 CFRelease(real_key);
111
112 CFStringRef value_str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%lld"), value);
113 if (NULL == value_str)
114 {
115 return false;
116 }
117
118 CFIndex value_str_numBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value_str), kCFStringEncodingUTF8);
119 value_str_numBytes += 1; // For null
120 char value_buffer[value_str_numBytes];
121 memset(value_buffer, 0, value_str_numBytes);
122 if (!CFStringGetCString(value_str, value_buffer, value_str_numBytes, kCFStringEncodingUTF8))
123 {
124 CFRelease(value_str);
125 return false;
126 }
127 CFRelease(value_str);
128
129 asl_set(mAsl, key_buffer, value_buffer);
130 return true;
131 }
132
133 /* --------------------------------------------------------------------------
134 Function: OSX_CloseCertificateLoggingTransaction
135
136 Description: Once a call to OSX_BeginCertificateLoggingTransaction
137 is done, and all of the items that are to be in the
138 "bunch" of items being logged, this function will do the
139 real logging and free the aslmsg context.
140 -------------------------------------------------------------------------- */
141 static void OSX_CloseCertificateLoggingTransaction(void* token)
142 {
143 if (NULL != token)
144 {
145 aslmsg mAsl = (aslmsg)token;
146 asl_log(NULL, mAsl, ASL_LEVEL_NOTICE, "");
147 asl_free(mAsl);
148 }
149 }
150
151 /* --------------------------------------------------------------------------
152 Function: OSX_SetCertificateTraceValueForKey
153
154 Description: If "bunching" of items either cannot be done or is not
155 desired, then this 'single shot' function should be used.
156 It will create the aslmsg context, register the domain,
157 fix up the key and log the key value pair, and then
158 do the real logging and free the aslmsg context.
159 -------------------------------------------------------------------------- */
160 static bool OSX_SetCertificateTraceValueForKey(CFStringRef key, int64_t value)
161 {
162 bool result = false;
163
164 if (NULL == key)
165 {
166 return result;
167 }
168
169 aslmsg mAsl = NULL;
170 mAsl = asl_new(ASL_TYPE_MSG);
171 if (NULL == mAsl)
172 {
173 return result;
174 }
175
176 // Fix up the key
177 CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%@"), gMessageTracerSetPrefix, key);
178 if (NULL == real_key)
179 {
180 return false;
181 }
182
183 CFIndex key_length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(real_key), kCFStringEncodingUTF8);
184 key_length += 1; // For null
185 char key_buffer[key_length];
186 memset(key_buffer, 0,key_length);
187 if (!CFStringGetCString(real_key, key_buffer, key_length, kCFStringEncodingUTF8))
188 {
189 return false;
190 }
191 CFRelease(real_key);
192
193
194 CFStringRef value_str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%lld"), value);
195 if (NULL == value_str)
196 {
197 asl_free(mAsl);
198 return result;
199 }
200
201 CFIndex value_str_numBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value_str), kCFStringEncodingUTF8);
202 value_str_numBytes += 1; // For null
203 char value_buffer[value_str_numBytes];
204 memset(value_buffer, 0, value_str_numBytes);
205 if (!CFStringGetCString(value_str, value_buffer, value_str_numBytes, kCFStringEncodingUTF8))
206 {
207 asl_free(mAsl);
208 CFRelease(value_str);
209 return result;
210 }
211 CFRelease(value_str);
212
213 /* Domain */
214 asl_set(mAsl, gMessageTracerDomainField, gTopLevelKeyForCertificateTracing);
215
216 /* Our custom key (starts with com.apple.message.) and its value */
217 asl_set(mAsl, key_buffer, value_buffer);
218
219 /* Log this ASL record (note actual log message is empty) */
220 asl_log(NULL, mAsl, ASL_LEVEL_NOTICE, "");
221 asl_free(mAsl);
222 return true;
223
224 }
225 #endif
226
227 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
228
229 static const char* gTopLevelKeyForCertificateTracing = "com.apple.certstats";
230
231 typedef void (*type_ADClientClearScalarKey)(CFStringRef key);
232 typedef void (*type_ADClientAddValueForScalarKey)(CFStringRef key, int64_t value);
233
234 static type_ADClientClearScalarKey gADClientClearScalarKey = NULL;
235 static type_ADClientAddValueForScalarKey gADClientAddValueForScalarKey = NULL;
236
237 static dispatch_once_t gADFunctionPointersSet = 0;
238 static CFBundleRef gAggdBundleRef = NULL;
239 static bool gFunctionPointersAreLoaded = false;
240
241 /* --------------------------------------------------------------------------
242 Function: InitializeADFunctionPointers
243
244 Description: Linking to the Aggregate library causes a build cycle,
245 so this function dynamically loads the needed function
246 pointers.
247 -------------------------------------------------------------------------- */
248 static bool InitializeADFunctionPointers()
249 {
250 if (gFunctionPointersAreLoaded)
251 {
252 return gFunctionPointersAreLoaded;
253 }
254
255 dispatch_once(&gADFunctionPointersSet,
256 ^{
257 CFStringRef path_to_aggd_framework = CFSTR("/System/Library/PrivateFrameworks/AggregateDictionary.framework");
258
259 CFURLRef aggd_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_to_aggd_framework, kCFURLPOSIXPathStyle, true);
260
261 if (NULL != aggd_url)
262 {
263 gAggdBundleRef = CFBundleCreate(kCFAllocatorDefault, aggd_url);
264 if (NULL != gAggdBundleRef)
265 {
266 gADClientClearScalarKey = (type_ADClientClearScalarKey)
267 CFBundleGetFunctionPointerForName(gAggdBundleRef, CFSTR("ADClientClearScalarKey"));
268
269 gADClientAddValueForScalarKey = (type_ADClientAddValueForScalarKey)
270 CFBundleGetFunctionPointerForName(gAggdBundleRef, CFSTR("ADClientAddValueForScalarKey"));
271 }
272 CFRelease(aggd_url);
273 }
274 });
275
276 gFunctionPointersAreLoaded = ((NULL != gADClientClearScalarKey) && (NULL != gADClientAddValueForScalarKey));
277 return gFunctionPointersAreLoaded;
278 }
279
280 /* --------------------------------------------------------------------------
281 Function: Internal_ADClientClearScalarKey
282
283 Description: This function is a wrapper around calling the
284 ADClientClearScalarKey function.
285
286 NOTE: The key should be a simple key such as
287 "numberOfPeers". This is because this function will
288 append the required prefix of "com.apple.certstats"
289 -------------------------------------------------------------------------- */
290 static void Internal_ADClientClearScalarKey(CFStringRef key)
291 {
292 if (InitializeADFunctionPointers())
293 {
294 CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%@"), gTopLevelKeyForCertificateTracing, key);
295 if (NULL == real_key)
296 {
297 return;
298 }
299
300 gADClientClearScalarKey(real_key);
301 CFRelease(real_key);
302 }
303 }
304
305 /* --------------------------------------------------------------------------
306 Function: Internal_ADClientAddValueForScalarKey
307
308 Description: This function is a wrapper around calling the
309 ADClientAddValueForScalarKey function.
310
311 NOTE: The key should be a simple key such as
312 "numberOfPeers". This is because this function will
313 append the required prefix of "com.apple.certstats"
314 -------------------------------------------------------------------------- */
315 static void Internal_ADClientAddValueForScalarKey(CFStringRef key, int64_t value)
316 {
317 if (InitializeADFunctionPointers())
318 {
319 CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%@"), gTopLevelKeyForCertificateTracing, key);
320 if (NULL == real_key)
321 {
322 return;
323 }
324
325 gADClientAddValueForScalarKey(real_key, value);
326 CFRelease(real_key);
327 }
328 }
329
330
331 /* --------------------------------------------------------------------------
332 Function: iOS_SetCertificateTraceValueForKey
333
334 Description: This function is a wrapper around calling either
335 ADClientAddValueForScalarKey or ADClientClearScalarKey,
336 depending on whether the value is 0.
337
338 NOTE: The key should be a simple key such as
339 "numberOfPeers". This is because this function will
340 append the required prefix of "com.apple.certstats"
341 -------------------------------------------------------------------------- */
342 static bool iOS_SetCertificateTraceValueForKey(CFStringRef key, int64_t value)
343 {
344 if (NULL == key)
345 {
346 return false;
347 }
348
349 if (0LL == value)
350 {
351 Internal_ADClientClearScalarKey(key);
352 }
353 else
354 {
355 Internal_ADClientAddValueForScalarKey(key, value);
356 }
357 return true;
358 }
359
360 /* --------------------------------------------------------------------------
361 Function: iOS_AddKeyValuePairToCertificateLoggingTransaction
362
363 Description: For iOS there is no "bunching". This function will simply
364 call iOS_SetCloudKeychainTraceValueForKey to log the
365 key-value pair.
366 -------------------------------------------------------------------------- */
367 static bool iOS_AddKeyValuePairToCertificateLoggingTransaction(void* token, CFStringRef key, int64_t value)
368 {
369 #pragma unused(token)
370 return iOS_SetCertificateTraceValueForKey(key, value);
371 }
372 #endif
373
374 /* --------------------------------------------------------------------------
375 Function: SetCertificateTraceValueForKey
376
377 Description: SPI to log a single key-value pair with the logging system
378 -------------------------------------------------------------------------- */
379 bool SetCertificateTraceValueForKey(CFStringRef key, int64_t value)
380 {
381 #if (TARGET_IPHONE_SIMULATOR)
382 return false;
383 #endif
384
385 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
386 return OSX_SetCertificateTraceValueForKey(key, value);
387 #endif
388
389 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
390 return iOS_SetCertificateTraceValueForKey(key, value);
391 #endif
392 }
393
394 /* --------------------------------------------------------------------------
395 Function: BeginCertificateLoggingTransaction
396
397 Description: SPI to begin a logging transaction
398 -------------------------------------------------------------------------- */
399 void* BeginCertificateLoggingTransaction(void)
400 {
401 #if (TARGET_IPHONE_SIMULATOR)
402 return (void *)-1;
403 #endif
404
405 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
406 return OSX_BeginCertificateLoggingTransaction();
407 #endif
408
409 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
410 return NULL;
411 #endif
412 }
413
414 /* --------------------------------------------------------------------------
415 Function: AddKeyValuePairToCertificateLoggingTransaction
416
417 Description: SPI to add a key-value pair to an outstanding logging
418 transaction
419 -------------------------------------------------------------------------- */
420 bool AddKeyValuePairToCertificateLoggingTransaction(void* token, CFStringRef key, int64_t value)
421 {
422 #if (TARGET_IPHONE_SIMULATOR)
423 return false;
424 #endif
425
426 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
427 return OSX_AddKeyValuePairToCertificateLoggingTransaction(token, key, value);
428 #endif
429
430 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
431 return iOS_AddKeyValuePairToCertificateLoggingTransaction(token, key, value);
432 #endif
433 }
434
435 /* --------------------------------------------------------------------------
436 Function: CloseCertificateLoggingTransaction
437
438 Description: SPI to complete a logging transaction and clean up the
439 context
440 -------------------------------------------------------------------------- */
441 void CloseCertificateLoggingTransaction(void* token)
442 {
443 #if (TARGET_IPHONE_SIMULATOR)
444 ; // nothing
445 #endif
446
447 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
448 OSX_CloseCertificateLoggingTransaction(token);
449 #endif
450
451 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
452 ; // nothing
453 #endif
454 }
455
456 /* --------------------------------------------------------------------------
457 Function: SecCFStringCopyEncodingDelimiters
458
459 Description: Utility to replace all characters in the given string
460 which match characters in the delimiters string. This
461 should be expanded later; for now we are assuming the
462 delimiters string is a single character, and we replace
463 it with an underscore instead of percent encoding.
464 -------------------------------------------------------------------------- */
465
466 CFStringRef SecCFStringCopyEncodingDelimiters(CFStringRef str, CFStringRef delimiters)
467 {
468 CFMutableStringRef newStr = (str) ? CFStringCreateMutableCopy(kCFAllocatorDefault, 0, str) : NULL;
469 if (str && delimiters)
470 {
471 CFStringRef stringToFind = delimiters;
472 CFStringRef replacementString = CFSTR("_");
473 CFStringFindAndReplace(newStr, stringToFind, replacementString, CFRangeMake(0, CFStringGetLength(newStr)), 0);
474 }
475 return (CFStringRef) newStr;
476 }
477
478 /* --------------------------------------------------------------------------
479 Function: SecCertificateTraceAddRecord
480
481 Description: High-level SPI that logs a single certificate record,
482 given a dictionary containing key-value pairs.
483 -------------------------------------------------------------------------- */
484 bool SecCertificateTraceAddRecord(CFDictionaryRef certRecord)
485 {
486 bool result = false;
487 if (!certRecord)
488 return result;
489
490 /* encode delimiter characters in supplied strings */
491 CFStringRef policy = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsPolicy), kCertStatsDelimiters);
492 CFStringRef notBefore = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsNotBefore), kCertStatsDelimiters);
493 CFStringRef notAfter = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsNotAfter), kCertStatsDelimiters);
494 CFStringRef serial = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsSerialNumber), kCertStatsDelimiters);
495 CFStringRef subject = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsSubjectSummary), kCertStatsDelimiters);
496 CFStringRef issuer = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsIssuerSummary), kCertStatsDelimiters);
497
498 /* this generates a key which includes delimited fields to identify the certificate */
499 CFStringRef keyStr = CFStringCreateWithFormat(NULL, NULL,
500 kCertStatsFormat, /* format string */
501 kCertStatsCert, /* certificate key */
502 kCertStatsPolicy, policy,
503 kCertStatsNotBefore, notBefore,
504 kCertStatsNotAfter, notAfter,
505 kCertStatsSerialNumber, serial,
506 kCertStatsSubjectSummary, subject,
507 kCertStatsIssuerSummary, issuer
508 );
509 CFReleaseSafe(policy);
510 CFReleaseSafe(notBefore);
511 CFReleaseSafe(notAfter);
512 CFReleaseSafe(serial);
513 CFReleaseSafe(subject);
514 CFReleaseSafe(issuer);
515
516 result = SetCertificateTraceValueForKey(keyStr, 1);
517 #if DEBUG
518 secerror("%@.%@ (%d)", kCertStatsPrefix, keyStr, (result) ? 1 : 0);
519 #endif
520 CFReleaseSafe(keyStr);
521
522 return result;
523 }
524