1/*
2 * Copyright (c) 2001-2013 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 * Modification History
26 *
27 * November 8, 2001	Dieter Siegmund
28 * - created
29 */
30
31#include <stdio.h>
32#include <unistd.h>
33#include <stdlib.h>
34#include <sys/errno.h>
35#include <signal.h>
36#include <mach/mach.h>
37#include <mach/message.h>
38#include <mach/mach_error.h>
39#include <servers/bootstrap.h>
40#include <syslog.h>
41#include <SystemConfiguration/SCPrivate.h>
42#include <SystemConfiguration/SCValidation.h>
43#include <SystemConfiguration/SCDPlugin.h>
44#include <CoreFoundation/CFMachPort.h>
45#include "controller.h"
46#include "eapolcontrollerServer.h"
47#include "eapolcontroller_types.h"
48#include "eapolcontroller_ext.h"
49#include "server.h"
50#include "myCFUtil.h"
51#include "EAPLog.h"
52
53extern boolean_t		eapolcontroller_server(mach_msg_header_t *,
54						       mach_msg_header_t *);
55
56static uid_t S_uid = -1;
57static gid_t S_gid = -1;
58
59static CFMachPortRef		server_cfport;
60
61static vm_address_t
62my_CFPropertyListCreateVMData(CFPropertyListRef plist,
63			      mach_msg_type_number_t * 	ret_data_len)
64{
65    vm_address_t	data;
66    int			data_len;
67    kern_return_t	status;
68    CFDataRef		xml_data;
69
70    data = 0;
71    *ret_data_len = 0;
72    xml_data = CFPropertyListCreateXMLData(NULL, plist);
73    if (xml_data == NULL) {
74	goto done;
75    }
76    data_len = CFDataGetLength(xml_data);
77    status = vm_allocate(mach_task_self(), &data, data_len, TRUE);
78    if (status != KERN_SUCCESS) {
79	goto done;
80    }
81    bcopy((char *)CFDataGetBytePtr(xml_data), (char *)data, data_len);
82    *ret_data_len = data_len;
83
84 done:
85    my_CFRelease(&xml_data);
86    return (data);
87}
88
89static __inline__ void
90read_trailer(mach_msg_header_t * request)
91{
92    mach_msg_format_0_trailer_t	*trailer;
93    trailer = (mach_msg_security_trailer_t *)((vm_offset_t)request +
94					      round_msg(request->msgh_size));
95
96    if ((trailer->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0) &&
97	(trailer->msgh_trailer_size >= MACH_MSG_TRAILER_FORMAT_0_SIZE)) {
98	S_uid = trailer->msgh_sender.val[0];
99	S_gid = trailer->msgh_sender.val[1];
100    }
101    else {
102	S_uid = -1;
103	S_gid = -1;
104    }
105}
106
107kern_return_t
108eapolcontroller_get_state(mach_port_t server,
109			  if_name_t if_name,
110			  int * state,
111			  int * result)
112{
113    *result = ControllerGetState(if_name, state);
114    return (KERN_SUCCESS);
115}
116
117
118kern_return_t
119eapolcontroller_copy_status(mach_port_t server,
120			    if_name_t if_name,
121			    xmlDataOut_t * status,
122			    mach_msg_type_number_t * status_len,
123			    int * state,
124			    int * result)
125{
126    CFDictionaryRef	dict = NULL;
127
128    *status = NULL;
129    *status_len = 0;
130    *result = ControllerCopyStateAndStatus(if_name, state, &dict);
131    if (dict != NULL) {
132	*status = (xmlDataOut_t)my_CFPropertyListCreateVMData(dict, status_len);
133	if (*status == NULL) {
134	    EAPLOG_FL(LOG_NOTICE, "failed to serialize data");
135	}
136    }
137    my_CFRelease(&dict);
138    return (KERN_SUCCESS);
139}
140
141#if ! TARGET_OS_EMBEDDED
142kern_return_t
143eapolcontroller_copy_loginwindow_config(mach_port_t server,
144					if_name_t if_name,
145					xmlDataOut_t * config,
146					mach_msg_type_number_t * config_len,
147					int * result)
148{
149    CFDictionaryRef	dict = NULL;
150
151    *config = NULL;
152    *config_len = 0;
153    *result = ControllerCopyLoginWindowConfiguration(if_name, &dict);
154    if (dict != NULL) {
155	*config = (xmlDataOut_t)my_CFPropertyListCreateVMData(dict, config_len);
156	if (*config == NULL) {
157	    EAPLOG_FL(LOG_NOTICE, "failed to serialize data");
158	}
159    }
160    my_CFRelease(&dict);
161    return (KERN_SUCCESS);
162}
163
164kern_return_t
165eapolcontroller_copy_autodetect_info(mach_port_t server,
166				     xmlDataOut_t * info,
167				     mach_msg_type_number_t * info_len,
168				     int * result)
169{
170    CFDictionaryRef	dict = NULL;
171
172    *info = NULL;
173    *info_len = 0;
174    *result = ControllerCopyAutoDetectInformation(&dict);
175    if (dict != NULL) {
176	*info = (xmlDataOut_t)my_CFPropertyListCreateVMData(dict, info_len);
177	if (*info == NULL) {
178	    EAPLOG_FL(LOG_NOTICE, "failed to serialize data");
179	}
180    }
181    my_CFRelease(&dict);
182    return (KERN_SUCCESS);
183}
184
185kern_return_t
186eapolcontroller_did_user_cancel(mach_port_t server,
187				if_name_t if_name,
188				boolean_t * user_cancelled)
189{
190    *user_cancelled = ControllerDidUserCancel(if_name);
191    return (KERN_SUCCESS);
192}
193
194#endif /* ! TARGET_OS_EMBEDDED */
195
196kern_return_t
197eapolcontroller_start(mach_port_t server,
198		      if_name_t if_name, xmlData_t config,
199		      mach_msg_type_number_t config_len,
200		      mach_port_t bootstrap,
201		      mach_port_t au_session,
202		      int * ret_result)
203{
204    CFDictionaryRef	dict = NULL;
205    int			result = EINVAL;
206
207    if (config == NULL) {
208	goto done;
209    }
210    dict = my_CFPropertyListCreateWithBytePtrAndLength(config, config_len);
211    (void)vm_deallocate(mach_task_self(), (vm_address_t)config, config_len);
212    if (isA_CFDictionary(dict) == NULL) {
213	goto done;
214    }
215    if (S_uid == -1) {
216	result = EPERM;
217	goto done;
218    }
219    result = ControllerStart(if_name, S_uid, S_gid, dict, bootstrap,
220			     au_session);
221
222done:
223    *ret_result = result;
224    my_CFRelease(&dict);
225    return (KERN_SUCCESS);
226}
227
228#if ! TARGET_OS_EMBEDDED
229kern_return_t
230eapolcontroller_start_system(mach_port_t server,
231			     if_name_t if_name,
232			     xmlData_t options,
233			     mach_msg_type_number_t options_len,
234			     int * ret_result)
235{
236    CFDictionaryRef	dict = NULL;
237    int			result = EINVAL;
238
239    if (options != NULL) {
240	dict = my_CFPropertyListCreateWithBytePtrAndLength(options,
241							   options_len);
242	(void)vm_deallocate(mach_task_self(), (vm_address_t)options,
243			    options_len);
244	if (isA_CFDictionary(dict) == NULL) {
245	    goto done;
246	}
247    }
248    if (S_uid == -1) {
249	result = EPERM;
250	goto done;
251    }
252    result = ControllerStartSystem(if_name, S_uid, S_gid, dict);
253
254 done:
255    my_CFRelease(&dict);
256    *ret_result = result;
257    return (KERN_SUCCESS);
258}
259
260kern_return_t
261eapolcontroller_client_user_cancelled(mach_port_t server,
262				      int * result)
263{
264    *result = ControllerClientUserCancelled(server);
265    return (KERN_SUCCESS);
266};
267
268#endif /* ! TARGET_OS_EMBEDDED */
269
270kern_return_t
271eapolcontroller_stop(mach_port_t server,
272		     if_name_t if_name, int * result)
273{
274    *result = ControllerStop(if_name, S_uid, S_gid);
275    return (KERN_SUCCESS);
276}
277
278kern_return_t
279eapolcontroller_update(mach_port_t server,
280		       if_name_t if_name, xmlData_t config,
281		       mach_msg_type_number_t config_len,
282		       int * ret_result)
283{
284    CFDictionaryRef	dict = NULL;
285    int			result = EINVAL;
286
287    if (config == NULL) {
288	goto done;
289    }
290    dict = my_CFPropertyListCreateWithBytePtrAndLength(config, config_len);
291    (void)vm_deallocate(mach_task_self(), (vm_address_t)config, config_len);
292    if (isA_CFDictionary(dict) == NULL) {
293	goto done;
294    }
295    result = ControllerUpdate(if_name, S_uid, S_gid, dict);
296
297 done:
298    my_CFRelease(&dict);
299    *ret_result = result;
300    return (KERN_SUCCESS);
301}
302
303kern_return_t
304eapolcontroller_provide_user_input(mach_port_t server,
305				   if_name_t if_name, xmlData_t user_input,
306				   mach_msg_type_number_t user_input_len,
307				   int * ret_result)
308{
309    CFDictionaryRef	dict = NULL;
310    int			result = EINVAL;
311
312    if (user_input == NULL) {
313	goto done;
314    }
315    dict = my_CFPropertyListCreateWithBytePtrAndLength(user_input,
316						       user_input_len);
317    (void)vm_deallocate(mach_task_self(), (vm_address_t)user_input,
318			user_input_len);
319    if (isA_CFDictionary(dict) == NULL) {
320	goto done;
321    }
322    result = ControllerProvideUserInput(if_name, S_uid, S_gid, dict);
323
324 done:
325    my_CFRelease(&dict);
326    *ret_result = result;
327    return (KERN_SUCCESS);
328}
329
330kern_return_t
331eapolcontroller_retry(mach_port_t server,
332		      if_name_t if_name, int * result)
333{
334    *result = ControllerRetry(if_name, S_uid, S_gid);
335    return (KERN_SUCCESS);
336}
337
338kern_return_t
339eapolcontroller_client_attach(mach_port_t server, task_t task,
340			      if_name_t if_name,
341			      mach_port_t notify_port,
342			      mach_port_t * session,
343			      xmlDataOut_t * control,
344			      mach_msg_type_number_t * control_len,
345			      mach_port_t * bootstrap,
346			      mach_port_t * au_session,
347			      int * ret_result)
348{
349    int			pid;
350    CFDictionaryRef	dict = NULL;
351    int			result = EINVAL;
352    kern_return_t	status;
353
354    *control = NULL;
355    *control_len = 0;
356    status = pid_for_task(task, &pid);
357    if (status != KERN_SUCCESS) {
358	(void)mach_port_deallocate(mach_task_self(), notify_port);
359	goto done;
360    }
361    result = ControllerClientAttach(pid, if_name, notify_port, session,
362				    &dict, bootstrap, au_session);
363    if (dict != NULL) {
364	*control = (xmlDataOut_t)my_CFPropertyListCreateVMData(dict,
365							       control_len);
366	if (*control == 0) {
367	    EAPLOG_FL(LOG_NOTICE, "failed to serialize data");
368	}
369    }
370    my_CFRelease(&dict);
371
372 done:
373    if (task != TASK_NULL) {
374	(void)mach_port_deallocate(mach_task_self(), task);
375    }
376    *ret_result = result;
377    return (KERN_SUCCESS);
378}
379
380kern_return_t
381eapolcontroller_client_detach(mach_port_t server, int * result)
382{
383    *result = ControllerClientDetach(server);
384    return (KERN_SUCCESS);
385}
386
387kern_return_t
388eapolcontroller_client_getconfig(mach_port_t server,
389				 xmlDataOut_t * control,
390				 mach_msg_type_number_t * control_len,
391				 int * result)
392{
393    CFDictionaryRef	dict = NULL;
394
395    *control = NULL;
396    *control_len = 0;
397
398    *result = ControllerClientGetConfig(server, &dict);
399    if (dict != NULL) {
400	*control = (xmlDataOut_t)my_CFPropertyListCreateVMData(dict,
401							       control_len);
402	if (*control == NULL) {
403	    EAPLOG_FL(LOG_NOTICE, "failed to serialize data");
404	}
405    }
406    my_CFRelease(&dict);
407    return (KERN_SUCCESS);
408}
409
410kern_return_t
411eapolcontroller_client_report_status(mach_port_t server,
412				     xmlData_t status_data,
413				     mach_msg_type_number_t status_data_len,
414				     int * ret_result)
415{
416    CFDictionaryRef	dict = NULL;
417    int			result = EINVAL;
418
419    if (status_data == NULL) {
420	goto done;
421    }
422    dict = my_CFPropertyListCreateWithBytePtrAndLength(status_data,
423						       status_data_len);
424    (void)vm_deallocate(mach_task_self(), (vm_address_t)status_data,
425			status_data_len);
426    if (isA_CFDictionary(dict) == NULL) {
427	goto done;
428    }
429    result = ControllerClientReportStatus(server, dict);
430
431 done:
432    my_CFRelease(&dict);
433    *ret_result = result;
434    return (KERN_SUCCESS);
435};
436
437kern_return_t
438eapolcontroller_client_force_renew(mach_port_t server,
439				   int * result)
440{
441    *result = ControllerClientForceRenew(server);
442    return (KERN_SUCCESS);
443};
444
445boolean_t
446server_active(void)
447{
448    mach_port_t		server;
449    kern_return_t	result;
450
451    result = eapolcontroller_server_port(&server);
452    if (result == BOOTSTRAP_SUCCESS) {
453	return (TRUE);
454    }
455    return (FALSE);
456}
457
458static boolean_t
459process_notification(mach_msg_header_t * request)
460{
461    mach_no_senders_notification_t * notify;
462
463    notify = (mach_no_senders_notification_t *)request;
464    if (notify->not_header.msgh_id > MACH_NOTIFY_LAST
465	|| notify->not_header.msgh_id < MACH_NOTIFY_FIRST) {
466	return FALSE;	/* if this is not a notification message */
467    }
468    return (TRUE);
469}
470
471void
472server_handle_request(CFMachPortRef port, void *msg, CFIndex size, void *info)
473{
474    mach_msg_return_t 	r;
475    mach_msg_header_t *	request = (mach_msg_header_t *)msg;
476    mach_msg_header_t *	reply;
477#define REPLY_SIZE_AS_UINT64 \
478	((eapolcontroller_subsystem.maxsize + sizeof(uint64_t) - 1) / sizeof(uint64_t))
479    uint64_t		reply_s[REPLY_SIZE_AS_UINT64];
480
481    if (process_notification(request) == FALSE) {
482	read_trailer(request);
483
484	/* ALIGN: reply is aligned to at least sizeof(uint64) bytes */
485	reply = (mach_msg_header_t *)(void *)reply_s;
486
487	if (eapolcontroller_server(request, reply) == FALSE) {
488	    EAPLOG(LOG_NOTICE, "EAPOLController: unknown message ID (%d)",
489		   request->msgh_id);
490	    mach_msg_destroy(request);
491	}
492	else {
493	    int		options;
494
495	    options = MACH_SEND_MSG;
496	    if (MACH_MSGH_BITS_REMOTE(reply->msgh_bits)
497                != MACH_MSG_TYPE_MOVE_SEND_ONCE) {
498		options |= MACH_SEND_TIMEOUT;
499	    }
500	    r = mach_msg(reply,
501			 options,
502			 reply->msgh_size,
503			 0,
504			 MACH_PORT_NULL,
505			 MACH_MSG_TIMEOUT_NONE,
506			 MACH_PORT_NULL);
507	    if (r != MACH_MSG_SUCCESS) {
508		EAPLOG(LOG_NOTICE, "EAPOLController: mach_msg(send): %s",
509		       mach_error_string(r));
510		mach_msg_destroy(reply);
511	    }
512	}
513    }
514    return;
515}
516
517void
518server_register(void)
519{
520    mach_port_t		server_port;
521    kern_return_t 	status;
522
523    status = bootstrap_check_in(bootstrap_port, EAPOLCONTROLLER_SERVER,
524				&server_port);
525    if (status != BOOTSTRAP_SUCCESS) {
526	EAPLOG(LOG_NOTICE, "EAPOLController: bootstrap_check_in failed, %s",
527	       mach_error_string(status));
528	return;
529    }
530    server_cfport = _SC_CFMachPortCreateWithPort(NULL, server_port,
531						 server_handle_request, NULL);
532    return;
533}
534
535void
536server_start(void)
537{
538    CFRunLoopSourceRef	rls;
539
540    if (server_cfport != NULL) {
541	rls = CFMachPortCreateRunLoopSource(NULL, server_cfport, 0);
542	CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
543	CFRelease(rls);
544    }
545}
546