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