2 * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 #include "SecCertificateTrace.h"
26 #include <TargetConditionals.h>
28 #include "SecCFWrappers.h"
30 #include <CoreFoundation/CoreFoundation.h>
31 #include "debugging.h"
33 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
35 SEC_CONST_DECL (kCertStatsPrefix
, "com.apple.certstats");
36 SEC_CONST_DECL (kCertStatsCert
, "cert");
37 SEC_CONST_DECL (kCertStatsPolicy
, "id");
38 SEC_CONST_DECL (kCertStatsNotBefore
, "nb");
39 SEC_CONST_DECL (kCertStatsNotAfter
, "na");
40 SEC_CONST_DECL (kCertStatsSerialNumber
, "sn");
41 SEC_CONST_DECL (kCertStatsSubjectSummary
, "s");
42 SEC_CONST_DECL (kCertStatsIssuerSummary
, "i");
44 static const CFStringRef kCertStatsFormat
= CFSTR("%@/%@=%@/%@=%@/%@=%@/%@=%@/%@=%@/%@=%@");
45 static const CFStringRef kCertStatsDelimiters
= CFSTR("/");
47 bool SetCertificateTraceValueForKey(CFStringRef key
, int64_t value
);
48 void* BeginCertificateLoggingTransaction(void);
49 bool AddKeyValuePairToCertificateLoggingTransaction(void* token
, CFStringRef key
, int64_t value
);
50 void CloseCertificateLoggingTransaction(void* token
);
51 CFStringRef
SecCFStringCopyEncodingDelimiters(CFStringRef str
, CFStringRef delimiters
);
53 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
56 static const char* gTopLevelKeyForCertificateTracing
= "com.apple.certstats";
58 static const char* gMessageTracerSetPrefix
= "com.apple.message.";
60 static const char* gMessageTracerDomainField
= "com.apple.message.domain";
62 /* --------------------------------------------------------------------------
63 Function: OSX_BeginCertificateLoggingTransaction
65 Description: For OSX, the message tracer back end wants its logging
66 done in "bunches". This function allows for beginning
67 a 'transaction' of logging which will allow for putting
68 all of the transaction's items into a single log making
69 the message tracer folks happy.
71 The work of this function is to create the aslmsg context
72 and set the domain field and then return the aslmsg
73 context as a void* result.
74 -------------------------------------------------------------------------- */
75 static void* OSX_BeginCertificateLoggingTransaction()
79 mAsl
= asl_new(ASL_TYPE_MSG
);
85 asl_set(mAsl
, gMessageTracerDomainField
, gTopLevelKeyForCertificateTracing
);
87 result
= (void *)mAsl
;
91 /* --------------------------------------------------------------------------
92 Function: OSX_AddKeyValuePairToCertificateLoggingTransaction
94 Description: Once a call to OSX_BeginCertificateLoggingTransaction
95 is done, this call will allow for adding items to the
96 "bunch" of items being logged.
98 NOTE: The key should be a simple key such as
99 "numberOfPeers". This is because this function will
100 append the required prefix of "com.apple.message."
101 -------------------------------------------------------------------------- */
102 static bool OSX_AddKeyValuePairToCertificateLoggingTransaction(void* token
, CFStringRef key
, int64_t value
)
104 if (NULL
== token
|| NULL
== key
)
109 aslmsg mAsl
= (aslmsg
)token
;
112 CFStringRef real_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%s%@"), gMessageTracerSetPrefix
, key
);
113 if (NULL
== real_key
)
118 CFIndex key_length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(real_key
), kCFStringEncodingUTF8
);
119 key_length
+= 1; // For null
120 char key_buffer
[key_length
];
121 memset(key_buffer
, 0,key_length
);
122 if (!CFStringGetCString(real_key
, key_buffer
, key_length
, kCFStringEncodingUTF8
))
129 CFStringRef value_str
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%lld"), value
);
130 if (NULL
== value_str
)
135 CFIndex value_str_numBytes
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(value_str
), kCFStringEncodingUTF8
);
136 value_str_numBytes
+= 1; // For null
137 char value_buffer
[value_str_numBytes
];
138 memset(value_buffer
, 0, value_str_numBytes
);
139 if (!CFStringGetCString(value_str
, value_buffer
, value_str_numBytes
, kCFStringEncodingUTF8
))
141 CFRelease(value_str
);
144 CFRelease(value_str
);
146 asl_set(mAsl
, key_buffer
, value_buffer
);
150 /* --------------------------------------------------------------------------
151 Function: OSX_CloseCertificateLoggingTransaction
153 Description: Once a call to OSX_BeginCertificateLoggingTransaction
154 is done, and all of the items that are to be in the
155 "bunch" of items being logged, this function will do the
156 real logging and free the aslmsg context.
157 -------------------------------------------------------------------------- */
158 static void OSX_CloseCertificateLoggingTransaction(void* token
)
162 aslmsg mAsl
= (aslmsg
)token
;
163 asl_log(NULL
, mAsl
, ASL_LEVEL_NOTICE
, "");
168 /* --------------------------------------------------------------------------
169 Function: OSX_SetCertificateTraceValueForKey
171 Description: If "bunching" of items either cannot be done or is not
172 desired, then this 'single shot' function should be used.
173 It will create the aslmsg context, register the domain,
174 fix up the key and log the key value pair, and then
175 do the real logging and free the aslmsg context.
176 -------------------------------------------------------------------------- */
177 static bool OSX_SetCertificateTraceValueForKey(CFStringRef key
, int64_t value
)
187 mAsl
= asl_new(ASL_TYPE_MSG
);
194 CFStringRef real_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%s%@"), gMessageTracerSetPrefix
, key
);
195 if (NULL
== real_key
)
200 CFIndex key_length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(real_key
), kCFStringEncodingUTF8
);
201 key_length
+= 1; // For null
202 char key_buffer
[key_length
];
203 memset(key_buffer
, 0,key_length
);
204 if (!CFStringGetCString(real_key
, key_buffer
, key_length
, kCFStringEncodingUTF8
))
212 CFStringRef value_str
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%lld"), value
);
213 if (NULL
== value_str
)
219 CFIndex value_str_numBytes
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(value_str
), kCFStringEncodingUTF8
);
220 value_str_numBytes
+= 1; // For null
221 char value_buffer
[value_str_numBytes
];
222 memset(value_buffer
, 0, value_str_numBytes
);
223 if (!CFStringGetCString(value_str
, value_buffer
, value_str_numBytes
, kCFStringEncodingUTF8
))
226 CFRelease(value_str
);
229 CFRelease(value_str
);
232 asl_set(mAsl
, gMessageTracerDomainField
, gTopLevelKeyForCertificateTracing
);
234 /* Our custom key (starts with com.apple.message.) and its value */
235 asl_set(mAsl
, key_buffer
, value_buffer
);
237 /* Log this ASL record (note actual log message is empty) */
238 asl_log(NULL
, mAsl
, ASL_LEVEL_NOTICE
, "");
245 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
247 static const char* gTopLevelKeyForCertificateTracing
= "com.apple.certstats";
249 typedef void (*type_ADClientClearScalarKey
)(CFStringRef key
);
250 typedef void (*type_ADClientAddValueForScalarKey
)(CFStringRef key
, int64_t value
);
252 static type_ADClientClearScalarKey gADClientClearScalarKey
= NULL
;
253 static type_ADClientAddValueForScalarKey gADClientAddValueForScalarKey
= NULL
;
255 static dispatch_once_t gADFunctionPointersSet
= 0;
256 static CFBundleRef gAggdBundleRef
= NULL
;
257 static bool gFunctionPointersAreLoaded
= false;
259 /* --------------------------------------------------------------------------
260 Function: InitializeADFunctionPointers
262 Description: Linking to the Aggregate library causes a build cycle,
263 so this function dynamically loads the needed function
265 -------------------------------------------------------------------------- */
266 static bool InitializeADFunctionPointers()
268 if (gFunctionPointersAreLoaded
)
270 return gFunctionPointersAreLoaded
;
273 dispatch_once(&gADFunctionPointersSet
,
275 CFStringRef path_to_aggd_framework
= CFSTR("/System/Library/PrivateFrameworks/AggregateDictionary.framework");
277 CFURLRef aggd_url
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, path_to_aggd_framework
, kCFURLPOSIXPathStyle
, true);
279 if (NULL
!= aggd_url
)
281 gAggdBundleRef
= CFBundleCreate(kCFAllocatorDefault
, aggd_url
);
282 if (NULL
!= gAggdBundleRef
)
284 gADClientClearScalarKey
= (type_ADClientClearScalarKey
)
285 CFBundleGetFunctionPointerForName(gAggdBundleRef
, CFSTR("ADClientClearScalarKey"));
287 gADClientAddValueForScalarKey
= (type_ADClientAddValueForScalarKey
)
288 CFBundleGetFunctionPointerForName(gAggdBundleRef
, CFSTR("ADClientAddValueForScalarKey"));
294 gFunctionPointersAreLoaded
= ((NULL
!= gADClientClearScalarKey
) && (NULL
!= gADClientAddValueForScalarKey
));
295 return gFunctionPointersAreLoaded
;
298 /* --------------------------------------------------------------------------
299 Function: Internal_ADClientClearScalarKey
301 Description: This function is a wrapper around calling the
302 ADClientClearScalarKey function.
304 NOTE: The key should be a simple key such as
305 "numberOfPeers". This is because this function will
306 append the required prefix of "com.apple.certstats"
307 -------------------------------------------------------------------------- */
308 static void Internal_ADClientClearScalarKey(CFStringRef key
)
310 if (InitializeADFunctionPointers())
312 CFStringRef real_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%s.%@"), gTopLevelKeyForCertificateTracing
, key
);
313 if (NULL
== real_key
)
318 gADClientClearScalarKey(real_key
);
323 /* --------------------------------------------------------------------------
324 Function: Internal_ADClientAddValueForScalarKey
326 Description: This function is a wrapper around calling the
327 ADClientAddValueForScalarKey function.
329 NOTE: The key should be a simple key such as
330 "numberOfPeers". This is because this function will
331 append the required prefix of "com.apple.certstats"
332 -------------------------------------------------------------------------- */
333 static void Internal_ADClientAddValueForScalarKey(CFStringRef key
, int64_t value
)
335 if (InitializeADFunctionPointers())
337 CFStringRef real_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%s.%@"), gTopLevelKeyForCertificateTracing
, key
);
338 if (NULL
== real_key
)
343 gADClientAddValueForScalarKey(real_key
, value
);
349 /* --------------------------------------------------------------------------
350 Function: iOS_SetCertificateTraceValueForKey
352 Description: This function is a wrapper around calling either
353 ADClientAddValueForScalarKey or ADClientClearScalarKey,
354 depending on whether the value is 0.
356 NOTE: The key should be a simple key such as
357 "numberOfPeers". This is because this function will
358 append the required prefix of "com.apple.certstats"
359 -------------------------------------------------------------------------- */
360 static bool iOS_SetCertificateTraceValueForKey(CFStringRef key
, int64_t value
)
369 Internal_ADClientClearScalarKey(key
);
373 Internal_ADClientAddValueForScalarKey(key
, value
);
378 /* --------------------------------------------------------------------------
379 Function: iOS_AddKeyValuePairToCertificateLoggingTransaction
381 Description: For iOS there is no "bunching". This function will simply
382 call iOS_SetCloudKeychainTraceValueForKey to log the
384 -------------------------------------------------------------------------- */
385 static bool iOS_AddKeyValuePairToCertificateLoggingTransaction(void* token
, CFStringRef key
, int64_t value
)
387 #pragma unused(token)
388 return iOS_SetCertificateTraceValueForKey(key
, value
);
392 /* --------------------------------------------------------------------------
393 Function: SetCertificateTraceValueForKey
395 Description: SPI to log a single key-value pair with the logging system
396 -------------------------------------------------------------------------- */
397 bool SetCertificateTraceValueForKey(CFStringRef key
, int64_t value
)
399 #if (TARGET_IPHONE_SIMULATOR)
403 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
404 return OSX_SetCertificateTraceValueForKey(key
, value
);
407 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
408 return iOS_SetCertificateTraceValueForKey(key
, value
);
412 /* --------------------------------------------------------------------------
413 Function: BeginCertificateLoggingTransaction
415 Description: SPI to begin a logging transaction
416 -------------------------------------------------------------------------- */
417 void* BeginCertificateLoggingTransaction(void)
419 #if (TARGET_IPHONE_SIMULATOR)
423 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
424 return OSX_BeginCertificateLoggingTransaction();
427 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
432 /* --------------------------------------------------------------------------
433 Function: AddKeyValuePairToCertificateLoggingTransaction
435 Description: SPI to add a key-value pair to an outstanding logging
437 -------------------------------------------------------------------------- */
438 bool AddKeyValuePairToCertificateLoggingTransaction(void* token
, CFStringRef key
, int64_t value
)
440 #if (TARGET_IPHONE_SIMULATOR)
444 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
445 return OSX_AddKeyValuePairToCertificateLoggingTransaction(token
, key
, value
);
448 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
449 return iOS_AddKeyValuePairToCertificateLoggingTransaction(token
, key
, value
);
453 /* --------------------------------------------------------------------------
454 Function: CloseCertificateLoggingTransaction
456 Description: SPI to complete a logging transaction and clean up the
458 -------------------------------------------------------------------------- */
459 void CloseCertificateLoggingTransaction(void* token
)
461 #if (TARGET_IPHONE_SIMULATOR)
465 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
466 OSX_CloseCertificateLoggingTransaction(token
);
469 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
474 /* --------------------------------------------------------------------------
475 Function: SecCFStringCopyEncodingDelimiters
477 Description: Utility to replace all characters in the given string
478 which match characters in the delimiters string. This
479 should be expanded later; for now we are assuming the
480 delimiters string is a single character, and we replace
481 it with an underscore instead of percent encoding.
482 -------------------------------------------------------------------------- */
484 CFStringRef
SecCFStringCopyEncodingDelimiters(CFStringRef str
, CFStringRef delimiters
)
486 CFMutableStringRef newStr
= (str
) ? CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, str
) : NULL
;
487 if (newStr
&& delimiters
)
489 CFStringRef replacementString
= CFSTR("_");
490 CFIndex idx
, count
= CFStringGetLength(delimiters
);
491 for (idx
= 0; idx
< count
; idx
++) {
492 UniChar uc
= CFStringGetCharacterAtIndex(delimiters
, idx
);
493 CFStringRef stringToFind
= CFStringCreateWithCharacters(kCFAllocatorDefault
, &uc
, 1);
495 CFStringFindAndReplace(newStr
, stringToFind
, replacementString
, CFRangeMake(0, CFStringGetLength(newStr
)), 0);
496 CFRelease(stringToFind
);
500 return (CFStringRef
) newStr
;
503 /* --------------------------------------------------------------------------
504 Function: SecCertificateTraceAddRecord
506 Description: High-level SPI that logs a single certificate record,
507 given a dictionary containing key-value pairs.
508 -------------------------------------------------------------------------- */
509 bool SecCertificateTraceAddRecord(CFDictionaryRef certRecord
)
515 /* encode delimiter characters in supplied strings */
516 CFStringRef policy
= SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord
, kCertStatsPolicy
), kCertStatsDelimiters
);
517 CFStringRef notBefore
= SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord
, kCertStatsNotBefore
), kCertStatsDelimiters
);
518 CFStringRef notAfter
= SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord
, kCertStatsNotAfter
), kCertStatsDelimiters
);
519 CFStringRef serial
= SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord
, kCertStatsSerialNumber
), kCertStatsDelimiters
);
520 CFStringRef subject
= SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord
, kCertStatsSubjectSummary
), kCertStatsDelimiters
);
521 CFStringRef issuer
= SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord
, kCertStatsIssuerSummary
), kCertStatsDelimiters
);
523 /* this generates a key which includes delimited fields to identify the certificate */
524 CFStringRef keyStr
= CFStringCreateWithFormat(NULL
, NULL
,
525 kCertStatsFormat
, /* format string */
526 kCertStatsCert
, /* certificate key */
527 kCertStatsPolicy
, policy
,
528 kCertStatsNotBefore
, notBefore
,
529 kCertStatsNotAfter
, notAfter
,
530 kCertStatsSerialNumber
, serial
,
531 kCertStatsSubjectSummary
, subject
,
532 kCertStatsIssuerSummary
, issuer
534 CFReleaseSafe(policy
);
535 CFReleaseSafe(notBefore
);
536 CFReleaseSafe(notAfter
);
537 CFReleaseSafe(serial
);
538 CFReleaseSafe(subject
);
539 CFReleaseSafe(issuer
);
541 result
= SetCertificateTraceValueForKey(keyStr
, 1);
543 secerror("%@.%@ (%d)", kCertStatsPrefix
, keyStr
, (result
) ? 1 : 0);
545 CFReleaseSafe(keyStr
);