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 * main.c
26 */
27
28/*
29 * Modification History
30 *
31 * October 26, 2001	Dieter Siegmund (dieter@apple)
32 * - created
33 */
34
35#include <stdlib.h>
36#include <unistd.h>
37#include <string.h>
38#include <fcntl.h>
39#include <stdio.h>
40#include <sys/types.h>
41#include <sys/stat.h>
42#include <sys/errno.h>
43#include <net/if_types.h>
44#include <sysexits.h>
45#include <sys/types.h>
46#include <paths.h>
47#include <pwd.h>
48#include <grp.h>
49#include <sys/time.h>
50
51#include <CoreFoundation/CFRunLoop.h>
52
53#include <EAP8021X/LinkAddresses.h>
54#include <EAP8021X/EAPClientModule.h>
55#include <SystemConfiguration/SCPreferences.h>
56#include <SystemConfiguration/SCValidation.h>
57#include <SystemConfiguration/SCPrivate.h>
58#include "EAPOLSocketPrivate.h"
59#include "EAPOLSocket.h"
60#include "Supplicant.h"
61#include "myCFUtil.h"
62#include "mylog.h"
63#include "EAPOLControlPrefs.h"
64
65extern EAPClientPluginFuncRef
66md5_introspect(EAPClientPluginFuncName name);
67
68extern EAPClientPluginFuncRef
69eaptls_introspect(EAPClientPluginFuncName name);
70
71extern EAPClientPluginFuncRef
72eapttls_introspect(EAPClientPluginFuncName name);
73
74extern EAPClientPluginFuncRef
75peap_introspect(EAPClientPluginFuncName name);
76
77extern EAPClientPluginFuncRef
78eapmschapv2_introspect(EAPClientPluginFuncName name);
79
80extern EAPClientPluginFuncRef
81eapgtc_introspect(EAPClientPluginFuncName name);
82
83extern EAPClientPluginFuncRef
84eapfast_introspect(EAPClientPluginFuncName name);
85
86extern EAPClientPluginFuncRef
87eapsim_introspect(EAPClientPluginFuncName name);
88
89extern EAPClientPluginFuncRef
90eapaka_introspect(EAPClientPluginFuncName name);
91
92typedef struct {
93    EAPClientPluginFuncIntrospect *	introspect_func;
94    const char *			name;
95} BuiltinEAPModule;
96
97typedef BuiltinEAPModule const * BuiltinEAPModuleRef;
98
99static const BuiltinEAPModule	S_builtin_modules[] = {
100    { md5_introspect, "md5" },
101    { eaptls_introspect, "eaptls" },
102    { eapttls_introspect, "eapttls" },
103    { peap_introspect, "peap" },
104    { eapmschapv2_introspect, "eapmschapv2" },
105    { eapgtc_introspect, "eapgtc" },
106    { eapfast_introspect, "eapfast" },
107    { eapsim_introspect, "eapsim" },
108    { eapaka_introspect, "eapaka" }
109};
110
111EAPClientModuleStatus
112S_load_modules()
113{
114    int				i;
115    BuiltinEAPModuleRef		scan;
116
117    for (i = 0, scan = S_builtin_modules;
118	 i < (sizeof(S_builtin_modules) / sizeof(S_builtin_modules[0]));
119	 i++, scan++) {
120	EAPClientModuleStatus	status;
121
122	status = EAPClientModuleAddBuiltinModule(scan->introspect_func);
123	if (status != kEAPClientModuleStatusOK) {
124	    EAPLOG_FL(LOG_NOTICE, "EAPClientAddBuiltinModule(%s) failed %d",
125		      scan->name, status);
126	    return (status);
127	}
128    }
129    return (kEAPClientModuleStatusOK);
130}
131
132static void
133usage(char * progname)
134{
135    fprintf(stderr, "usage:\n"
136	    "%s -i <if_name> [ -u <uid> ] [ -g <gid> ]\n",
137	    progname);
138    exit(EX_USAGE);
139}
140
141static void
142log_then_exit(int exit_code)
143{
144    eapolclient_log(kLogFlagBasic, "exit");
145    exit(exit_code);
146    return;
147}
148
149static uint32_t
150check_prefs_common(SCPreferencesRef prefs, bool log_it)
151{
152    uint32_t	log_flags;
153
154    log_flags = EAPOLControlPrefsGetLogFlags();
155    eapolclient_log_set_flags(log_flags, log_it);
156    if (prefs == NULL) {
157	return (log_flags);
158    }
159    EAPOLSocketSetGlobals(prefs);
160    Supplicant_set_globals(prefs);
161    EAPOLControlPrefsSynchronize();
162    return (log_flags);
163}
164
165static void
166check_prefs(SCPreferencesRef prefs)
167{
168    check_prefs_common(prefs, TRUE);
169    return;
170}
171
172int
173main(int argc, char * argv[1])
174{
175    char			ch;
176    CFDictionaryRef		config_dict = NULL;
177    char *			config_file = NULL;
178    CFDictionaryRef		control_dict = NULL;
179    bool			g_flag = FALSE;
180    gid_t			gid = -1;
181    char *			if_name = NULL;
182    LinkAddressesRef		link_addrs = NULL;
183    struct sockaddr_dl *	link = NULL;
184    uint32_t			log_flags = 0;
185    SCPreferencesRef		prefs;
186    EAPOLSocketSourceRef	source;
187    SupplicantRef 		supp = NULL;
188    bool			u_flag = FALSE;
189    uid_t			uid = -1;
190
191    while ((ch = getopt(argc, argv, "c:g:i:lu:")) != EOF) {
192	switch ((char) ch) {
193	case 'c':
194	    config_file = optarg;
195	    break;
196	case 'i':
197	    if (if_name != NULL) {
198		usage(argv[0]);
199	    }
200	    if_name = optarg;
201	    break;
202	case 'u':
203	    if (u_flag) {
204		usage(argv[0]);
205	    }
206	    uid = (uid_t)strtoul(optarg, NULL, 0);
207	    u_flag = TRUE;
208	    break;
209	case 'g':
210	    if (g_flag) {
211		usage(argv[0]);
212	    }
213	    gid = (gid_t)strtoul(optarg, NULL, 0);
214	    g_flag = TRUE;
215	    break;
216	default:
217	    usage(argv[0]);
218	    break;
219	}
220    }
221    if ((argc - optind) != 0 || if_name == NULL) {
222	usage(argv[0]);
223    }
224    if (uid == -1) {
225	uid = getuid();
226    }
227    if (gid == -1) {
228	gid = getgid();
229    }
230    openlog("eapolclient", LOG_CONS | LOG_PID, LOG_DAEMON);
231    prefs = EAPOLControlPrefsInit(CFRunLoopGetCurrent(), check_prefs);
232    log_flags = check_prefs_common(prefs, FALSE);
233    if (log_flags == 0) {
234	EAPLOG(LOG_NOTICE, "%s START uid %d gid %d", if_name, uid, gid);
235    }
236    else {
237	EAPLOG(LOG_NOTICE, "%s START uid %d gid %d", if_name, uid, gid);
238	EAPLOG(LOG_NOTICE, "Verbose mode enabled (LogFlags 0x%x)", log_flags);
239    }
240    link_addrs = LinkAddresses_create();
241    if (link_addrs == NULL) {
242	EAPLOG_FL(LOG_NOTICE, "Could not build interface list");
243	exit(EX_OSERR);
244    }
245    link = LinkAddresses_lookup(link_addrs, if_name);
246    if (link == NULL) {
247	EAPLOG(LOG_NOTICE, "interface '%s' does not exist", if_name);
248	exit(EX_CONFIG);
249    }
250    if (link->sdl_type != IFT_ETHER) {
251	EAPLOG(LOG_NOTICE, "interface '%s' is not ethernet", if_name);
252	exit(EX_CONFIG);
253    }
254    source = EAPOLSocketSourceCreate(if_name,
255				     (const struct ether_addr *)
256				     (link->sdl_data + link->sdl_nlen),
257				     &control_dict);
258    if (source == NULL) {
259	EAPLOG_FL(LOG_NOTICE, "EAPOLSocketSourceCreate(%s) failed", if_name);
260	log_then_exit(EX_UNAVAILABLE);
261    }
262    if (g_flag) {
263	if (setgid(gid) < 0) {
264	    EAPLOG_FL(LOG_NOTICE, "setgid(%d) failed, %s", gid,
265		      strerror(errno));
266	    log_then_exit(EX_NOPERM);
267	}
268    }
269    if (u_flag) {
270	if (setuid(uid) < 0) {
271	    EAPLOG_FL(LOG_NOTICE, "setuid(%d) failed, %s", uid,
272		      strerror(errno));
273	    log_then_exit(EX_NOPERM);
274	}
275    }
276    if (config_file != NULL) {
277	if (control_dict != NULL) {
278	    fprintf(stderr, "Ignoring -c %s\n", config_file);
279	}
280	else {
281	    config_dict = (CFDictionaryRef)
282		my_CFPropertyListCreateFromFile(config_file);
283	    if (isA_CFDictionary(config_dict) == NULL) {
284		fprintf(stderr,
285			"contents of file %s invalid\n", config_file);
286		my_CFRelease(&config_dict);
287		log_then_exit(EX_CONFIG);
288	    }
289	}
290    }
291    if (config_dict == NULL && control_dict == NULL) {
292	EAPLOG_FL(LOG_NOTICE, "%s: config/control dictionary missing", if_name);
293	log_then_exit(EX_SOFTWARE);
294    }
295    if (S_load_modules() != kEAPClientModuleStatusOK) {
296	log_then_exit(EX_SOFTWARE);
297    }
298    supp = EAPOLSocketSourceCreateSupplicant(source, control_dict);
299    if (supp == NULL) {
300	EAPLOG_FL(LOG_NOTICE, "EAPOLSocketSourceCreateSupplicant failed");
301	EAPOLSocketSourceFree(&source);
302	log_then_exit(EX_UNAVAILABLE);
303    }
304    if (control_dict != NULL) {
305	(void)setsid();
306	(void)chdir("/");
307    }
308    else {
309	bool	should_stop = FALSE;
310
311	(void)Supplicant_update_configuration(supp, config_dict, &should_stop);
312	EAPLOG(LOG_NOTICE,
313	       "Supplicant_update_configuration says we should stop - exiting");
314	exit(EX_UNAVAILABLE);
315    }
316    my_CFRelease(&control_dict);
317    my_CFRelease(&config_dict);
318    Supplicant_start(supp);
319
320    LinkAddresses_free(&link_addrs);
321    CFRunLoopRun();
322
323    log_then_exit(EX_OK);
324    return (0);
325}
326