]> git.saurik.com Git - apple/security.git/blobdiff - Security/sec/securityd/iCloudTrace.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / sec / securityd / iCloudTrace.c
diff --git a/Security/sec/securityd/iCloudTrace.c b/Security/sec/securityd/iCloudTrace.c
new file mode 100644 (file)
index 0000000..2b3d2a1
--- /dev/null
@@ -0,0 +1,536 @@
+/*
+ * 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 "iCloudTrace.h"
+#include <SecureObjectSync/SOSCloudCircle.h>
+#include <Security/SecItem.h>
+#include <utilities/iCloudKeychainTrace.h>
+#include <securityd/SecItemServer.h>
+#include <sys/stat.h>
+#include <string.h>
+#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
+#include <pwd.h>
+#endif
+#include <utilities/SecCFWrappers.h>
+
+extern bool SOSCCThisDeviceDefinitelyNotActiveInCircle(void);
+
+
+#define MAX_PATH 1024
+
+/* --------------------------------------------------------------------------
+       Function:               GetNumberOfItemsBeingSynced
+       
+       Description:    Determine the number of items being synced.  NOTE:
+                                       This uses the SOSDataSourceFactoryRef instead of 
+                                       calling the SecItem interface because the SecItem 
+                                       interface requires an entitlement but the 
+                                       SOSDataSourceFactoryRef does not.
+   -------------------------------------------------------------------------- */
+static int64_t GetNumberOfItemsBeingSynced()
+{
+       __block int64_t result = 0;
+       SOSDataSourceFactoryRef dsFactRef = SecItemDataSourceFactoryGetDefault();
+       CFArrayRef ds_names = dsFactRef->copy_names(dsFactRef);
+    
+       if (ds_names) {
+        CFArrayForEach(ds_names, ^(const void *value) {
+            if (isString(value)) {
+                SOSManifestRef manifestRef = NULL;
+                SOSDataSourceRef ds = dsFactRef->create_datasource(dsFactRef, value, NULL);
+                
+                if (ds) {
+                    manifestRef = SOSDataSourceCopyManifest(ds, NULL);
+                    if (manifestRef)
+                        result += SOSManifestGetCount(manifestRef);
+                }
+                
+                CFReleaseSafe(manifestRef);
+                SOSDataSourceRelease(ds, NULL);
+            }
+        });
+    }
+
+       CFReleaseSafe(ds_names);
+       return result;
+}
+
+/* --------------------------------------------------------------------------
+       Function:               GetNumberOfPeers
+       
+       Description:    Determine the number of peers in the circle that this
+                                       device is in
+   -------------------------------------------------------------------------- */
+static int64_t GetNumberOfPeers()
+{
+       int64_t result = 0;
+       
+       CFErrorRef error = NULL;
+       CFArrayRef peers = NULL;
+       
+       peers = SOSCCCopyPeerPeerInfo(&error);
+       if (NULL != error)
+       {
+               CFRelease(error);
+               if (NULL != peers)
+               {
+                       CFRelease(peers);
+               }
+               return result;
+       }
+       
+       if (NULL != peers)
+       {
+               result = (int64_t)CFArrayGetCount(peers);
+               CFRelease(peers);
+       }
+       
+       return result;
+}
+
+static const char* kLoggingPlistPartialPath = "/Library/Preferences/com.apple.security.logging.plist";
+
+/* --------------------------------------------------------------------------
+       Function:               PathExists
+       
+       Description:    Utility fucntion to see if a file path exists
+   -------------------------------------------------------------------------- */
+static Boolean PathExists(const char* path, size_t* pFileSize)
+{
+       Boolean result = false;
+       struct stat         sb;
+       
+       if (NULL != pFileSize)
+       {
+               *pFileSize = 0;
+       }
+       
+       int stat_result = stat(path, &sb);
+       result = (stat_result == 0);
+       
+    if (result)
+    {
+        if (S_ISDIR(sb.st_mode))
+        {
+            // It is a directory
+            ;
+        }
+        else
+        {
+            // It is a file
+            if (NULL != pFileSize)
+            {
+                *pFileSize = (size_t)sb.st_size;
+            }
+        }
+    }
+       return result;
+}
+
+/* --------------------------------------------------------------------------
+       Function:               CopyFileContents
+       
+       Description:    Given a file path read the entire contents of the file 
+                                       into a CFDataRef
+   -------------------------------------------------------------------------- */
+static CFDataRef CopyFileContents(const char *path)
+{
+    CFMutableDataRef data = NULL;
+    int fd = open(path, O_RDONLY, 0666);
+
+    if (fd == -1) 
+       {
+        goto badFile;
+    }
+
+    off_t fsize = lseek(fd, 0, SEEK_END);
+    if (fsize == (off_t)-1) 
+       {
+        goto badFile;
+    }
+
+       if (fsize > (off_t)INT32_MAX) 
+       {
+               goto badFile;
+       }
+
+    data = CFDataCreateMutable(kCFAllocatorDefault, (CFIndex)fsize);
+       if (NULL == data)
+       {
+               goto badFile;
+       }
+       
+    CFDataSetLength(data, (CFIndex)fsize);
+    void *buf = CFDataGetMutableBytePtr(data);
+       if (NULL == buf)
+       {
+               goto badFile;
+       }
+       
+    off_t total_read = 0;
+    while (total_read < fsize) 
+       {
+        ssize_t bytes_read;
+
+        bytes_read = pread(fd, buf, (size_t)(fsize - total_read), total_read);
+        if (bytes_read == -1) 
+               {
+            goto badFile;
+        }
+        if (bytes_read == 0) 
+               {
+            goto badFile;
+        }
+        total_read += bytes_read;
+    }
+
+       close(fd);
+    return data;
+
+badFile:
+    if (fd != -1) 
+       {
+               close(fd);
+    }
+
+    if (data)
+       {       
+               CFRelease(data);
+       }
+        
+    return NULL;
+}
+
+static const CFStringRef kLoggingPlistKey = CFSTR("LoggingTime");
+
+/* --------------------------------------------------------------------------
+       Function:               CopyPlistPath
+       
+       Description:    Return the fully qualified file path to the logging
+                                       plist
+   -------------------------------------------------------------------------- */
+static const char* CopyPlistPath()
+{
+    const char* result = NULL;
+    CFURLRef url = NULL;
+    
+    
+#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
+    CFStringRef path_string = NULL;
+    const char *homeDir = getenv("HOME");
+    
+    if (!homeDir)
+    {
+        struct passwd* pwd = getpwuid(getuid());
+        if (pwd)
+            homeDir = pwd->pw_dir;
+    }
+    
+    path_string = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, homeDir, kCFStringEncodingUTF8, kCFAllocatorNull);
+    if (NULL == path_string)
+    {
+        return result;
+    }
+    
+    url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_string, kCFURLPOSIXPathStyle, true);
+    CFRelease(path_string);
+#endif
+#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
+    url =  CFCopyHomeDirectoryURL();
+#endif
+    
+    if (url)
+    {
+        UInt8 file_path_buffer[MAX_PATH+1];
+        memset(file_path_buffer, 0, MAX_PATH);
+        size_t length = 0;
+        
+        if(!CFURLGetFileSystemRepresentation(url, false, file_path_buffer, MAX_PATH))
+        {
+            CFRelease(url);
+            return result;  // better to log too much than not at all
+        }
+       
+        CFRelease(url);
+       
+        if (strnlen((const char *)file_path_buffer, MAX_PATH) + strlen(kLoggingPlistPartialPath) >= MAX_PATH)
+        {
+            return result;  // better to log too much than not at all
+        }
+       
+        strncat((char *)file_path_buffer, kLoggingPlistPartialPath, MAX_PATH);
+        length  = strlen((char *)file_path_buffer) + 1;
+        result = malloc(length);
+        if (NULL == result)
+        {
+            return result;
+        }
+        
+        memset((void *)result, 0, length);
+        strncpy((char *)result, (char *)file_path_buffer, length - 1);
+        return result;
+    }
+    
+    return NULL;
+    
+}
+
+/* --------------------------------------------------------------------------
+       Function:               ShouldLog
+       
+       Description:    Get the current time and match that against the value
+                                       in the logging plist and see if logging should be done
+   -------------------------------------------------------------------------- */
+static bool ShouldLog()
+{
+       bool result = false;
+       size_t fileSize = 0;
+       CFDataRef file_data = NULL;
+       CFPropertyListRef logging_time_data = NULL;
+       CFDataRef time_data = NULL;
+    const char* plist_path = NULL;
+       
+       plist_path =  CopyPlistPath();
+       if (NULL == plist_path)
+       {
+               return true;  // better to log too much than not at all
+       }
+       
+       if (!PathExists((const char *)plist_path, &fileSize))
+       {
+        free((void *)plist_path);
+               return true;  // better to log too much than not at all
+       }
+       
+       file_data = CopyFileContents((const char *)plist_path);
+    free((void *)plist_path);
+       if (NULL == file_data)
+       {
+               return true;  // better to log too much than not at all
+       }
+       
+       logging_time_data = CFPropertyListCreateWithData(kCFAllocatorDefault, file_data, kCFPropertyListMutableContainersAndLeaves, NULL, NULL);
+    CFRelease(file_data);
+
+       if (NULL == logging_time_data)
+       {
+               return true;  // better to log too much than not at all
+       }
+       
+       require_action(CFDictionaryGetTypeID() == CFGetTypeID(logging_time_data), xit, result = true); // better to log too much than not at all
+       
+       time_data = (CFDataRef)CFDictionaryGetValue((CFDictionaryRef)logging_time_data, kLoggingPlistKey);
+       require_action(time_data, xit, result = true); // better to log too much than not at all
+       
+       CFAbsoluteTime startTime = 0;
+    memcpy(&startTime, CFDataGetBytePtr(time_data), sizeof(startTime));
+
+    CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent();
+
+    int days = 0;
+
+    CFCalendarRef gregorian = CFCalendarCopyCurrent();
+    CFCalendarGetComponentDifference(gregorian, startTime, endTime, 0, "d",  &days);
+
+    CFRelease(gregorian);
+
+#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
+    if (days > 6) 
+    {
+               result = true;
+    }
+#endif
+            
+#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
+     if (days > 0)
+     {
+               result = true;
+     }
+#endif
+
+xit:
+       CFReleaseSafe(logging_time_data);
+    return result;
+}
+
+/* --------------------------------------------------------------------------
+       Function:               WriteOutLoggingTime
+       
+       Description:    Write out the logging plist with the time that the 
+                                       last logging was done.
+   -------------------------------------------------------------------------- */
+static void WriteOutLoggingTime(void)
+{
+       int fd = -1;
+       CFAbsoluteTime now;
+       CFDataRef now_data;
+       CFDictionaryRef plistData = NULL;
+       CFErrorRef error = NULL;
+       CFDataRef output_data = NULL;
+       
+       const char* filepath = CopyPlistPath();
+       if (NULL == filepath)
+    {
+               return;
+    }
+       
+       fd = open(filepath, (O_WRONLY | O_CREAT | O_TRUNC), 0666);
+    free((void *)filepath);
+       if (fd <= 0)
+       {
+               return;  
+       }
+       
+       now = CFAbsoluteTimeGetCurrent();
+       now_data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)&now, sizeof(now));
+       if (NULL == now_data)
+       {
+               close(fd);
+               return;
+       }
+       
+       plistData = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&kLoggingPlistKey, (const void **)&now_data, 1,
+                                    &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+       if (NULL == plistData)
+       {
+               close(fd);
+               CFRelease(now_data);
+               return;
+       }
+       CFRelease(now_data);
+       
+       output_data = CFPropertyListCreateData(kCFAllocatorDefault, plistData, kCFPropertyListBinaryFormat_v1_0, 0, &error);
+       CFRelease(plistData);
+       if (NULL != error || NULL == output_data)
+       {
+               close(fd);
+               if (NULL != error)
+               {
+                       CFRelease(error);
+               }
+               
+               if (NULL != output_data)
+               {
+                       CFRelease(output_data);
+               }
+               
+               return;
+       }
+       
+       write(fd, CFDataGetBytePtr(output_data), CFDataGetLength(output_data));
+       close(fd);
+       
+       CFRelease(output_data);
+       
+}
+
+/* --------------------------------------------------------------------------
+       Function:               Bucket
+       
+       Description:    In order to preserve annominity of a user, take an
+                                       absolute value and return back the most significant 
+                                       value in base 10 
+   -------------------------------------------------------------------------- */
+static int64_t Bucket(int64_t value)
+{
+    if (value < 10)
+    {
+       return value;
+    }
+
+    if (value < 100)
+    {
+        return (value / 10) * 10;
+    }
+
+    if (value < 1000)
+    {
+        return (value / 100) * 100;
+    }
+
+    if (value < 10000)
+    {
+        return (value / 1000) * 1000;
+    }
+
+    if (value < 100000)
+    {
+        return (value / 10000) * 10000;
+    }
+
+    if (value < 1000000)
+    {
+        return (value / 100000) * 10000;
+    }
+
+    return value;
+}
+
+/* --------------------------------------------------------------------------
+       Function:               DoLogging
+       
+       Description:    If it has been determined that logging should be done
+                                       this function will perform the logging
+   -------------------------------------------------------------------------- */
+static void DoLogging()
+{
+       int64_t value = 1;
+       
+       void* token = BeginCloudKeychainLoggingTransaction();
+               
+       value = GetNumberOfPeers();
+       value = Bucket(value);
+    AddKeyValuePairToKeychainLoggingTransaction(token, kNumberOfiCloudKeychainPeers, value);
+
+       value = GetNumberOfItemsBeingSynced();
+       value = Bucket(value);
+       
+    AddKeyValuePairToKeychainLoggingTransaction(token, kNumberOfiCloudKeychainItemsBeingSynced, value);
+       CloseCloudKeychainLoggingTransaction(token);
+
+       WriteOutLoggingTime();
+}
+
+/* --------------------------------------------------------------------------
+       Function:               InitializeCloudKeychainTracing
+       
+       Description:    Called when secd starts up.  It will first determine if
+                                       the device is in a circle and if so it will see if 
+                                       logging should be done (if enough time has expired since
+                                       the last time logging was done) and if logging should 
+                                       be done will perform the logging.
+   -------------------------------------------------------------------------- */
+void InitializeCloudKeychainTracing()
+{
+    if (SOSCCThisDeviceDefinitelyNotActiveInCircle())   // No circle no logging
+    {
+               return;
+    }
+
+       if (ShouldLog())
+       {
+               DoLogging();
+       }       
+}