1/*
2 * Taken from:
3 * WPA Supplicant / Configuration parser and common functions
4 * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
5 */
6
7#include <stdlib.h>
8#include <stdio.h>
9#include <errno.h>
10#include <string.h>
11#include <unistd.h>
12
13#include "logging.h"
14#include "config.h"
15
16char * rel2abs_path(const char *rel_path)
17{
18	char *buf = NULL, *cwd, *ret;
19	size_t len = 128, cwd_len, rel_len, ret_len;
20	int last_errno;
21
22	if (rel_path[0] == '/')
23		return strdup(rel_path);
24
25	for (;;) {
26		buf = malloc(len);
27		if (buf == NULL)
28			return NULL;
29		cwd = getcwd(buf, len);
30		if (cwd == NULL) {
31			last_errno = errno;
32			free(buf);
33			if (last_errno != ERANGE)
34				return NULL;
35			len *= 2;
36			if (len > 2000)
37				return NULL;
38		} else {
39			buf[len - 1] = '\0';
40			break;
41		}
42	}
43
44	cwd_len = strlen(cwd);
45	rel_len = strlen(rel_path);
46	ret_len = cwd_len + 1 + rel_len + 1;
47	ret = malloc(ret_len);
48	if (ret) {
49		memcpy(ret, cwd, cwd_len);
50		ret[cwd_len] = '/';
51		memcpy(ret + cwd_len + 1, rel_path, rel_len);
52		ret[ret_len - 1] = '\0';
53	}
54	free(buf);
55	return ret;
56}
57
58
59struct parse_data {
60	/* Configuration variable name */
61	char *name;
62
63	/* Parser function for this variable */
64	int (*parser)(const struct parse_data *data, struct gct_config *config,
65		      int line, const char *value);
66
67	/* Variable specific parameters for the parser. */
68	void *param1, *param2, *param3, *param4;
69
70	/* 0 = this variable can be included in debug output and ctrl_iface
71	 * 1 = this variable contains key/private data and it must not be
72	 *     included in debug output unless explicitly requested. In
73	 *     addition, this variable will not be readable through the
74	 *     ctrl_iface.
75	 */
76	int key_data;
77};
78
79
80static
81char * gct_config_parse_string(const char *value, size_t *len)
82{
83	if (*value == '"') {
84		const char *pos;
85		char *str;
86		value++;
87		pos = strrchr(value, '"');
88		if (pos == NULL || pos[1] != '\0')
89			return NULL;
90		*len = pos - value;
91		str = malloc(*len + 1);
92		if (str == NULL)
93			return NULL;
94		memcpy(str, value, *len);
95		str[*len] = '\0';
96		return str;
97	} else {
98		u8 *str;
99		size_t tlen, hlen = strlen(value);
100		if (hlen & 1)
101			return NULL;
102		tlen = hlen / 2;
103		str = malloc(tlen + 1);
104		if (str == NULL)
105			return NULL;
106		if (hexstr2bin(value, str, tlen)) {
107			free(str);
108			return NULL;
109		}
110		str[tlen] = '\0';
111		*len = tlen;
112		return (char *) str;
113	}
114}
115
116
117static
118int gct_config_parse_str(const struct parse_data *data,
119				struct gct_config *config,
120				int line, const char *value)
121{
122	size_t res_len, *dst_len;
123	char **dst, *tmp;
124
125	if (strcmp(value, "NULL") == 0) {
126		wmlog_msg(4, "Unset configuration string '%s'",
127			   data->name);
128		tmp = NULL;
129		res_len = 0;
130		goto set;
131	}
132
133	tmp = gct_config_parse_string(value, &res_len);
134	if (tmp == NULL) {
135		wmlog_msg(0, "Line %d: failed to parse %s '%s'.",
136			   line, data->name,
137			   data->key_data ? "[KEY DATA REMOVED]" : value);
138		return -1;
139	}
140	wmlog_dumphexasc(4, (u8 *) tmp, res_len, "%s", data->name);
141/*
142	if (data->key_data) {
143		gct_hexdump_ascii_key(MSG_MSGDUMP, data->name,
144				      (u8 *) tmp, res_len);
145	} else {
146		gct_hexdump_ascii(MSG_MSGDUMP, data->name,
147				  (u8 *) tmp, res_len);
148	}
149*/
150	if (data->param3 && res_len < (size_t) data->param3) {
151		wmlog_msg(0, "E: Line %d: too short %s (len=%lu "
152			   "min_len=%ld)", line, data->name,
153			   (unsigned long) res_len, (long) data->param3);
154		free(tmp);
155		return -1;
156	}
157
158	if (data->param4 && res_len > (size_t) data->param4) {
159		wmlog_msg(0, "E: Line %d: too long %s (len=%lu "
160			   "max_len=%ld)", line, data->name,
161			   (unsigned long) res_len, (long) data->param4);
162		free(tmp);
163		return -1;
164	}
165
166set:
167	dst = (char **) (((u8 *) config) + (long) data->param1);
168	dst_len = (size_t *) (((u8 *) config) + (long) data->param2);
169	free(*dst);
170	*dst = tmp;
171	if (data->param2)
172		*dst_len = res_len;
173
174	return 0;
175}
176
177
178static
179int gct_config_parse_int(const struct parse_data *data,
180				struct gct_config *config,
181				int line, const char *value)
182{
183	int *dst;
184
185	dst = (int *) (((u8 *) config) + (long) data->param1);
186	*dst = atoi(value);
187	wmlog_msg(4, "%s=%d (0x%x)", data->name, *dst, *dst);
188
189	if (data->param3 && *dst < (long) data->param3) {
190		wmlog_msg(0, "E: Line %d: too small %s (value=%d "
191			   "min_value=%ld)", line, data->name, *dst,
192			   (long) data->param3);
193		*dst = (long) data->param3;
194		return -1;
195	}
196
197	if (data->param4 && *dst > (long) data->param4) {
198		wmlog_msg(0, "E: Line %d: too large %s (value=%d "
199			   "max_value=%ld)", line, data->name, *dst,
200			   (long) data->param4);
201		*dst = (long) data->param4;
202		return -1;
203	}
204
205	return 0;
206}
207
208static
209int gct_config_parse_eap(const struct parse_data *data,
210				struct gct_config *config, int line,
211				const char *value)
212{
213	int last, errors = 0;
214	char *start, *end, *buf;
215	struct eap_method_type *methods = NULL, *tmp;
216	size_t num_methods = 0;
217
218	buf = strdup(value);
219	if (buf == NULL)
220		return -1;
221	start = buf;
222
223	while (*start != '\0') {
224		while (*start == ' ' || *start == '\t')
225			start++;
226		if (*start == '\0')
227			break;
228		end = start;
229		while (*end != ' ' && *end != '\t' && *end != '\0')
230			end++;
231		last = *end == '\0';
232		*end = '\0';
233		tmp = methods;
234		methods = realloc(methods,
235				     (num_methods + 1) * sizeof(*methods));
236		if (methods == NULL) {
237			free(tmp);
238			free(buf);
239			return -1;
240		}
241		methods[num_methods].method = eap_peer_get_type(
242			start, &methods[num_methods].vendor);
243		if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
244		    methods[num_methods].method == EAP_TYPE_NONE) {
245			wmlog_msg(0, "Line %d: unknown EAP method "
246				   "'%s'", line, start);
247			wmlog_msg(0, "You may need to add support for"
248				   " this EAP method during wpa_supplicant\n"
249				   "build time configuration.\n"
250				   "See README for more information.");
251			errors++;
252		}/* else if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
253			   methods[num_methods].method == EAP_TYPE_LEAP)
254			ssid->leap++;
255		else
256			ssid->non_leap++;*/
257		num_methods++;
258		if (last)
259			break;
260		start = end + 1;
261	}
262	free(buf);
263
264	tmp = methods;
265	methods = realloc(methods, (num_methods + 1) * sizeof(*methods));
266	if (methods == NULL) {
267		free(tmp);
268		return -1;
269	}
270	methods[num_methods].vendor = EAP_VENDOR_IETF;
271	methods[num_methods].method = EAP_TYPE_NONE;
272	num_methods++;
273
274	wmlog_dumphexasc(4, (u8 *) methods,
275		num_methods * sizeof(*methods),"eap methods");
276	config->eap_methods = methods;
277	return errors ? -1 : 0;
278}
279
280
281static
282int gct_config_parse_password(const struct parse_data *data,
283				     struct gct_config *config, int line,
284				     const char *value)
285{
286	u8 *hash;
287
288	if (strcmp(value, "NULL") == 0) {
289		wmlog_msg(4, "Unset configuration string 'password'\n");
290		free(config->password);
291		config->password = NULL;
292		config->password_len = 0;
293		return 0;
294	}
295
296	if (strncmp(value, "hash:", 5) != 0) {
297		char *tmp;
298		size_t res_len;
299
300		tmp = gct_config_parse_string(value, &res_len);
301		if (tmp == NULL) {
302			wmlog_msg(0, "E: Line %d: failed to parse "
303				   "password.", line);
304			return -1;
305		}
306		wmlog_dumphexasc(4, (u8 *) tmp, res_len,"%s",data->name);
307//		gct_hexdump_ascii_key(MSG_MSGDUMP, data->name,
308//				      (u8 *) tmp, res_len);
309
310		free(config->password);
311		config->password = (u8 *) tmp;
312		config->password_len = res_len;
313		config->flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
314
315		return 0;
316	}
317
318
319	/* NtPasswordHash: hash:<32 hex digits> */
320	if (strlen(value + 5) != 2 * 16) {
321		wmlog_msg(0, "E: Line %d: Invalid password hash length "
322			   "(expected 32 hex digits)", line);
323		return -1;
324	}
325
326	hash = malloc(16);
327	if (hash == NULL)
328		return -1;
329
330	if (hexstr2bin(value + 5, hash, 16)) {
331		free(hash);
332		wmlog_msg(0, "E: Line %d: Invalid password hash", line);
333		return -1;
334	}
335
336	wmlog_dumphexasc(4,	hash, 16,"%s",data->name);
337//	gct_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
338
339	free(config->password);
340	config->password = hash;
341	config->password_len = 16;
342	config->flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
343
344	return 0;
345}
346
347
348#define OFFSET(v) ((void *) &((struct gct_config *) 0)->v)
349
350#define _STR(f) #f, gct_config_parse_str, OFFSET(f)
351#define STR(f) _STR(f), NULL, NULL, NULL, 0
352#define STR_KEY(f) _STR(f), NULL, NULL, NULL, 1
353#define _STR_LEN(f) _STR(f), OFFSET(f ## _len)
354#define STR_LEN(f) _STR_LEN(f), NULL, NULL, 0
355#define _INT(f) #f, gct_config_parse_int, OFFSET(f), (void *) 0
356#define INT(f) _INT(f), NULL, NULL, 0
357#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0
358
359#define _FUNC(f) #f, gct_config_parse_ ## f, NULL, NULL, NULL, NULL
360#define FUNC(f) _FUNC(f), 0
361#define FUNC_KEY(f) _FUNC(f), 1
362
363static
364const struct parse_data ssid_fields[] = {
365
366	{ INT_RANGE(nspid, 0, 0xffffff) },
367	{ INT_RANGE(use_pkm, 0, 1) },
368	{ INT_RANGE(use_nv, 0, 1) },
369	{ INT_RANGE(eap_type, -1, 7) },
370	{ INT_RANGE(ca_cert_null, 0, 1) },
371	{ INT_RANGE(dev_cert_null, 0, 1) },
372	{ INT_RANGE(cert_nv, 0, 1) },
373	{ INT_RANGE(wimax_verbose_level, -1, 3) },
374	{ INT_RANGE(wpa_debug_level, 0, 4) },
375	{ STR(log_file) },
376	{ STR(event_script) },
377	{ FUNC(eap) },
378	{ STR_LEN(identity) },
379	{ STR_LEN(anonymous_identity) },
380	{ FUNC_KEY(password) },
381	{ STR(ca_cert) },
382	{ STR(client_cert) },
383	{ STR(private_key) },
384	{ STR_KEY(private_key_passwd) },
385	{ STR(phase1) },
386	{ STR(phase2) },
387};
388
389#undef OFFSET
390#undef _STR
391#undef STR
392#undef STR_KEY
393#undef _STR_LEN
394#undef STR_LEN
395#undef _INT
396#undef INT
397#undef INT_RANGE
398#undef _FUNC
399#undef FUNC
400#undef FUNC_KEY
401#define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0]))
402
403static
404int gct_config_set(struct gct_config *config, const char *var, const char *value,
405		   int line)
406{
407	size_t i;
408	int ret = 0;
409
410	if (config == NULL || var == NULL || value == NULL)
411		return -1;
412
413	for (i = 0; i < NUM_SSID_FIELDS; i++) {
414		const struct parse_data *field = &ssid_fields[i];
415		if (strcmp(var, field->name) != 0)
416			continue;
417
418		if (field->parser(field, config, line, value)) {
419			if (line) {
420				wmlog_msg(0, "E: Line %d: failed to "
421					   "parse %s '%s'.", line, var, value);
422			}
423			ret = -1;
424		}
425		break;
426	}
427	if (i == NUM_SSID_FIELDS) {
428		if (line) {
429			wmlog_msg(0, "E: Line %d: unknown network field "
430				   "'%s'.", line, var);
431		}
432		ret = -1;
433	}
434
435	return ret;
436}
437
438
439static
440char * gct_config_get_line(char *s, int size, FILE *stream, int *line,
441				  char **_pos)
442{
443	char *pos, *end, *sstart;
444
445	while (fgets(s, size, stream)) {
446		(*line)++;
447		s[size - 1] = '\0';
448		pos = s;
449
450		/* Skip white space from the beginning of line. */
451		while (*pos == ' ' || *pos == '\t' || *pos == '\r')
452			pos++;
453
454		/* Skip comment lines and empty lines */
455		if (*pos == '#' || *pos == '\n' || *pos == '\0' || *pos == '[')
456			continue;
457
458		/*
459		 * Remove # comments unless they are within a double quoted
460		 * string.
461		 */
462		sstart = strchr(pos, '"');
463		if (sstart)
464			sstart = strrchr(sstart + 1, '"');
465		if (!sstart)
466			sstart = pos;
467		end = strchr(sstart, '#');
468		if (end)
469			*end-- = '\0';
470		else
471			end = pos + strlen(pos) - 1;
472
473		/* Remove trailing white space. */
474		while (end > pos &&
475		       (*end == '\n' || *end == ' ' || *end == '\t' ||
476			*end == '\r'))
477			*end-- = '\0';
478
479		if (*pos == '\0')
480			continue;
481
482		if (_pos)
483			*_pos = pos;
484		return pos;
485	}
486
487	if (_pos)
488		*_pos = NULL;
489	return NULL;
490}
491
492
493static
494int gct_config_validate_network(struct gct_config *config)
495{
496	int errors = 0;
497
498	if (config->use_pkm == 0)
499		errors++;
500	if (config->eap_type == 0)
501		errors++;
502
503	return errors;
504}
505
506
507static
508int gct_config_read_network(struct gct_config *config, FILE *f, int *line)
509{
510	int errors = 0;
511	char buf[256], *pos, *pos2;
512
513	while (gct_config_get_line(buf, sizeof(buf), f, line, &pos)) {
514
515		pos2 = strchr(pos, '=');
516		if (pos2 == NULL) {
517			wmlog_msg(0, "E: Line %d: Invalid CFG line "
518				   "'%s'.", *line, pos);
519			errors++;
520			continue;
521		}
522
523		*pos2++ = '\0';
524		if (*pos2 == '"') {
525			if (strchr(pos2 + 1, '"') == NULL) {
526				wmlog_msg(0, "E: Line %d: invalid "
527					   "quotation '%s'.", *line, pos2);
528				errors++;
529				continue;
530			}
531		}
532
533		if (gct_config_set(config, pos, pos2, *line) < 0)
534			errors++;
535
536	}
537
538	errors += gct_config_validate_network(config);
539
540	return errors;
541}
542
543
544static
545struct gct_config * gct_config_alloc_empty()
546{
547	struct gct_config *config;
548
549	config = calloc(1, sizeof(*config));
550	if (config == NULL)
551		return NULL;
552	return config;
553}
554
555
556void gct_config_free(struct gct_config *config)
557{
558	free(config->eap_methods);
559	free(config->identity);
560	free(config->anonymous_identity);
561	free(config->password);
562	free(config->ca_cert);
563	free(config->client_cert);
564	free(config->private_key);
565	free(config->private_key_passwd);
566	free(config->phase1);
567	free(config->phase2);
568	free(config);
569}
570
571
572struct gct_config * gct_config_read(const char *name)
573{
574	FILE *f;
575//	char buf[256], *pos;
576	int errors = 0, line = 0;
577	struct gct_config *config;
578
579	config = gct_config_alloc_empty();
580	if (config == NULL)
581		return NULL;
582	if (eap_peer_register_methods() < 0)
583		return NULL;
584	wmlog_msg(3, "Reading configuration file '%s'", name);
585	f = fopen(name, "r");
586	if (f == NULL) {
587		free(config);
588		wmlog_msg(0, "Configuration file error: %s",strerror(errno));
589		return NULL;
590	}
591
592//	while (gct_config_get_line(buf, sizeof(buf), f, &line, &pos)) {
593		errors += gct_config_read_network(config, f, &line);
594//	}
595
596	fclose(f);
597
598	if (errors) {
599		gct_config_free(config);
600		config = NULL;
601	}
602	eap_peer_unregister_methods();
603	return config;
604}
605
606