1/*
2 * Copyright (c) 2010-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/*
26 * eapolcfg_auth.c
27 * - launch-on-demand daemon running as root to perform privileged operations
28 *   required by certain EAPOLClientConfiguration.h APIs
29 * - uses AuthorizationRef for validating that caller has permission to
30 *   perform the operation
31 */
32
33/*
34 * Modification History
35 *
36 * February 22, 2010		Dieter Siegmund (dieter@apple.com)
37 * - initial revision
38 */
39#include <stdlib.h>
40#include <unistd.h>
41#include <bsm/libbsm.h>
42#include <sys/types.h>
43#include <sysexits.h>
44#include <servers/bootstrap.h>
45
46#include <CoreFoundation/CFRunLoop.h>
47#include <CoreFoundation/CFRuntime.h>
48#include <SystemConfiguration/SystemConfiguration.h>
49#include <SystemConfiguration/SCPrivate.h>
50#include <SystemConfiguration/SCValidation.h>
51#include "EAPOLClientConfiguration.h"
52#include "EAPOLClientConfigurationPrivate.h"
53#include "EAPCertificateUtil.h"
54#include "symbol_scope.h"
55#include "eapolcfg_authServer.h"
56#include "eapolcfg_auth_types.h"
57#include "myCFUtil.h"
58
59STATIC Boolean	S_handled_request;
60
61INLINE void
62my_vm_deallocate(vm_address_t data, int data_length)
63{
64    if (data != 0) {
65	(void)vm_deallocate(mach_task_self(), data, data_length);
66    }
67    return;
68}
69
70STATIC Boolean
71authorization_is_valid(const void * auth_data, int auth_data_length)
72{
73    AuthorizationExternalForm *	auth_ext_p;
74    AuthorizationRef 		authorization;
75    AuthorizationFlags		flags;
76    AuthorizationItem		item;
77    AuthorizationRights		rights;
78    OSStatus			status;
79
80    if (auth_data == NULL
81	|| auth_data_length != sizeof(auth_ext_p->bytes)) {
82	syslog(LOG_ERR, "eapolcfg_auth: authorization NULL/invalid size");
83	return (FALSE);
84    }
85    auth_ext_p = (AuthorizationExternalForm *)auth_data;
86    status = AuthorizationCreateFromExternalForm(auth_ext_p, &authorization);
87    if (status != errAuthorizationSuccess) {
88	syslog(LOG_ERR, "eapolcfg_auth: authorization is invalid (%d)",
89	       (int)status);
90	return (FALSE);
91    }
92    rights.count = 1;
93    rights.items = &item;
94    item.name = "system.preferences";
95    item.value = NULL;
96    item.valueLength = 0;
97    item.flags = 0;
98    flags = kAuthorizationFlagDefaults;
99    flags |= kAuthorizationFlagExtendRights;
100    flags |= kAuthorizationFlagInteractionAllowed;
101    status = AuthorizationCopyRights(authorization,
102				     &rights,
103				     kAuthorizationEmptyEnvironment,
104				     flags,
105				     NULL);
106    AuthorizationFree(authorization, kAuthorizationFlagDefaults);
107    return (status == errAuthorizationSuccess);
108}
109
110STATIC int
111init_itemID_and_cfg(xmlData_t itemID_data,
112		    mach_msg_type_number_t itemID_data_length,
113		    EAPOLClientItemIDRef * itemID_p,
114		    EAPOLClientConfigurationRef * cfg_p)
115{
116    EAPOLClientConfigurationRef	cfg = NULL;
117    EAPOLClientItemIDRef	itemID = NULL;
118    CFDictionaryRef		itemID_dict = NULL;
119    int				ret = 0;
120
121    itemID_dict
122	= my_CFPropertyListCreateWithBytePtrAndLength(itemID_data,
123						      itemID_data_length);
124    if (isA_CFDictionary(itemID_dict) == NULL) {
125	ret = EINVAL;
126	goto done;
127    }
128    cfg = EAPOLClientConfigurationCreate(NULL);
129    if (cfg == NULL) {
130	ret = ENOMEM;
131	goto done;
132    }
133    itemID = EAPOLClientItemIDCreateWithDictionary(cfg, itemID_dict);
134    if (itemID == NULL) {
135	ret = EINVAL;
136	goto done;
137    }
138 done:
139    my_CFRelease(&itemID_dict);
140    if (ret == 0) {
141	*cfg_p = cfg;
142	*itemID_p = itemID;
143    }
144    else {
145	my_CFRelease(&itemID);
146	my_CFRelease(&cfg);
147    }
148    return (ret);
149}
150
151/**
152 ** MiG server routines
153 **/
154PRIVATE_EXTERN kern_return_t
155eapolclientitemid_set_identity(mach_port_t server,
156			       OOBData_t auth_data,
157			       mach_msg_type_number_t auth_data_length,
158			       xmlData_t itemID_data,
159			       mach_msg_type_number_t itemID_data_length,
160			       OOBData_t id_handle,
161			       mach_msg_type_number_t id_handle_length,
162			       int * result)
163{
164    EAPOLClientConfigurationRef	cfg = NULL;
165    SecIdentityRef		identity = NULL;
166    EAPOLClientItemIDRef	itemID = NULL;
167    int				ret = 0;
168
169    if (authorization_is_valid(auth_data, auth_data_length) == FALSE) {
170	ret = EPERM;
171	goto done;
172    }
173    ret = init_itemID_and_cfg(itemID_data, itemID_data_length, &itemID, &cfg);
174    if (ret != 0) {
175	goto done;
176    }
177    if (id_handle != NULL) {
178	CFDataRef		data;
179	OSStatus		status;
180
181	data = CFDataCreateWithBytesNoCopy(NULL,
182					   (const UInt8 *)id_handle,
183					   id_handle_length,
184					   kCFAllocatorNull);
185	if (data == NULL) {
186	    ret = EINVAL;
187	    goto done;
188	}
189	status = EAPSecIdentityHandleCreateSecIdentity(data, &identity);
190	CFRelease(data);
191	if (status != noErr) {
192	    ret = ENOENT;
193	    goto done;
194	}
195    }
196    if (EAPOLClientItemIDSetIdentity(itemID,
197				     kEAPOLClientDomainSystem,
198				     identity) == FALSE) {
199	ret = ENXIO;
200    }
201
202 done:
203    my_vm_deallocate((vm_address_t)auth_data, auth_data_length);
204    my_vm_deallocate((vm_address_t)itemID_data, itemID_data_length);
205    my_vm_deallocate((vm_address_t)id_handle, id_handle_length);
206    my_CFRelease(&cfg);
207    my_CFRelease(&itemID);
208    my_CFRelease(&identity);
209    *result = ret;
210    return (KERN_SUCCESS);
211}
212
213kern_return_t
214eapolclientitemid_set_password(mach_port_t server,
215			       OOBData_t auth_data,
216			       mach_msg_type_number_t auth_data_length,
217			       xmlData_t itemID_data,
218			       mach_msg_type_number_t itemID_data_length,
219			       uint32_t flags,
220			       OOBData_t name,
221			       mach_msg_type_number_t name_length,
222			       OOBData_t password,
223			       mach_msg_type_number_t password_length,
224			       int * result)
225{
226    EAPOLClientConfigurationRef	cfg = NULL;
227    EAPOLClientItemIDRef	itemID = NULL;
228    CFDataRef			name_data = NULL;
229    CFDataRef			password_data = NULL;
230    int				ret = 0;
231
232    if (authorization_is_valid(auth_data, auth_data_length) == FALSE) {
233	ret = EPERM;
234	goto done;
235    }
236    ret = init_itemID_and_cfg(itemID_data, itemID_data_length, &itemID, &cfg);
237    if (ret != 0) {
238	goto done;
239    }
240    if ((flags & keapolcfg_auth_set_name) != 0) {
241	name_data = CFDataCreate(NULL, (const UInt8 *)name,
242				 name_length);
243    }
244    else {
245	syslog(LOG_NOTICE, "not setting name");
246    }
247    if ((flags & keapolcfg_auth_set_password) != 0) {
248	password_data = CFDataCreate(NULL, (const UInt8 *)password,
249				     password_length);
250    }
251    else {
252	syslog(LOG_NOTICE, "not setting password");
253    }
254    if (EAPOLClientItemIDSetPasswordItem(itemID,
255					 kEAPOLClientDomainSystem,
256					 name_data, password_data) == FALSE) {
257	ret = ENXIO;
258    }
259
260 done:
261    my_vm_deallocate((vm_address_t)auth_data, auth_data_length);
262    my_vm_deallocate((vm_address_t)itemID_data, itemID_data_length);
263    my_vm_deallocate((vm_address_t)name, name_length);
264    my_vm_deallocate((vm_address_t)password, password_length);
265    my_CFRelease(&name_data);
266    my_CFRelease(&password_data);
267    my_CFRelease(&cfg);
268    my_CFRelease(&itemID);
269    *result = ret;
270    return (KERN_SUCCESS);
271}
272
273PRIVATE_EXTERN kern_return_t
274eapolclientitemid_remove_password(mach_port_t server,
275				  OOBData_t auth_data,
276				  mach_msg_type_number_t auth_data_length,
277				  xmlData_t itemID_data,
278				  mach_msg_type_number_t itemID_data_length,
279				  int * result)
280{
281    EAPOLClientConfigurationRef	cfg = NULL;
282    SecIdentityRef		identity = NULL;
283    EAPOLClientItemIDRef	itemID = NULL;
284    CFDataRef			name_data = NULL;
285    CFDataRef			password_data = NULL;
286    int				ret = 0;
287
288    if (authorization_is_valid(auth_data, auth_data_length) == FALSE) {
289	ret = EPERM;
290	goto done;
291    }
292    ret = init_itemID_and_cfg(itemID_data, itemID_data_length, &itemID, &cfg);
293    if (ret != 0) {
294	goto done;
295    }
296    if (EAPOLClientItemIDRemovePasswordItem(itemID, kEAPOLClientDomainSystem)
297	== FALSE) {
298	ret = ENXIO;
299    }
300
301 done:
302    my_vm_deallocate((vm_address_t)auth_data, auth_data_length);
303    my_vm_deallocate((vm_address_t)itemID_data, itemID_data_length);
304    my_CFRelease(&cfg);
305    my_CFRelease(&itemID);
306    *result = ret;
307    return (KERN_SUCCESS);
308}
309
310PRIVATE_EXTERN kern_return_t
311eapolclientitemid_check_password(mach_port_t server,
312				 OOBData_t auth_data,
313				 mach_msg_type_number_t auth_data_length,
314				 xmlData_t itemID_data,
315				 mach_msg_type_number_t itemID_data_length,
316				 OOBDataOut_t * name,
317				 mach_msg_type_number_t * name_length,
318				 boolean_t * password_set,
319				 int * result)
320{
321    EAPOLClientConfigurationRef	cfg = NULL;
322    SecIdentityRef		identity = NULL;
323    EAPOLClientItemIDRef	itemID = NULL;
324    CFDataRef			name_data = NULL;
325    vm_address_t		name_vm_data = 0;
326    mach_msg_type_number_t	name_vm_length = 0;
327    CFDataRef			password_data = NULL;
328    vm_address_t		password_vm_data = 0;
329    mach_msg_type_number_t	password_vm_length = 0;
330    int				ret = 0;
331
332    *password_set = FALSE;
333    if (authorization_is_valid(auth_data, auth_data_length) == FALSE) {
334	ret = EPERM;
335	goto done;
336    }
337    ret = init_itemID_and_cfg(itemID_data, itemID_data_length, &itemID, &cfg);
338    if (ret != 0) {
339	goto done;
340    }
341    if (EAPOLClientItemIDCopyPasswordItem(itemID, kEAPOLClientDomainSystem,
342					  &name_data, &password_data)
343	== FALSE) {
344	ret = ENOENT;
345    }
346    else {
347	kern_return_t	kret;
348
349	if (name_data != NULL) {
350	    name_vm_length = CFDataGetLength(name_data);
351	    kret = vm_allocate(mach_task_self(), &name_vm_data,
352			       name_vm_length, TRUE);
353	    if (kret != KERN_SUCCESS) {
354		ret = ENOMEM;
355		goto done;
356	    }
357	    bcopy((char *)CFDataGetBytePtr(name_data),
358		  (char *)(name_vm_data), name_vm_length);
359	    CFRelease(name_data);
360	}
361	if (password_data != NULL) {
362	    *password_set = TRUE;
363	    CFRelease(password_data);
364	}
365    }
366
367 done:
368    if (ret != 0) {
369	if (name_vm_data != 0) {
370	    (void)vm_deallocate(mach_task_self(), name_vm_data,
371				name_vm_length);
372	    name_vm_data = 0;
373	    name_vm_length = 0;
374	}
375    }
376    *name = (char *)name_vm_data;
377    *name_length = name_vm_length;
378    my_vm_deallocate((vm_address_t)auth_data, auth_data_length);
379    my_vm_deallocate((vm_address_t)itemID_data, itemID_data_length);
380    my_CFRelease(&cfg);
381    my_CFRelease(&itemID);
382    *result = ret;
383    return (KERN_SUCCESS);
384}
385
386
387/**
388 ** server message handling
389 **/
390
391STATIC boolean_t
392process_notification(mach_msg_header_t * request)
393{
394    mach_no_senders_notification_t * notify;
395
396    notify = (mach_no_senders_notification_t *)request;
397    if (notify->not_header.msgh_id > MACH_NOTIFY_LAST
398	|| notify->not_header.msgh_id < MACH_NOTIFY_FIRST) {
399	return FALSE;	/* if this is not a notification message */
400    }
401    return (TRUE);
402}
403
404STATIC void
405server_handle_request(CFMachPortRef port, void * msg, CFIndex size, void * info)
406{
407    mach_msg_return_t 	r;
408    mach_msg_header_t *	request = (mach_msg_header_t *)msg;
409    mach_msg_header_t *	reply;
410    char		reply_s[eapolcfg_auth_subsystem.maxsize];
411
412    if (process_notification(request) == FALSE) {
413	reply = (mach_msg_header_t *)reply_s;
414	if (eapolcfg_auth_server(request, reply) == FALSE) {
415	    syslog(LOG_NOTICE,
416		   "eapolcfg_auth: unknown message ID (%d)",
417		   request->msgh_id);
418	    mach_msg_destroy(request);
419	}
420	else {
421	    int		options;
422
423	    S_handled_request = TRUE;
424
425	    options = MACH_SEND_MSG;
426	    if (MACH_MSGH_BITS_REMOTE(reply->msgh_bits)
427                != MACH_MSG_TYPE_MOVE_SEND_ONCE) {
428		options |= MACH_SEND_TIMEOUT;
429	    }
430	    r = mach_msg(reply,
431			 options,
432			 reply->msgh_size,
433			 0,
434			 MACH_PORT_NULL,
435			 MACH_MSG_TIMEOUT_NONE,
436			 MACH_PORT_NULL);
437	    if (r != MACH_MSG_SUCCESS) {
438		syslog(LOG_NOTICE, "eapolcfg_auth: mach_msg(send): %s",
439		       mach_error_string(r));
440		mach_msg_destroy(reply);
441	    }
442	}
443    }
444    return;
445}
446
447static void
448start_service(mach_port_t service_port)
449{
450    CFMachPortRef	mp;
451    CFRunLoopSourceRef	rls;
452
453    mp = CFMachPortCreateWithPort(NULL, service_port, server_handle_request,
454				  NULL, NULL);
455    rls = CFMachPortCreateRunLoopSource(NULL, mp, 0);
456    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
457    CFRelease(mp);
458    CFRelease(rls);
459    return;
460}
461
462int
463main(int argc, char **argv)
464{
465    kern_return_t	kr;
466    mach_port_t		service_port = MACH_PORT_NULL;
467
468    openlog("eapolcfg_auth", LOG_CONS | LOG_PID, LOG_DAEMON);
469    if (geteuid() != 0) {
470	syslog(LOG_ERR, "not running as root - exiting");
471	exit(EX_CONFIG);
472    }
473    kr = bootstrap_check_in(bootstrap_port, EAPOLCFG_AUTH_SERVER,
474			    &service_port);
475    if (kr != BOOTSTRAP_SUCCESS) {
476	syslog(LOG_ERR, "bootstrap_check_in() failed: %s",
477	       bootstrap_strerror(kr));
478	exit(EX_UNAVAILABLE);
479    }
480    start_service(service_port);
481    while (1) {
482	SInt32	rlStatus;
483
484	rlStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 15.0, TRUE);
485	if (rlStatus == kCFRunLoopRunTimedOut) {
486	    if (S_handled_request == FALSE) {
487		/* we didn't handle a request in the last time interval */
488		break;
489	    }
490	    S_handled_request = FALSE;
491	}
492    }
493    exit(EX_OK);
494    return (0);
495}
496