1/*
2 * Copyright (c) 2012 Apple Computer, 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
125void testPutObjectInCloudAndSync(CFStringRef key, CFTypeRef object, CFErrorRef *error, dispatch_group_t dgroup, dispatch_queue_t processQueue)
126{
127    testPutObjectInCloud(key, object, error, dgroup, processQueue);
128    testSynchronize(processQueue, dgroup);
129}
130
131void testPutObjectInCloud(CFStringRef key, CFTypeRef object, CFErrorRef *error, dispatch_group_t dgroup, dispatch_queue_t processQueue)
132{
133    secerror("testPutObjectInCloud: key: %@, %@", key, object);
134    CFDictionaryRef objects = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, key, object, NULL);
135    if (objects)
136    {
137        dispatch_group_enter(dgroup);
138        SOSCloudKeychainPutObjectsInCloud(objects, processQueue, ^ (CFDictionaryRef returnedValues, CFErrorRef error)
139        {
140            secerror("testPutObjectInCloud returned: %@", returnedValues);
141            if (error)
142            {
143                secerror("testPutObjectInCloud returned: %@", error);
144                CFRelease(error);
145            }
146            dispatch_group_leave(dgroup);
147        });
148        CFRelease(objects);
149    }
150}
151
152CFTypeRef testGetObjectFromCloud(CFStringRef key, dispatch_queue_t processQueue, dispatch_group_t dgroup)
153{
154    // TODO: make sure we return NULL, not CFNull
155    secerror("start");
156    CFMutableArrayRef keysToGet = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
157    CFArrayAppendValue(keysToGet, key);
158
159    __block CFTypeRef object = NULL;
160
161    dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
162    dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
163
164    dispatch_group_enter(dgroup);
165    SOSCloudKeychainGetObjectsFromCloud(keysToGet, processQueue, ^ (CFDictionaryRef returnedValues, CFErrorRef error)
166    {
167        secerror("SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues);
168        if (returnedValues)
169        {
170            object = (CFTypeRef)CFDictionaryGetValue(returnedValues, key);
171            if (object)
172                CFRetain(object);
173        }
174        if (error)
175        {
176            secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error);
177     //       CFRelease(*error);
178        }
179        dispatch_group_leave(dgroup);
180        secerror("SOSCloudKeychainGetObjectsFromCloud block exit: %@", object);
181        dispatch_semaphore_signal(waitSemaphore);
182    });
183
184	dispatch_semaphore_wait(waitSemaphore, finishTime);
185	dispatch_release(waitSemaphore);
186    if (object && (CFGetTypeID(object) == CFNullGetTypeID()))   // return a NULL instead of a CFNull
187    {
188        CFRelease(object);
189        object = NULL;
190    }
191    secerror("returned: %@", object);
192    return object;
193}
194
195CFTypeRef testGetObjectsFromCloud(CFArrayRef keysToGet, dispatch_queue_t processQueue, dispatch_group_t dgroup)
196{
197    __block CFTypeRef object = NULL;
198
199    dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
200    dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
201
202    dispatch_group_enter(dgroup);
203
204    CloudKeychainReplyBlock replyBlock =
205        ^ (CFDictionaryRef returnedValues, CFErrorRef error)
206    {
207        secerror("SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues);
208        object = returnedValues;
209        if (object)
210            CFRetain(object);
211        if (error)
212        {
213            secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error);
214     //       CFRelease(*error);
215        }
216        dispatch_group_leave(dgroup);
217        secerror("SOSCloudKeychainGetObjectsFromCloud block exit: %@", object);
218        dispatch_semaphore_signal(waitSemaphore);
219    };
220
221    if (!keysToGet)
222        SOSCloudKeychainGetAllObjectsFromCloud(processQueue, replyBlock);
223    else
224        SOSCloudKeychainGetObjectsFromCloud(keysToGet, processQueue, replyBlock);
225
226	dispatch_semaphore_wait(waitSemaphore, finishTime);
227	dispatch_release(waitSemaphore);
228    if (object && (CFGetTypeID(object) == CFNullGetTypeID()))   // return a NULL instead of a CFNull
229    {
230        CFRelease(object);
231        object = NULL;
232    }
233    secerror("returned: %@", object);
234    return object;
235}
236
237bool testRegisterKeys(CFArrayRef keysToRegister, dispatch_queue_t processQueue, dispatch_group_t dgroup)
238{
239    __block bool result = false;
240
241    dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
242    dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
243
244        dispatch_group_enter(dgroup);
245    SOSCloudKeychainRegisterKeysAndGet(keysToRegister, processQueue,
246            ^ (CFDictionaryRef returnedValues, CFErrorRef error)
247            {
248                secerror("testRegisterKeys returned: %@", returnedValues);
249                if (error)
250                {
251                secerror("testRegisterKeys returned: %@", error);
252                CFRelease(error);
253                }
254                dispatch_group_leave(dgroup);
255                result = true;
256            dispatch_semaphore_signal(waitSemaphore);
257            },
258           ^ (CFDictionaryRef returnedValues)
259           {
260               secerror("testRegisterKeys returned: %@", returnedValues);
261               dispatch_group_leave(dgroup);
262               result = true;
263               dispatch_semaphore_signal(waitSemaphore);
264           });
265
266	dispatch_semaphore_wait(waitSemaphore, finishTime);
267	dispatch_release(waitSemaphore);
268//    printTimeNow("finished registerKeysForKVS");
269    return result;
270}
271
272bool testSynchronize(dispatch_queue_t processQueue, dispatch_group_t dgroup)
273{
274    __block bool result = false;
275    dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
276    dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
277
278    dispatch_group_enter(dgroup);
279
280    SOSCloudKeychainSynchronize(processQueue, ^(CFDictionaryRef returnedValues, CFErrorRef error)
281        {
282            result = true;
283            dispatch_group_leave(dgroup);
284            dispatch_semaphore_signal(waitSemaphore);
285        });
286
287	dispatch_semaphore_wait(waitSemaphore, finishTime);
288	dispatch_release(waitSemaphore);
289    return result;
290}
291
292bool testClearAll(dispatch_queue_t processQueue, dispatch_group_t dgroup)
293{
294    __block bool result = false;
295    dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
296    dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
297
298    dispatch_group_enter(dgroup);
299
300    SOSCloudKeychainClearAll(processQueue, ^(CFDictionaryRef returnedValues, CFErrorRef error)
301        {
302            result = true;
303            secerror("SOSCloudKeychainClearAll returned: %@", error);
304            dispatch_group_leave(dgroup);
305            dispatch_semaphore_signal(waitSemaphore);
306        });
307
308	dispatch_semaphore_wait(waitSemaphore, finishTime);
309	dispatch_release(waitSemaphore);
310    secerror("SOSCloudKeychainClearAll exit");
311    return result;
312}
313
314void unregisterFromKVSNotifications(const void *observer)
315{
316    CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetDarwinNotifyCenter(), observer);
317}
318
319//
320// MARK: SOSPeerInfo creation helpers
321//
322
323CFDictionaryRef SOSCreatePeerGestaltFromName(CFStringRef name)
324{
325    return CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
326                                        kPIUserDefinedDeviceName, name,
327                                        NULL);
328}
329
330
331SOSPeerInfoRef SOSCreatePeerInfoFromName(CFStringRef name, SecKeyRef* outSigningKey, CFErrorRef *error)
332{
333    SOSPeerInfoRef result = NULL;
334    SecKeyRef publicKey = NULL;
335    CFDictionaryRef gestalt = NULL;
336
337    require(outSigningKey, exit);
338
339    GeneratePermanentECPair(256, &publicKey, outSigningKey);
340
341    gestalt = SOSCreatePeerGestaltFromName(name);
342    require(gestalt, exit);
343
344    result = SOSPeerInfoCreate(NULL, gestalt, *outSigningKey, error);
345
346exit:
347    CFReleaseNull(gestalt);
348    CFReleaseNull(publicKey);
349
350    return result;
351}
352
353SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(CFStringRef name, SecKeyRef* outSigningKey, CFErrorRef *error)
354{
355    SOSFullPeerInfoRef result = NULL;
356    SecKeyRef publicKey = NULL;
357    CFDictionaryRef gestalt = NULL;
358
359    require(outSigningKey, exit);
360
361    GeneratePermanentECPair(256, &publicKey, outSigningKey);
362
363    gestalt = SOSCreatePeerGestaltFromName(name);
364    require(gestalt, exit);
365
366    result = SOSFullPeerInfoCreate(NULL, gestalt, *outSigningKey, error);
367
368exit:
369    CFReleaseNull(gestalt);
370    CFReleaseNull(publicKey);
371
372    return result;
373}
374
375// MARK: ----- MAC Address -----
376
377/*
378 *	Name:			GetHardwareAdress
379 *
380 *	Parameters:		None.
381 *
382 *	Returns:		Nothing
383 *
384 *	Description:	Retrieve the hardare address for a specified network interface
385 *
386 */
387
388#include <stdlib.h>
389#include <string.h>
390
391#include <sys/socket.h>
392#include <netinet/in.h>
393#include <sys/sysctl.h>
394#include <net/if.h>
395#include <net/if_dl.h>
396#include <net/route.h>
397
398#include <unistd.h>
399#include <netdb.h>
400#include <sys/stat.h>
401
402static int getHardwareAddress(const char *interfaceName, size_t maxLenAllowed, size_t *outActualLength, char *outHardwareAddress)
403{
404	char				*end;
405	struct if_msghdr	*ifm;
406	struct sockaddr_dl	*sdl;
407	char				*buf;
408	int				result;
409	size_t				buffSize;
410	int					mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_IFLIST, 0 };
411
412	buf = 0;
413	*outActualLength = 0;
414	result = -1;
415	//	see how much space is needed
416	require_noerr(result = sysctl(mib, 6, NULL, &buffSize, NULL, 0), xit);
417
418	//	allocate the buffer
419	require(buf = malloc(buffSize), xit);
420
421	//	get the interface info
422	require_noerr(result = sysctl(mib, 6, buf, &buffSize, NULL, 0), xit);
423
424	ifm = (struct if_msghdr *) buf;
425	end = buf + buffSize;
426	do
427	{
428		if (ifm->ifm_type == RTM_IFINFO) 		//	should always be true
429		{
430			sdl = (struct sockaddr_dl *) (ifm + 1);
431			if ( sdl->sdl_nlen == strlen( interfaceName ) && ( bcmp( sdl->sdl_data, interfaceName, sdl->sdl_nlen ) == 0 ) )
432			{
433				if (  sdl->sdl_alen > 0 )
434				{
435					size_t hardwareLen;
436
437					result = 0;						//	indicate found the interface
438					hardwareLen = sdl->sdl_alen;
439					if ( hardwareLen > maxLenAllowed )
440					{
441						hardwareLen = maxLenAllowed;
442						result = -2;				//	indicate truncation of the address
443					}
444					memcpy( outHardwareAddress, sdl->sdl_data + sdl->sdl_nlen, hardwareLen );
445					*outActualLength = hardwareLen;
446					break;
447
448				}
449			}
450		}
451		ifm = (struct if_msghdr *)  ((char*)ifm + ifm->ifm_msglen);
452	} while ( (char*)ifm < end );
453
454xit:
455	if (buf)
456		free(buf);
457
458	return result;
459}
460
461// MARK: ----- cloudTransportTests -----
462
463CFStringRef myMacAddress(void)
464{
465    // 6 bytes, no ":"s
466    CFStringRef result = NULL;
467    const char *interfaceName = "en0";
468    size_t maxLenAllowed = 1024;
469    size_t outActualLength = 0;
470    char outHardwareAddress[1024];
471
472    require_noerr(getHardwareAddress(interfaceName, maxLenAllowed, &outActualLength, outHardwareAddress), xit);
473    require(outActualLength==6, xit);
474    unsigned char buf[32]={0,};
475
476    unsigned char *ps = (unsigned char *)buf;
477    unsigned char *pa = (unsigned char *)outHardwareAddress;
478    for (int ix = 0; ix < 6; ix++, pa++)
479        ps += sprintf((char *)ps, "%02x", *pa);
480
481    result = CFStringCreateWithCString(kCFAllocatorDefault, (const char *)buf, kCFStringEncodingUTF8);
482
483xit:
484    return result;
485}
486