]> git.saurik.com Git - apple/security.git/blob - Security/sec/securityd/iCloudTrace.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / sec / securityd / iCloudTrace.c
1 /*
2 * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 #include "iCloudTrace.h"
26 #include <SecureObjectSync/SOSCloudCircle.h>
27 #include <Security/SecItem.h>
28 #include <utilities/iCloudKeychainTrace.h>
29 #include <securityd/SecItemServer.h>
30 #include <sys/stat.h>
31 #include <string.h>
32 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
33 #include <pwd.h>
34 #endif
35 #include <utilities/SecCFWrappers.h>
36
37 extern bool SOSCCThisDeviceDefinitelyNotActiveInCircle(void);
38
39
40 #define MAX_PATH 1024
41
42 /* --------------------------------------------------------------------------
43 Function: GetNumberOfItemsBeingSynced
44
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()
52 {
53 __block int64_t result = 0;
54 SOSDataSourceFactoryRef dsFactRef = SecItemDataSourceFactoryGetDefault();
55 CFArrayRef ds_names = dsFactRef->copy_names(dsFactRef);
56
57 if (ds_names) {
58 CFArrayForEach(ds_names, ^(const void *value) {
59 if (isString(value)) {
60 SOSManifestRef manifestRef = NULL;
61 SOSDataSourceRef ds = dsFactRef->create_datasource(dsFactRef, value, NULL);
62
63 if (ds) {
64 manifestRef = SOSDataSourceCopyManifest(ds, NULL);
65 if (manifestRef)
66 result += SOSManifestGetCount(manifestRef);
67 }
68
69 CFReleaseSafe(manifestRef);
70 SOSDataSourceRelease(ds, NULL);
71 }
72 });
73 }
74
75 CFReleaseSafe(ds_names);
76 return result;
77 }
78
79 /* --------------------------------------------------------------------------
80 Function: GetNumberOfPeers
81
82 Description: Determine the number of peers in the circle that this
83 device is in
84 -------------------------------------------------------------------------- */
85 static int64_t GetNumberOfPeers()
86 {
87 int64_t result = 0;
88
89 CFErrorRef error = NULL;
90 CFArrayRef peers = NULL;
91
92 peers = SOSCCCopyPeerPeerInfo(&error);
93 if (NULL != error)
94 {
95 CFRelease(error);
96 if (NULL != peers)
97 {
98 CFRelease(peers);
99 }
100 return result;
101 }
102
103 if (NULL != peers)
104 {
105 result = (int64_t)CFArrayGetCount(peers);
106 CFRelease(peers);
107 }
108
109 return result;
110 }
111
112 static const char* kLoggingPlistPartialPath = "/Library/Preferences/com.apple.security.logging.plist";
113
114 /* --------------------------------------------------------------------------
115 Function: PathExists
116
117 Description: Utility fucntion to see if a file path exists
118 -------------------------------------------------------------------------- */
119 static Boolean PathExists(const char* path, size_t* pFileSize)
120 {
121 Boolean result = false;
122 struct stat sb;
123
124 if (NULL != pFileSize)
125 {
126 *pFileSize = 0;
127 }
128
129 int stat_result = stat(path, &sb);
130 result = (stat_result == 0);
131
132 if (result)
133 {
134 if (S_ISDIR(sb.st_mode))
135 {
136 // It is a directory
137 ;
138 }
139 else
140 {
141 // It is a file
142 if (NULL != pFileSize)
143 {
144 *pFileSize = (size_t)sb.st_size;
145 }
146 }
147 }
148 return result;
149 }
150
151 /* --------------------------------------------------------------------------
152 Function: CopyFileContents
153
154 Description: Given a file path read the entire contents of the file
155 into a CFDataRef
156 -------------------------------------------------------------------------- */
157 static CFDataRef CopyFileContents(const char *path)
158 {
159 CFMutableDataRef data = NULL;
160 int fd = open(path, O_RDONLY, 0666);
161
162 if (fd == -1)
163 {
164 goto badFile;
165 }
166
167 off_t fsize = lseek(fd, 0, SEEK_END);
168 if (fsize == (off_t)-1)
169 {
170 goto badFile;
171 }
172
173 if (fsize > (off_t)INT32_MAX)
174 {
175 goto badFile;
176 }
177
178 data = CFDataCreateMutable(kCFAllocatorDefault, (CFIndex)fsize);
179 if (NULL == data)
180 {
181 goto badFile;
182 }
183
184 CFDataSetLength(data, (CFIndex)fsize);
185 void *buf = CFDataGetMutableBytePtr(data);
186 if (NULL == buf)
187 {
188 goto badFile;
189 }
190
191 off_t total_read = 0;
192 while (total_read < fsize)
193 {
194 ssize_t bytes_read;
195
196 bytes_read = pread(fd, buf, (size_t)(fsize - total_read), total_read);
197 if (bytes_read == -1)
198 {
199 goto badFile;
200 }
201 if (bytes_read == 0)
202 {
203 goto badFile;
204 }
205 total_read += bytes_read;
206 }
207
208 close(fd);
209 return data;
210
211 badFile:
212 if (fd != -1)
213 {
214 close(fd);
215 }
216
217 if (data)
218 {
219 CFRelease(data);
220 }
221
222 return NULL;
223 }
224
225 static const CFStringRef kLoggingPlistKey = CFSTR("LoggingTime");
226
227 /* --------------------------------------------------------------------------
228 Function: CopyPlistPath
229
230 Description: Return the fully qualified file path to the logging
231 plist
232 -------------------------------------------------------------------------- */
233 static const char* CopyPlistPath()
234 {
235 const char* result = NULL;
236 CFURLRef url = NULL;
237
238
239 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
240 CFStringRef path_string = NULL;
241 const char *homeDir = getenv("HOME");
242
243 if (!homeDir)
244 {
245 struct passwd* pwd = getpwuid(getuid());
246 if (pwd)
247 homeDir = pwd->pw_dir;
248 }
249
250 path_string = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, homeDir, kCFStringEncodingUTF8, kCFAllocatorNull);
251 if (NULL == path_string)
252 {
253 return result;
254 }
255
256 url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_string, kCFURLPOSIXPathStyle, true);
257 CFRelease(path_string);
258 #endif
259
260 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
261 url = CFCopyHomeDirectoryURL();
262 #endif
263
264 if (url)
265 {
266 UInt8 file_path_buffer[MAX_PATH+1];
267 memset(file_path_buffer, 0, MAX_PATH);
268 size_t length = 0;
269
270 if(!CFURLGetFileSystemRepresentation(url, false, file_path_buffer, MAX_PATH))
271 {
272 CFRelease(url);
273 return result; // better to log too much than not at all
274 }
275
276 CFRelease(url);
277
278 if (strnlen((const char *)file_path_buffer, MAX_PATH) + strlen(kLoggingPlistPartialPath) >= MAX_PATH)
279 {
280 return result; // better to log too much than not at all
281 }
282
283 strncat((char *)file_path_buffer, kLoggingPlistPartialPath, MAX_PATH);
284 length = strlen((char *)file_path_buffer) + 1;
285 result = malloc(length);
286 if (NULL == result)
287 {
288 return result;
289 }
290
291 memset((void *)result, 0, length);
292 strncpy((char *)result, (char *)file_path_buffer, length - 1);
293 return result;
294 }
295
296 return NULL;
297
298 }
299
300 /* --------------------------------------------------------------------------
301 Function: ShouldLog
302
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()
307 {
308 bool result = false;
309 size_t fileSize = 0;
310 CFDataRef file_data = NULL;
311 CFPropertyListRef logging_time_data = NULL;
312 CFDataRef time_data = NULL;
313 const char* plist_path = NULL;
314
315 plist_path = CopyPlistPath();
316 if (NULL == plist_path)
317 {
318 return true; // better to log too much than not at all
319 }
320
321 if (!PathExists((const char *)plist_path, &fileSize))
322 {
323 free((void *)plist_path);
324 return true; // better to log too much than not at all
325 }
326
327 file_data = CopyFileContents((const char *)plist_path);
328 free((void *)plist_path);
329 if (NULL == file_data)
330 {
331 return true; // better to log too much than not at all
332 }
333
334 logging_time_data = CFPropertyListCreateWithData(kCFAllocatorDefault, file_data, kCFPropertyListMutableContainersAndLeaves, NULL, NULL);
335 CFRelease(file_data);
336
337 if (NULL == logging_time_data)
338 {
339 return true; // better to log too much than not at all
340 }
341
342 require_action(CFDictionaryGetTypeID() == CFGetTypeID(logging_time_data), xit, result = true); // better to log too much than not at all
343
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
346
347 CFAbsoluteTime startTime = 0;
348 memcpy(&startTime, CFDataGetBytePtr(time_data), sizeof(startTime));
349
350 CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent();
351
352 int days = 0;
353
354 CFCalendarRef gregorian = CFCalendarCopyCurrent();
355 CFCalendarGetComponentDifference(gregorian, startTime, endTime, 0, "d", &days);
356
357 CFRelease(gregorian);
358
359 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
360 if (days > 6)
361 {
362 result = true;
363 }
364 #endif
365
366 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
367 if (days > 0)
368 {
369 result = true;
370 }
371 #endif
372
373 xit:
374 CFReleaseSafe(logging_time_data);
375 return result;
376 }
377
378 /* --------------------------------------------------------------------------
379 Function: WriteOutLoggingTime
380
381 Description: Write out the logging plist with the time that the
382 last logging was done.
383 -------------------------------------------------------------------------- */
384 static void WriteOutLoggingTime(void)
385 {
386 int fd = -1;
387 CFAbsoluteTime now;
388 CFDataRef now_data;
389 CFDictionaryRef plistData = NULL;
390 CFErrorRef error = NULL;
391 CFDataRef output_data = NULL;
392
393 const char* filepath = CopyPlistPath();
394 if (NULL == filepath)
395 {
396 return;
397 }
398
399 fd = open(filepath, (O_WRONLY | O_CREAT | O_TRUNC), 0666);
400 free((void *)filepath);
401 if (fd <= 0)
402 {
403 return;
404 }
405
406 now = CFAbsoluteTimeGetCurrent();
407 now_data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)&now, sizeof(now));
408 if (NULL == now_data)
409 {
410 close(fd);
411 return;
412 }
413
414 plistData = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&kLoggingPlistKey, (const void **)&now_data, 1,
415 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
416 if (NULL == plistData)
417 {
418 close(fd);
419 CFRelease(now_data);
420 return;
421 }
422 CFRelease(now_data);
423
424 output_data = CFPropertyListCreateData(kCFAllocatorDefault, plistData, kCFPropertyListBinaryFormat_v1_0, 0, &error);
425 CFRelease(plistData);
426 if (NULL != error || NULL == output_data)
427 {
428 close(fd);
429 if (NULL != error)
430 {
431 CFRelease(error);
432 }
433
434 if (NULL != output_data)
435 {
436 CFRelease(output_data);
437 }
438
439 return;
440 }
441
442 write(fd, CFDataGetBytePtr(output_data), CFDataGetLength(output_data));
443 close(fd);
444
445 CFRelease(output_data);
446
447 }
448
449 /* --------------------------------------------------------------------------
450 Function: Bucket
451
452 Description: In order to preserve annominity of a user, take an
453 absolute value and return back the most significant
454 value in base 10
455 -------------------------------------------------------------------------- */
456 static int64_t Bucket(int64_t value)
457 {
458 if (value < 10)
459 {
460 return value;
461 }
462
463 if (value < 100)
464 {
465 return (value / 10) * 10;
466 }
467
468 if (value < 1000)
469 {
470 return (value / 100) * 100;
471 }
472
473 if (value < 10000)
474 {
475 return (value / 1000) * 1000;
476 }
477
478 if (value < 100000)
479 {
480 return (value / 10000) * 10000;
481 }
482
483 if (value < 1000000)
484 {
485 return (value / 100000) * 10000;
486 }
487
488 return value;
489 }
490
491 /* --------------------------------------------------------------------------
492 Function: DoLogging
493
494 Description: If it has been determined that logging should be done
495 this function will perform the logging
496 -------------------------------------------------------------------------- */
497 static void DoLogging()
498 {
499 int64_t value = 1;
500
501 void* token = BeginCloudKeychainLoggingTransaction();
502
503 value = GetNumberOfPeers();
504 value = Bucket(value);
505 AddKeyValuePairToKeychainLoggingTransaction(token, kNumberOfiCloudKeychainPeers, value);
506
507 value = GetNumberOfItemsBeingSynced();
508 value = Bucket(value);
509
510 AddKeyValuePairToKeychainLoggingTransaction(token, kNumberOfiCloudKeychainItemsBeingSynced, value);
511 CloseCloudKeychainLoggingTransaction(token);
512
513 WriteOutLoggingTime();
514 }
515
516 /* --------------------------------------------------------------------------
517 Function: InitializeCloudKeychainTracing
518
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()
526 {
527 if (SOSCCThisDeviceDefinitelyNotActiveInCircle()) // No circle no logging
528 {
529 return;
530 }
531
532 if (ShouldLog())
533 {
534 DoLogging();
535 }
536 }