1/* 2 * Copyright (c) 2012-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// SOSRegressionUtilities.c 26// 27 28#include <AssertMacros.h> 29#include <stdio.h> 30#include <Security/SecItem.h> 31 32#include <utilities/SecCFWrappers.h> 33#include <utilities/debugging.h> 34 35#include <SecureObjectSync/SOSAccount.h> 36#include <SecureObjectSync/SOSCircle.h> 37#include <SecureObjectSync/SOSInternal.h> 38#include <SecureObjectSync/SOSPeerInfoInternal.h> 39 40#include <SOSCloudKeychainClient.h> 41#include "SOSRegressionUtilities.h" 42#include "SOSInternal.h" 43 44#if TARGET_OS_IPHONE 45#include <MobileGestalt.h> 46#endif 47 48static const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC; 49 50// MARK: ----- SOS General ----- 51 52const char *cloudKeychainProxyPath = "/System/Library/Frameworks/Security.framework/Resources/CloudKeychainProxy.bundle/CloudKeychainProxy"; 53 54static const char *basecfabsoluteTimeToString(CFAbsoluteTime abstime, CFTimeZoneRef tz) 55{ 56 CFGregorianDate greg = CFAbsoluteTimeGetGregorianDate(abstime, NULL); 57 char str[20]; 58 if (19 != snprintf(str, 20, "%4.4d-%2.2d-%2.2d_%2.2d:%2.2d:%2.2d", 59 (int)greg.year, greg.month, greg.day, greg.hour, greg.minute, (int)greg.second)) 60 str[0]=0; 61 char *data = (char *)malloc(20); 62 strncpy(data, str, 20); 63 return data; 64} 65 66const char *cfabsoluteTimeToString(CFAbsoluteTime abstime) 67{ 68 return basecfabsoluteTimeToString(abstime, NULL); 69} 70 71const char *cfabsoluteTimeToStringLocal(CFAbsoluteTime abstime) 72{ 73 // Caller must release using free 74 CFDateFormatterRef formatter = NULL; 75 CFTimeZoneRef tz = NULL; 76 CFLocaleRef locale = NULL; 77 CFDateRef date = NULL; 78 CFStringRef cftime_string = NULL; 79 char *time_string = NULL; 80 char buffer[1024] = {0,}; 81 size_t sz; 82 83 require(tz = CFTimeZoneCopySystem(), xit); 84 require(locale = CFLocaleCreate(NULL, CFSTR("en_US")), xit); 85 86 require(formatter = CFDateFormatterCreate(kCFAllocatorDefault, locale, kCFDateFormatterShortStyle, kCFDateFormatterShortStyle), xit); 87 CFDateFormatterSetFormat(formatter, CFSTR("MM/dd/yy HH:mm:ss.SSS zzz")); 88 require(date = CFDateCreate(kCFAllocatorDefault, abstime), xit); 89 require(cftime_string = CFDateFormatterCreateStringWithDate(kCFAllocatorDefault, formatter, date), xit); 90 91 CFStringGetCString(cftime_string, buffer, 1024, kCFStringEncodingUTF8); 92 sz = strnlen(buffer, 1024); 93 time_string = (char *)malloc(sz); 94 strncpy(time_string, buffer, sz+1); 95xit: 96 CFReleaseSafe(tz); 97 CFReleaseSafe(formatter); 98 CFReleaseSafe(locale); 99 CFReleaseSafe(date); 100 CFReleaseSafe(cftime_string); 101 return time_string; 102} 103 104#include <sys/stat.h> 105 106static int file_exist (const char *filename) 107{ 108 struct stat buffer; 109 return (stat (filename, &buffer) == 0); 110} 111 112bool XPCServiceInstalled(void) 113{ 114 return file_exist(cloudKeychainProxyPath); 115} 116 117void registerForKVSNotifications(const void *observer, CFStringRef name, CFNotificationCallback callBack) 118{ 119 // observer is basically a context; name may not be null 120 CFNotificationCenterRef center = CFNotificationCenterGetDarwinNotifyCenter(); 121 CFNotificationSuspensionBehavior suspensionBehavior = CFNotificationSuspensionBehaviorDeliverImmediately; //ignored? 122 CFNotificationCenterAddObserver(center, observer, callBack, name, NULL, suspensionBehavior); 123} 124 125bool testPutObjectInCloudAndSync(CFStringRef key, CFTypeRef object, CFErrorRef *error, dispatch_group_t dgroup, dispatch_queue_t processQueue) 126{ 127 bool result = testPutObjectInCloud(key, object, error, dgroup, processQueue); 128 testSynchronize(processQueue, dgroup); 129 130 return result; 131} 132 133bool testPutObjectInCloud(CFStringRef key, CFTypeRef object, CFErrorRef *error, dispatch_group_t dgroup, dispatch_queue_t processQueue) 134{ 135 secerror("testPutObjectInCloud: key: %@, %@", key, object); 136 CFDictionaryRef objects = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, key, object, NULL); 137 if (objects) 138 { 139 dispatch_group_enter(dgroup); 140 SOSCloudKeychainPutObjectsInCloud(objects, processQueue, ^ (CFDictionaryRef returnedValues, CFErrorRef error) 141 { 142 secerror("testPutObjectInCloud returned: %@", returnedValues); 143 if (error) 144 { 145 secerror("testPutObjectInCloud returned: %@", error); 146 CFRelease(error); 147 } 148 dispatch_group_leave(dgroup); 149 }); 150 CFRelease(objects); 151 } 152 153 return true; 154} 155 156CFTypeRef testGetObjectFromCloud(CFStringRef key, dispatch_queue_t processQueue, dispatch_group_t dgroup) 157{ 158 // TODO: make sure we return NULL, not CFNull 159 secerror("start"); 160 CFMutableArrayRef keysToGet = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 161 CFArrayAppendValue(keysToGet, key); 162 163 __block CFTypeRef object = NULL; 164 165 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0); 166 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds); 167 168 dispatch_group_enter(dgroup); 169 SOSCloudKeychainGetObjectsFromCloud(keysToGet, processQueue, ^ (CFDictionaryRef returnedValues, CFErrorRef error) 170 { 171 secerror("SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues); 172 if (returnedValues) 173 { 174 object = (CFTypeRef)CFDictionaryGetValue(returnedValues, key); 175 if (object) 176 CFRetain(object); 177 } 178 if (error) 179 { 180 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error); 181 // CFRelease(*error); 182 } 183 dispatch_group_leave(dgroup); 184 secerror("SOSCloudKeychainGetObjectsFromCloud block exit: %@", object); 185 dispatch_semaphore_signal(waitSemaphore); 186 }); 187 188 dispatch_semaphore_wait(waitSemaphore, finishTime); 189 dispatch_release(waitSemaphore); 190 if (object && (CFGetTypeID(object) == CFNullGetTypeID())) // return a NULL instead of a CFNull 191 { 192 CFRelease(object); 193 object = NULL; 194 } 195 secerror("returned: %@", object); 196 return object; 197} 198 199CFTypeRef testGetObjectsFromCloud(CFArrayRef keysToGet, dispatch_queue_t processQueue, dispatch_group_t dgroup) 200{ 201 __block CFTypeRef object = NULL; 202 203 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0); 204 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds); 205 206 dispatch_group_enter(dgroup); 207 208 CloudKeychainReplyBlock replyBlock = 209 ^ (CFDictionaryRef returnedValues, CFErrorRef error) 210 { 211 secerror("SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues); 212 object = returnedValues; 213 if (object) 214 CFRetain(object); 215 if (error) 216 { 217 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error); 218 // CFRelease(*error); 219 } 220 dispatch_group_leave(dgroup); 221 secerror("SOSCloudKeychainGetObjectsFromCloud block exit: %@", object); 222 dispatch_semaphore_signal(waitSemaphore); 223 }; 224 225 if (!keysToGet) 226 SOSCloudKeychainGetAllObjectsFromCloud(processQueue, replyBlock); 227 else 228 SOSCloudKeychainGetObjectsFromCloud(keysToGet, processQueue, replyBlock); 229 230 dispatch_semaphore_wait(waitSemaphore, finishTime); 231 dispatch_release(waitSemaphore); 232 if (object && (CFGetTypeID(object) == CFNullGetTypeID())) // return a NULL instead of a CFNull 233 { 234 CFRelease(object); 235 object = NULL; 236 } 237 secerror("returned: %@", object); 238 return object; 239} 240 241bool testSynchronize(dispatch_queue_t processQueue, dispatch_group_t dgroup) 242{ 243 __block bool result = false; 244 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0); 245 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds); 246 247 dispatch_group_enter(dgroup); 248 249 SOSCloudKeychainSynchronize(processQueue, ^(CFDictionaryRef returnedValues, CFErrorRef error) 250 { 251 result = true; 252 dispatch_group_leave(dgroup); 253 dispatch_semaphore_signal(waitSemaphore); 254 }); 255 256 dispatch_semaphore_wait(waitSemaphore, finishTime); 257 dispatch_release(waitSemaphore); 258 return result; 259} 260 261bool testClearAll(dispatch_queue_t processQueue, dispatch_group_t dgroup) 262{ 263 __block bool result = false; 264 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0); 265 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds); 266 267 dispatch_group_enter(dgroup); 268 269 SOSCloudKeychainClearAll(processQueue, ^(CFDictionaryRef returnedValues, CFErrorRef error) 270 { 271 result = true; 272 secerror("SOSCloudKeychainClearAll returned: %@", error); 273 dispatch_group_leave(dgroup); 274 dispatch_semaphore_signal(waitSemaphore); 275 }); 276 277 dispatch_semaphore_wait(waitSemaphore, finishTime); 278 dispatch_release(waitSemaphore); 279 secerror("SOSCloudKeychainClearAll exit"); 280 return result; 281} 282 283void unregisterFromKVSNotifications(const void *observer) 284{ 285 CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetDarwinNotifyCenter(), observer); 286} 287 288// 289// MARK: SOSPeerInfo creation helpers 290// 291 292CFDictionaryRef SOSCreatePeerGestaltFromName(CFStringRef name) 293{ 294 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 295 kPIUserDefinedDeviceName, name, 296 NULL); 297} 298 299 300SOSPeerInfoRef SOSCreatePeerInfoFromName(CFStringRef name, SecKeyRef* outSigningKey, CFErrorRef *error) 301{ 302 SOSPeerInfoRef result = NULL; 303 SecKeyRef publicKey = NULL; 304 CFDictionaryRef gestalt = NULL; 305 306 require(outSigningKey, exit); 307 308 GeneratePermanentECPair(256, &publicKey, outSigningKey); 309 310 gestalt = SOSCreatePeerGestaltFromName(name); 311 require(gestalt, exit); 312 313 result = SOSPeerInfoCreate(NULL, gestalt, *outSigningKey, error); 314 315exit: 316 CFReleaseNull(gestalt); 317 CFReleaseNull(publicKey); 318 319 return result; 320} 321 322SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(CFStringRef name, SecKeyRef* outSigningKey, CFErrorRef *error) 323{ 324 SOSFullPeerInfoRef result = NULL; 325 SecKeyRef publicKey = NULL; 326 CFDictionaryRef gestalt = NULL; 327 328 require(outSigningKey, exit); 329 330 GeneratePermanentECPair(256, &publicKey, outSigningKey); 331 332 gestalt = SOSCreatePeerGestaltFromName(name); 333 require(gestalt, exit); 334 335 result = SOSFullPeerInfoCreate(NULL, gestalt, *outSigningKey, error); 336 337exit: 338 CFReleaseNull(gestalt); 339 CFReleaseNull(publicKey); 340 341 return result; 342} 343 344// MARK: ----- MAC Address ----- 345 346/* 347 * Name: GetHardwareAdress 348 * 349 * Parameters: None. 350 * 351 * Returns: Nothing 352 * 353 * Description: Retrieve the hardare address for a specified network interface 354 * 355 */ 356 357#include <stdlib.h> 358#include <string.h> 359 360#include <sys/socket.h> 361#include <netinet/in.h> 362#include <sys/sysctl.h> 363#include <net/if.h> 364#include <net/if_dl.h> 365#include <net/route.h> 366 367#include <unistd.h> 368#include <netdb.h> 369#include <sys/stat.h> 370 371static int getHardwareAddress(const char *interfaceName, size_t maxLenAllowed, size_t *outActualLength, char *outHardwareAddress) 372{ 373 char *end; 374 struct if_msghdr *ifm; 375 struct sockaddr_dl *sdl; 376 char *buf; 377 int result = -1; 378 size_t buffSize; 379 int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_IFLIST, 0 }; 380 381 buf = 0; 382 *outActualLength = 0; 383 // see how much space is needed 384 require_noerr(result = sysctl(mib, 6, NULL, &buffSize, NULL, 0), xit); 385 386 // allocate the buffer 387 require(buf = malloc(buffSize), xit); 388 389 // get the interface info 390 require_noerr(result = sysctl(mib, 6, buf, &buffSize, NULL, 0), xit); 391 392 ifm = (struct if_msghdr *) buf; 393 end = buf + buffSize; 394 do 395 { 396 if (ifm->ifm_type == RTM_IFINFO) // should always be true 397 { 398 sdl = (struct sockaddr_dl *) (ifm + 1); 399 if ( sdl->sdl_nlen == strlen( interfaceName ) && ( bcmp( sdl->sdl_data, interfaceName, sdl->sdl_nlen ) == 0 ) ) 400 { 401 if ( sdl->sdl_alen > 0 ) 402 { 403 size_t hardwareLen; 404 405 result = 0; // indicate found the interface 406 hardwareLen = sdl->sdl_alen; 407 if ( hardwareLen > maxLenAllowed ) 408 { 409 hardwareLen = maxLenAllowed; 410 result = -2; // indicate truncation of the address 411 } 412 memcpy( outHardwareAddress, sdl->sdl_data + sdl->sdl_nlen, hardwareLen ); 413 *outActualLength = hardwareLen; 414 break; 415 416 } 417 } 418 } 419 ifm = (struct if_msghdr *) ((char*)ifm + ifm->ifm_msglen); 420 } while ( (char*)ifm < end ); 421 422xit: 423 if (buf) 424 free(buf); 425 426 return result; 427} 428 429// MARK: ----- cloudTransportTests ----- 430 431CFStringRef myMacAddress(void) 432{ 433 // 6 bytes, no ":"s 434 CFStringRef result = NULL; 435 const char *interfaceName = "en0"; 436 size_t maxLenAllowed = 1024; 437 size_t outActualLength = 0; 438 char outHardwareAddress[1024]; 439 440 require_noerr(getHardwareAddress(interfaceName, maxLenAllowed, &outActualLength, outHardwareAddress), xit); 441 require(outActualLength==6, xit); 442 unsigned char buf[32]={0,}; 443 444 unsigned char *ps = (unsigned char *)buf; 445 unsigned char *pa = (unsigned char *)outHardwareAddress; 446 for (int ix = 0; ix < 6; ix++, pa++) 447 ps += sprintf((char *)ps, "%02x", *pa); 448 449 result = CFStringCreateWithCString(kCFAllocatorDefault, (const char *)buf, kCFStringEncodingUTF8); 450 451xit: 452 return result; 453} 454