]> git.saurik.com Git - apple/security.git/blobdiff - Security/utilities/src/SecCertificateTrace.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / utilities / src / SecCertificateTrace.c
diff --git a/Security/utilities/src/SecCertificateTrace.c b/Security/utilities/src/SecCertificateTrace.c
new file mode 100644 (file)
index 0000000..e3246cd
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#include "SecCertificateTrace.h"
+#include <TargetConditionals.h>
+#include <inttypes.h>
+#include "SecCFWrappers.h"
+#include <sys/time.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include "debugging.h"
+
+#define SEC_CONST_DECL(k,v) CFTypeRef k = (CFTypeRef)(CFSTR(v));
+
+SEC_CONST_DECL (kCertStatsPrefix, "com.apple.certstats");
+SEC_CONST_DECL (kCertStatsCert, "cert");
+SEC_CONST_DECL (kCertStatsPolicy, "id");
+SEC_CONST_DECL (kCertStatsNotBefore, "nb");
+SEC_CONST_DECL (kCertStatsNotAfter, "na");
+SEC_CONST_DECL (kCertStatsSerialNumber, "sn");
+SEC_CONST_DECL (kCertStatsSubjectSummary, "s");
+SEC_CONST_DECL (kCertStatsIssuerSummary, "i");
+
+static const CFStringRef kCertStatsFormat = CFSTR("%@/%@=%@/%@=%@/%@=%@/%@=%@/%@=%@/%@=%@");
+static const CFStringRef kCertStatsDelimiters = CFSTR("/");
+
+bool SetCertificateTraceValueForKey(CFStringRef key, int64_t value);
+void* BeginCertificateLoggingTransaction(void);
+bool AddKeyValuePairToCertificateLoggingTransaction(void* token, CFStringRef key, int64_t value);
+void CloseCertificateLoggingTransaction(void* token);
+CFStringRef SecCFStringCopyEncodingDelimiters(CFStringRef str, CFStringRef delimiters);
+
+#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
+#include <asl.h>
+
+static const char* gTopLevelKeyForCertificateTracing = "com.apple.certstats";
+
+static const char* gMessageTracerSetPrefix = "com.apple.message.";
+
+static const char* gMessageTracerDomainField = "com.apple.message.domain";
+
+/* --------------------------------------------------------------------------
+       Function:               OSX_BeginCertificateLoggingTransaction
+
+       Description:    For OSX, the message tracer back end wants its logging
+                                       done in "bunches".  This function allows for beginning
+                                       a 'transaction' of logging which will allow for putting
+                                       all of the transaction's items into a single log making
+                                       the message tracer folks happy.
+
+                                       The work of this function is to create the aslmsg context
+                                       and set the domain field and then return the aslmsg
+                                       context as a void* result.
+   -------------------------------------------------------------------------- */
+static void* OSX_BeginCertificateLoggingTransaction()
+{
+       void* result = NULL;
+       aslmsg mAsl = NULL;
+       mAsl = asl_new(ASL_TYPE_MSG);
+       if (NULL == mAsl)
+       {
+               return result;
+       }
+
+       asl_set(mAsl, gMessageTracerDomainField, gTopLevelKeyForCertificateTracing);
+
+       result = (void *)mAsl;
+       return result;
+}
+
+/* --------------------------------------------------------------------------
+       Function:               OSX_AddKeyValuePairToCertificateLoggingTransaction
+
+       Description:    Once a call to OSX_BeginCertificateLoggingTransaction
+                                       is done, this call will allow for adding items to the
+                                       "bunch" of items being logged.
+
+                                       NOTE: The key should be a simple key such as
+                                       "numberOfPeers".  This is because this function will
+                                       append the required prefix of "com.apple.message."
+   -------------------------------------------------------------------------- */
+static bool OSX_AddKeyValuePairToCertificateLoggingTransaction(void* token, CFStringRef key, int64_t value)
+{
+       if (NULL == token || NULL == key)
+       {
+               return false;
+       }
+
+       aslmsg mAsl = (aslmsg)token;
+
+       // Fix up the key
+       CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%@"), gMessageTracerSetPrefix, key);
+       if (NULL == real_key)
+       {
+               return false;
+       }
+
+       CFIndex key_length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(real_key), kCFStringEncodingUTF8);
+    key_length += 1; // For null
+    char key_buffer[key_length];
+    memset(key_buffer, 0,key_length);
+    if (!CFStringGetCString(real_key, key_buffer, key_length, kCFStringEncodingUTF8))
+    {
+        CFRelease(real_key);
+        return false;
+    }
+       CFRelease(real_key);
+
+       CFStringRef value_str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%lld"), value);
+    if (NULL == value_str)
+    {
+        return false;
+    }
+
+    CFIndex value_str_numBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value_str), kCFStringEncodingUTF8);
+    value_str_numBytes += 1; // For null
+    char value_buffer[value_str_numBytes];
+    memset(value_buffer, 0, value_str_numBytes);
+    if (!CFStringGetCString(value_str, value_buffer, value_str_numBytes, kCFStringEncodingUTF8))
+    {
+        CFRelease(value_str);
+        return false;
+    }
+    CFRelease(value_str);
+
+       asl_set(mAsl, key_buffer, value_buffer);
+       return true;
+}
+
+/* --------------------------------------------------------------------------
+       Function:               OSX_CloseCertificateLoggingTransaction
+
+       Description:    Once a call to OSX_BeginCertificateLoggingTransaction
+                                       is done, and all of the items that are to be in the
+                                       "bunch" of items being logged, this function will do the
+                                       real logging and free the aslmsg context.
+   -------------------------------------------------------------------------- */
+static void OSX_CloseCertificateLoggingTransaction(void* token)
+{
+       if (NULL != token)
+       {
+               aslmsg mAsl = (aslmsg)token;
+               asl_log(NULL, mAsl, ASL_LEVEL_NOTICE, "");
+               asl_free(mAsl);
+       }
+}
+
+/* --------------------------------------------------------------------------
+       Function:               OSX_SetCertificateTraceValueForKey
+
+       Description:    If "bunching" of items either cannot be done or is not
+                                       desired, then this 'single shot' function should be used.
+                                       It will create the aslmsg context, register the domain,
+                                       fix up the key and log the key value pair, and then
+                                       do the real logging and free the aslmsg context.
+   -------------------------------------------------------------------------- */
+static bool OSX_SetCertificateTraceValueForKey(CFStringRef key, int64_t value)
+{
+       bool result = false;
+
+       if (NULL == key)
+       {
+               return result;
+       }
+
+       aslmsg mAsl = NULL;
+       mAsl = asl_new(ASL_TYPE_MSG);
+       if (NULL == mAsl)
+       {
+               return result;
+       }
+
+       // Fix up the key
+       CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%@"), gMessageTracerSetPrefix, key);
+       if (NULL == real_key)
+       {
+               return false;
+       }
+
+       CFIndex key_length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(real_key), kCFStringEncodingUTF8);
+    key_length += 1; // For null
+    char key_buffer[key_length];
+    memset(key_buffer, 0,key_length);
+    if (!CFStringGetCString(real_key, key_buffer, key_length, kCFStringEncodingUTF8))
+    {
+        CFRelease(real_key);
+        return false;
+    }
+       CFRelease(real_key);
+
+
+       CFStringRef value_str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%lld"), value);
+    if (NULL == value_str)
+    {
+        asl_free(mAsl);
+        return result;
+    }
+
+    CFIndex value_str_numBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value_str), kCFStringEncodingUTF8);
+    value_str_numBytes += 1; // For null
+    char value_buffer[value_str_numBytes];
+    memset(value_buffer, 0, value_str_numBytes);
+    if (!CFStringGetCString(value_str, value_buffer, value_str_numBytes, kCFStringEncodingUTF8))
+    {
+        asl_free(mAsl);
+        CFRelease(value_str);
+        return result;
+    }
+    CFRelease(value_str);
+
+       /* Domain */
+       asl_set(mAsl, gMessageTracerDomainField, gTopLevelKeyForCertificateTracing);
+
+       /* Our custom key (starts with com.apple.message.) and its value */
+       asl_set(mAsl, key_buffer, value_buffer);
+
+       /* Log this ASL record (note actual log message is empty) */
+       asl_log(NULL, mAsl, ASL_LEVEL_NOTICE, "");
+       asl_free(mAsl);
+       return true;
+
+}
+#endif
+
+#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
+
+static const char* gTopLevelKeyForCertificateTracing = "com.apple.certstats";
+
+typedef void (*type_ADClientClearScalarKey)(CFStringRef key);
+typedef void (*type_ADClientAddValueForScalarKey)(CFStringRef key, int64_t value);
+
+static type_ADClientClearScalarKey gADClientClearScalarKey = NULL;
+static type_ADClientAddValueForScalarKey gADClientAddValueForScalarKey = NULL;
+
+static dispatch_once_t gADFunctionPointersSet = 0;
+static CFBundleRef gAggdBundleRef = NULL;
+static bool gFunctionPointersAreLoaded = false;
+
+/* --------------------------------------------------------------------------
+       Function:               InitializeADFunctionPointers
+
+       Description:    Linking to the Aggregate library causes a build cycle,
+                                       so this function dynamically loads the needed function
+                                       pointers.
+   -------------------------------------------------------------------------- */
+static bool InitializeADFunctionPointers()
+{
+       if (gFunctionPointersAreLoaded)
+       {
+               return gFunctionPointersAreLoaded;
+       }
+
+    dispatch_once(&gADFunctionPointersSet,
+      ^{
+          CFStringRef path_to_aggd_framework = CFSTR("/System/Library/PrivateFrameworks/AggregateDictionary.framework");
+
+          CFURLRef aggd_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_to_aggd_framework, kCFURLPOSIXPathStyle, true);
+
+          if (NULL != aggd_url)
+          {
+              gAggdBundleRef = CFBundleCreate(kCFAllocatorDefault, aggd_url);
+              if (NULL != gAggdBundleRef)
+              {
+                  gADClientClearScalarKey = (type_ADClientClearScalarKey)
+                    CFBundleGetFunctionPointerForName(gAggdBundleRef, CFSTR("ADClientClearScalarKey"));
+
+                  gADClientAddValueForScalarKey = (type_ADClientAddValueForScalarKey)
+                    CFBundleGetFunctionPointerForName(gAggdBundleRef, CFSTR("ADClientAddValueForScalarKey"));
+              }
+              CFRelease(aggd_url);
+          }
+      });
+
+    gFunctionPointersAreLoaded = ((NULL != gADClientClearScalarKey) && (NULL != gADClientAddValueForScalarKey));
+    return gFunctionPointersAreLoaded;
+}
+
+/* --------------------------------------------------------------------------
+       Function:               Internal_ADClientClearScalarKey
+
+       Description:    This function is a wrapper around calling the
+                                       ADClientClearScalarKey function.
+
+                                       NOTE: The key should be a simple key such as
+                                       "numberOfPeers".  This is because this function will
+                                       append the required prefix of "com.apple.certstats"
+   -------------------------------------------------------------------------- */
+static void Internal_ADClientClearScalarKey(CFStringRef key)
+{
+    if (InitializeADFunctionPointers())
+    {
+               CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%@"), gTopLevelKeyForCertificateTracing, key);
+               if (NULL == real_key)
+               {
+                       return;
+               }
+
+        gADClientClearScalarKey(real_key);
+               CFRelease(real_key);
+    }
+}
+
+/* --------------------------------------------------------------------------
+       Function:               Internal_ADClientAddValueForScalarKey
+
+       Description:    This function is a wrapper around calling the
+                                       ADClientAddValueForScalarKey function.
+
+                                       NOTE: The key should be a simple key such as
+                                       "numberOfPeers".  This is because this function will
+                                       append the required prefix of "com.apple.certstats"
+   -------------------------------------------------------------------------- */
+static void Internal_ADClientAddValueForScalarKey(CFStringRef key, int64_t value)
+{
+    if (InitializeADFunctionPointers())
+    {
+               CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%@"), gTopLevelKeyForCertificateTracing, key);
+               if (NULL == real_key)
+               {
+                       return;
+               }
+
+        gADClientAddValueForScalarKey(real_key, value);
+               CFRelease(real_key);
+    }
+}
+
+
+/* --------------------------------------------------------------------------
+       Function:               iOS_SetCertificateTraceValueForKey
+
+       Description:    This function is a wrapper around calling either
+                                       ADClientAddValueForScalarKey or ADClientClearScalarKey,
+                                       depending on whether the value is 0.
+
+                                       NOTE: The key should be a simple key such as
+                                       "numberOfPeers".  This is because this function will
+                                       append the required prefix of "com.apple.certstats"
+   -------------------------------------------------------------------------- */
+static bool iOS_SetCertificateTraceValueForKey(CFStringRef key, int64_t value)
+{
+       if (NULL == key)
+       {
+               return false;
+       }
+
+    if (0LL == value)
+    {
+        Internal_ADClientClearScalarKey(key);
+    }
+    else
+    {
+        Internal_ADClientAddValueForScalarKey(key, value);
+    }
+       return true;
+}
+
+/* --------------------------------------------------------------------------
+       Function:               iOS_AddKeyValuePairToCertificateLoggingTransaction
+
+       Description:    For iOS there is no "bunching". This function will simply
+                                       call iOS_SetCloudKeychainTraceValueForKey to log the
+                                       key-value pair.
+   -------------------------------------------------------------------------- */
+static bool iOS_AddKeyValuePairToCertificateLoggingTransaction(void* token, CFStringRef key, int64_t value)
+{
+#pragma unused(token)
+       return iOS_SetCertificateTraceValueForKey(key, value);
+}
+#endif
+
+/* --------------------------------------------------------------------------
+       Function:               SetCertificateTraceValueForKey
+
+       Description:    SPI to log a single key-value pair with the logging system
+   -------------------------------------------------------------------------- */
+bool SetCertificateTraceValueForKey(CFStringRef key, int64_t value)
+{
+#if (TARGET_IPHONE_SIMULATOR)
+       return false;
+#endif
+
+#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
+       return OSX_SetCertificateTraceValueForKey(key, value);
+#endif
+
+#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
+       return iOS_SetCertificateTraceValueForKey(key, value);
+#endif
+}
+
+/* --------------------------------------------------------------------------
+       Function:               BeginCertificateLoggingTransaction
+
+       Description:    SPI to begin a logging transaction
+   -------------------------------------------------------------------------- */
+void* BeginCertificateLoggingTransaction(void)
+{
+#if (TARGET_IPHONE_SIMULATOR)
+       return (void *)-1;
+#endif
+
+#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
+       return OSX_BeginCertificateLoggingTransaction();
+#endif
+
+#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
+       return NULL;
+#endif
+}
+
+/* --------------------------------------------------------------------------
+       Function:               AddKeyValuePairToCertificateLoggingTransaction
+
+       Description:    SPI to add a key-value pair to an outstanding logging
+                                       transaction
+   -------------------------------------------------------------------------- */
+bool AddKeyValuePairToCertificateLoggingTransaction(void* token, CFStringRef key, int64_t value)
+{
+#if (TARGET_IPHONE_SIMULATOR)
+       return false;
+#endif
+
+#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
+       return OSX_AddKeyValuePairToCertificateLoggingTransaction(token, key, value);
+#endif
+
+#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
+       return iOS_AddKeyValuePairToCertificateLoggingTransaction(token, key, value);
+#endif
+}
+
+/* --------------------------------------------------------------------------
+       Function:               CloseCertificateLoggingTransaction
+
+       Description:    SPI to complete a logging transaction and clean up the
+                                       context
+   -------------------------------------------------------------------------- */
+void CloseCertificateLoggingTransaction(void* token)
+{
+#if (TARGET_IPHONE_SIMULATOR)
+       ; // nothing
+#endif
+
+#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
+       OSX_CloseCertificateLoggingTransaction(token);
+#endif
+
+#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
+       ; // nothing
+#endif
+}
+
+/* --------------------------------------------------------------------------
+ Function:             SecCFStringCopyEncodingDelimiters
+
+ Description:  Utility to replace all characters in the given string
+                which match characters in the delimiters string. This
+                should be expanded later; for now we are assuming the
+                delimiters string is a single character, and we replace
+                it with an underscore instead of percent encoding.
+ -------------------------------------------------------------------------- */
+
+CFStringRef SecCFStringCopyEncodingDelimiters(CFStringRef str, CFStringRef delimiters)
+{
+    CFMutableStringRef newStr = (str) ? CFStringCreateMutableCopy(kCFAllocatorDefault, 0, str) : NULL;
+    if (newStr && delimiters)
+    {
+        CFStringRef replacementString = CFSTR("_");
+               CFIndex idx, count = CFStringGetLength(delimiters);
+               for (idx = 0; idx < count; idx++) {
+                       UniChar uc = CFStringGetCharacterAtIndex(delimiters, idx);
+                       CFStringRef stringToFind = CFStringCreateWithCharacters(kCFAllocatorDefault, &uc, 1);
+                       if (stringToFind) {
+        CFStringFindAndReplace(newStr, stringToFind, replacementString, CFRangeMake(0, CFStringGetLength(newStr)), 0);
+                               CFRelease(stringToFind);
+    }
+               }
+    }
+    return (CFStringRef) newStr;
+}
+
+/* --------------------------------------------------------------------------
+       Function:               SecCertificateTraceAddRecord
+
+       Description:    High-level SPI that logs a single certificate record,
+                                       given a dictionary containing key-value pairs.
+   -------------------------------------------------------------------------- */
+bool SecCertificateTraceAddRecord(CFDictionaryRef certRecord)
+{
+       bool result = false;
+       if (!certRecord)
+               return result;
+
+       /* encode delimiter characters in supplied strings */
+    CFStringRef policy = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsPolicy), kCertStatsDelimiters);
+    CFStringRef notBefore = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsNotBefore), kCertStatsDelimiters);
+    CFStringRef notAfter = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsNotAfter), kCertStatsDelimiters);
+    CFStringRef serial = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsSerialNumber), kCertStatsDelimiters);
+    CFStringRef subject = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsSubjectSummary), kCertStatsDelimiters);
+    CFStringRef issuer = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsIssuerSummary), kCertStatsDelimiters);
+
+       /* this generates a key which includes delimited fields to identify the certificate */
+       CFStringRef keyStr = CFStringCreateWithFormat(NULL, NULL,
+               kCertStatsFormat, /* format string */
+               kCertStatsCert, /* certificate key */
+               kCertStatsPolicy, policy,
+               kCertStatsNotBefore, notBefore,
+               kCertStatsNotAfter, notAfter,
+               kCertStatsSerialNumber, serial,
+               kCertStatsSubjectSummary, subject,
+               kCertStatsIssuerSummary, issuer
+       );
+    CFReleaseSafe(policy);
+    CFReleaseSafe(notBefore);
+    CFReleaseSafe(notAfter);
+    CFReleaseSafe(serial);
+    CFReleaseSafe(subject);
+    CFReleaseSafe(issuer);
+
+       result = SetCertificateTraceValueForKey(keyStr, 1);
+#if DEBUG
+       secerror("%@.%@ (%d)", kCertStatsPrefix, keyStr, (result) ? 1 : 0);
+#endif
+       CFReleaseSafe(keyStr);
+
+       return result;
+}
+