1/*
2 * Copyright (c) 2006, 2008, 2009, 2011-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 * SCNetworkSignature.c
26 * - implementation of SCNetworkSignatureRef API that allows access to
27     network identification information
28 *
29 */
30/*
31 * Modification History
32 *
33 * November 6, 2006	Dieter Siegmund (dieter@apple.com)
34 * - initial revision
35 */
36
37
38#include <netinet/in.h>
39#include <CoreFoundation/CFDictionary.h>
40#include <CoreFoundation/CFString.h>
41#include <CoreFoundation/CFArray.h>
42#include <CoreFoundation/CFRuntime.h>
43#include <SystemConfiguration/SCDynamicStore.h>
44#include <SystemConfiguration/SCValidation.h>
45#include <SystemConfiguration/SCPrivate.h>
46#include "SCNetworkSignature.h"
47#include "SCNetworkSignaturePrivate.h"
48#include <arpa/inet.h>
49#include <sys/types.h>
50#include <sys/socket.h>
51#include <net/if.h>
52#include <network/conninfo.h>
53
54#pragma mark SCNetworkSignature Supporting APIs
55
56static CFStringRef
57create_global_state_v4_key(void)
58{
59	return SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
60							  kSCDynamicStoreDomainState,
61							  kSCEntNetIPv4);
62
63}
64
65static CFStringRef
66create_global_setup_v4_key(void)
67{
68	return SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
69							  kSCDynamicStoreDomainSetup,
70							  kSCEntNetIPv4);
71}
72
73static CFStringRef
74create_ipv4_services_pattern(void)
75{
76	return SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
77							   kSCDynamicStoreDomainState,
78							   kSCCompAnyRegex,
79							   kSCEntNetIPv4);
80}
81
82static CFStringRef
83create_ipv6_services_pattern(void)
84{
85	return SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
86							   kSCDynamicStoreDomainState,
87							   kSCCompAnyRegex,
88							   kSCEntNetIPv6);
89}
90
91static CFDictionaryRef
92copy_services_for_address_family(CFAllocatorRef alloc,
93				 int af)
94{
95	CFDictionaryRef info;
96	CFArrayRef      patterns;
97	CFStringRef     pattern;
98	CFStringRef     prop;
99
100	prop = (af == AF_INET) ? kSCEntNetIPv4 : kSCEntNetIPv6;
101	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
102							      kSCDynamicStoreDomainState,
103							      kSCCompAnyRegex,
104							      prop);
105	patterns = CFArrayCreate(NULL,
106				 (const void * *)&pattern, 1,
107				 &kCFTypeArrayCallBacks);
108	CFRelease(pattern);
109	info = SCDynamicStoreCopyMultiple(NULL, NULL, patterns);
110	CFRelease(patterns);
111
112	return (info);
113}
114
115
116static CF_RETURNS_RETAINED CFStringRef
117my_IPAddressToCFString(int af, const void * src_p)
118{
119	char		ntopbuf[INET6_ADDRSTRLEN];
120
121	if (inet_ntop(af, src_p, ntopbuf, sizeof(ntopbuf)) != NULL) {
122		return (CFStringCreateWithCString(NULL, ntopbuf,
123						  kCFStringEncodingASCII));
124	}
125	return (NULL);
126}
127
128#pragma mark -
129
130
131#pragma mark SCNetworkSignature APIs
132
133CFStringRef
134SCNetworkSignatureCopyActiveIdentifierForAddress(CFAllocatorRef alloc,
135						 const struct sockaddr * addr)
136{
137	CFStringRef		ident	= NULL;
138	CFDictionaryRef		info = NULL;
139	CFStringRef		global_state_v4_key = NULL;
140	CFDictionaryRef		global_v4_state_dict = NULL;
141	CFArrayRef		keys = NULL;
142	CFArrayRef		patterns = NULL;
143	in_addr_t 		s_addr;
144	CFStringRef		service = NULL;
145	CFDictionaryRef		service_dict = NULL;
146	CFStringRef		service_id = NULL;
147	struct sockaddr_in *	sin_p;
148	CFStringRef		v4_service_pattern = NULL;
149
150	/* only accept 0.0.0.0 (i.e. default) for now */
151	if (addr == NULL
152	    || addr->sa_family != AF_INET
153	    || addr->sa_len != sizeof(struct sockaddr_in)) {
154		_SCErrorSet(kSCStatusInvalidArgument);
155		goto done;
156	}
157
158	/* ALIGN: force alignment */
159	sin_p = (struct sockaddr_in *)(void *)addr;
160	bcopy(&sin_p->sin_addr.s_addr, &s_addr, sizeof(s_addr));
161	if (s_addr != 0) {
162		_SCErrorSet(kSCStatusInvalidArgument);
163		goto done;
164	}
165
166	global_state_v4_key = create_global_state_v4_key();
167	keys = CFArrayCreate(NULL, (const void * *)&global_state_v4_key,
168			     1, &kCFTypeArrayCallBacks);
169
170	v4_service_pattern = create_ipv4_services_pattern();
171	patterns = CFArrayCreate(NULL,  (const void * *)&v4_service_pattern, 1,
172				 &kCFTypeArrayCallBacks);
173
174	info = SCDynamicStoreCopyMultiple(NULL, keys, patterns);
175
176	if (info == NULL
177	    || CFDictionaryGetCount(info) == 0) {
178		goto done;
179	}
180
181	global_v4_state_dict = CFDictionaryGetValue(info, global_state_v4_key);
182
183	if (isA_CFDictionary(global_v4_state_dict) == NULL) {
184		goto done;
185	}
186
187	service_id = CFDictionaryGetValue(global_v4_state_dict,
188					   kSCDynamicStorePropNetPrimaryService);
189
190	if (isA_CFString(service_id) == NULL) {
191		goto done;
192	}
193
194	service = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
195							      kSCDynamicStoreDomainState,
196							      service_id, kSCEntNetIPv4);
197
198	service_dict = CFDictionaryGetValue(info, service);
199
200
201	if (isA_CFDictionary(service_dict) == NULL
202	    || CFDictionaryGetCount(service_dict) == 0) {
203		goto done;
204	}
205
206	ident = CFDictionaryGetValue(service_dict, kStoreKeyNetworkSignature);
207	ident = isA_CFString(ident);
208done:
209	if (ident != NULL) {
210		CFRetain(ident);
211	} else {
212		_SCErrorSet(kSCStatusFailed);
213	}
214	if (info != NULL) {
215		CFRelease(info);
216	}
217	if (global_state_v4_key != NULL) {
218		CFRelease(global_state_v4_key);
219	}
220	if (service != NULL) {
221		CFRelease(service);
222	}
223	if (keys != NULL) {
224		CFRelease(keys);
225	}
226	if (patterns != NULL) {
227		CFRelease(patterns);
228	}
229	if (v4_service_pattern != NULL) {
230		CFRelease(v4_service_pattern);
231	}
232	return (ident);
233}
234
235CFArrayRef /* of CFStringRef's */
236SCNetworkSignatureCopyActiveIdentifiers(CFAllocatorRef alloc)
237{
238	CFMutableArrayRef	active = NULL;
239	CFIndex			count = 0;
240	CFStringRef		global_setup_v4_key = NULL;
241	CFDictionaryRef		global_v4_dict;
242	int			i;
243	CFDictionaryRef		info = NULL;
244	CFArrayRef		keys = NULL;
245	CFMutableArrayRef	patterns = NULL;
246	CFRange			range;
247	CFMutableDictionaryRef	services_dict = NULL;
248	CFArrayRef		service_order;
249	CFStringRef		v4_service_pattern = NULL;
250	CFStringRef		v6_service_pattern = NULL;
251	const void * *		values = NULL;
252#define KEYS_STATIC_COUNT	10
253	const void *		values_static[KEYS_STATIC_COUNT];
254
255	patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
256
257	global_setup_v4_key = create_global_setup_v4_key();
258	keys = CFArrayCreate(NULL, (const void * *)&global_setup_v4_key, 1,
259			     &kCFTypeArrayCallBacks);
260
261	v4_service_pattern = create_ipv4_services_pattern();
262	CFArrayAppendValue(patterns, v4_service_pattern);
263
264	v6_service_pattern = create_ipv6_services_pattern();
265	CFArrayAppendValue(patterns, v6_service_pattern);
266
267	info = SCDynamicStoreCopyMultiple(NULL, keys, patterns);
268
269	if (info == NULL
270	    || CFDictionaryGetCount(info) == 0) {
271		goto done;
272	}
273
274	services_dict = CFDictionaryCreateMutableCopy(NULL, 0, info);
275	/*
276	 * The service_dict should only contain services and once each
277	 * service has been visited, it will be removed from the dictionary.
278	 */
279	CFDictionaryRemoveValue(services_dict, global_setup_v4_key);
280
281	global_v4_dict = CFDictionaryGetValue(info, global_setup_v4_key);
282
283	if (isA_CFDictionary(global_v4_dict) != NULL) {
284		service_order = CFDictionaryGetValue(global_v4_dict,
285						     kSCPropNetServiceOrder);
286		if (isA_CFArray(service_order) != NULL) {
287			count = CFArrayGetCount(service_order);
288		}
289	}
290
291	active = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
292
293	range = CFRangeMake(0, 0);
294
295	for (i = 0; i < count ; i++) {
296		int			j;
297		CFStringRef		network_sig;
298		CFStringRef		service;
299		CFStringRef		service_id;
300		CFDictionaryRef		service_info;
301		CFStringRef		afs[2] = {kSCEntNetIPv4, kSCEntNetIPv6};
302
303		service_id = CFArrayGetValueAtIndex(service_order, i);
304
305		if (isA_CFString(service_id) == NULL) {
306			continue;
307		}
308
309		for (j = 0; j < 2; j++) {
310			service = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
311						kSCDynamicStoreDomainState,
312						service_id, afs[j]);
313
314			service_info = CFDictionaryGetValue(services_dict, service);
315
316			/* Does this service have a signature? */
317			if (isA_CFDictionary(service_info) != NULL) {
318				network_sig = CFDictionaryGetValue(service_info, kStoreKeyNetworkSignature);
319				if (isA_CFString(network_sig) != NULL
320				    && CFArrayContainsValue(active, range, network_sig) == FALSE) {
321					CFArrayAppendValue(active, network_sig);
322					network_sig = NULL;
323					range.length++;
324				}
325				CFDictionaryRemoveValue(services_dict, service);
326			}
327			CFRelease(service);
328		}
329	}
330
331	count = CFDictionaryGetCount(services_dict);
332	if (count != 0) {
333		if (count > KEYS_STATIC_COUNT) {
334			values = (const void * *)malloc(sizeof(*values) * count);
335		} else {
336			values = values_static;
337		}
338		CFDictionaryGetKeysAndValues(services_dict, NULL,
339					     (const void * *)values);
340	}
341
342	for (i = 0; i < count; i++) {
343		CFStringRef 		network_sig;
344		CFDictionaryRef 	service_dict = (CFDictionaryRef)values[i];
345
346		if (isA_CFDictionary(service_dict) == NULL) {
347			continue;
348		}
349
350		network_sig = CFDictionaryGetValue(service_dict,
351						   kStoreKeyNetworkSignature);
352		/* Does this service have a signature? */
353		if (isA_CFString(network_sig) != NULL
354		    && CFArrayContainsValue(active, range, network_sig) == FALSE) {
355			CFArrayAppendValue(active, network_sig);
356			range.length++;
357			network_sig = NULL;
358		}
359	}
360 done:
361	if (info != NULL) {
362		CFRelease(info);
363	}
364	if (services_dict != NULL) {
365		CFRelease(services_dict);
366	}
367	if (global_setup_v4_key != NULL) {
368		CFRelease(global_setup_v4_key);
369	}
370	if (v4_service_pattern != NULL) {
371		CFRelease(v4_service_pattern);
372	}
373	if (v6_service_pattern != NULL) {
374		CFRelease(v6_service_pattern);
375	}
376	if (values != NULL && values != values_static) {
377		free(values);
378	}
379	if (keys != NULL) {
380		CFRelease(keys);
381	}
382	if (patterns != NULL) {
383		CFRelease(patterns);
384	}
385	if (active != NULL && CFArrayGetCount(active) == 0) {
386		CFRelease(active);
387		active = NULL;
388	}
389	if (active == NULL) {
390		_SCErrorSet(kSCStatusFailed);
391	}
392	return (active);
393}
394
395
396CFStringRef
397SCNetworkSignatureCopyIdentifierForConnectedSocket(CFAllocatorRef alloc,
398						   int sock_fd)
399{
400	CFStringRef		addresses_key;
401	int			af;
402	CFIndex			count;
403	int			i;
404	char			if_name[IFNAMSIZ];
405	CFStringRef		if_name_cf	= NULL;
406	conninfo_t *		info		= NULL;
407	const void * *		keys		= NULL;
408#define KEYS_STATIC_COUNT	10
409	const void *		keys_static[KEYS_STATIC_COUNT];
410	const void *		local_ip_p;
411	CFStringRef		local_ip_str	= NULL;
412	CFStringRef		ret_signature	= NULL;
413	CFDictionaryRef		service_info	= NULL;
414	int			status		= kSCStatusFailed;
415
416	if (copyconninfo(sock_fd, CONNID_ANY, &info) != 0) {
417		status = kSCStatusInvalidArgument;
418		goto done;
419	}
420	if ((info->ci_flags & CIF_CONNECTED) == 0
421	    || info->ci_src == NULL) {
422	    goto done;
423	}
424	af = info->ci_src->sa_family;
425	switch (af) {
426	case AF_INET:
427		addresses_key = kSCPropNetIPv4Addresses;
428		local_ip_p = &((struct sockaddr_in *)
429			       (void *)info->ci_src)->sin_addr;
430		break;
431	case AF_INET6:
432		addresses_key = kSCPropNetIPv6Addresses;
433		local_ip_p = &((struct sockaddr_in6 *)
434			       (void *)info->ci_src)->sin6_addr;
435		break;
436	default:
437		status = kSCStatusInvalidArgument;
438		goto done;
439	}
440
441	/* search for service with matching IP address and interface name */
442	service_info = copy_services_for_address_family(alloc, af);
443	if (service_info == NULL) {
444		goto done;
445	}
446	local_ip_str = my_IPAddressToCFString(af, local_ip_p);
447	if (local_ip_str == NULL) {
448		goto done;
449	}
450	if (info->ci_ifindex != 0
451	    && if_indextoname(info->ci_ifindex, if_name) != NULL) {
452		if_name_cf
453			= CFStringCreateWithCString(NULL, if_name,
454						    kCFStringEncodingASCII);
455	}
456	count = CFDictionaryGetCount(service_info);
457	if (count > KEYS_STATIC_COUNT) {
458		keys = (const void * *)malloc(sizeof(*keys) * count);
459	}
460	else {
461		keys = keys_static;
462	}
463	CFDictionaryGetKeysAndValues(service_info, keys, NULL);
464	for (i = 0; i < count; i++) {
465		CFArrayRef		addrs;
466		CFRange			range;
467		CFStringRef		signature;
468		CFDictionaryRef		value;
469
470		value = CFDictionaryGetValue(service_info, keys[i]);
471		if (isA_CFDictionary(value) == NULL) {
472			continue;
473		}
474		signature = CFDictionaryGetValue(value,
475						 kStoreKeyNetworkSignature);
476		if (isA_CFString(signature) == NULL) {
477			/* no signature */
478			continue;
479		}
480		if (if_name_cf != NULL) {
481			CFStringRef		confirmed_if;
482			CFStringRef		this_if;
483
484			this_if = CFDictionaryGetValue(value,
485						       kSCPropInterfaceName);
486			if (isA_CFString(this_if) == NULL
487			    || !CFEqual(this_if, if_name_cf)) {
488				/* no interface or it doesn't match */
489				continue;
490			}
491			confirmed_if
492				= CFDictionaryGetValue(value,
493						       kSCPropConfirmedInterfaceName);
494			if (isA_CFString(confirmed_if) != NULL
495			    && !CFEqual(confirmed_if, if_name_cf)) {
496				/* confirmed interface doesn't match */
497				continue;
498			}
499		}
500
501		addrs = CFDictionaryGetValue(value, addresses_key);
502		if (isA_CFArray(addrs) == NULL) {
503			continue;
504		}
505		range = CFRangeMake(0, CFArrayGetCount(addrs));
506		if (CFArrayContainsValue(addrs, range, local_ip_str)) {
507			ret_signature = CFRetain(signature);
508			status = kSCStatusOK;
509			break;
510		}
511	}
512
513 done:
514	if (info != NULL) {
515		freeconninfo(info);
516	}
517	if (if_name_cf != NULL) {
518		CFRelease(if_name_cf);
519	}
520	if (local_ip_str != NULL) {
521		CFRelease(local_ip_str);
522	}
523	if (keys != NULL && keys != keys_static) {
524		free(keys);
525	}
526	if (service_info != NULL) {
527		CFRelease(service_info);
528	}
529	if (status != kSCStatusOK) {
530		_SCErrorSet(status);
531	}
532	return (ret_signature);
533}
534
535#pragma mark -
536