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