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 "iCloudKeychainTrace.h"
26#include <TargetConditionals.h>
27#include <inttypes.h>
28#include "SecCFWrappers.h"
29#include <sys/time.h>
30#include <CoreFoundation/CoreFoundation.h>
31
32const CFStringRef kNumberOfiCloudKeychainPeers = CFSTR("numberOfPeers");
33const CFStringRef kNumberOfiCloudKeychainItemsBeingSynced = CFSTR("numberOfItemsBeingSynced");
34const CFStringRef kCloudKeychainNumberOfSyncingConflicts = CFSTR("conflictsCount");
35const CFStringRef kCloudKeychainNumberOfTimesSyncFailed = CFSTR("syncFailureCount");
36const CFStringRef kCloudKeychainNumberOfConflictsResolved = CFSTR("conflictsResolved");
37const CFStringRef kCloudKeychainNumberOfTimesSyncedWithPeers = CFSTR("syncedWithPeers");
38
39#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
40static const char* gTopLevelKeyForiCloudKeychainTracing = "com.apple.cloudkeychain";
41#endif
42
43#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
44static const char* gTopLevelKeyForiCloudKeychainTracing = "com.apple.cloudkeychain";
45#endif
46
47#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
48#include <asl.h>
49
50static const char* gMessageTracerSetPrefix = "com.apple.message.";
51
52static const char* gMessageTracerDomainField = "com.apple.message.domain";
53
54/* --------------------------------------------------------------------------
55	Function:		OSX_BeginCloudKeychainLoggingTransaction
56
57	Description:	For OSX the message tracer back end wants its logging
58					done in "bunches".  This function allows for beginning
59					a 'transaction' of logging which will allow for putting
60					all of the transactions items into a single log making
61					the message tracer folks happy.
62
63					The work of this function is to create the aslmsg context
64					and set the domain field and then return the aslmsg
65					context as a void*
66   -------------------------------------------------------------------------- */
67static void* OSX_BeginCloudKeychainLoggingTransaction()
68{
69	void* result = NULL;
70	aslmsg mAsl = NULL;
71	mAsl = asl_new(ASL_TYPE_MSG);
72	if (NULL == mAsl)
73	{
74		return result;
75	}
76
77	asl_set(mAsl, gMessageTracerDomainField, gTopLevelKeyForiCloudKeychainTracing);
78
79	result = (void *)mAsl;
80	return result;
81}
82
83/* --------------------------------------------------------------------------
84	Function:		OSX_AddKeyValuePairToKeychainLoggingTransaction
85
86	Description:	Once a call to OSX_BeginCloudKeychainLoggingTransaction
87					is done, this call all allow for adding items to the
88					"bunch" of items being logged.
89
90					NOTE: The key should be a simple key such as
91					"numberOfPeers".  This is because this function will
92					apptend the required prefix of "com.apple.message."
93   -------------------------------------------------------------------------- */
94static bool OSX_AddKeyValuePairToKeychainLoggingTransaction(void* token, CFStringRef key, int64_t value)
95{
96	if (NULL == token || NULL == key)
97	{
98		return false;
99	}
100
101	aslmsg mAsl = (aslmsg)token;
102
103	// Fix up the key
104	CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%@"), gMessageTracerSetPrefix, key);
105	if (NULL == real_key)
106	{
107		return false;
108	}
109
110	CFIndex key_length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(real_key), kCFStringEncodingUTF8);
111    key_length += 1; // For null
112    char key_buffer[key_length];
113    memset(key_buffer, 0,key_length);
114    if (!CFStringGetCString(real_key, key_buffer, key_length, kCFStringEncodingUTF8))
115    {
116        CFRelease(real_key);
117        return false;
118    }
119	CFRelease(real_key);
120
121	CFStringRef value_str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%lld"), value);
122    if (NULL == value_str)
123    {
124        return false;
125    }
126
127    CFIndex value_str_numBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value_str), kCFStringEncodingUTF8);
128    value_str_numBytes += 1; // For null
129    char value_buffer[value_str_numBytes];
130    memset(value_buffer, 0, value_str_numBytes);
131    if (!CFStringGetCString(value_str, value_buffer, value_str_numBytes, kCFStringEncodingUTF8))
132    {
133        CFRelease(value_str);
134        return false;
135    }
136    CFRelease(value_str);
137
138	asl_set(mAsl, key_buffer, value_buffer);
139	return true;
140}
141
142/* --------------------------------------------------------------------------
143	Function:		OSX_CloseCloudKeychainLoggingTransaction
144
145	Description:	Once a call to OSX_BeginCloudKeychainLoggingTransaction
146					is done, and all of the items that are to be in the
147					"bunch" of items being logged, this function will do the
148					real logging and free the aslmsg context.
149   -------------------------------------------------------------------------- */
150static void OSX_CloseCloudKeychainLoggingTransaction(void* token)
151{
152	if (NULL != token)
153	{
154		aslmsg mAsl = (aslmsg)token;
155		asl_log(NULL, mAsl, ASL_LEVEL_NOTICE, "");
156		asl_free(mAsl);
157	}
158}
159
160/* --------------------------------------------------------------------------
161	Function:		OSX_SetCloudKeychainTraceValueForKey
162
163	Description:	If "bunching" of items either cannot be done or is not
164					desired, then this 'single shot' function shold be used.
165					It will create the aslmsg context, register the domain
166					fix up the key and log the key value pair and then
167					do the real logging and free the aslmsg context.
168   -------------------------------------------------------------------------- */
169static bool OSX_SetCloudKeychainTraceValueForKey(CFStringRef key, int64_t value)
170{
171	bool result = false;
172
173	if (NULL == key)
174	{
175		return result;
176	}
177
178	aslmsg mAsl = NULL;
179	mAsl = asl_new(ASL_TYPE_MSG);
180	if (NULL == mAsl)
181	{
182		return result;
183	}
184
185	// Fix up the key
186	CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%@"), gMessageTracerSetPrefix, key);
187	if (NULL == real_key)
188	{
189		return false;
190	}
191
192	CFIndex key_length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(real_key), kCFStringEncodingUTF8);
193    key_length += 1; // For null
194    char key_buffer[key_length];
195    memset(key_buffer, 0,key_length);
196    if (!CFStringGetCString(real_key, key_buffer, key_length, kCFStringEncodingUTF8))
197    {
198        CFRelease(real_key);
199        return false;
200    }
201	CFRelease(real_key);
202
203
204	CFStringRef value_str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%lld"), value);
205    if (NULL == value_str)
206    {
207        asl_free(mAsl);
208        return result;
209    }
210
211    CFIndex value_str_numBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value_str), kCFStringEncodingUTF8);
212    value_str_numBytes += 1; // For null
213    char value_buffer[value_str_numBytes];
214    memset(value_buffer, 0, value_str_numBytes);
215    if (!CFStringGetCString(value_str, value_buffer, value_str_numBytes, kCFStringEncodingUTF8))
216    {
217        asl_free(mAsl);
218        CFRelease(value_str);
219        return result;
220    }
221    CFRelease(value_str);
222
223    asl_set(mAsl, gMessageTracerDomainField, gTopLevelKeyForiCloudKeychainTracing);
224
225	asl_set(mAsl, key_buffer, value_buffer);
226	asl_log(NULL, mAsl, ASL_LEVEL_NOTICE, "%s is %lld", key_buffer, value);
227	asl_free(mAsl);
228	return true;
229
230}
231#endif
232
233#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
234
235typedef void (*type_ADClientClearScalarKey)(CFStringRef key);
236typedef void (*type_ADClientAddValueForScalarKey)(CFStringRef key, int64_t value);
237
238static type_ADClientClearScalarKey gADClientClearScalarKey = NULL;
239static type_ADClientAddValueForScalarKey gADClientAddValueForScalarKey = NULL;
240
241static dispatch_once_t gADFunctionPointersSet = 0;
242static CFBundleRef gAggdBundleRef = NULL;
243static bool gFunctionPointersAreLoaded = false;
244
245/* --------------------------------------------------------------------------
246	Function:		InitializeADFunctionPointers
247
248	Description:	Linking to the Aggregate library causes a build cycle so
249					This function will dynamically load the needed function
250					pointers.
251   -------------------------------------------------------------------------- */
252static bool InitializeADFunctionPointers()
253{
254	if (gFunctionPointersAreLoaded)
255	{
256		return gFunctionPointersAreLoaded;
257	}
258
259    dispatch_once(&gADFunctionPointersSet,
260      ^{
261          CFStringRef path_to_aggd_framework = CFSTR("/System/Library/PrivateFrameworks/AggregateDictionary.framework");
262
263          CFURLRef aggd_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_to_aggd_framework, kCFURLPOSIXPathStyle, true);
264
265          if (NULL != aggd_url)
266          {
267              gAggdBundleRef = CFBundleCreate(kCFAllocatorDefault, aggd_url);
268              if (NULL != gAggdBundleRef)
269              {
270                  gADClientClearScalarKey = (type_ADClientClearScalarKey)
271                    CFBundleGetFunctionPointerForName(gAggdBundleRef, CFSTR("ADClientClearScalarKey"));
272
273                  gADClientAddValueForScalarKey = (type_ADClientAddValueForScalarKey)
274                    CFBundleGetFunctionPointerForName(gAggdBundleRef, CFSTR("ADClientAddValueForScalarKey"));
275              }
276              CFRelease(aggd_url);
277          }
278      });
279
280    gFunctionPointersAreLoaded = ((NULL != gADClientClearScalarKey) && (NULL != gADClientAddValueForScalarKey));
281    return gFunctionPointersAreLoaded;
282}
283
284/* --------------------------------------------------------------------------
285	Function:		Internal_ADClientClearScalarKey
286
287	Description:	This fucntion is a wrapper around calling the
288					ADClientClearScalarKey function.
289
290					NOTE: The key should be a simple key such as
291					"numberOfPeers".  This is because this function will
292					apptend the required prefix of "com.apple.cloudkeychain"
293   -------------------------------------------------------------------------- */
294static void Internal_ADClientClearScalarKey(CFStringRef key)
295{
296    if (InitializeADFunctionPointers())
297    {
298		CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%@"), gTopLevelKeyForiCloudKeychainTracing, key);
299		if (NULL == real_key)
300		{
301			return;
302		}
303
304        gADClientClearScalarKey(real_key);
305		CFRelease(real_key);
306    }
307}
308
309/* --------------------------------------------------------------------------
310	Function:		Internal_ADClientAddValueForScalarKey
311
312	Description:	This fucntion is a wrapper around calling the
313					ADClientAddValueForScalarKey function.
314
315					NOTE: The key should be a simple key such as
316					"numberOfPeers".  This is because this function will
317					apptend the required prefix of "com.apple.cloudkeychain"
318   -------------------------------------------------------------------------- */
319static void Internal_ADClientAddValueForScalarKey(CFStringRef key, int64_t value)
320{
321    if (InitializeADFunctionPointers())
322    {
323		CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%@"), gTopLevelKeyForiCloudKeychainTracing, key);
324		if (NULL == real_key)
325		{
326			return;
327		}
328
329        gADClientAddValueForScalarKey(real_key, value);
330		CFRelease(real_key);
331    }
332}
333
334
335/* --------------------------------------------------------------------------
336	Function:		iOS_SetCloudKeychainTraceValueForKey
337
338	Description:	This fucntion is a wrapper around calling either
339					ADClientAddValueForScalarKey  or ADClientClearScalarKey
340					depending on if the value is 0.
341
342					NOTE: The key should be a simple key such as
343					"numberOfPeers".  This is because this function will
344					apptend the required prefix of "com.apple.cloudkeychain"
345   -------------------------------------------------------------------------- */
346static bool iOS_SetCloudKeychainTraceValueForKey(CFStringRef key, int64_t value)
347{
348	if (NULL == key)
349	{
350		return false;
351	}
352
353    if (0LL == value)
354    {
355        Internal_ADClientClearScalarKey(key);
356    }
357    else
358    {
359        Internal_ADClientAddValueForScalarKey(key, value);
360    }
361	return true;
362}
363
364/* --------------------------------------------------------------------------
365	Function:		iOS_AddKeyValuePairToKeychainLoggingTransaction
366
367	Description:	For iOS the is no "bunching"  This function will simply
368					call iOS_SetCloudKeychainTraceValueForKey to log the
369					key value pair
370   -------------------------------------------------------------------------- */
371static bool iOS_AddKeyValuePairToKeychainLoggingTransaction(void* token, CFStringRef key, int64_t value)
372{
373#pragma unused(token)
374	return iOS_SetCloudKeychainTraceValueForKey(key, value);
375}
376#endif
377
378/* --------------------------------------------------------------------------
379	Function:		SetCloudKeychainTraceValueForKey
380
381	Description:	SPI to log a single key value pair with the logging system
382   -------------------------------------------------------------------------- */
383bool SetCloudKeychainTraceValueForKey(CFStringRef key, int64_t value)
384{
385#if (TARGET_IPHONE_SIMULATOR)
386	return false;
387#endif
388
389#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
390	return OSX_SetCloudKeychainTraceValueForKey(key, value);
391#endif
392
393#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
394	return iOS_SetCloudKeychainTraceValueForKey(key, value);
395#endif
396}
397
398/* --------------------------------------------------------------------------
399	Function:		BeginCloudKeychainLoggingTransaction
400
401	Description:	SPI to begin a logging transaction
402   -------------------------------------------------------------------------- */
403void* BeginCloudKeychainLoggingTransaction()
404{
405#if (TARGET_IPHONE_SIMULATOR)
406	return (void *)-1;
407#endif
408
409#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
410	return OSX_BeginCloudKeychainLoggingTransaction();
411#endif
412
413#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
414	return NULL;
415#endif
416}
417
418/* --------------------------------------------------------------------------
419	Function:		AddKeyValuePairToKeychainLoggingTransaction
420
421	Description:	SPI to add a key value pair to an outstanding logging
422					tansaction
423   -------------------------------------------------------------------------- */
424bool AddKeyValuePairToKeychainLoggingTransaction(void* token, CFStringRef key, int64_t value)
425{
426#if (TARGET_IPHONE_SIMULATOR)
427	return false;
428#endif
429
430#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
431	return OSX_AddKeyValuePairToKeychainLoggingTransaction(token, key, value);
432#endif
433
434#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
435	return iOS_AddKeyValuePairToKeychainLoggingTransaction(token, key, value);
436#endif
437}
438
439/* --------------------------------------------------------------------------
440	Function:		CloseCloudKeychainLoggingTransaction
441
442	Description:	SPI to complete a logging transaction and clean up the
443					context
444   -------------------------------------------------------------------------- */
445void CloseCloudKeychainLoggingTransaction(void* token)
446{
447#if (TARGET_IPHONE_SIMULATOR)
448	; // nothing
449#endif
450
451#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
452	OSX_CloseCloudKeychainLoggingTransaction(token);
453#endif
454
455#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
456	; // nothing
457#endif
458}
459
460