2 // SecCertificateTrace.c
5 // Created on 10/18/13.
6 // Copyright (c) 2013 Apple Inc. All rights reserved.
9 #include "SecCertificateTrace.h"
10 #include <TargetConditionals.h>
12 #include "SecCFWrappers.h"
14 #include <CoreFoundation/CoreFoundation.h>
15 #include "debugging.h"
17 #define SEC_CONST_DECL(k,v) CFTypeRef k = (CFTypeRef)(CFSTR(v));
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");
28 static const CFStringRef kCertStatsFormat
= CFSTR("%@/%@=%@/%@=%@/%@=%@/%@=%@/%@=%@/%@=%@");
29 static const CFStringRef kCertStatsDelimiters
= CFSTR("/");
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
);
37 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
40 static const char* gTopLevelKeyForCertificateTracing
= "com.apple.certstats";
42 static const char* gMessageTracerSetPrefix
= "com.apple.message.";
44 static const char* gMessageTracerDomainField
= "com.apple.message.domain";
46 /* --------------------------------------------------------------------------
47 Function: OSX_BeginCertificateLoggingTransaction
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.
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()
63 mAsl
= asl_new(ASL_TYPE_MSG
);
69 asl_set(mAsl
, gMessageTracerDomainField
, gTopLevelKeyForCertificateTracing
);
71 result
= (void *)mAsl
;
75 /* --------------------------------------------------------------------------
76 Function: OSX_AddKeyValuePairToCertificateLoggingTransaction
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.
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
)
88 if (NULL
== token
|| NULL
== key
)
93 aslmsg mAsl
= (aslmsg
)token
;
96 CFStringRef real_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%s%@"), gMessageTracerSetPrefix
, key
);
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
))
112 CFStringRef value_str
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%lld"), value
);
113 if (NULL
== value_str
)
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
))
124 CFRelease(value_str
);
127 CFRelease(value_str
);
129 asl_set(mAsl
, key_buffer
, value_buffer
);
133 /* --------------------------------------------------------------------------
134 Function: OSX_CloseCertificateLoggingTransaction
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
)
145 aslmsg mAsl
= (aslmsg
)token
;
146 asl_log(NULL
, mAsl
, ASL_LEVEL_NOTICE
, "");
151 /* --------------------------------------------------------------------------
152 Function: OSX_SetCertificateTraceValueForKey
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
)
170 mAsl
= asl_new(ASL_TYPE_MSG
);
177 CFStringRef real_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%s%@"), gMessageTracerSetPrefix
, key
);
178 if (NULL
== real_key
)
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
))
194 CFStringRef value_str
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%lld"), value
);
195 if (NULL
== value_str
)
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
))
208 CFRelease(value_str
);
211 CFRelease(value_str
);
214 asl_set(mAsl
, gMessageTracerDomainField
, gTopLevelKeyForCertificateTracing
);
216 /* Our custom key (starts with com.apple.message.) and its value */
217 asl_set(mAsl
, key_buffer
, value_buffer
);
219 /* Log this ASL record (note actual log message is empty) */
220 asl_log(NULL
, mAsl
, ASL_LEVEL_NOTICE
, "");
227 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
229 static const char* gTopLevelKeyForCertificateTracing
= "com.apple.certstats";
231 typedef void (*type_ADClientClearScalarKey
)(CFStringRef key
);
232 typedef void (*type_ADClientAddValueForScalarKey
)(CFStringRef key
, int64_t value
);
234 static type_ADClientClearScalarKey gADClientClearScalarKey
= NULL
;
235 static type_ADClientAddValueForScalarKey gADClientAddValueForScalarKey
= NULL
;
237 static dispatch_once_t gADFunctionPointersSet
= 0;
238 static CFBundleRef gAggdBundleRef
= NULL
;
239 static bool gFunctionPointersAreLoaded
= false;
241 /* --------------------------------------------------------------------------
242 Function: InitializeADFunctionPointers
244 Description: Linking to the Aggregate library causes a build cycle,
245 so this function dynamically loads the needed function
247 -------------------------------------------------------------------------- */
248 static bool InitializeADFunctionPointers()
250 if (gFunctionPointersAreLoaded
)
252 return gFunctionPointersAreLoaded
;
255 dispatch_once(&gADFunctionPointersSet
,
257 CFStringRef path_to_aggd_framework
= CFSTR("/System/Library/PrivateFrameworks/AggregateDictionary.framework");
259 CFURLRef aggd_url
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, path_to_aggd_framework
, kCFURLPOSIXPathStyle
, true);
261 if (NULL
!= aggd_url
)
263 gAggdBundleRef
= CFBundleCreate(kCFAllocatorDefault
, aggd_url
);
264 if (NULL
!= gAggdBundleRef
)
266 gADClientClearScalarKey
= (type_ADClientClearScalarKey
)
267 CFBundleGetFunctionPointerForName(gAggdBundleRef
, CFSTR("ADClientClearScalarKey"));
269 gADClientAddValueForScalarKey
= (type_ADClientAddValueForScalarKey
)
270 CFBundleGetFunctionPointerForName(gAggdBundleRef
, CFSTR("ADClientAddValueForScalarKey"));
276 gFunctionPointersAreLoaded
= ((NULL
!= gADClientClearScalarKey
) && (NULL
!= gADClientAddValueForScalarKey
));
277 return gFunctionPointersAreLoaded
;
280 /* --------------------------------------------------------------------------
281 Function: Internal_ADClientClearScalarKey
283 Description: This function is a wrapper around calling the
284 ADClientClearScalarKey function.
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
)
292 if (InitializeADFunctionPointers())
294 CFStringRef real_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%s.%@"), gTopLevelKeyForCertificateTracing
, key
);
295 if (NULL
== real_key
)
300 gADClientClearScalarKey(real_key
);
305 /* --------------------------------------------------------------------------
306 Function: Internal_ADClientAddValueForScalarKey
308 Description: This function is a wrapper around calling the
309 ADClientAddValueForScalarKey function.
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
)
317 if (InitializeADFunctionPointers())
319 CFStringRef real_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%s.%@"), gTopLevelKeyForCertificateTracing
, key
);
320 if (NULL
== real_key
)
325 gADClientAddValueForScalarKey(real_key
, value
);
331 /* --------------------------------------------------------------------------
332 Function: iOS_SetCertificateTraceValueForKey
334 Description: This function is a wrapper around calling either
335 ADClientAddValueForScalarKey or ADClientClearScalarKey,
336 depending on whether the value is 0.
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
)
351 Internal_ADClientClearScalarKey(key
);
355 Internal_ADClientAddValueForScalarKey(key
, value
);
360 /* --------------------------------------------------------------------------
361 Function: iOS_AddKeyValuePairToCertificateLoggingTransaction
363 Description: For iOS there is no "bunching". This function will simply
364 call iOS_SetCloudKeychainTraceValueForKey to log the
366 -------------------------------------------------------------------------- */
367 static bool iOS_AddKeyValuePairToCertificateLoggingTransaction(void* token
, CFStringRef key
, int64_t value
)
369 #pragma unused(token)
370 return iOS_SetCertificateTraceValueForKey(key
, value
);
374 /* --------------------------------------------------------------------------
375 Function: SetCertificateTraceValueForKey
377 Description: SPI to log a single key-value pair with the logging system
378 -------------------------------------------------------------------------- */
379 bool SetCertificateTraceValueForKey(CFStringRef key
, int64_t value
)
381 #if (TARGET_IPHONE_SIMULATOR)
385 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
386 return OSX_SetCertificateTraceValueForKey(key
, value
);
389 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
390 return iOS_SetCertificateTraceValueForKey(key
, value
);
394 /* --------------------------------------------------------------------------
395 Function: BeginCertificateLoggingTransaction
397 Description: SPI to begin a logging transaction
398 -------------------------------------------------------------------------- */
399 void* BeginCertificateLoggingTransaction(void)
401 #if (TARGET_IPHONE_SIMULATOR)
405 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
406 return OSX_BeginCertificateLoggingTransaction();
409 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
414 /* --------------------------------------------------------------------------
415 Function: AddKeyValuePairToCertificateLoggingTransaction
417 Description: SPI to add a key-value pair to an outstanding logging
419 -------------------------------------------------------------------------- */
420 bool AddKeyValuePairToCertificateLoggingTransaction(void* token
, CFStringRef key
, int64_t value
)
422 #if (TARGET_IPHONE_SIMULATOR)
426 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
427 return OSX_AddKeyValuePairToCertificateLoggingTransaction(token
, key
, value
);
430 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
431 return iOS_AddKeyValuePairToCertificateLoggingTransaction(token
, key
, value
);
435 /* --------------------------------------------------------------------------
436 Function: CloseCertificateLoggingTransaction
438 Description: SPI to complete a logging transaction and clean up the
440 -------------------------------------------------------------------------- */
441 void CloseCertificateLoggingTransaction(void* token
)
443 #if (TARGET_IPHONE_SIMULATOR)
447 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
448 OSX_CloseCertificateLoggingTransaction(token
);
451 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
456 /* --------------------------------------------------------------------------
457 Function: SecCFStringCopyEncodingDelimiters
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 -------------------------------------------------------------------------- */
466 CFStringRef
SecCFStringCopyEncodingDelimiters(CFStringRef str
, CFStringRef delimiters
)
468 CFMutableStringRef newStr
= (str
) ? CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, str
) : NULL
;
469 if (str
&& delimiters
)
471 CFStringRef stringToFind
= delimiters
;
472 CFStringRef replacementString
= CFSTR("_");
473 CFStringFindAndReplace(newStr
, stringToFind
, replacementString
, CFRangeMake(0, CFStringGetLength(newStr
)), 0);
475 return (CFStringRef
) newStr
;
478 /* --------------------------------------------------------------------------
479 Function: SecCertificateTraceAddRecord
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
)
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
);
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
509 CFReleaseSafe(policy
);
510 CFReleaseSafe(notBefore
);
511 CFReleaseSafe(notAfter
);
512 CFReleaseSafe(serial
);
513 CFReleaseSafe(subject
);
514 CFReleaseSafe(issuer
);
516 result
= SetCertificateTraceValueForKey(keyStr
, 1);
518 secerror("%@.%@ (%d)", kCertStatsPrefix
, keyStr
, (result
) ? 1 : 0);
520 CFReleaseSafe(keyStr
);