1/*
2 * Copyright (c) 2000-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#include <stdio.h>
25#include <unistd.h>
26#include <stdlib.h>
27#include <mach/mach.h>
28#include <mach/mach_error.h>
29#include <servers/bootstrap.h>
30#include <syslog.h>
31#include <CoreFoundation/CFMachPort.h>
32#include <CoreFoundation/CFData.h>
33#include <CoreFoundation/CFPropertyList.h>
34#include <SystemConfiguration/SCPrivate.h>
35#include <bsm/libbsm.h>
36#include <TargetConditionals.h>
37
38#include "symbol_scope.h"
39#include "ipconfigServer.h"
40#include "ipconfigd.h"
41#include "ipconfig_ext.h"
42#include "globals.h"
43
44static uid_t S_uid = -1;
45static pid_t S_pid = -1;
46
47
48#if ! TARGET_OS_EMBEDDED
49#define	kIPConfigurationServiceEntitlement	NULL
50
51static boolean_t
52S_has_entitlement(audit_token_t token, CFStringRef entitlement)
53{
54    return (FALSE);
55}
56
57#else /* ! TARGET_OS_EMBEDDED */
58#define	kIPConfigurationServiceEntitlement	CFSTR("com.apple.IPConfigurationService")	/* boolean */
59
60#include <Security/SecTask.h>
61static boolean_t
62S_has_entitlement(audit_token_t token, CFStringRef entitlement)
63{
64    boolean_t		ret = FALSE;
65    SecTaskRef		task;
66
67    task = SecTaskCreateWithAuditToken(NULL, token);
68    if (task != NULL) {
69	CFBooleanRef	allow;
70
71	allow = SecTaskCopyValueForEntitlement(task, entitlement, NULL);
72	if (allow != NULL) {
73	    if (isA_CFBoolean(allow) != NULL) {
74		ret = CFBooleanGetValue(allow);
75	    }
76	    CFRelease(allow);
77	}
78	CFRelease(task);
79    }
80    return (ret);
81}
82#endif /* ! TARGET_OS_EMBEDDED */
83
84static void
85S_process_audit_token(audit_token_t audit_token)
86{
87    audit_token_to_au32(audit_token,
88			NULL,		/* auidp */
89			&S_uid,		/* euid */
90			NULL,		/* egid */
91			NULL,		/* ruid */
92			NULL,		/* rgid */
93			&S_pid,		/* pid */
94			NULL,		/* asid */
95			NULL);		/* tid */
96    return;
97}
98
99static boolean_t
100S_IPConfigurationServiceOperationAllowed(audit_token_t audit_token)
101{
102    S_process_audit_token(audit_token);
103    if (S_uid == 0
104	|| S_has_entitlement(audit_token,
105			     kIPConfigurationServiceEntitlement)) {
106	return (TRUE);
107    }
108    return (FALSE);
109}
110
111static CFPropertyListRef
112my_CFPropertyListCreateWithBytePtrAndLength(const void * data, int data_len)
113{
114    CFPropertyListRef	plist;
115    CFDataRef		xml_data;
116
117    xml_data = CFDataCreateWithBytesNoCopy(NULL,
118					   (const UInt8 *)data, data_len,
119					   kCFAllocatorNull);
120    if (xml_data == NULL) {
121	return (NULL);
122    }
123    plist = CFPropertyListCreateWithData(NULL,
124					 xml_data,
125					 kCFPropertyListImmutable,
126					 NULL,
127					 NULL);
128    CFRelease(xml_data);
129    return (plist);
130}
131
132static ipconfig_status_t
133method_info_from_xml_data(xmlData_t xml_data,
134			  mach_msg_type_number_t xml_data_len,
135			  ipconfig_method_t * method_p,
136			  ipconfig_method_data_t * * method_data_p)
137{
138    CFPropertyListRef	plist = NULL;
139    ipconfig_status_t	status;
140
141    if (xml_data != NULL) {
142	plist = my_CFPropertyListCreateWithBytePtrAndLength(xml_data,
143							    xml_data_len);
144    }
145    status = ipconfig_method_info_from_plist(plist, method_p, method_data_p);
146    if (plist != NULL) {
147	CFRelease(plist);
148    }
149    return (status);
150}
151
152PRIVATE_EXTERN kern_return_t
153_ipconfig_if_addr(mach_port_t p, if_name_t name,
154		  u_int32_t * addr, ipconfig_status_t * status)
155{
156    *status = get_if_addr(name, addr);
157    return (KERN_SUCCESS);
158}
159
160PRIVATE_EXTERN kern_return_t
161_ipconfig_if_count(mach_port_t p, int * count)
162{
163    *count = get_if_count();
164    return (KERN_SUCCESS);
165}
166
167PRIVATE_EXTERN kern_return_t
168_ipconfig_get_option(mach_port_t p, if_name_t name, int option_code,
169		     inline_data_t option_data,
170		     mach_msg_type_number_t * option_dataCnt,
171		     ipconfig_status_t * status)
172
173{
174    *status = get_if_option(name, option_code, option_data, option_dataCnt);
175    return (KERN_SUCCESS);
176}
177
178PRIVATE_EXTERN kern_return_t
179_ipconfig_get_packet(mach_port_t p, if_name_t name,
180		     inline_data_t packet_data,
181		     mach_msg_type_number_t * packet_dataCnt,
182		     ipconfig_status_t * status)
183{
184    *status = get_if_packet(name, packet_data, packet_dataCnt);
185    return (KERN_SUCCESS);
186}
187
188PRIVATE_EXTERN kern_return_t
189_ipconfig_get_v6_packet(mach_port_t p, if_name_t name,
190		     inline_data_t packet_data,
191		     mach_msg_type_number_t * packet_dataCnt,
192		     ipconfig_status_t * status)
193{
194    *status = get_if_v6_packet(name, packet_data, packet_dataCnt);
195    return (KERN_SUCCESS);
196}
197
198PRIVATE_EXTERN kern_return_t
199_ipconfig_set(mach_port_t p, if_name_t name,
200	      xmlData_t xml_data,
201	      mach_msg_type_number_t xml_data_len,
202	      ipconfig_status_t * ret_status,
203	      audit_token_t audit_token)
204{
205    ipconfig_method_t		method;
206    ipconfig_method_data_t *	method_data;
207    ipconfig_status_t		status;
208
209    S_process_audit_token(audit_token);
210    if (S_uid != 0) {
211	status = ipconfig_status_permission_denied_e;
212	goto done;
213    }
214    status = method_info_from_xml_data(xml_data, xml_data_len,
215				       &method, &method_data);
216    if (status != ipconfig_status_success_e) {
217	goto done;
218    }
219    status = set_if(name, method, method_data);
220    if (method_data != NULL) {
221	free(method_data);
222    }
223 done:
224    if (xml_data != NULL) {
225	(void)vm_deallocate(mach_task_self(), (vm_address_t)xml_data,
226			    xml_data_len);
227    }
228    *ret_status = status;
229    return (KERN_SUCCESS);
230}
231
232PRIVATE_EXTERN kern_return_t
233_ipconfig_set_verbose(mach_port_t p, int verbose,
234		      ipconfig_status_t * status,
235		      audit_token_t audit_token)
236{
237    *status = ipconfig_status_permission_denied_e;
238    return (KERN_SUCCESS);
239}
240
241#ifdef IPCONFIG_TEST_NO_ENTRY
242PRIVATE_EXTERN kern_return_t
243_ipconfig_set_something(mach_port_t p, int verbose,
244			ipconfig_status_t * status)
245{
246    return (KERN_SUCCESS);
247}
248#endif /* IPCONFIG_TEST_NO_ENTRY */
249
250PRIVATE_EXTERN kern_return_t
251_ipconfig_add_service(mach_port_t p,
252		      if_name_t name,
253		      xmlData_t xml_data,
254		      mach_msg_type_number_t xml_data_len,
255		      inline_data_t service_id,
256		      mach_msg_type_number_t * service_id_len,
257		      ipconfig_status_t * ret_status,
258		      audit_token_t audit_token)
259{
260    ipconfig_method_t		method;
261    ipconfig_method_data_t *	method_data;
262    CFPropertyListRef		plist = NULL;
263    ipconfig_status_t		status;
264
265    if (!S_IPConfigurationServiceOperationAllowed(audit_token)) {
266	status = ipconfig_status_permission_denied_e;
267	goto done;
268    }
269    if (xml_data != NULL) {
270	plist = my_CFPropertyListCreateWithBytePtrAndLength(xml_data,
271							    xml_data_len);
272    }
273    status = ipconfig_method_info_from_plist(plist, &method, &method_data);
274    if (status != ipconfig_status_success_e) {
275	goto done;
276    }
277    status = add_service(name, method, method_data, service_id, service_id_len,
278			 plist, S_pid);
279    if (method_data != NULL) {
280	free(method_data);
281    }
282 done:
283    if (plist != NULL) {
284	CFRelease(plist);
285    }
286    if (xml_data != NULL) {
287	(void)vm_deallocate(mach_task_self(), (vm_address_t)xml_data,
288			    xml_data_len);
289    }
290    *ret_status = status;
291    return (KERN_SUCCESS);
292}
293
294kern_return_t
295_ipconfig_set_service(mach_port_t p,
296		      if_name_t name,
297		      xmlData_t xml_data,
298		      mach_msg_type_number_t xml_data_len,
299		      inline_data_t service_id,
300		      mach_msg_type_number_t * service_id_len,
301		      ipconfig_status_t * ret_status,
302		      audit_token_t audit_token)
303{
304    ipconfig_method_t		method;
305    ipconfig_method_data_t *	method_data;
306    ipconfig_status_t		status;
307
308    if (!S_IPConfigurationServiceOperationAllowed(audit_token)) {
309	status = ipconfig_status_permission_denied_e;
310	goto done;
311    }
312    status = method_info_from_xml_data(xml_data, xml_data_len,
313				       &method, &method_data);
314    if (status != ipconfig_status_success_e) {
315	goto done;
316    }
317    status = set_service(name, method, method_data,
318			 service_id, service_id_len);
319    if (method_data != NULL) {
320	free(method_data);
321    }
322 done:
323    if (xml_data != NULL) {
324	(void)vm_deallocate(mach_task_self(), (vm_address_t)xml_data,
325			    xml_data_len);
326    }
327    *ret_status = status;
328    return (KERN_SUCCESS);
329}
330
331PRIVATE_EXTERN kern_return_t
332_ipconfig_remove_service_with_id(mach_port_t server,
333				 inline_data_t service_id,
334				 mach_msg_type_number_t service_id_len,
335				 ipconfig_status_t * ret_status,
336				 audit_token_t audit_token)
337{
338    if (!S_IPConfigurationServiceOperationAllowed(audit_token)) {
339	*ret_status = ipconfig_status_permission_denied_e;
340    }
341    else {
342	*ret_status = remove_service_with_id(NULL, service_id, service_id_len);
343    }
344    return (KERN_SUCCESS);
345}
346
347PRIVATE_EXTERN kern_return_t
348_ipconfig_remove_service_on_interface(mach_port_t server,
349				      if_name_t name,
350				      inline_data_t service_id,
351				      mach_msg_type_number_t service_id_len,
352				      ipconfig_status_t * ret_status,
353				      audit_token_t audit_token)
354{
355    if (!S_IPConfigurationServiceOperationAllowed(audit_token)) {
356	*ret_status = ipconfig_status_permission_denied_e;
357    }
358    else {
359	*ret_status = remove_service_with_id(name, service_id, service_id_len);
360    }
361    return (KERN_SUCCESS);
362}
363
364PRIVATE_EXTERN kern_return_t
365_ipconfig_find_service(mach_port_t server,
366		       if_name_t name,
367		       boolean_t exact,
368		       xmlData_t xml_data,
369		       mach_msg_type_number_t xml_data_len,
370		       inline_data_t service_id,
371		       mach_msg_type_number_t *service_id_len,
372		       ipconfig_status_t * ret_status)
373{
374    ipconfig_method_t		method;
375    ipconfig_method_data_t *	method_data;
376    ipconfig_status_t		status;
377
378    status = method_info_from_xml_data(xml_data, xml_data_len,
379				       &method, &method_data);
380    if (status != ipconfig_status_success_e) {
381	goto done;
382    }
383    status = find_service(name, exact, method, method_data,
384			  service_id, service_id_len);
385    if (method_data != NULL) {
386	free(method_data);
387    }
388
389 done:
390    if (xml_data != NULL) {
391	(void)vm_deallocate(mach_task_self(), (vm_address_t)xml_data,
392			    xml_data_len);
393    }
394    *ret_status = status;
395    return (KERN_SUCCESS);
396}
397
398PRIVATE_EXTERN kern_return_t
399_ipconfig_remove_service(mach_port_t server,
400			 if_name_t name,
401			 xmlData_t xml_data,
402			 mach_msg_type_number_t xml_data_len,
403			 ipconfig_status_t * ret_status,
404			 audit_token_t audit_token)
405{
406    ipconfig_method_t		method;
407    ipconfig_method_data_t *	method_data;
408    ipconfig_status_t		status;
409
410    if (!S_IPConfigurationServiceOperationAllowed(audit_token)) {
411	status = ipconfig_status_permission_denied_e;
412	goto done;
413    }
414    status = method_info_from_xml_data(xml_data, xml_data_len,
415				       &method, &method_data);
416    if (status != ipconfig_status_success_e) {
417	goto done;
418    }
419    status = remove_service(name, method, method_data);
420    if (method_data != NULL) {
421	free(method_data);
422    }
423
424 done:
425    if (xml_data != NULL) {
426	(void)vm_deallocate(mach_task_self(), (vm_address_t)xml_data,
427			    xml_data_len);
428    }
429    *ret_status = status;
430    return (KERN_SUCCESS);
431}
432
433PRIVATE_EXTERN kern_return_t
434_ipconfig_refresh_service(mach_port_t server,
435			  if_name_t name,
436			  inline_data_t service_id,
437			  mach_msg_type_number_t service_id_len,
438			  ipconfig_status_t * ret_status,
439			  audit_token_t audit_token)
440{
441    if (!S_IPConfigurationServiceOperationAllowed(audit_token)) {
442	*ret_status = ipconfig_status_permission_denied_e;
443    }
444    else {
445	*ret_status = refresh_service(name, service_id, service_id_len);
446    }
447    return (KERN_SUCCESS);
448}
449
450static void
451S_ipconfig_server(CFMachPortRef port, void *msg, CFIndex size, void *info)
452{
453    uint64_t 		reply_buf[(2048 + 256)/sizeof(uint64_t)];
454    mach_msg_options_t 	options = 0;
455    mig_reply_error_t * request = (mig_reply_error_t *)msg;
456    mig_reply_error_t *	reply;
457    mach_msg_return_t 	r = MACH_MSG_SUCCESS;
458
459    if (_ipconfig_subsystem.maxsize > sizeof(reply_buf)) {
460	syslog(LOG_NOTICE, "IPConfiguration server: %d > %ld",
461	       _ipconfig_subsystem.maxsize, sizeof(reply_buf));
462	reply = (mig_reply_error_t *)
463	    malloc(_ipconfig_subsystem.maxsize);
464    }
465    else {
466	reply = (mig_reply_error_t *)(void *)reply_buf;
467    }
468    if (ipconfig_server(&request->Head, &reply->Head) == FALSE) {
469	my_log(LOG_DEBUG, "IPConfiguration: unknown message ID (%d) received",
470	       request->Head.msgh_id);
471    }
472
473    /* Copied from Libc/mach/mach_msg.c:mach_msg_server_once(): Start */
474    if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
475	if (reply->RetCode == MIG_NO_REPLY)
476	    reply->Head.msgh_remote_port = MACH_PORT_NULL;
477	else if ((reply->RetCode != KERN_SUCCESS) &&
478		 (request->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
479	    /* destroy the request - but not the reply port */
480	    request->Head.msgh_remote_port = MACH_PORT_NULL;
481	    mach_msg_destroy(&request->Head);
482	}
483    }
484    /*
485     *	We don't want to block indefinitely because the client
486     *	isn't receiving messages from the reply port.
487     *	If we have a send-once right for the reply port, then
488     *	this isn't a concern because the send won't block.
489     *	If we have a send right, we need to use MACH_SEND_TIMEOUT.
490     *	To avoid falling off the kernel's fast RPC path unnecessarily,
491     *	we only supply MACH_SEND_TIMEOUT when absolutely necessary.
492     */
493    if (reply->Head.msgh_remote_port != MACH_PORT_NULL) {
494	r = mach_msg(&reply->Head,
495		     (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) ==
496		      MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
497		     MACH_SEND_MSG|options :
498		     MACH_SEND_MSG|MACH_SEND_TIMEOUT|options,
499		     reply->Head.msgh_size, 0, MACH_PORT_NULL,
500		     MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
501	if ((r != MACH_SEND_INVALID_DEST) &&
502	    (r != MACH_SEND_TIMED_OUT))
503	    goto done_once;
504	r = MACH_MSG_SUCCESS;
505    }
506    if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
507	mach_msg_destroy(&reply->Head);
508 done_once:
509    /* Copied from Libc/mach/mach_msg.c:mach_msg_server_once(): End */
510
511    /* ALIGN: reply_buf is aligned to at least sizeof(uint64_t) bytes */
512    if (reply != (mig_reply_error_t *)(void *)reply_buf) {
513	free(reply);
514    }
515
516    if (r != MACH_MSG_SUCCESS) {
517	my_log(LOG_DEBUG, "IPConfiguration msg_send: %s", mach_error_string(r));
518    }
519    return;
520}
521
522PRIVATE_EXTERN void
523server_init()
524{
525    CFRunLoopSourceRef	rls;
526    CFMachPortRef	ipconfigd_port;
527    mach_port_t		server_port;
528    kern_return_t 	status;
529
530    status = bootstrap_check_in(bootstrap_port, IPCONFIG_SERVER,
531				&server_port);
532    if (status != BOOTSTRAP_SUCCESS) {
533	my_log(LOG_NOTICE,
534	       "IPConfiguration: bootstrap_check_in failed, %s",
535	       mach_error_string(status));
536	return;
537    }
538    ipconfigd_port = _SC_CFMachPortCreateWithPort(NULL, server_port,
539						  S_ipconfig_server,
540						  NULL);
541    rls = CFMachPortCreateRunLoopSource(NULL, ipconfigd_port, 0);
542    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
543    CFRelease(rls);
544    CFRelease(ipconfigd_port);
545    return;
546}
547