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 "iCloudTrace.h"
26 #include <SecureObjectSync/SOSCloudCircle.h>
27 #include <Security/SecItem.h>
28 #include <utilities/iCloudKeychainTrace.h>
29 #include <securityd/SecItemServer.h>
32 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
35 #include <utilities/SecCFWrappers.h>
37 extern bool SOSCCThisDeviceDefinitelyNotActiveInCircle(void);
42 /* --------------------------------------------------------------------------
43 Function: GetNumberOfItemsBeingSynced
45 Description: Determine the number of items being synced. NOTE:
46 This uses the SOSDataSourceFactoryRef instead of
47 calling the SecItem interface because the SecItem
48 interface requires an entitlement but the
49 SOSDataSourceFactoryRef does not.
50 -------------------------------------------------------------------------- */
51 static int64_t GetNumberOfItemsBeingSynced()
53 __block
int64_t result
= 0;
54 SOSDataSourceFactoryRef dsFactRef
= SecItemDataSourceFactoryGetDefault();
55 CFArrayRef ds_names
= dsFactRef
->copy_names(dsFactRef
);
58 CFArrayForEach(ds_names
, ^(const void *value
) {
59 if (isString(value
)) {
60 SOSManifestRef manifestRef
= NULL
;
61 SOSDataSourceRef ds
= dsFactRef
->create_datasource(dsFactRef
, value
, NULL
);
64 manifestRef
= SOSDataSourceCopyManifest(ds
, NULL
);
66 result
+= SOSManifestGetCount(manifestRef
);
69 CFReleaseSafe(manifestRef
);
70 SOSDataSourceRelease(ds
, NULL
);
75 CFReleaseSafe(ds_names
);
79 /* --------------------------------------------------------------------------
80 Function: GetNumberOfPeers
82 Description: Determine the number of peers in the circle that this
84 -------------------------------------------------------------------------- */
85 static int64_t GetNumberOfPeers()
89 CFErrorRef error
= NULL
;
90 CFArrayRef peers
= NULL
;
92 peers
= SOSCCCopyPeerPeerInfo(&error
);
105 result
= (int64_t)CFArrayGetCount(peers
);
112 static const char* kLoggingPlistPartialPath
= "/Library/Preferences/com.apple.security.logging.plist";
114 /* --------------------------------------------------------------------------
117 Description: Utility fucntion to see if a file path exists
118 -------------------------------------------------------------------------- */
119 static Boolean
PathExists(const char* path
, size_t* pFileSize
)
121 Boolean result
= false;
124 if (NULL
!= pFileSize
)
129 int stat_result
= stat(path
, &sb
);
130 result
= (stat_result
== 0);
134 if (S_ISDIR(sb
.st_mode
))
142 if (NULL
!= pFileSize
)
144 *pFileSize
= (size_t)sb
.st_size
;
151 /* --------------------------------------------------------------------------
152 Function: CopyFileContents
154 Description: Given a file path read the entire contents of the file
156 -------------------------------------------------------------------------- */
157 static CFDataRef
CopyFileContents(const char *path
)
159 CFMutableDataRef data
= NULL
;
160 int fd
= open(path
, O_RDONLY
, 0666);
167 off_t fsize
= lseek(fd
, 0, SEEK_END
);
168 if (fsize
== (off_t
)-1)
173 if (fsize
> (off_t
)INT32_MAX
)
178 data
= CFDataCreateMutable(kCFAllocatorDefault
, (CFIndex
)fsize
);
184 CFDataSetLength(data
, (CFIndex
)fsize
);
185 void *buf
= CFDataGetMutableBytePtr(data
);
191 off_t total_read
= 0;
192 while (total_read
< fsize
)
196 bytes_read
= pread(fd
, buf
, (size_t)(fsize
- total_read
), total_read
);
197 if (bytes_read
== -1)
205 total_read
+= bytes_read
;
225 static const CFStringRef kLoggingPlistKey
= CFSTR("LoggingTime");
227 /* --------------------------------------------------------------------------
228 Function: CopyPlistPath
230 Description: Return the fully qualified file path to the logging
232 -------------------------------------------------------------------------- */
233 static const char* CopyPlistPath()
235 const char* result
= NULL
;
239 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
240 CFStringRef path_string
= NULL
;
241 const char *homeDir
= getenv("HOME");
245 struct passwd
* pwd
= getpwuid(getuid());
247 homeDir
= pwd
->pw_dir
;
250 path_string
= CFStringCreateWithCStringNoCopy(kCFAllocatorDefault
, homeDir
, kCFStringEncodingUTF8
, kCFAllocatorNull
);
251 if (NULL
== path_string
)
256 url
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, path_string
, kCFURLPOSIXPathStyle
, true);
257 CFRelease(path_string
);
260 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
261 url
= CFCopyHomeDirectoryURL();
266 UInt8 file_path_buffer
[MAX_PATH
+1];
267 memset(file_path_buffer
, 0, MAX_PATH
);
270 if(!CFURLGetFileSystemRepresentation(url
, false, file_path_buffer
, MAX_PATH
))
273 return result
; // better to log too much than not at all
278 if (strnlen((const char *)file_path_buffer
, MAX_PATH
) + strlen(kLoggingPlistPartialPath
) >= MAX_PATH
)
280 return result
; // better to log too much than not at all
283 strncat((char *)file_path_buffer
, kLoggingPlistPartialPath
, MAX_PATH
);
284 length
= strlen((char *)file_path_buffer
) + 1;
285 result
= malloc(length
);
291 memset((void *)result
, 0, length
);
292 strncpy((char *)result
, (char *)file_path_buffer
, length
- 1);
300 /* --------------------------------------------------------------------------
303 Description: Get the current time and match that against the value
304 in the logging plist and see if logging should be done
305 -------------------------------------------------------------------------- */
306 static bool ShouldLog()
310 CFDataRef file_data
= NULL
;
311 CFPropertyListRef logging_time_data
= NULL
;
312 CFDataRef time_data
= NULL
;
313 const char* plist_path
= NULL
;
315 plist_path
= CopyPlistPath();
316 if (NULL
== plist_path
)
318 return true; // better to log too much than not at all
321 if (!PathExists((const char *)plist_path
, &fileSize
))
323 free((void *)plist_path
);
324 return true; // better to log too much than not at all
327 file_data
= CopyFileContents((const char *)plist_path
);
328 free((void *)plist_path
);
329 if (NULL
== file_data
)
331 return true; // better to log too much than not at all
334 logging_time_data
= CFPropertyListCreateWithData(kCFAllocatorDefault
, file_data
, kCFPropertyListMutableContainersAndLeaves
, NULL
, NULL
);
335 CFRelease(file_data
);
337 if (NULL
== logging_time_data
)
339 return true; // better to log too much than not at all
342 require_action(CFDictionaryGetTypeID() == CFGetTypeID(logging_time_data
), xit
, result
= true); // better to log too much than not at all
344 time_data
= (CFDataRef
)CFDictionaryGetValue((CFDictionaryRef
)logging_time_data
, kLoggingPlistKey
);
345 require_action(time_data
, xit
, result
= true); // better to log too much than not at all
347 CFAbsoluteTime startTime
= 0;
348 memcpy(&startTime
, CFDataGetBytePtr(time_data
), sizeof(startTime
));
350 CFAbsoluteTime endTime
= CFAbsoluteTimeGetCurrent();
354 CFCalendarRef gregorian
= CFCalendarCopyCurrent();
355 CFCalendarGetComponentDifference(gregorian
, startTime
, endTime
, 0, "d", &days
);
357 CFRelease(gregorian
);
359 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
366 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
374 CFReleaseSafe(logging_time_data
);
378 /* --------------------------------------------------------------------------
379 Function: WriteOutLoggingTime
381 Description: Write out the logging plist with the time that the
382 last logging was done.
383 -------------------------------------------------------------------------- */
384 static void WriteOutLoggingTime(void)
389 CFDictionaryRef plistData
= NULL
;
390 CFErrorRef error
= NULL
;
391 CFDataRef output_data
= NULL
;
393 const char* filepath
= CopyPlistPath();
394 if (NULL
== filepath
)
399 fd
= open(filepath
, (O_WRONLY
| O_CREAT
| O_TRUNC
), 0666);
400 free((void *)filepath
);
406 now
= CFAbsoluteTimeGetCurrent();
407 now_data
= CFDataCreate(kCFAllocatorDefault
, (const UInt8
*)&now
, sizeof(now
));
408 if (NULL
== now_data
)
414 plistData
= CFDictionaryCreate(kCFAllocatorDefault
, (const void **)&kLoggingPlistKey
, (const void **)&now_data
, 1,
415 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
416 if (NULL
== plistData
)
424 output_data
= CFPropertyListCreateData(kCFAllocatorDefault
, plistData
, kCFPropertyListBinaryFormat_v1_0
, 0, &error
);
425 CFRelease(plistData
);
426 if (NULL
!= error
|| NULL
== output_data
)
434 if (NULL
!= output_data
)
436 CFRelease(output_data
);
442 write(fd
, CFDataGetBytePtr(output_data
), CFDataGetLength(output_data
));
445 CFRelease(output_data
);
449 /* --------------------------------------------------------------------------
452 Description: In order to preserve annominity of a user, take an
453 absolute value and return back the most significant
455 -------------------------------------------------------------------------- */
456 static int64_t Bucket(int64_t value
)
465 return (value
/ 10) * 10;
470 return (value
/ 100) * 100;
475 return (value
/ 1000) * 1000;
480 return (value
/ 10000) * 10000;
485 return (value
/ 100000) * 10000;
491 /* --------------------------------------------------------------------------
494 Description: If it has been determined that logging should be done
495 this function will perform the logging
496 -------------------------------------------------------------------------- */
497 static void DoLogging()
501 void* token
= BeginCloudKeychainLoggingTransaction();
503 value
= GetNumberOfPeers();
504 value
= Bucket(value
);
505 AddKeyValuePairToKeychainLoggingTransaction(token
, kNumberOfiCloudKeychainPeers
, value
);
507 value
= GetNumberOfItemsBeingSynced();
508 value
= Bucket(value
);
510 AddKeyValuePairToKeychainLoggingTransaction(token
, kNumberOfiCloudKeychainItemsBeingSynced
, value
);
511 CloseCloudKeychainLoggingTransaction(token
);
513 WriteOutLoggingTime();
516 /* --------------------------------------------------------------------------
517 Function: InitializeCloudKeychainTracing
519 Description: Called when secd starts up. It will first determine if
520 the device is in a circle and if so it will see if
521 logging should be done (if enough time has expired since
522 the last time logging was done) and if logging should
523 be done will perform the logging.
524 -------------------------------------------------------------------------- */
525 void InitializeCloudKeychainTracing()
527 if (SOSCCThisDeviceDefinitelyNotActiveInCircle()) // No circle no logging