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