1189251Ssam/*
2189251Ssam * WPA Supplicant / Configuration parser and common functions
3252726Srpaulo * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam
11189251Ssam#include "common.h"
12252726Srpaulo#include "utils/uuid.h"
13214734Srpaulo#include "crypto/sha1.h"
14214734Srpaulo#include "rsn_supp/wpa.h"
15189251Ssam#include "eap_peer/eap.h"
16252726Srpaulo#include "p2p/p2p.h"
17189251Ssam#include "config.h"
18189251Ssam
19189251Ssam
20189251Ssam#if !defined(CONFIG_CTRL_IFACE) && defined(CONFIG_NO_CONFIG_WRITE)
21189251Ssam#define NO_CONFIG_WRITE
22189251Ssam#endif
23189251Ssam
24189251Ssam/*
25189251Ssam * Structure for network configuration parsing. This data is used to implement
26189251Ssam * a generic parser for each network block variable. The table of configuration
27189251Ssam * variables is defined below in this file (ssid_fields[]).
28189251Ssam */
29189251Ssamstruct parse_data {
30189251Ssam	/* Configuration variable name */
31189251Ssam	char *name;
32189251Ssam
33189251Ssam	/* Parser function for this variable */
34189251Ssam	int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid,
35189251Ssam		      int line, const char *value);
36189251Ssam
37189251Ssam#ifndef NO_CONFIG_WRITE
38189251Ssam	/* Writer function (i.e., to get the variable in text format from
39189251Ssam	 * internal presentation). */
40189251Ssam	char * (*writer)(const struct parse_data *data, struct wpa_ssid *ssid);
41189251Ssam#endif /* NO_CONFIG_WRITE */
42189251Ssam
43189251Ssam	/* Variable specific parameters for the parser. */
44189251Ssam	void *param1, *param2, *param3, *param4;
45189251Ssam
46189251Ssam	/* 0 = this variable can be included in debug output and ctrl_iface
47189251Ssam	 * 1 = this variable contains key/private data and it must not be
48189251Ssam	 *     included in debug output unless explicitly requested. In
49189251Ssam	 *     addition, this variable will not be readable through the
50189251Ssam	 *     ctrl_iface.
51189251Ssam	 */
52189251Ssam	int key_data;
53189251Ssam};
54189251Ssam
55189251Ssam
56189251Ssamstatic int wpa_config_parse_str(const struct parse_data *data,
57189251Ssam				struct wpa_ssid *ssid,
58189251Ssam				int line, const char *value)
59189251Ssam{
60189251Ssam	size_t res_len, *dst_len;
61189251Ssam	char **dst, *tmp;
62189251Ssam
63189251Ssam	if (os_strcmp(value, "NULL") == 0) {
64189251Ssam		wpa_printf(MSG_DEBUG, "Unset configuration string '%s'",
65189251Ssam			   data->name);
66189251Ssam		tmp = NULL;
67189251Ssam		res_len = 0;
68189251Ssam		goto set;
69189251Ssam	}
70189251Ssam
71189251Ssam	tmp = wpa_config_parse_string(value, &res_len);
72189251Ssam	if (tmp == NULL) {
73189251Ssam		wpa_printf(MSG_ERROR, "Line %d: failed to parse %s '%s'.",
74189251Ssam			   line, data->name,
75189251Ssam			   data->key_data ? "[KEY DATA REMOVED]" : value);
76189251Ssam		return -1;
77189251Ssam	}
78189251Ssam
79189251Ssam	if (data->key_data) {
80189251Ssam		wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
81189251Ssam				      (u8 *) tmp, res_len);
82189251Ssam	} else {
83189251Ssam		wpa_hexdump_ascii(MSG_MSGDUMP, data->name,
84189251Ssam				  (u8 *) tmp, res_len);
85189251Ssam	}
86189251Ssam
87189251Ssam	if (data->param3 && res_len < (size_t) data->param3) {
88189251Ssam		wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
89189251Ssam			   "min_len=%ld)", line, data->name,
90189251Ssam			   (unsigned long) res_len, (long) data->param3);
91189251Ssam		os_free(tmp);
92189251Ssam		return -1;
93189251Ssam	}
94189251Ssam
95189251Ssam	if (data->param4 && res_len > (size_t) data->param4) {
96189251Ssam		wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
97189251Ssam			   "max_len=%ld)", line, data->name,
98189251Ssam			   (unsigned long) res_len, (long) data->param4);
99189251Ssam		os_free(tmp);
100189251Ssam		return -1;
101189251Ssam	}
102189251Ssam
103189251Ssamset:
104189251Ssam	dst = (char **) (((u8 *) ssid) + (long) data->param1);
105189251Ssam	dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2);
106189251Ssam	os_free(*dst);
107189251Ssam	*dst = tmp;
108189251Ssam	if (data->param2)
109189251Ssam		*dst_len = res_len;
110189251Ssam
111189251Ssam	return 0;
112189251Ssam}
113189251Ssam
114189251Ssam
115189251Ssam#ifndef NO_CONFIG_WRITE
116189251Ssamstatic char * wpa_config_write_string_ascii(const u8 *value, size_t len)
117189251Ssam{
118189251Ssam	char *buf;
119189251Ssam
120189251Ssam	buf = os_malloc(len + 3);
121189251Ssam	if (buf == NULL)
122189251Ssam		return NULL;
123189251Ssam	buf[0] = '"';
124189251Ssam	os_memcpy(buf + 1, value, len);
125189251Ssam	buf[len + 1] = '"';
126189251Ssam	buf[len + 2] = '\0';
127189251Ssam
128189251Ssam	return buf;
129189251Ssam}
130189251Ssam
131189251Ssam
132189251Ssamstatic char * wpa_config_write_string_hex(const u8 *value, size_t len)
133189251Ssam{
134189251Ssam	char *buf;
135189251Ssam
136189251Ssam	buf = os_zalloc(2 * len + 1);
137189251Ssam	if (buf == NULL)
138189251Ssam		return NULL;
139189251Ssam	wpa_snprintf_hex(buf, 2 * len + 1, value, len);
140189251Ssam
141189251Ssam	return buf;
142189251Ssam}
143189251Ssam
144189251Ssam
145189251Ssamstatic char * wpa_config_write_string(const u8 *value, size_t len)
146189251Ssam{
147189251Ssam	if (value == NULL)
148189251Ssam		return NULL;
149189251Ssam
150189251Ssam	if (is_hex(value, len))
151189251Ssam		return wpa_config_write_string_hex(value, len);
152189251Ssam	else
153189251Ssam		return wpa_config_write_string_ascii(value, len);
154189251Ssam}
155189251Ssam
156189251Ssam
157189251Ssamstatic char * wpa_config_write_str(const struct parse_data *data,
158189251Ssam				   struct wpa_ssid *ssid)
159189251Ssam{
160189251Ssam	size_t len;
161189251Ssam	char **src;
162189251Ssam
163189251Ssam	src = (char **) (((u8 *) ssid) + (long) data->param1);
164189251Ssam	if (*src == NULL)
165189251Ssam		return NULL;
166189251Ssam
167189251Ssam	if (data->param2)
168189251Ssam		len = *((size_t *) (((u8 *) ssid) + (long) data->param2));
169189251Ssam	else
170189251Ssam		len = os_strlen(*src);
171189251Ssam
172189251Ssam	return wpa_config_write_string((const u8 *) *src, len);
173189251Ssam}
174189251Ssam#endif /* NO_CONFIG_WRITE */
175189251Ssam
176189251Ssam
177189251Ssamstatic int wpa_config_parse_int(const struct parse_data *data,
178189251Ssam				struct wpa_ssid *ssid,
179189251Ssam				int line, const char *value)
180189251Ssam{
181189251Ssam	int *dst;
182189251Ssam
183189251Ssam	dst = (int *) (((u8 *) ssid) + (long) data->param1);
184189251Ssam	*dst = atoi(value);
185189251Ssam	wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
186189251Ssam
187189251Ssam	if (data->param3 && *dst < (long) data->param3) {
188189251Ssam		wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
189189251Ssam			   "min_value=%ld)", line, data->name, *dst,
190189251Ssam			   (long) data->param3);
191189251Ssam		*dst = (long) data->param3;
192189251Ssam		return -1;
193189251Ssam	}
194189251Ssam
195189251Ssam	if (data->param4 && *dst > (long) data->param4) {
196189251Ssam		wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
197189251Ssam			   "max_value=%ld)", line, data->name, *dst,
198189251Ssam			   (long) data->param4);
199189251Ssam		*dst = (long) data->param4;
200189251Ssam		return -1;
201189251Ssam	}
202189251Ssam
203189251Ssam	return 0;
204189251Ssam}
205189251Ssam
206189251Ssam
207189251Ssam#ifndef NO_CONFIG_WRITE
208189251Ssamstatic char * wpa_config_write_int(const struct parse_data *data,
209189251Ssam				   struct wpa_ssid *ssid)
210189251Ssam{
211189251Ssam	int *src, res;
212189251Ssam	char *value;
213189251Ssam
214189251Ssam	src = (int *) (((u8 *) ssid) + (long) data->param1);
215189251Ssam
216189251Ssam	value = os_malloc(20);
217189251Ssam	if (value == NULL)
218189251Ssam		return NULL;
219189251Ssam	res = os_snprintf(value, 20, "%d", *src);
220189251Ssam	if (res < 0 || res >= 20) {
221189251Ssam		os_free(value);
222189251Ssam		return NULL;
223189251Ssam	}
224189251Ssam	value[20 - 1] = '\0';
225189251Ssam	return value;
226189251Ssam}
227189251Ssam#endif /* NO_CONFIG_WRITE */
228189251Ssam
229189251Ssam
230189251Ssamstatic int wpa_config_parse_bssid(const struct parse_data *data,
231189251Ssam				  struct wpa_ssid *ssid, int line,
232189251Ssam				  const char *value)
233189251Ssam{
234252726Srpaulo	if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
235252726Srpaulo	    os_strcmp(value, "any") == 0) {
236252726Srpaulo		ssid->bssid_set = 0;
237252726Srpaulo		wpa_printf(MSG_MSGDUMP, "BSSID any");
238252726Srpaulo		return 0;
239252726Srpaulo	}
240189251Ssam	if (hwaddr_aton(value, ssid->bssid)) {
241189251Ssam		wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.",
242189251Ssam			   line, value);
243189251Ssam		return -1;
244189251Ssam	}
245189251Ssam	ssid->bssid_set = 1;
246189251Ssam	wpa_hexdump(MSG_MSGDUMP, "BSSID", ssid->bssid, ETH_ALEN);
247189251Ssam	return 0;
248189251Ssam}
249189251Ssam
250189251Ssam
251189251Ssam#ifndef NO_CONFIG_WRITE
252189251Ssamstatic char * wpa_config_write_bssid(const struct parse_data *data,
253189251Ssam				     struct wpa_ssid *ssid)
254189251Ssam{
255189251Ssam	char *value;
256189251Ssam	int res;
257189251Ssam
258189251Ssam	if (!ssid->bssid_set)
259189251Ssam		return NULL;
260189251Ssam
261189251Ssam	value = os_malloc(20);
262189251Ssam	if (value == NULL)
263189251Ssam		return NULL;
264189251Ssam	res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid));
265189251Ssam	if (res < 0 || res >= 20) {
266189251Ssam		os_free(value);
267189251Ssam		return NULL;
268189251Ssam	}
269189251Ssam	value[20 - 1] = '\0';
270189251Ssam	return value;
271189251Ssam}
272189251Ssam#endif /* NO_CONFIG_WRITE */
273189251Ssam
274189251Ssam
275189251Ssamstatic int wpa_config_parse_psk(const struct parse_data *data,
276189251Ssam				struct wpa_ssid *ssid, int line,
277189251Ssam				const char *value)
278189251Ssam{
279252726Srpaulo#ifdef CONFIG_EXT_PASSWORD
280252726Srpaulo	if (os_strncmp(value, "ext:", 4) == 0) {
281252726Srpaulo		os_free(ssid->passphrase);
282252726Srpaulo		ssid->passphrase = NULL;
283252726Srpaulo		ssid->psk_set = 0;
284252726Srpaulo		os_free(ssid->ext_psk);
285252726Srpaulo		ssid->ext_psk = os_strdup(value + 4);
286252726Srpaulo		if (ssid->ext_psk == NULL)
287252726Srpaulo			return -1;
288252726Srpaulo		wpa_printf(MSG_DEBUG, "PSK: External password '%s'",
289252726Srpaulo			   ssid->ext_psk);
290252726Srpaulo		return 0;
291252726Srpaulo	}
292252726Srpaulo#endif /* CONFIG_EXT_PASSWORD */
293252726Srpaulo
294189251Ssam	if (*value == '"') {
295189251Ssam#ifndef CONFIG_NO_PBKDF2
296189251Ssam		const char *pos;
297189251Ssam		size_t len;
298189251Ssam
299189251Ssam		value++;
300189251Ssam		pos = os_strrchr(value, '"');
301189251Ssam		if (pos)
302189251Ssam			len = pos - value;
303189251Ssam		else
304189251Ssam			len = os_strlen(value);
305189251Ssam		if (len < 8 || len > 63) {
306189251Ssam			wpa_printf(MSG_ERROR, "Line %d: Invalid passphrase "
307189251Ssam				   "length %lu (expected: 8..63) '%s'.",
308189251Ssam				   line, (unsigned long) len, value);
309189251Ssam			return -1;
310189251Ssam		}
311189251Ssam		wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)",
312189251Ssam				      (u8 *) value, len);
313189251Ssam		if (ssid->passphrase && os_strlen(ssid->passphrase) == len &&
314189251Ssam		    os_memcmp(ssid->passphrase, value, len) == 0)
315189251Ssam			return 0;
316189251Ssam		ssid->psk_set = 0;
317189251Ssam		os_free(ssid->passphrase);
318189251Ssam		ssid->passphrase = os_malloc(len + 1);
319189251Ssam		if (ssid->passphrase == NULL)
320189251Ssam			return -1;
321189251Ssam		os_memcpy(ssid->passphrase, value, len);
322189251Ssam		ssid->passphrase[len] = '\0';
323189251Ssam		return 0;
324189251Ssam#else /* CONFIG_NO_PBKDF2 */
325189251Ssam		wpa_printf(MSG_ERROR, "Line %d: ASCII passphrase not "
326189251Ssam			   "supported.", line);
327189251Ssam		return -1;
328189251Ssam#endif /* CONFIG_NO_PBKDF2 */
329189251Ssam	}
330189251Ssam
331189251Ssam	if (hexstr2bin(value, ssid->psk, PMK_LEN) ||
332189251Ssam	    value[PMK_LEN * 2] != '\0') {
333189251Ssam		wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
334189251Ssam			   line, value);
335189251Ssam		return -1;
336189251Ssam	}
337189251Ssam
338189251Ssam	os_free(ssid->passphrase);
339189251Ssam	ssid->passphrase = NULL;
340189251Ssam
341189251Ssam	ssid->psk_set = 1;
342189251Ssam	wpa_hexdump_key(MSG_MSGDUMP, "PSK", ssid->psk, PMK_LEN);
343189251Ssam	return 0;
344189251Ssam}
345189251Ssam
346189251Ssam
347189251Ssam#ifndef NO_CONFIG_WRITE
348189251Ssamstatic char * wpa_config_write_psk(const struct parse_data *data,
349189251Ssam				   struct wpa_ssid *ssid)
350189251Ssam{
351252726Srpaulo#ifdef CONFIG_EXT_PASSWORD
352252726Srpaulo	if (ssid->ext_psk) {
353252726Srpaulo		size_t len = 4 + os_strlen(ssid->ext_psk) + 1;
354252726Srpaulo		char *buf = os_malloc(len);
355252726Srpaulo		if (buf == NULL)
356252726Srpaulo			return NULL;
357252726Srpaulo		os_snprintf(buf, len, "ext:%s", ssid->ext_psk);
358252726Srpaulo		return buf;
359252726Srpaulo	}
360252726Srpaulo#endif /* CONFIG_EXT_PASSWORD */
361252726Srpaulo
362189251Ssam	if (ssid->passphrase)
363189251Ssam		return wpa_config_write_string_ascii(
364189251Ssam			(const u8 *) ssid->passphrase,
365189251Ssam			os_strlen(ssid->passphrase));
366189251Ssam
367189251Ssam	if (ssid->psk_set)
368189251Ssam		return wpa_config_write_string_hex(ssid->psk, PMK_LEN);
369189251Ssam
370189251Ssam	return NULL;
371189251Ssam}
372189251Ssam#endif /* NO_CONFIG_WRITE */
373189251Ssam
374189251Ssam
375189251Ssamstatic int wpa_config_parse_proto(const struct parse_data *data,
376189251Ssam				  struct wpa_ssid *ssid, int line,
377189251Ssam				  const char *value)
378189251Ssam{
379189251Ssam	int val = 0, last, errors = 0;
380189251Ssam	char *start, *end, *buf;
381189251Ssam
382189251Ssam	buf = os_strdup(value);
383189251Ssam	if (buf == NULL)
384189251Ssam		return -1;
385189251Ssam	start = buf;
386189251Ssam
387189251Ssam	while (*start != '\0') {
388189251Ssam		while (*start == ' ' || *start == '\t')
389189251Ssam			start++;
390189251Ssam		if (*start == '\0')
391189251Ssam			break;
392189251Ssam		end = start;
393189251Ssam		while (*end != ' ' && *end != '\t' && *end != '\0')
394189251Ssam			end++;
395189251Ssam		last = *end == '\0';
396189251Ssam		*end = '\0';
397189251Ssam		if (os_strcmp(start, "WPA") == 0)
398189251Ssam			val |= WPA_PROTO_WPA;
399189251Ssam		else if (os_strcmp(start, "RSN") == 0 ||
400189251Ssam			 os_strcmp(start, "WPA2") == 0)
401189251Ssam			val |= WPA_PROTO_RSN;
402189251Ssam		else {
403189251Ssam			wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'",
404189251Ssam				   line, start);
405189251Ssam			errors++;
406189251Ssam		}
407189251Ssam
408189251Ssam		if (last)
409189251Ssam			break;
410189251Ssam		start = end + 1;
411189251Ssam	}
412189251Ssam	os_free(buf);
413189251Ssam
414189251Ssam	if (val == 0) {
415189251Ssam		wpa_printf(MSG_ERROR,
416189251Ssam			   "Line %d: no proto values configured.", line);
417189251Ssam		errors++;
418189251Ssam	}
419189251Ssam
420189251Ssam	wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val);
421189251Ssam	ssid->proto = val;
422189251Ssam	return errors ? -1 : 0;
423189251Ssam}
424189251Ssam
425189251Ssam
426189251Ssam#ifndef NO_CONFIG_WRITE
427189251Ssamstatic char * wpa_config_write_proto(const struct parse_data *data,
428189251Ssam				     struct wpa_ssid *ssid)
429189251Ssam{
430189251Ssam	int first = 1, ret;
431189251Ssam	char *buf, *pos, *end;
432189251Ssam
433189251Ssam	pos = buf = os_zalloc(10);
434189251Ssam	if (buf == NULL)
435189251Ssam		return NULL;
436189251Ssam	end = buf + 10;
437189251Ssam
438189251Ssam	if (ssid->proto & WPA_PROTO_WPA) {
439189251Ssam		ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " ");
440189251Ssam		if (ret < 0 || ret >= end - pos)
441189251Ssam			return buf;
442189251Ssam		pos += ret;
443189251Ssam		first = 0;
444189251Ssam	}
445189251Ssam
446189251Ssam	if (ssid->proto & WPA_PROTO_RSN) {
447189251Ssam		ret = os_snprintf(pos, end - pos, "%sRSN", first ? "" : " ");
448189251Ssam		if (ret < 0 || ret >= end - pos)
449189251Ssam			return buf;
450189251Ssam		pos += ret;
451189251Ssam		first = 0;
452189251Ssam	}
453189251Ssam
454189251Ssam	return buf;
455189251Ssam}
456189251Ssam#endif /* NO_CONFIG_WRITE */
457189251Ssam
458189251Ssam
459189251Ssamstatic int wpa_config_parse_key_mgmt(const struct parse_data *data,
460189251Ssam				     struct wpa_ssid *ssid, int line,
461189251Ssam				     const char *value)
462189251Ssam{
463189251Ssam	int val = 0, last, errors = 0;
464189251Ssam	char *start, *end, *buf;
465189251Ssam
466189251Ssam	buf = os_strdup(value);
467189251Ssam	if (buf == NULL)
468189251Ssam		return -1;
469189251Ssam	start = buf;
470189251Ssam
471189251Ssam	while (*start != '\0') {
472189251Ssam		while (*start == ' ' || *start == '\t')
473189251Ssam			start++;
474189251Ssam		if (*start == '\0')
475189251Ssam			break;
476189251Ssam		end = start;
477189251Ssam		while (*end != ' ' && *end != '\t' && *end != '\0')
478189251Ssam			end++;
479189251Ssam		last = *end == '\0';
480189251Ssam		*end = '\0';
481189251Ssam		if (os_strcmp(start, "WPA-PSK") == 0)
482189251Ssam			val |= WPA_KEY_MGMT_PSK;
483189251Ssam		else if (os_strcmp(start, "WPA-EAP") == 0)
484189251Ssam			val |= WPA_KEY_MGMT_IEEE8021X;
485189251Ssam		else if (os_strcmp(start, "IEEE8021X") == 0)
486189251Ssam			val |= WPA_KEY_MGMT_IEEE8021X_NO_WPA;
487189251Ssam		else if (os_strcmp(start, "NONE") == 0)
488189251Ssam			val |= WPA_KEY_MGMT_NONE;
489189251Ssam		else if (os_strcmp(start, "WPA-NONE") == 0)
490189251Ssam			val |= WPA_KEY_MGMT_WPA_NONE;
491189251Ssam#ifdef CONFIG_IEEE80211R
492189251Ssam		else if (os_strcmp(start, "FT-PSK") == 0)
493189251Ssam			val |= WPA_KEY_MGMT_FT_PSK;
494189251Ssam		else if (os_strcmp(start, "FT-EAP") == 0)
495189251Ssam			val |= WPA_KEY_MGMT_FT_IEEE8021X;
496189251Ssam#endif /* CONFIG_IEEE80211R */
497189251Ssam#ifdef CONFIG_IEEE80211W
498189251Ssam		else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
499189251Ssam			val |= WPA_KEY_MGMT_PSK_SHA256;
500189251Ssam		else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
501189251Ssam			val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
502189251Ssam#endif /* CONFIG_IEEE80211W */
503189251Ssam#ifdef CONFIG_WPS
504189251Ssam		else if (os_strcmp(start, "WPS") == 0)
505189251Ssam			val |= WPA_KEY_MGMT_WPS;
506189251Ssam#endif /* CONFIG_WPS */
507252726Srpaulo#ifdef CONFIG_SAE
508252726Srpaulo		else if (os_strcmp(start, "SAE") == 0)
509252726Srpaulo			val |= WPA_KEY_MGMT_SAE;
510252726Srpaulo		else if (os_strcmp(start, "FT-SAE") == 0)
511252726Srpaulo			val |= WPA_KEY_MGMT_FT_SAE;
512252726Srpaulo#endif /* CONFIG_SAE */
513189251Ssam		else {
514189251Ssam			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
515189251Ssam				   line, start);
516189251Ssam			errors++;
517189251Ssam		}
518189251Ssam
519189251Ssam		if (last)
520189251Ssam			break;
521189251Ssam		start = end + 1;
522189251Ssam	}
523189251Ssam	os_free(buf);
524189251Ssam
525189251Ssam	if (val == 0) {
526189251Ssam		wpa_printf(MSG_ERROR,
527189251Ssam			   "Line %d: no key_mgmt values configured.", line);
528189251Ssam		errors++;
529189251Ssam	}
530189251Ssam
531189251Ssam	wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val);
532189251Ssam	ssid->key_mgmt = val;
533189251Ssam	return errors ? -1 : 0;
534189251Ssam}
535189251Ssam
536189251Ssam
537189251Ssam#ifndef NO_CONFIG_WRITE
538189251Ssamstatic char * wpa_config_write_key_mgmt(const struct parse_data *data,
539189251Ssam					struct wpa_ssid *ssid)
540189251Ssam{
541189251Ssam	char *buf, *pos, *end;
542189251Ssam	int ret;
543189251Ssam
544189251Ssam	pos = buf = os_zalloc(50);
545189251Ssam	if (buf == NULL)
546189251Ssam		return NULL;
547189251Ssam	end = buf + 50;
548189251Ssam
549189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
550189251Ssam		ret = os_snprintf(pos, end - pos, "%sWPA-PSK",
551189251Ssam				  pos == buf ? "" : " ");
552189251Ssam		if (ret < 0 || ret >= end - pos) {
553189251Ssam			end[-1] = '\0';
554189251Ssam			return buf;
555189251Ssam		}
556189251Ssam		pos += ret;
557189251Ssam	}
558189251Ssam
559189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
560189251Ssam		ret = os_snprintf(pos, end - pos, "%sWPA-EAP",
561189251Ssam				  pos == buf ? "" : " ");
562189251Ssam		if (ret < 0 || ret >= end - pos) {
563189251Ssam			end[-1] = '\0';
564189251Ssam			return buf;
565189251Ssam		}
566189251Ssam		pos += ret;
567189251Ssam	}
568189251Ssam
569189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
570189251Ssam		ret = os_snprintf(pos, end - pos, "%sIEEE8021X",
571189251Ssam				  pos == buf ? "" : " ");
572189251Ssam		if (ret < 0 || ret >= end - pos) {
573189251Ssam			end[-1] = '\0';
574189251Ssam			return buf;
575189251Ssam		}
576189251Ssam		pos += ret;
577189251Ssam	}
578189251Ssam
579189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_NONE) {
580189251Ssam		ret = os_snprintf(pos, end - pos, "%sNONE",
581189251Ssam				  pos == buf ? "" : " ");
582189251Ssam		if (ret < 0 || ret >= end - pos) {
583189251Ssam			end[-1] = '\0';
584189251Ssam			return buf;
585189251Ssam		}
586189251Ssam		pos += ret;
587189251Ssam	}
588189251Ssam
589189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
590189251Ssam		ret = os_snprintf(pos, end - pos, "%sWPA-NONE",
591189251Ssam				  pos == buf ? "" : " ");
592189251Ssam		if (ret < 0 || ret >= end - pos) {
593189251Ssam			end[-1] = '\0';
594189251Ssam			return buf;
595189251Ssam		}
596189251Ssam		pos += ret;
597189251Ssam	}
598189251Ssam
599189251Ssam#ifdef CONFIG_IEEE80211R
600189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK)
601189251Ssam		pos += os_snprintf(pos, end - pos, "%sFT-PSK",
602189251Ssam				   pos == buf ? "" : " ");
603189251Ssam
604189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
605189251Ssam		pos += os_snprintf(pos, end - pos, "%sFT-EAP",
606189251Ssam				   pos == buf ? "" : " ");
607189251Ssam#endif /* CONFIG_IEEE80211R */
608189251Ssam
609189251Ssam#ifdef CONFIG_IEEE80211W
610189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
611189251Ssam		pos += os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
612189251Ssam				   pos == buf ? "" : " ");
613189251Ssam
614189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
615189251Ssam		pos += os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256",
616189251Ssam				   pos == buf ? "" : " ");
617189251Ssam#endif /* CONFIG_IEEE80211W */
618189251Ssam
619189251Ssam#ifdef CONFIG_WPS
620189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
621189251Ssam		pos += os_snprintf(pos, end - pos, "%sWPS",
622189251Ssam				   pos == buf ? "" : " ");
623189251Ssam#endif /* CONFIG_WPS */
624189251Ssam
625189251Ssam	return buf;
626189251Ssam}
627189251Ssam#endif /* NO_CONFIG_WRITE */
628189251Ssam
629189251Ssam
630189251Ssamstatic int wpa_config_parse_cipher(int line, const char *value)
631189251Ssam{
632189251Ssam	int val = 0, last;
633189251Ssam	char *start, *end, *buf;
634189251Ssam
635189251Ssam	buf = os_strdup(value);
636189251Ssam	if (buf == NULL)
637189251Ssam		return -1;
638189251Ssam	start = buf;
639189251Ssam
640189251Ssam	while (*start != '\0') {
641189251Ssam		while (*start == ' ' || *start == '\t')
642189251Ssam			start++;
643189251Ssam		if (*start == '\0')
644189251Ssam			break;
645189251Ssam		end = start;
646189251Ssam		while (*end != ' ' && *end != '\t' && *end != '\0')
647189251Ssam			end++;
648189251Ssam		last = *end == '\0';
649189251Ssam		*end = '\0';
650189251Ssam		if (os_strcmp(start, "CCMP") == 0)
651189251Ssam			val |= WPA_CIPHER_CCMP;
652252726Srpaulo		else if (os_strcmp(start, "GCMP") == 0)
653252726Srpaulo			val |= WPA_CIPHER_GCMP;
654189251Ssam		else if (os_strcmp(start, "TKIP") == 0)
655189251Ssam			val |= WPA_CIPHER_TKIP;
656189251Ssam		else if (os_strcmp(start, "WEP104") == 0)
657189251Ssam			val |= WPA_CIPHER_WEP104;
658189251Ssam		else if (os_strcmp(start, "WEP40") == 0)
659189251Ssam			val |= WPA_CIPHER_WEP40;
660189251Ssam		else if (os_strcmp(start, "NONE") == 0)
661189251Ssam			val |= WPA_CIPHER_NONE;
662189251Ssam		else {
663189251Ssam			wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
664189251Ssam				   line, start);
665189251Ssam			os_free(buf);
666189251Ssam			return -1;
667189251Ssam		}
668189251Ssam
669189251Ssam		if (last)
670189251Ssam			break;
671189251Ssam		start = end + 1;
672189251Ssam	}
673189251Ssam	os_free(buf);
674189251Ssam
675189251Ssam	if (val == 0) {
676189251Ssam		wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.",
677189251Ssam			   line);
678189251Ssam		return -1;
679189251Ssam	}
680189251Ssam	return val;
681189251Ssam}
682189251Ssam
683189251Ssam
684189251Ssam#ifndef NO_CONFIG_WRITE
685189251Ssamstatic char * wpa_config_write_cipher(int cipher)
686189251Ssam{
687189251Ssam	char *buf, *pos, *end;
688189251Ssam	int ret;
689189251Ssam
690189251Ssam	pos = buf = os_zalloc(50);
691189251Ssam	if (buf == NULL)
692189251Ssam		return NULL;
693189251Ssam	end = buf + 50;
694189251Ssam
695189251Ssam	if (cipher & WPA_CIPHER_CCMP) {
696189251Ssam		ret = os_snprintf(pos, end - pos, "%sCCMP",
697189251Ssam				  pos == buf ? "" : " ");
698189251Ssam		if (ret < 0 || ret >= end - pos) {
699189251Ssam			end[-1] = '\0';
700189251Ssam			return buf;
701189251Ssam		}
702189251Ssam		pos += ret;
703189251Ssam	}
704189251Ssam
705252726Srpaulo	if (cipher & WPA_CIPHER_GCMP) {
706252726Srpaulo		ret = os_snprintf(pos, end - pos, "%sGCMP",
707252726Srpaulo				  pos == buf ? "" : " ");
708252726Srpaulo		if (ret < 0 || ret >= end - pos) {
709252726Srpaulo			end[-1] = '\0';
710252726Srpaulo			return buf;
711252726Srpaulo		}
712252726Srpaulo		pos += ret;
713252726Srpaulo	}
714252726Srpaulo
715189251Ssam	if (cipher & WPA_CIPHER_TKIP) {
716189251Ssam		ret = os_snprintf(pos, end - pos, "%sTKIP",
717189251Ssam				  pos == buf ? "" : " ");
718189251Ssam		if (ret < 0 || ret >= end - pos) {
719189251Ssam			end[-1] = '\0';
720189251Ssam			return buf;
721189251Ssam		}
722189251Ssam		pos += ret;
723189251Ssam	}
724189251Ssam
725189251Ssam	if (cipher & WPA_CIPHER_WEP104) {
726189251Ssam		ret = os_snprintf(pos, end - pos, "%sWEP104",
727189251Ssam				  pos == buf ? "" : " ");
728189251Ssam		if (ret < 0 || ret >= end - pos) {
729189251Ssam			end[-1] = '\0';
730189251Ssam			return buf;
731189251Ssam		}
732189251Ssam		pos += ret;
733189251Ssam	}
734189251Ssam
735189251Ssam	if (cipher & WPA_CIPHER_WEP40) {
736189251Ssam		ret = os_snprintf(pos, end - pos, "%sWEP40",
737189251Ssam				  pos == buf ? "" : " ");
738189251Ssam		if (ret < 0 || ret >= end - pos) {
739189251Ssam			end[-1] = '\0';
740189251Ssam			return buf;
741189251Ssam		}
742189251Ssam		pos += ret;
743189251Ssam	}
744189251Ssam
745189251Ssam	if (cipher & WPA_CIPHER_NONE) {
746189251Ssam		ret = os_snprintf(pos, end - pos, "%sNONE",
747189251Ssam				  pos == buf ? "" : " ");
748189251Ssam		if (ret < 0 || ret >= end - pos) {
749189251Ssam			end[-1] = '\0';
750189251Ssam			return buf;
751189251Ssam		}
752189251Ssam		pos += ret;
753189251Ssam	}
754189251Ssam
755189251Ssam	return buf;
756189251Ssam}
757189251Ssam#endif /* NO_CONFIG_WRITE */
758189251Ssam
759189251Ssam
760189251Ssamstatic int wpa_config_parse_pairwise(const struct parse_data *data,
761189251Ssam				     struct wpa_ssid *ssid, int line,
762189251Ssam				     const char *value)
763189251Ssam{
764189251Ssam	int val;
765189251Ssam	val = wpa_config_parse_cipher(line, value);
766189251Ssam	if (val == -1)
767189251Ssam		return -1;
768252726Srpaulo	if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP |
769252726Srpaulo		    WPA_CIPHER_NONE)) {
770189251Ssam		wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher "
771189251Ssam			   "(0x%x).", line, val);
772189251Ssam		return -1;
773189251Ssam	}
774189251Ssam
775189251Ssam	wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val);
776189251Ssam	ssid->pairwise_cipher = val;
777189251Ssam	return 0;
778189251Ssam}
779189251Ssam
780189251Ssam
781189251Ssam#ifndef NO_CONFIG_WRITE
782189251Ssamstatic char * wpa_config_write_pairwise(const struct parse_data *data,
783189251Ssam					struct wpa_ssid *ssid)
784189251Ssam{
785189251Ssam	return wpa_config_write_cipher(ssid->pairwise_cipher);
786189251Ssam}
787189251Ssam#endif /* NO_CONFIG_WRITE */
788189251Ssam
789189251Ssam
790189251Ssamstatic int wpa_config_parse_group(const struct parse_data *data,
791189251Ssam				  struct wpa_ssid *ssid, int line,
792189251Ssam				  const char *value)
793189251Ssam{
794189251Ssam	int val;
795189251Ssam	val = wpa_config_parse_cipher(line, value);
796189251Ssam	if (val == -1)
797189251Ssam		return -1;
798252726Srpaulo	if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP |
799252726Srpaulo		    WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) {
800189251Ssam		wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher "
801189251Ssam			   "(0x%x).", line, val);
802189251Ssam		return -1;
803189251Ssam	}
804189251Ssam
805189251Ssam	wpa_printf(MSG_MSGDUMP, "group: 0x%x", val);
806189251Ssam	ssid->group_cipher = val;
807189251Ssam	return 0;
808189251Ssam}
809189251Ssam
810189251Ssam
811189251Ssam#ifndef NO_CONFIG_WRITE
812189251Ssamstatic char * wpa_config_write_group(const struct parse_data *data,
813189251Ssam				     struct wpa_ssid *ssid)
814189251Ssam{
815189251Ssam	return wpa_config_write_cipher(ssid->group_cipher);
816189251Ssam}
817189251Ssam#endif /* NO_CONFIG_WRITE */
818189251Ssam
819189251Ssam
820189251Ssamstatic int wpa_config_parse_auth_alg(const struct parse_data *data,
821189251Ssam				     struct wpa_ssid *ssid, int line,
822189251Ssam				     const char *value)
823189251Ssam{
824189251Ssam	int val = 0, last, errors = 0;
825189251Ssam	char *start, *end, *buf;
826189251Ssam
827189251Ssam	buf = os_strdup(value);
828189251Ssam	if (buf == NULL)
829189251Ssam		return -1;
830189251Ssam	start = buf;
831189251Ssam
832189251Ssam	while (*start != '\0') {
833189251Ssam		while (*start == ' ' || *start == '\t')
834189251Ssam			start++;
835189251Ssam		if (*start == '\0')
836189251Ssam			break;
837189251Ssam		end = start;
838189251Ssam		while (*end != ' ' && *end != '\t' && *end != '\0')
839189251Ssam			end++;
840189251Ssam		last = *end == '\0';
841189251Ssam		*end = '\0';
842189251Ssam		if (os_strcmp(start, "OPEN") == 0)
843189251Ssam			val |= WPA_AUTH_ALG_OPEN;
844189251Ssam		else if (os_strcmp(start, "SHARED") == 0)
845189251Ssam			val |= WPA_AUTH_ALG_SHARED;
846189251Ssam		else if (os_strcmp(start, "LEAP") == 0)
847189251Ssam			val |= WPA_AUTH_ALG_LEAP;
848189251Ssam		else {
849189251Ssam			wpa_printf(MSG_ERROR, "Line %d: invalid auth_alg '%s'",
850189251Ssam				   line, start);
851189251Ssam			errors++;
852189251Ssam		}
853189251Ssam
854189251Ssam		if (last)
855189251Ssam			break;
856189251Ssam		start = end + 1;
857189251Ssam	}
858189251Ssam	os_free(buf);
859189251Ssam
860189251Ssam	if (val == 0) {
861189251Ssam		wpa_printf(MSG_ERROR,
862189251Ssam			   "Line %d: no auth_alg values configured.", line);
863189251Ssam		errors++;
864189251Ssam	}
865189251Ssam
866189251Ssam	wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val);
867189251Ssam	ssid->auth_alg = val;
868189251Ssam	return errors ? -1 : 0;
869189251Ssam}
870189251Ssam
871189251Ssam
872189251Ssam#ifndef NO_CONFIG_WRITE
873189251Ssamstatic char * wpa_config_write_auth_alg(const struct parse_data *data,
874189251Ssam					struct wpa_ssid *ssid)
875189251Ssam{
876189251Ssam	char *buf, *pos, *end;
877189251Ssam	int ret;
878189251Ssam
879189251Ssam	pos = buf = os_zalloc(30);
880189251Ssam	if (buf == NULL)
881189251Ssam		return NULL;
882189251Ssam	end = buf + 30;
883189251Ssam
884189251Ssam	if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) {
885189251Ssam		ret = os_snprintf(pos, end - pos, "%sOPEN",
886189251Ssam				  pos == buf ? "" : " ");
887189251Ssam		if (ret < 0 || ret >= end - pos) {
888189251Ssam			end[-1] = '\0';
889189251Ssam			return buf;
890189251Ssam		}
891189251Ssam		pos += ret;
892189251Ssam	}
893189251Ssam
894189251Ssam	if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) {
895189251Ssam		ret = os_snprintf(pos, end - pos, "%sSHARED",
896189251Ssam				  pos == buf ? "" : " ");
897189251Ssam		if (ret < 0 || ret >= end - pos) {
898189251Ssam			end[-1] = '\0';
899189251Ssam			return buf;
900189251Ssam		}
901189251Ssam		pos += ret;
902189251Ssam	}
903189251Ssam
904189251Ssam	if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) {
905189251Ssam		ret = os_snprintf(pos, end - pos, "%sLEAP",
906189251Ssam				  pos == buf ? "" : " ");
907189251Ssam		if (ret < 0 || ret >= end - pos) {
908189251Ssam			end[-1] = '\0';
909189251Ssam			return buf;
910189251Ssam		}
911189251Ssam		pos += ret;
912189251Ssam	}
913189251Ssam
914189251Ssam	return buf;
915189251Ssam}
916189251Ssam#endif /* NO_CONFIG_WRITE */
917189251Ssam
918189251Ssam
919214734Srpaulostatic int * wpa_config_parse_freqs(const struct parse_data *data,
920214734Srpaulo				    struct wpa_ssid *ssid, int line,
921214734Srpaulo				    const char *value)
922214734Srpaulo{
923214734Srpaulo	int *freqs;
924214734Srpaulo	size_t used, len;
925214734Srpaulo	const char *pos;
926214734Srpaulo
927214734Srpaulo	used = 0;
928214734Srpaulo	len = 10;
929252726Srpaulo	freqs = os_calloc(len + 1, sizeof(int));
930214734Srpaulo	if (freqs == NULL)
931214734Srpaulo		return NULL;
932214734Srpaulo
933214734Srpaulo	pos = value;
934214734Srpaulo	while (pos) {
935214734Srpaulo		while (*pos == ' ')
936214734Srpaulo			pos++;
937214734Srpaulo		if (used == len) {
938214734Srpaulo			int *n;
939214734Srpaulo			size_t i;
940252726Srpaulo			n = os_realloc_array(freqs, len * 2 + 1, sizeof(int));
941214734Srpaulo			if (n == NULL) {
942214734Srpaulo				os_free(freqs);
943214734Srpaulo				return NULL;
944214734Srpaulo			}
945214734Srpaulo			for (i = len; i <= len * 2; i++)
946214734Srpaulo				n[i] = 0;
947214734Srpaulo			freqs = n;
948214734Srpaulo			len *= 2;
949214734Srpaulo		}
950214734Srpaulo
951214734Srpaulo		freqs[used] = atoi(pos);
952214734Srpaulo		if (freqs[used] == 0)
953214734Srpaulo			break;
954214734Srpaulo		used++;
955214734Srpaulo		pos = os_strchr(pos + 1, ' ');
956214734Srpaulo	}
957214734Srpaulo
958214734Srpaulo	return freqs;
959214734Srpaulo}
960214734Srpaulo
961214734Srpaulo
962214734Srpaulostatic int wpa_config_parse_scan_freq(const struct parse_data *data,
963214734Srpaulo				      struct wpa_ssid *ssid, int line,
964214734Srpaulo				      const char *value)
965214734Srpaulo{
966214734Srpaulo	int *freqs;
967214734Srpaulo
968214734Srpaulo	freqs = wpa_config_parse_freqs(data, ssid, line, value);
969214734Srpaulo	if (freqs == NULL)
970214734Srpaulo		return -1;
971214734Srpaulo	os_free(ssid->scan_freq);
972214734Srpaulo	ssid->scan_freq = freqs;
973214734Srpaulo
974214734Srpaulo	return 0;
975214734Srpaulo}
976214734Srpaulo
977214734Srpaulo
978214734Srpaulostatic int wpa_config_parse_freq_list(const struct parse_data *data,
979214734Srpaulo				      struct wpa_ssid *ssid, int line,
980214734Srpaulo				      const char *value)
981214734Srpaulo{
982214734Srpaulo	int *freqs;
983214734Srpaulo
984214734Srpaulo	freqs = wpa_config_parse_freqs(data, ssid, line, value);
985214734Srpaulo	if (freqs == NULL)
986214734Srpaulo		return -1;
987214734Srpaulo	os_free(ssid->freq_list);
988214734Srpaulo	ssid->freq_list = freqs;
989214734Srpaulo
990214734Srpaulo	return 0;
991214734Srpaulo}
992214734Srpaulo
993214734Srpaulo
994214734Srpaulo#ifndef NO_CONFIG_WRITE
995214734Srpaulostatic char * wpa_config_write_freqs(const struct parse_data *data,
996214734Srpaulo				     const int *freqs)
997214734Srpaulo{
998214734Srpaulo	char *buf, *pos, *end;
999214734Srpaulo	int i, ret;
1000214734Srpaulo	size_t count;
1001214734Srpaulo
1002214734Srpaulo	if (freqs == NULL)
1003214734Srpaulo		return NULL;
1004214734Srpaulo
1005214734Srpaulo	count = 0;
1006214734Srpaulo	for (i = 0; freqs[i]; i++)
1007214734Srpaulo		count++;
1008214734Srpaulo
1009214734Srpaulo	pos = buf = os_zalloc(10 * count + 1);
1010214734Srpaulo	if (buf == NULL)
1011214734Srpaulo		return NULL;
1012214734Srpaulo	end = buf + 10 * count + 1;
1013214734Srpaulo
1014214734Srpaulo	for (i = 0; freqs[i]; i++) {
1015214734Srpaulo		ret = os_snprintf(pos, end - pos, "%s%u",
1016214734Srpaulo				  i == 0 ? "" : " ", freqs[i]);
1017214734Srpaulo		if (ret < 0 || ret >= end - pos) {
1018214734Srpaulo			end[-1] = '\0';
1019214734Srpaulo			return buf;
1020214734Srpaulo		}
1021214734Srpaulo		pos += ret;
1022214734Srpaulo	}
1023214734Srpaulo
1024214734Srpaulo	return buf;
1025214734Srpaulo}
1026214734Srpaulo
1027214734Srpaulo
1028214734Srpaulostatic char * wpa_config_write_scan_freq(const struct parse_data *data,
1029214734Srpaulo					 struct wpa_ssid *ssid)
1030214734Srpaulo{
1031214734Srpaulo	return wpa_config_write_freqs(data, ssid->scan_freq);
1032214734Srpaulo}
1033214734Srpaulo
1034214734Srpaulo
1035214734Srpaulostatic char * wpa_config_write_freq_list(const struct parse_data *data,
1036214734Srpaulo					 struct wpa_ssid *ssid)
1037214734Srpaulo{
1038214734Srpaulo	return wpa_config_write_freqs(data, ssid->freq_list);
1039214734Srpaulo}
1040214734Srpaulo#endif /* NO_CONFIG_WRITE */
1041214734Srpaulo
1042214734Srpaulo
1043189251Ssam#ifdef IEEE8021X_EAPOL
1044189251Ssamstatic int wpa_config_parse_eap(const struct parse_data *data,
1045189251Ssam				struct wpa_ssid *ssid, int line,
1046189251Ssam				const char *value)
1047189251Ssam{
1048189251Ssam	int last, errors = 0;
1049189251Ssam	char *start, *end, *buf;
1050189251Ssam	struct eap_method_type *methods = NULL, *tmp;
1051189251Ssam	size_t num_methods = 0;
1052189251Ssam
1053189251Ssam	buf = os_strdup(value);
1054189251Ssam	if (buf == NULL)
1055189251Ssam		return -1;
1056189251Ssam	start = buf;
1057189251Ssam
1058189251Ssam	while (*start != '\0') {
1059189251Ssam		while (*start == ' ' || *start == '\t')
1060189251Ssam			start++;
1061189251Ssam		if (*start == '\0')
1062189251Ssam			break;
1063189251Ssam		end = start;
1064189251Ssam		while (*end != ' ' && *end != '\t' && *end != '\0')
1065189251Ssam			end++;
1066189251Ssam		last = *end == '\0';
1067189251Ssam		*end = '\0';
1068189251Ssam		tmp = methods;
1069252726Srpaulo		methods = os_realloc_array(methods, num_methods + 1,
1070252726Srpaulo					   sizeof(*methods));
1071189251Ssam		if (methods == NULL) {
1072189251Ssam			os_free(tmp);
1073189251Ssam			os_free(buf);
1074189251Ssam			return -1;
1075189251Ssam		}
1076189251Ssam		methods[num_methods].method = eap_peer_get_type(
1077189251Ssam			start, &methods[num_methods].vendor);
1078189251Ssam		if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
1079189251Ssam		    methods[num_methods].method == EAP_TYPE_NONE) {
1080189251Ssam			wpa_printf(MSG_ERROR, "Line %d: unknown EAP method "
1081189251Ssam				   "'%s'", line, start);
1082189251Ssam			wpa_printf(MSG_ERROR, "You may need to add support for"
1083189251Ssam				   " this EAP method during wpa_supplicant\n"
1084189251Ssam				   "build time configuration.\n"
1085189251Ssam				   "See README for more information.");
1086189251Ssam			errors++;
1087189251Ssam		} else if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
1088189251Ssam			   methods[num_methods].method == EAP_TYPE_LEAP)
1089189251Ssam			ssid->leap++;
1090189251Ssam		else
1091189251Ssam			ssid->non_leap++;
1092189251Ssam		num_methods++;
1093189251Ssam		if (last)
1094189251Ssam			break;
1095189251Ssam		start = end + 1;
1096189251Ssam	}
1097189251Ssam	os_free(buf);
1098189251Ssam
1099189251Ssam	tmp = methods;
1100252726Srpaulo	methods = os_realloc_array(methods, num_methods + 1, sizeof(*methods));
1101189251Ssam	if (methods == NULL) {
1102189251Ssam		os_free(tmp);
1103189251Ssam		return -1;
1104189251Ssam	}
1105189251Ssam	methods[num_methods].vendor = EAP_VENDOR_IETF;
1106189251Ssam	methods[num_methods].method = EAP_TYPE_NONE;
1107189251Ssam	num_methods++;
1108189251Ssam
1109189251Ssam	wpa_hexdump(MSG_MSGDUMP, "eap methods",
1110189251Ssam		    (u8 *) methods, num_methods * sizeof(*methods));
1111252726Srpaulo	os_free(ssid->eap.eap_methods);
1112189251Ssam	ssid->eap.eap_methods = methods;
1113189251Ssam	return errors ? -1 : 0;
1114189251Ssam}
1115189251Ssam
1116189251Ssam
1117189251Ssamstatic char * wpa_config_write_eap(const struct parse_data *data,
1118189251Ssam				   struct wpa_ssid *ssid)
1119189251Ssam{
1120189251Ssam	int i, ret;
1121189251Ssam	char *buf, *pos, *end;
1122189251Ssam	const struct eap_method_type *eap_methods = ssid->eap.eap_methods;
1123189251Ssam	const char *name;
1124189251Ssam
1125189251Ssam	if (eap_methods == NULL)
1126189251Ssam		return NULL;
1127189251Ssam
1128189251Ssam	pos = buf = os_zalloc(100);
1129189251Ssam	if (buf == NULL)
1130189251Ssam		return NULL;
1131189251Ssam	end = buf + 100;
1132189251Ssam
1133189251Ssam	for (i = 0; eap_methods[i].vendor != EAP_VENDOR_IETF ||
1134189251Ssam		     eap_methods[i].method != EAP_TYPE_NONE; i++) {
1135189251Ssam		name = eap_get_name(eap_methods[i].vendor,
1136189251Ssam				    eap_methods[i].method);
1137189251Ssam		if (name) {
1138189251Ssam			ret = os_snprintf(pos, end - pos, "%s%s",
1139189251Ssam					  pos == buf ? "" : " ", name);
1140189251Ssam			if (ret < 0 || ret >= end - pos)
1141189251Ssam				break;
1142189251Ssam			pos += ret;
1143189251Ssam		}
1144189251Ssam	}
1145189251Ssam
1146189251Ssam	end[-1] = '\0';
1147189251Ssam
1148189251Ssam	return buf;
1149189251Ssam}
1150189251Ssam
1151189251Ssam
1152189251Ssamstatic int wpa_config_parse_password(const struct parse_data *data,
1153189251Ssam				     struct wpa_ssid *ssid, int line,
1154189251Ssam				     const char *value)
1155189251Ssam{
1156189251Ssam	u8 *hash;
1157189251Ssam
1158189251Ssam	if (os_strcmp(value, "NULL") == 0) {
1159189251Ssam		wpa_printf(MSG_DEBUG, "Unset configuration string 'password'");
1160189251Ssam		os_free(ssid->eap.password);
1161189251Ssam		ssid->eap.password = NULL;
1162189251Ssam		ssid->eap.password_len = 0;
1163189251Ssam		return 0;
1164189251Ssam	}
1165189251Ssam
1166252726Srpaulo#ifdef CONFIG_EXT_PASSWORD
1167252726Srpaulo	if (os_strncmp(value, "ext:", 4) == 0) {
1168252726Srpaulo		char *name = os_strdup(value + 4);
1169252726Srpaulo		if (name == NULL)
1170252726Srpaulo			return -1;
1171252726Srpaulo		os_free(ssid->eap.password);
1172252726Srpaulo		ssid->eap.password = (u8 *) name;
1173252726Srpaulo		ssid->eap.password_len = os_strlen(name);
1174252726Srpaulo		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
1175252726Srpaulo		ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_PASSWORD;
1176252726Srpaulo		return 0;
1177252726Srpaulo	}
1178252726Srpaulo#endif /* CONFIG_EXT_PASSWORD */
1179252726Srpaulo
1180189251Ssam	if (os_strncmp(value, "hash:", 5) != 0) {
1181189251Ssam		char *tmp;
1182189251Ssam		size_t res_len;
1183189251Ssam
1184189251Ssam		tmp = wpa_config_parse_string(value, &res_len);
1185189251Ssam		if (tmp == NULL) {
1186189251Ssam			wpa_printf(MSG_ERROR, "Line %d: failed to parse "
1187189251Ssam				   "password.", line);
1188189251Ssam			return -1;
1189189251Ssam		}
1190189251Ssam		wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
1191189251Ssam				      (u8 *) tmp, res_len);
1192189251Ssam
1193189251Ssam		os_free(ssid->eap.password);
1194189251Ssam		ssid->eap.password = (u8 *) tmp;
1195189251Ssam		ssid->eap.password_len = res_len;
1196189251Ssam		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
1197252726Srpaulo		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
1198189251Ssam
1199189251Ssam		return 0;
1200189251Ssam	}
1201189251Ssam
1202189251Ssam
1203189251Ssam	/* NtPasswordHash: hash:<32 hex digits> */
1204189251Ssam	if (os_strlen(value + 5) != 2 * 16) {
1205189251Ssam		wpa_printf(MSG_ERROR, "Line %d: Invalid password hash length "
1206189251Ssam			   "(expected 32 hex digits)", line);
1207189251Ssam		return -1;
1208189251Ssam	}
1209189251Ssam
1210189251Ssam	hash = os_malloc(16);
1211189251Ssam	if (hash == NULL)
1212189251Ssam		return -1;
1213189251Ssam
1214189251Ssam	if (hexstr2bin(value + 5, hash, 16)) {
1215189251Ssam		os_free(hash);
1216189251Ssam		wpa_printf(MSG_ERROR, "Line %d: Invalid password hash", line);
1217189251Ssam		return -1;
1218189251Ssam	}
1219189251Ssam
1220189251Ssam	wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
1221189251Ssam
1222189251Ssam	os_free(ssid->eap.password);
1223189251Ssam	ssid->eap.password = hash;
1224189251Ssam	ssid->eap.password_len = 16;
1225189251Ssam	ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
1226252726Srpaulo	ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
1227189251Ssam
1228189251Ssam	return 0;
1229189251Ssam}
1230189251Ssam
1231189251Ssam
1232189251Ssamstatic char * wpa_config_write_password(const struct parse_data *data,
1233189251Ssam					struct wpa_ssid *ssid)
1234189251Ssam{
1235189251Ssam	char *buf;
1236189251Ssam
1237189251Ssam	if (ssid->eap.password == NULL)
1238189251Ssam		return NULL;
1239189251Ssam
1240252726Srpaulo#ifdef CONFIG_EXT_PASSWORD
1241252726Srpaulo	if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
1242252726Srpaulo		buf = os_zalloc(4 + ssid->eap.password_len + 1);
1243252726Srpaulo		if (buf == NULL)
1244252726Srpaulo			return NULL;
1245252726Srpaulo		os_memcpy(buf, "ext:", 4);
1246252726Srpaulo		os_memcpy(buf + 4, ssid->eap.password, ssid->eap.password_len);
1247252726Srpaulo		return buf;
1248252726Srpaulo	}
1249252726Srpaulo#endif /* CONFIG_EXT_PASSWORD */
1250252726Srpaulo
1251189251Ssam	if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) {
1252189251Ssam		return wpa_config_write_string(
1253189251Ssam			ssid->eap.password, ssid->eap.password_len);
1254189251Ssam	}
1255189251Ssam
1256189251Ssam	buf = os_malloc(5 + 32 + 1);
1257189251Ssam	if (buf == NULL)
1258189251Ssam		return NULL;
1259189251Ssam
1260189251Ssam	os_memcpy(buf, "hash:", 5);
1261189251Ssam	wpa_snprintf_hex(buf + 5, 32 + 1, ssid->eap.password, 16);
1262189251Ssam
1263189251Ssam	return buf;
1264189251Ssam}
1265189251Ssam#endif /* IEEE8021X_EAPOL */
1266189251Ssam
1267189251Ssam
1268189251Ssamstatic int wpa_config_parse_wep_key(u8 *key, size_t *len, int line,
1269189251Ssam				    const char *value, int idx)
1270189251Ssam{
1271189251Ssam	char *buf, title[20];
1272189251Ssam	int res;
1273189251Ssam
1274189251Ssam	buf = wpa_config_parse_string(value, len);
1275189251Ssam	if (buf == NULL) {
1276189251Ssam		wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key %d '%s'.",
1277189251Ssam			   line, idx, value);
1278189251Ssam		return -1;
1279189251Ssam	}
1280189251Ssam	if (*len > MAX_WEP_KEY_LEN) {
1281189251Ssam		wpa_printf(MSG_ERROR, "Line %d: Too long WEP key %d '%s'.",
1282189251Ssam			   line, idx, value);
1283189251Ssam		os_free(buf);
1284189251Ssam		return -1;
1285189251Ssam	}
1286252726Srpaulo	if (*len && *len != 5 && *len != 13 && *len != 16) {
1287252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key length %u - "
1288252726Srpaulo			   "this network block will be ignored",
1289252726Srpaulo			   line, (unsigned int) *len);
1290252726Srpaulo	}
1291189251Ssam	os_memcpy(key, buf, *len);
1292189251Ssam	os_free(buf);
1293189251Ssam	res = os_snprintf(title, sizeof(title), "wep_key%d", idx);
1294189251Ssam	if (res >= 0 && (size_t) res < sizeof(title))
1295189251Ssam		wpa_hexdump_key(MSG_MSGDUMP, title, key, *len);
1296189251Ssam	return 0;
1297189251Ssam}
1298189251Ssam
1299189251Ssam
1300189251Ssamstatic int wpa_config_parse_wep_key0(const struct parse_data *data,
1301189251Ssam				     struct wpa_ssid *ssid, int line,
1302189251Ssam				     const char *value)
1303189251Ssam{
1304189251Ssam	return wpa_config_parse_wep_key(ssid->wep_key[0],
1305189251Ssam					&ssid->wep_key_len[0], line,
1306189251Ssam					value, 0);
1307189251Ssam}
1308189251Ssam
1309189251Ssam
1310189251Ssamstatic int wpa_config_parse_wep_key1(const struct parse_data *data,
1311189251Ssam				     struct wpa_ssid *ssid, int line,
1312189251Ssam				     const char *value)
1313189251Ssam{
1314189251Ssam	return wpa_config_parse_wep_key(ssid->wep_key[1],
1315189251Ssam					&ssid->wep_key_len[1], line,
1316189251Ssam					value, 1);
1317189251Ssam}
1318189251Ssam
1319189251Ssam
1320189251Ssamstatic int wpa_config_parse_wep_key2(const struct parse_data *data,
1321189251Ssam				     struct wpa_ssid *ssid, int line,
1322189251Ssam				     const char *value)
1323189251Ssam{
1324189251Ssam	return wpa_config_parse_wep_key(ssid->wep_key[2],
1325189251Ssam					&ssid->wep_key_len[2], line,
1326189251Ssam					value, 2);
1327189251Ssam}
1328189251Ssam
1329189251Ssam
1330189251Ssamstatic int wpa_config_parse_wep_key3(const struct parse_data *data,
1331189251Ssam				     struct wpa_ssid *ssid, int line,
1332189251Ssam				     const char *value)
1333189251Ssam{
1334189251Ssam	return wpa_config_parse_wep_key(ssid->wep_key[3],
1335189251Ssam					&ssid->wep_key_len[3], line,
1336189251Ssam					value, 3);
1337189251Ssam}
1338189251Ssam
1339189251Ssam
1340189251Ssam#ifndef NO_CONFIG_WRITE
1341189251Ssamstatic char * wpa_config_write_wep_key(struct wpa_ssid *ssid, int idx)
1342189251Ssam{
1343189251Ssam	if (ssid->wep_key_len[idx] == 0)
1344189251Ssam		return NULL;
1345189251Ssam	return wpa_config_write_string(ssid->wep_key[idx],
1346189251Ssam				       ssid->wep_key_len[idx]);
1347189251Ssam}
1348189251Ssam
1349189251Ssam
1350189251Ssamstatic char * wpa_config_write_wep_key0(const struct parse_data *data,
1351189251Ssam					struct wpa_ssid *ssid)
1352189251Ssam{
1353189251Ssam	return wpa_config_write_wep_key(ssid, 0);
1354189251Ssam}
1355189251Ssam
1356189251Ssam
1357189251Ssamstatic char * wpa_config_write_wep_key1(const struct parse_data *data,
1358189251Ssam					struct wpa_ssid *ssid)
1359189251Ssam{
1360189251Ssam	return wpa_config_write_wep_key(ssid, 1);
1361189251Ssam}
1362189251Ssam
1363189251Ssam
1364189251Ssamstatic char * wpa_config_write_wep_key2(const struct parse_data *data,
1365189251Ssam					struct wpa_ssid *ssid)
1366189251Ssam{
1367189251Ssam	return wpa_config_write_wep_key(ssid, 2);
1368189251Ssam}
1369189251Ssam
1370189251Ssam
1371189251Ssamstatic char * wpa_config_write_wep_key3(const struct parse_data *data,
1372189251Ssam					struct wpa_ssid *ssid)
1373189251Ssam{
1374189251Ssam	return wpa_config_write_wep_key(ssid, 3);
1375189251Ssam}
1376189251Ssam#endif /* NO_CONFIG_WRITE */
1377189251Ssam
1378189251Ssam
1379252726Srpaulo#ifdef CONFIG_P2P
1380252726Srpaulo
1381252726Srpaulostatic int wpa_config_parse_p2p_client_list(const struct parse_data *data,
1382252726Srpaulo					    struct wpa_ssid *ssid, int line,
1383252726Srpaulo					    const char *value)
1384252726Srpaulo{
1385252726Srpaulo	const char *pos;
1386252726Srpaulo	u8 *buf, *n, addr[ETH_ALEN];
1387252726Srpaulo	size_t count;
1388252726Srpaulo
1389252726Srpaulo	buf = NULL;
1390252726Srpaulo	count = 0;
1391252726Srpaulo
1392252726Srpaulo	pos = value;
1393252726Srpaulo	while (pos && *pos) {
1394252726Srpaulo		while (*pos == ' ')
1395252726Srpaulo			pos++;
1396252726Srpaulo
1397252726Srpaulo		if (hwaddr_aton(pos, addr)) {
1398252726Srpaulo			if (count == 0) {
1399252726Srpaulo				wpa_printf(MSG_ERROR, "Line %d: Invalid "
1400252726Srpaulo					   "p2p_client_list address '%s'.",
1401252726Srpaulo					   line, value);
1402252726Srpaulo				os_free(buf);
1403252726Srpaulo				return -1;
1404252726Srpaulo			}
1405252726Srpaulo			/* continue anyway since this could have been from a
1406252726Srpaulo			 * truncated configuration file line */
1407252726Srpaulo			wpa_printf(MSG_INFO, "Line %d: Ignore likely "
1408252726Srpaulo				   "truncated p2p_client_list address '%s'",
1409252726Srpaulo				   line, pos);
1410252726Srpaulo		} else {
1411252726Srpaulo			n = os_realloc_array(buf, count + 1, ETH_ALEN);
1412252726Srpaulo			if (n == NULL) {
1413252726Srpaulo				os_free(buf);
1414252726Srpaulo				return -1;
1415252726Srpaulo			}
1416252726Srpaulo			buf = n;
1417252726Srpaulo			os_memmove(buf + ETH_ALEN, buf, count * ETH_ALEN);
1418252726Srpaulo			os_memcpy(buf, addr, ETH_ALEN);
1419252726Srpaulo			count++;
1420252726Srpaulo			wpa_hexdump(MSG_MSGDUMP, "p2p_client_list",
1421252726Srpaulo				    addr, ETH_ALEN);
1422252726Srpaulo		}
1423252726Srpaulo
1424252726Srpaulo		pos = os_strchr(pos, ' ');
1425252726Srpaulo	}
1426252726Srpaulo
1427252726Srpaulo	os_free(ssid->p2p_client_list);
1428252726Srpaulo	ssid->p2p_client_list = buf;
1429252726Srpaulo	ssid->num_p2p_clients = count;
1430252726Srpaulo
1431252726Srpaulo	return 0;
1432252726Srpaulo}
1433252726Srpaulo
1434252726Srpaulo
1435252726Srpaulo#ifndef NO_CONFIG_WRITE
1436252726Srpaulostatic char * wpa_config_write_p2p_client_list(const struct parse_data *data,
1437252726Srpaulo					       struct wpa_ssid *ssid)
1438252726Srpaulo{
1439252726Srpaulo	char *value, *end, *pos;
1440252726Srpaulo	int res;
1441252726Srpaulo	size_t i;
1442252726Srpaulo
1443252726Srpaulo	if (ssid->p2p_client_list == NULL || ssid->num_p2p_clients == 0)
1444252726Srpaulo		return NULL;
1445252726Srpaulo
1446252726Srpaulo	value = os_malloc(20 * ssid->num_p2p_clients);
1447252726Srpaulo	if (value == NULL)
1448252726Srpaulo		return NULL;
1449252726Srpaulo	pos = value;
1450252726Srpaulo	end = value + 20 * ssid->num_p2p_clients;
1451252726Srpaulo
1452252726Srpaulo	for (i = ssid->num_p2p_clients; i > 0; i--) {
1453252726Srpaulo		res = os_snprintf(pos, end - pos, MACSTR " ",
1454252726Srpaulo				  MAC2STR(ssid->p2p_client_list +
1455252726Srpaulo					  (i - 1) * ETH_ALEN));
1456252726Srpaulo		if (res < 0 || res >= end - pos) {
1457252726Srpaulo			os_free(value);
1458252726Srpaulo			return NULL;
1459252726Srpaulo		}
1460252726Srpaulo		pos += res;
1461252726Srpaulo	}
1462252726Srpaulo
1463252726Srpaulo	if (pos > value)
1464252726Srpaulo		pos[-1] = '\0';
1465252726Srpaulo
1466252726Srpaulo	return value;
1467252726Srpaulo}
1468252726Srpaulo#endif /* NO_CONFIG_WRITE */
1469252726Srpaulo
1470252726Srpaulo#endif /* CONFIG_P2P */
1471252726Srpaulo
1472189251Ssam/* Helper macros for network block parser */
1473189251Ssam
1474189251Ssam#ifdef OFFSET
1475189251Ssam#undef OFFSET
1476189251Ssam#endif /* OFFSET */
1477189251Ssam/* OFFSET: Get offset of a variable within the wpa_ssid structure */
1478189251Ssam#define OFFSET(v) ((void *) &((struct wpa_ssid *) 0)->v)
1479189251Ssam
1480189251Ssam/* STR: Define a string variable for an ASCII string; f = field name */
1481189251Ssam#ifdef NO_CONFIG_WRITE
1482189251Ssam#define _STR(f) #f, wpa_config_parse_str, OFFSET(f)
1483189251Ssam#define _STRe(f) #f, wpa_config_parse_str, OFFSET(eap.f)
1484189251Ssam#else /* NO_CONFIG_WRITE */
1485189251Ssam#define _STR(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(f)
1486189251Ssam#define _STRe(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(eap.f)
1487189251Ssam#endif /* NO_CONFIG_WRITE */
1488189251Ssam#define STR(f) _STR(f), NULL, NULL, NULL, 0
1489189251Ssam#define STRe(f) _STRe(f), NULL, NULL, NULL, 0
1490189251Ssam#define STR_KEY(f) _STR(f), NULL, NULL, NULL, 1
1491189251Ssam#define STR_KEYe(f) _STRe(f), NULL, NULL, NULL, 1
1492189251Ssam
1493189251Ssam/* STR_LEN: Define a string variable with a separate variable for storing the
1494189251Ssam * data length. Unlike STR(), this can be used to store arbitrary binary data
1495189251Ssam * (i.e., even nul termination character). */
1496189251Ssam#define _STR_LEN(f) _STR(f), OFFSET(f ## _len)
1497189251Ssam#define _STR_LENe(f) _STRe(f), OFFSET(eap.f ## _len)
1498189251Ssam#define STR_LEN(f) _STR_LEN(f), NULL, NULL, 0
1499189251Ssam#define STR_LENe(f) _STR_LENe(f), NULL, NULL, 0
1500189251Ssam#define STR_LEN_KEY(f) _STR_LEN(f), NULL, NULL, 1
1501189251Ssam
1502189251Ssam/* STR_RANGE: Like STR_LEN(), but with minimum and maximum allowed length
1503189251Ssam * explicitly specified. */
1504189251Ssam#define _STR_RANGE(f, min, max) _STR_LEN(f), (void *) (min), (void *) (max)
1505189251Ssam#define STR_RANGE(f, min, max) _STR_RANGE(f, min, max), 0
1506189251Ssam#define STR_RANGE_KEY(f, min, max) _STR_RANGE(f, min, max), 1
1507189251Ssam
1508189251Ssam#ifdef NO_CONFIG_WRITE
1509189251Ssam#define _INT(f) #f, wpa_config_parse_int, OFFSET(f), (void *) 0
1510189251Ssam#define _INTe(f) #f, wpa_config_parse_int, OFFSET(eap.f), (void *) 0
1511189251Ssam#else /* NO_CONFIG_WRITE */
1512189251Ssam#define _INT(f) #f, wpa_config_parse_int, wpa_config_write_int, \
1513189251Ssam	OFFSET(f), (void *) 0
1514189251Ssam#define _INTe(f) #f, wpa_config_parse_int, wpa_config_write_int, \
1515189251Ssam	OFFSET(eap.f), (void *) 0
1516189251Ssam#endif /* NO_CONFIG_WRITE */
1517189251Ssam
1518189251Ssam/* INT: Define an integer variable */
1519189251Ssam#define INT(f) _INT(f), NULL, NULL, 0
1520189251Ssam#define INTe(f) _INTe(f), NULL, NULL, 0
1521189251Ssam
1522189251Ssam/* INT_RANGE: Define an integer variable with allowed value range */
1523189251Ssam#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0
1524189251Ssam
1525189251Ssam/* FUNC: Define a configuration variable that uses a custom function for
1526189251Ssam * parsing and writing the value. */
1527189251Ssam#ifdef NO_CONFIG_WRITE
1528189251Ssam#define _FUNC(f) #f, wpa_config_parse_ ## f, NULL, NULL, NULL, NULL
1529189251Ssam#else /* NO_CONFIG_WRITE */
1530189251Ssam#define _FUNC(f) #f, wpa_config_parse_ ## f, wpa_config_write_ ## f, \
1531189251Ssam	NULL, NULL, NULL, NULL
1532189251Ssam#endif /* NO_CONFIG_WRITE */
1533189251Ssam#define FUNC(f) _FUNC(f), 0
1534189251Ssam#define FUNC_KEY(f) _FUNC(f), 1
1535189251Ssam
1536189251Ssam/*
1537189251Ssam * Table of network configuration variables. This table is used to parse each
1538189251Ssam * network configuration variable, e.g., each line in wpa_supplicant.conf file
1539189251Ssam * that is inside a network block.
1540189251Ssam *
1541189251Ssam * This table is generated using the helper macros defined above and with
1542189251Ssam * generous help from the C pre-processor. The field name is stored as a string
1543189251Ssam * into .name and for STR and INT types, the offset of the target buffer within
1544189251Ssam * struct wpa_ssid is stored in .param1. .param2 (if not NULL) is similar
1545189251Ssam * offset to the field containing the length of the configuration variable.
1546189251Ssam * .param3 and .param4 can be used to mark the allowed range (length for STR
1547189251Ssam * and value for INT).
1548189251Ssam *
1549189251Ssam * For each configuration line in wpa_supplicant.conf, the parser goes through
1550189251Ssam * this table and select the entry that matches with the field name. The parser
1551189251Ssam * function (.parser) is then called to parse the actual value of the field.
1552189251Ssam *
1553189251Ssam * This kind of mechanism makes it easy to add new configuration parameters,
1554189251Ssam * since only one line needs to be added into this table and into the
1555189251Ssam * struct wpa_ssid definition if the new variable is either a string or
1556189251Ssam * integer. More complex types will need to use their own parser and writer
1557189251Ssam * functions.
1558189251Ssam */
1559189251Ssamstatic const struct parse_data ssid_fields[] = {
1560189251Ssam	{ STR_RANGE(ssid, 0, MAX_SSID_LEN) },
1561189251Ssam	{ INT_RANGE(scan_ssid, 0, 1) },
1562189251Ssam	{ FUNC(bssid) },
1563189251Ssam	{ FUNC_KEY(psk) },
1564189251Ssam	{ FUNC(proto) },
1565189251Ssam	{ FUNC(key_mgmt) },
1566252726Srpaulo	{ INT(bg_scan_period) },
1567189251Ssam	{ FUNC(pairwise) },
1568189251Ssam	{ FUNC(group) },
1569189251Ssam	{ FUNC(auth_alg) },
1570214734Srpaulo	{ FUNC(scan_freq) },
1571214734Srpaulo	{ FUNC(freq_list) },
1572189251Ssam#ifdef IEEE8021X_EAPOL
1573189251Ssam	{ FUNC(eap) },
1574189251Ssam	{ STR_LENe(identity) },
1575189251Ssam	{ STR_LENe(anonymous_identity) },
1576189251Ssam	{ FUNC_KEY(password) },
1577189251Ssam	{ STRe(ca_cert) },
1578189251Ssam	{ STRe(ca_path) },
1579189251Ssam	{ STRe(client_cert) },
1580189251Ssam	{ STRe(private_key) },
1581189251Ssam	{ STR_KEYe(private_key_passwd) },
1582189251Ssam	{ STRe(dh_file) },
1583189251Ssam	{ STRe(subject_match) },
1584189251Ssam	{ STRe(altsubject_match) },
1585189251Ssam	{ STRe(ca_cert2) },
1586189251Ssam	{ STRe(ca_path2) },
1587189251Ssam	{ STRe(client_cert2) },
1588189251Ssam	{ STRe(private_key2) },
1589189251Ssam	{ STR_KEYe(private_key2_passwd) },
1590189251Ssam	{ STRe(dh_file2) },
1591189251Ssam	{ STRe(subject_match2) },
1592189251Ssam	{ STRe(altsubject_match2) },
1593189251Ssam	{ STRe(phase1) },
1594189251Ssam	{ STRe(phase2) },
1595189251Ssam	{ STRe(pcsc) },
1596189251Ssam	{ STR_KEYe(pin) },
1597189251Ssam	{ STRe(engine_id) },
1598189251Ssam	{ STRe(key_id) },
1599189251Ssam	{ STRe(cert_id) },
1600189251Ssam	{ STRe(ca_cert_id) },
1601189251Ssam	{ STR_KEYe(pin2) },
1602189251Ssam	{ STRe(engine2_id) },
1603189251Ssam	{ STRe(key2_id) },
1604189251Ssam	{ STRe(cert2_id) },
1605189251Ssam	{ STRe(ca_cert2_id) },
1606189251Ssam	{ INTe(engine) },
1607189251Ssam	{ INTe(engine2) },
1608189251Ssam	{ INT(eapol_flags) },
1609189251Ssam#endif /* IEEE8021X_EAPOL */
1610189251Ssam	{ FUNC_KEY(wep_key0) },
1611189251Ssam	{ FUNC_KEY(wep_key1) },
1612189251Ssam	{ FUNC_KEY(wep_key2) },
1613189251Ssam	{ FUNC_KEY(wep_key3) },
1614189251Ssam	{ INT(wep_tx_keyidx) },
1615189251Ssam	{ INT(priority) },
1616189251Ssam#ifdef IEEE8021X_EAPOL
1617189251Ssam	{ INT(eap_workaround) },
1618189251Ssam	{ STRe(pac_file) },
1619189251Ssam	{ INTe(fragment_size) },
1620189251Ssam#endif /* IEEE8021X_EAPOL */
1621252726Srpaulo	{ INT_RANGE(mode, 0, 4) },
1622189251Ssam	{ INT_RANGE(proactive_key_caching, 0, 1) },
1623252726Srpaulo	{ INT_RANGE(disabled, 0, 2) },
1624189251Ssam	{ STR(id_str) },
1625189251Ssam#ifdef CONFIG_IEEE80211W
1626189251Ssam	{ INT_RANGE(ieee80211w, 0, 2) },
1627189251Ssam#endif /* CONFIG_IEEE80211W */
1628189251Ssam	{ INT_RANGE(peerkey, 0, 1) },
1629189251Ssam	{ INT_RANGE(mixed_cell, 0, 1) },
1630252726Srpaulo	{ INT_RANGE(frequency, 0, 65000) },
1631214734Srpaulo	{ INT(wpa_ptk_rekey) },
1632214734Srpaulo	{ STR(bgscan) },
1633252726Srpaulo	{ INT_RANGE(ignore_broadcast_ssid, 0, 2) },
1634252726Srpaulo#ifdef CONFIG_P2P
1635252726Srpaulo	{ FUNC(p2p_client_list) },
1636252726Srpaulo#endif /* CONFIG_P2P */
1637252726Srpaulo#ifdef CONFIG_HT_OVERRIDES
1638252726Srpaulo	{ INT_RANGE(disable_ht, 0, 1) },
1639252726Srpaulo	{ INT_RANGE(disable_ht40, -1, 1) },
1640252726Srpaulo	{ INT_RANGE(disable_sgi, 0, 1) },
1641252726Srpaulo	{ INT_RANGE(disable_max_amsdu, -1, 1) },
1642252726Srpaulo	{ INT_RANGE(ampdu_factor, -1, 3) },
1643252726Srpaulo	{ INT_RANGE(ampdu_density, -1, 7) },
1644252726Srpaulo	{ STR(ht_mcs) },
1645252726Srpaulo#endif /* CONFIG_HT_OVERRIDES */
1646252726Srpaulo	{ INT(ap_max_inactivity) },
1647252726Srpaulo	{ INT(dtim_period) },
1648189251Ssam};
1649189251Ssam
1650189251Ssam#undef OFFSET
1651189251Ssam#undef _STR
1652189251Ssam#undef STR
1653189251Ssam#undef STR_KEY
1654189251Ssam#undef _STR_LEN
1655189251Ssam#undef STR_LEN
1656189251Ssam#undef STR_LEN_KEY
1657189251Ssam#undef _STR_RANGE
1658189251Ssam#undef STR_RANGE
1659189251Ssam#undef STR_RANGE_KEY
1660189251Ssam#undef _INT
1661189251Ssam#undef INT
1662189251Ssam#undef INT_RANGE
1663189251Ssam#undef _FUNC
1664189251Ssam#undef FUNC
1665189251Ssam#undef FUNC_KEY
1666189251Ssam#define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0]))
1667189251Ssam
1668189251Ssam
1669189251Ssam/**
1670189251Ssam * wpa_config_add_prio_network - Add a network to priority lists
1671189251Ssam * @config: Configuration data from wpa_config_read()
1672189251Ssam * @ssid: Pointer to the network configuration to be added to the list
1673189251Ssam * Returns: 0 on success, -1 on failure
1674189251Ssam *
1675189251Ssam * This function is used to add a network block to the priority list of
1676189251Ssam * networks. This must be called for each network when reading in the full
1677189251Ssam * configuration. In addition, this can be used indirectly when updating
1678189251Ssam * priorities by calling wpa_config_update_prio_list().
1679189251Ssam */
1680189251Ssamint wpa_config_add_prio_network(struct wpa_config *config,
1681189251Ssam				struct wpa_ssid *ssid)
1682189251Ssam{
1683189251Ssam	int prio;
1684189251Ssam	struct wpa_ssid *prev, **nlist;
1685189251Ssam
1686189251Ssam	/*
1687189251Ssam	 * Add to an existing priority list if one is available for the
1688189251Ssam	 * configured priority level for this network.
1689189251Ssam	 */
1690189251Ssam	for (prio = 0; prio < config->num_prio; prio++) {
1691189251Ssam		prev = config->pssid[prio];
1692189251Ssam		if (prev->priority == ssid->priority) {
1693189251Ssam			while (prev->pnext)
1694189251Ssam				prev = prev->pnext;
1695189251Ssam			prev->pnext = ssid;
1696189251Ssam			return 0;
1697189251Ssam		}
1698189251Ssam	}
1699189251Ssam
1700189251Ssam	/* First network for this priority - add a new priority list */
1701252726Srpaulo	nlist = os_realloc_array(config->pssid, config->num_prio + 1,
1702252726Srpaulo				 sizeof(struct wpa_ssid *));
1703189251Ssam	if (nlist == NULL)
1704189251Ssam		return -1;
1705189251Ssam
1706189251Ssam	for (prio = 0; prio < config->num_prio; prio++) {
1707252726Srpaulo		if (nlist[prio]->priority < ssid->priority) {
1708252726Srpaulo			os_memmove(&nlist[prio + 1], &nlist[prio],
1709252726Srpaulo				   (config->num_prio - prio) *
1710252726Srpaulo				   sizeof(struct wpa_ssid *));
1711189251Ssam			break;
1712252726Srpaulo		}
1713189251Ssam	}
1714189251Ssam
1715189251Ssam	nlist[prio] = ssid;
1716189251Ssam	config->num_prio++;
1717189251Ssam	config->pssid = nlist;
1718189251Ssam
1719189251Ssam	return 0;
1720189251Ssam}
1721189251Ssam
1722189251Ssam
1723189251Ssam/**
1724189251Ssam * wpa_config_update_prio_list - Update network priority list
1725189251Ssam * @config: Configuration data from wpa_config_read()
1726189251Ssam * Returns: 0 on success, -1 on failure
1727189251Ssam *
1728189251Ssam * This function is called to update the priority list of networks in the
1729189251Ssam * configuration when a network is being added or removed. This is also called
1730189251Ssam * if a priority for a network is changed.
1731189251Ssam */
1732214734Srpauloint wpa_config_update_prio_list(struct wpa_config *config)
1733189251Ssam{
1734189251Ssam	struct wpa_ssid *ssid;
1735189251Ssam	int ret = 0;
1736189251Ssam
1737189251Ssam	os_free(config->pssid);
1738189251Ssam	config->pssid = NULL;
1739189251Ssam	config->num_prio = 0;
1740189251Ssam
1741189251Ssam	ssid = config->ssid;
1742189251Ssam	while (ssid) {
1743189251Ssam		ssid->pnext = NULL;
1744189251Ssam		if (wpa_config_add_prio_network(config, ssid) < 0)
1745189251Ssam			ret = -1;
1746189251Ssam		ssid = ssid->next;
1747189251Ssam	}
1748189251Ssam
1749189251Ssam	return ret;
1750189251Ssam}
1751189251Ssam
1752189251Ssam
1753189251Ssam#ifdef IEEE8021X_EAPOL
1754189251Ssamstatic void eap_peer_config_free(struct eap_peer_config *eap)
1755189251Ssam{
1756189251Ssam	os_free(eap->eap_methods);
1757189251Ssam	os_free(eap->identity);
1758189251Ssam	os_free(eap->anonymous_identity);
1759189251Ssam	os_free(eap->password);
1760189251Ssam	os_free(eap->ca_cert);
1761189251Ssam	os_free(eap->ca_path);
1762189251Ssam	os_free(eap->client_cert);
1763189251Ssam	os_free(eap->private_key);
1764189251Ssam	os_free(eap->private_key_passwd);
1765189251Ssam	os_free(eap->dh_file);
1766189251Ssam	os_free(eap->subject_match);
1767189251Ssam	os_free(eap->altsubject_match);
1768189251Ssam	os_free(eap->ca_cert2);
1769189251Ssam	os_free(eap->ca_path2);
1770189251Ssam	os_free(eap->client_cert2);
1771189251Ssam	os_free(eap->private_key2);
1772189251Ssam	os_free(eap->private_key2_passwd);
1773189251Ssam	os_free(eap->dh_file2);
1774189251Ssam	os_free(eap->subject_match2);
1775189251Ssam	os_free(eap->altsubject_match2);
1776189251Ssam	os_free(eap->phase1);
1777189251Ssam	os_free(eap->phase2);
1778189251Ssam	os_free(eap->pcsc);
1779189251Ssam	os_free(eap->pin);
1780189251Ssam	os_free(eap->engine_id);
1781189251Ssam	os_free(eap->key_id);
1782189251Ssam	os_free(eap->cert_id);
1783189251Ssam	os_free(eap->ca_cert_id);
1784189251Ssam	os_free(eap->key2_id);
1785189251Ssam	os_free(eap->cert2_id);
1786189251Ssam	os_free(eap->ca_cert2_id);
1787189251Ssam	os_free(eap->pin2);
1788189251Ssam	os_free(eap->engine2_id);
1789189251Ssam	os_free(eap->otp);
1790189251Ssam	os_free(eap->pending_req_otp);
1791189251Ssam	os_free(eap->pac_file);
1792189251Ssam	os_free(eap->new_password);
1793189251Ssam}
1794189251Ssam#endif /* IEEE8021X_EAPOL */
1795189251Ssam
1796189251Ssam
1797189251Ssam/**
1798189251Ssam * wpa_config_free_ssid - Free network/ssid configuration data
1799189251Ssam * @ssid: Configuration data for the network
1800189251Ssam *
1801189251Ssam * This function frees all resources allocated for the network configuration
1802189251Ssam * data.
1803189251Ssam */
1804189251Ssamvoid wpa_config_free_ssid(struct wpa_ssid *ssid)
1805189251Ssam{
1806189251Ssam	os_free(ssid->ssid);
1807189251Ssam	os_free(ssid->passphrase);
1808252726Srpaulo	os_free(ssid->ext_psk);
1809189251Ssam#ifdef IEEE8021X_EAPOL
1810189251Ssam	eap_peer_config_free(&ssid->eap);
1811189251Ssam#endif /* IEEE8021X_EAPOL */
1812189251Ssam	os_free(ssid->id_str);
1813214734Srpaulo	os_free(ssid->scan_freq);
1814214734Srpaulo	os_free(ssid->freq_list);
1815214734Srpaulo	os_free(ssid->bgscan);
1816252726Srpaulo	os_free(ssid->p2p_client_list);
1817252726Srpaulo#ifdef CONFIG_HT_OVERRIDES
1818252726Srpaulo	os_free(ssid->ht_mcs);
1819252726Srpaulo#endif /* CONFIG_HT_OVERRIDES */
1820189251Ssam	os_free(ssid);
1821189251Ssam}
1822189251Ssam
1823189251Ssam
1824252726Srpaulovoid wpa_config_free_cred(struct wpa_cred *cred)
1825252726Srpaulo{
1826252726Srpaulo	os_free(cred->realm);
1827252726Srpaulo	os_free(cred->username);
1828252726Srpaulo	os_free(cred->password);
1829252726Srpaulo	os_free(cred->ca_cert);
1830252726Srpaulo	os_free(cred->client_cert);
1831252726Srpaulo	os_free(cred->private_key);
1832252726Srpaulo	os_free(cred->private_key_passwd);
1833252726Srpaulo	os_free(cred->imsi);
1834252726Srpaulo	os_free(cred->milenage);
1835252726Srpaulo	os_free(cred->domain);
1836252726Srpaulo	os_free(cred->eap_method);
1837252726Srpaulo	os_free(cred->phase1);
1838252726Srpaulo	os_free(cred->phase2);
1839252726Srpaulo	os_free(cred->excluded_ssid);
1840252726Srpaulo	os_free(cred);
1841252726Srpaulo}
1842252726Srpaulo
1843252726Srpaulo
1844189251Ssam/**
1845189251Ssam * wpa_config_free - Free configuration data
1846189251Ssam * @config: Configuration data from wpa_config_read()
1847189251Ssam *
1848189251Ssam * This function frees all resources allocated for the configuration data by
1849189251Ssam * wpa_config_read().
1850189251Ssam */
1851189251Ssamvoid wpa_config_free(struct wpa_config *config)
1852189251Ssam{
1853189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS
1854189251Ssam	struct wpa_config_blob *blob, *prevblob;
1855189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */
1856189251Ssam	struct wpa_ssid *ssid, *prev = NULL;
1857252726Srpaulo	struct wpa_cred *cred, *cprev;
1858252726Srpaulo
1859189251Ssam	ssid = config->ssid;
1860189251Ssam	while (ssid) {
1861189251Ssam		prev = ssid;
1862189251Ssam		ssid = ssid->next;
1863189251Ssam		wpa_config_free_ssid(prev);
1864189251Ssam	}
1865189251Ssam
1866252726Srpaulo	cred = config->cred;
1867252726Srpaulo	while (cred) {
1868252726Srpaulo		cprev = cred;
1869252726Srpaulo		cred = cred->next;
1870252726Srpaulo		wpa_config_free_cred(cprev);
1871252726Srpaulo	}
1872252726Srpaulo
1873189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS
1874189251Ssam	blob = config->blobs;
1875189251Ssam	prevblob = NULL;
1876189251Ssam	while (blob) {
1877189251Ssam		prevblob = blob;
1878189251Ssam		blob = blob->next;
1879189251Ssam		wpa_config_free_blob(prevblob);
1880189251Ssam	}
1881189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */
1882189251Ssam
1883252726Srpaulo	wpabuf_free(config->wps_vendor_ext_m1);
1884189251Ssam	os_free(config->ctrl_interface);
1885189251Ssam	os_free(config->ctrl_interface_group);
1886189251Ssam	os_free(config->opensc_engine_path);
1887189251Ssam	os_free(config->pkcs11_engine_path);
1888189251Ssam	os_free(config->pkcs11_module_path);
1889252726Srpaulo	os_free(config->pcsc_reader);
1890252726Srpaulo	os_free(config->pcsc_pin);
1891189251Ssam	os_free(config->driver_param);
1892189251Ssam	os_free(config->device_name);
1893189251Ssam	os_free(config->manufacturer);
1894189251Ssam	os_free(config->model_name);
1895189251Ssam	os_free(config->model_number);
1896189251Ssam	os_free(config->serial_number);
1897214734Srpaulo	os_free(config->config_methods);
1898252726Srpaulo	os_free(config->p2p_ssid_postfix);
1899189251Ssam	os_free(config->pssid);
1900252726Srpaulo	os_free(config->p2p_pref_chan);
1901252726Srpaulo	os_free(config->autoscan);
1902252726Srpaulo	wpabuf_free(config->wps_nfc_dh_pubkey);
1903252726Srpaulo	wpabuf_free(config->wps_nfc_dh_privkey);
1904252726Srpaulo	wpabuf_free(config->wps_nfc_dev_pw);
1905252726Srpaulo	os_free(config->ext_password_backend);
1906189251Ssam	os_free(config);
1907189251Ssam}
1908189251Ssam
1909189251Ssam
1910189251Ssam/**
1911252726Srpaulo * wpa_config_foreach_network - Iterate over each configured network
1912252726Srpaulo * @config: Configuration data from wpa_config_read()
1913252726Srpaulo * @func: Callback function to process each network
1914252726Srpaulo * @arg: Opaque argument to pass to callback function
1915252726Srpaulo *
1916252726Srpaulo * Iterate over the set of configured networks calling the specified
1917252726Srpaulo * function for each item. We guard against callbacks removing the
1918252726Srpaulo * supplied network.
1919252726Srpaulo */
1920252726Srpaulovoid wpa_config_foreach_network(struct wpa_config *config,
1921252726Srpaulo				void (*func)(void *, struct wpa_ssid *),
1922252726Srpaulo				void *arg)
1923252726Srpaulo{
1924252726Srpaulo	struct wpa_ssid *ssid, *next;
1925252726Srpaulo
1926252726Srpaulo	ssid = config->ssid;
1927252726Srpaulo	while (ssid) {
1928252726Srpaulo		next = ssid->next;
1929252726Srpaulo		func(arg, ssid);
1930252726Srpaulo		ssid = next;
1931252726Srpaulo	}
1932252726Srpaulo}
1933252726Srpaulo
1934252726Srpaulo
1935252726Srpaulo/**
1936189251Ssam * wpa_config_get_network - Get configured network based on id
1937189251Ssam * @config: Configuration data from wpa_config_read()
1938189251Ssam * @id: Unique network id to search for
1939189251Ssam * Returns: Network configuration or %NULL if not found
1940189251Ssam */
1941189251Ssamstruct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id)
1942189251Ssam{
1943189251Ssam	struct wpa_ssid *ssid;
1944189251Ssam
1945189251Ssam	ssid = config->ssid;
1946189251Ssam	while (ssid) {
1947189251Ssam		if (id == ssid->id)
1948189251Ssam			break;
1949189251Ssam		ssid = ssid->next;
1950189251Ssam	}
1951189251Ssam
1952189251Ssam	return ssid;
1953189251Ssam}
1954189251Ssam
1955189251Ssam
1956189251Ssam/**
1957189251Ssam * wpa_config_add_network - Add a new network with empty configuration
1958189251Ssam * @config: Configuration data from wpa_config_read()
1959189251Ssam * Returns: The new network configuration or %NULL if operation failed
1960189251Ssam */
1961189251Ssamstruct wpa_ssid * wpa_config_add_network(struct wpa_config *config)
1962189251Ssam{
1963189251Ssam	int id;
1964189251Ssam	struct wpa_ssid *ssid, *last = NULL;
1965189251Ssam
1966189251Ssam	id = -1;
1967189251Ssam	ssid = config->ssid;
1968189251Ssam	while (ssid) {
1969189251Ssam		if (ssid->id > id)
1970189251Ssam			id = ssid->id;
1971189251Ssam		last = ssid;
1972189251Ssam		ssid = ssid->next;
1973189251Ssam	}
1974189251Ssam	id++;
1975189251Ssam
1976189251Ssam	ssid = os_zalloc(sizeof(*ssid));
1977189251Ssam	if (ssid == NULL)
1978189251Ssam		return NULL;
1979189251Ssam	ssid->id = id;
1980189251Ssam	if (last)
1981189251Ssam		last->next = ssid;
1982189251Ssam	else
1983189251Ssam		config->ssid = ssid;
1984189251Ssam
1985189251Ssam	wpa_config_update_prio_list(config);
1986189251Ssam
1987189251Ssam	return ssid;
1988189251Ssam}
1989189251Ssam
1990189251Ssam
1991189251Ssam/**
1992189251Ssam * wpa_config_remove_network - Remove a configured network based on id
1993189251Ssam * @config: Configuration data from wpa_config_read()
1994189251Ssam * @id: Unique network id to search for
1995189251Ssam * Returns: 0 on success, or -1 if the network was not found
1996189251Ssam */
1997189251Ssamint wpa_config_remove_network(struct wpa_config *config, int id)
1998189251Ssam{
1999189251Ssam	struct wpa_ssid *ssid, *prev = NULL;
2000189251Ssam
2001189251Ssam	ssid = config->ssid;
2002189251Ssam	while (ssid) {
2003189251Ssam		if (id == ssid->id)
2004189251Ssam			break;
2005189251Ssam		prev = ssid;
2006189251Ssam		ssid = ssid->next;
2007189251Ssam	}
2008189251Ssam
2009189251Ssam	if (ssid == NULL)
2010189251Ssam		return -1;
2011189251Ssam
2012189251Ssam	if (prev)
2013189251Ssam		prev->next = ssid->next;
2014189251Ssam	else
2015189251Ssam		config->ssid = ssid->next;
2016189251Ssam
2017189251Ssam	wpa_config_update_prio_list(config);
2018189251Ssam	wpa_config_free_ssid(ssid);
2019189251Ssam	return 0;
2020189251Ssam}
2021189251Ssam
2022189251Ssam
2023189251Ssam/**
2024189251Ssam * wpa_config_set_network_defaults - Set network default values
2025189251Ssam * @ssid: Pointer to network configuration data
2026189251Ssam */
2027189251Ssamvoid wpa_config_set_network_defaults(struct wpa_ssid *ssid)
2028189251Ssam{
2029189251Ssam	ssid->proto = DEFAULT_PROTO;
2030189251Ssam	ssid->pairwise_cipher = DEFAULT_PAIRWISE;
2031189251Ssam	ssid->group_cipher = DEFAULT_GROUP;
2032189251Ssam	ssid->key_mgmt = DEFAULT_KEY_MGMT;
2033252726Srpaulo	ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
2034189251Ssam#ifdef IEEE8021X_EAPOL
2035189251Ssam	ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
2036189251Ssam	ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
2037189251Ssam	ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;
2038189251Ssam#endif /* IEEE8021X_EAPOL */
2039252726Srpaulo#ifdef CONFIG_HT_OVERRIDES
2040252726Srpaulo	ssid->disable_ht = DEFAULT_DISABLE_HT;
2041252726Srpaulo	ssid->disable_ht40 = DEFAULT_DISABLE_HT40;
2042252726Srpaulo	ssid->disable_sgi = DEFAULT_DISABLE_SGI;
2043252726Srpaulo	ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU;
2044252726Srpaulo	ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR;
2045252726Srpaulo	ssid->ampdu_density = DEFAULT_AMPDU_DENSITY;
2046252726Srpaulo#endif /* CONFIG_HT_OVERRIDES */
2047252726Srpaulo	ssid->proactive_key_caching = -1;
2048252726Srpaulo#ifdef CONFIG_IEEE80211W
2049252726Srpaulo	ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT;
2050252726Srpaulo#endif /* CONFIG_IEEE80211W */
2051189251Ssam}
2052189251Ssam
2053189251Ssam
2054189251Ssam/**
2055189251Ssam * wpa_config_set - Set a variable in network configuration
2056189251Ssam * @ssid: Pointer to network configuration data
2057189251Ssam * @var: Variable name, e.g., "ssid"
2058189251Ssam * @value: Variable value
2059189251Ssam * @line: Line number in configuration file or 0 if not used
2060189251Ssam * Returns: 0 on success, -1 on failure
2061189251Ssam *
2062189251Ssam * This function can be used to set network configuration variables based on
2063189251Ssam * both the configuration file and management interface input. The value
2064189251Ssam * parameter must be in the same format as the text-based configuration file is
2065189251Ssam * using. For example, strings are using double quotation marks.
2066189251Ssam */
2067189251Ssamint wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
2068189251Ssam		   int line)
2069189251Ssam{
2070189251Ssam	size_t i;
2071189251Ssam	int ret = 0;
2072189251Ssam
2073189251Ssam	if (ssid == NULL || var == NULL || value == NULL)
2074189251Ssam		return -1;
2075189251Ssam
2076189251Ssam	for (i = 0; i < NUM_SSID_FIELDS; i++) {
2077189251Ssam		const struct parse_data *field = &ssid_fields[i];
2078189251Ssam		if (os_strcmp(var, field->name) != 0)
2079189251Ssam			continue;
2080189251Ssam
2081189251Ssam		if (field->parser(field, ssid, line, value)) {
2082189251Ssam			if (line) {
2083189251Ssam				wpa_printf(MSG_ERROR, "Line %d: failed to "
2084189251Ssam					   "parse %s '%s'.", line, var, value);
2085189251Ssam			}
2086189251Ssam			ret = -1;
2087189251Ssam		}
2088189251Ssam		break;
2089189251Ssam	}
2090189251Ssam	if (i == NUM_SSID_FIELDS) {
2091189251Ssam		if (line) {
2092189251Ssam			wpa_printf(MSG_ERROR, "Line %d: unknown network field "
2093189251Ssam				   "'%s'.", line, var);
2094189251Ssam		}
2095189251Ssam		ret = -1;
2096189251Ssam	}
2097189251Ssam
2098189251Ssam	return ret;
2099189251Ssam}
2100189251Ssam
2101189251Ssam
2102252726Srpauloint wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
2103252726Srpaulo			  const char *value)
2104252726Srpaulo{
2105252726Srpaulo	size_t len;
2106252726Srpaulo	char *buf;
2107252726Srpaulo	int ret;
2108252726Srpaulo
2109252726Srpaulo	len = os_strlen(value);
2110252726Srpaulo	buf = os_malloc(len + 3);
2111252726Srpaulo	if (buf == NULL)
2112252726Srpaulo		return -1;
2113252726Srpaulo	buf[0] = '"';
2114252726Srpaulo	os_memcpy(buf + 1, value, len);
2115252726Srpaulo	buf[len + 1] = '"';
2116252726Srpaulo	buf[len + 2] = '\0';
2117252726Srpaulo	ret = wpa_config_set(ssid, var, buf, 0);
2118252726Srpaulo	os_free(buf);
2119252726Srpaulo	return ret;
2120252726Srpaulo}
2121252726Srpaulo
2122252726Srpaulo
2123214734Srpaulo/**
2124214734Srpaulo * wpa_config_get_all - Get all options from network configuration
2125214734Srpaulo * @ssid: Pointer to network configuration data
2126214734Srpaulo * @get_keys: Determines if keys/passwords will be included in returned list
2127252726Srpaulo *	(if they may be exported)
2128214734Srpaulo * Returns: %NULL terminated list of all set keys and their values in the form
2129214734Srpaulo * of [key1, val1, key2, val2, ... , NULL]
2130214734Srpaulo *
2131214734Srpaulo * This function can be used to get list of all configured network properties.
2132214734Srpaulo * The caller is responsible for freeing the returned list and all its
2133214734Srpaulo * elements.
2134214734Srpaulo */
2135214734Srpaulochar ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys)
2136214734Srpaulo{
2137214734Srpaulo	const struct parse_data *field;
2138214734Srpaulo	char *key, *value;
2139214734Srpaulo	size_t i;
2140214734Srpaulo	char **props;
2141214734Srpaulo	int fields_num;
2142214734Srpaulo
2143252726Srpaulo	get_keys = get_keys && ssid->export_keys;
2144252726Srpaulo
2145252726Srpaulo	props = os_calloc(2 * NUM_SSID_FIELDS + 1, sizeof(char *));
2146214734Srpaulo	if (!props)
2147214734Srpaulo		return NULL;
2148214734Srpaulo
2149214734Srpaulo	fields_num = 0;
2150214734Srpaulo	for (i = 0; i < NUM_SSID_FIELDS; i++) {
2151214734Srpaulo		field = &ssid_fields[i];
2152214734Srpaulo		if (field->key_data && !get_keys)
2153214734Srpaulo			continue;
2154214734Srpaulo		value = field->writer(field, ssid);
2155214734Srpaulo		if (value == NULL)
2156214734Srpaulo			continue;
2157214734Srpaulo		if (os_strlen(value) == 0) {
2158214734Srpaulo			os_free(value);
2159214734Srpaulo			continue;
2160214734Srpaulo		}
2161214734Srpaulo
2162214734Srpaulo		key = os_strdup(field->name);
2163214734Srpaulo		if (key == NULL) {
2164214734Srpaulo			os_free(value);
2165214734Srpaulo			goto err;
2166214734Srpaulo		}
2167214734Srpaulo
2168214734Srpaulo		props[fields_num * 2] = key;
2169214734Srpaulo		props[fields_num * 2 + 1] = value;
2170214734Srpaulo
2171214734Srpaulo		fields_num++;
2172214734Srpaulo	}
2173214734Srpaulo
2174214734Srpaulo	return props;
2175214734Srpaulo
2176214734Srpauloerr:
2177214734Srpaulo	value = *props;
2178214734Srpaulo	while (value)
2179214734Srpaulo		os_free(value++);
2180214734Srpaulo	os_free(props);
2181214734Srpaulo	return NULL;
2182214734Srpaulo}
2183214734Srpaulo
2184214734Srpaulo
2185189251Ssam#ifndef NO_CONFIG_WRITE
2186189251Ssam/**
2187189251Ssam * wpa_config_get - Get a variable in network configuration
2188189251Ssam * @ssid: Pointer to network configuration data
2189189251Ssam * @var: Variable name, e.g., "ssid"
2190189251Ssam * Returns: Value of the variable or %NULL on failure
2191189251Ssam *
2192189251Ssam * This function can be used to get network configuration variables. The
2193189251Ssam * returned value is a copy of the configuration variable in text format, i.e,.
2194189251Ssam * the same format that the text-based configuration file and wpa_config_set()
2195189251Ssam * are using for the value. The caller is responsible for freeing the returned
2196189251Ssam * value.
2197189251Ssam */
2198189251Ssamchar * wpa_config_get(struct wpa_ssid *ssid, const char *var)
2199189251Ssam{
2200189251Ssam	size_t i;
2201189251Ssam
2202189251Ssam	if (ssid == NULL || var == NULL)
2203189251Ssam		return NULL;
2204189251Ssam
2205189251Ssam	for (i = 0; i < NUM_SSID_FIELDS; i++) {
2206189251Ssam		const struct parse_data *field = &ssid_fields[i];
2207189251Ssam		if (os_strcmp(var, field->name) == 0)
2208189251Ssam			return field->writer(field, ssid);
2209189251Ssam	}
2210189251Ssam
2211189251Ssam	return NULL;
2212189251Ssam}
2213189251Ssam
2214189251Ssam
2215189251Ssam/**
2216189251Ssam * wpa_config_get_no_key - Get a variable in network configuration (no keys)
2217189251Ssam * @ssid: Pointer to network configuration data
2218189251Ssam * @var: Variable name, e.g., "ssid"
2219189251Ssam * Returns: Value of the variable or %NULL on failure
2220189251Ssam *
2221189251Ssam * This function can be used to get network configuration variable like
2222189251Ssam * wpa_config_get(). The only difference is that this functions does not expose
2223189251Ssam * key/password material from the configuration. In case a key/password field
2224189251Ssam * is requested, the returned value is an empty string or %NULL if the variable
2225189251Ssam * is not set or "*" if the variable is set (regardless of its value). The
2226189251Ssam * returned value is a copy of the configuration variable in text format, i.e,.
2227189251Ssam * the same format that the text-based configuration file and wpa_config_set()
2228189251Ssam * are using for the value. The caller is responsible for freeing the returned
2229189251Ssam * value.
2230189251Ssam */
2231189251Ssamchar * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var)
2232189251Ssam{
2233189251Ssam	size_t i;
2234189251Ssam
2235189251Ssam	if (ssid == NULL || var == NULL)
2236189251Ssam		return NULL;
2237189251Ssam
2238189251Ssam	for (i = 0; i < NUM_SSID_FIELDS; i++) {
2239189251Ssam		const struct parse_data *field = &ssid_fields[i];
2240189251Ssam		if (os_strcmp(var, field->name) == 0) {
2241189251Ssam			char *res = field->writer(field, ssid);
2242189251Ssam			if (field->key_data) {
2243189251Ssam				if (res && res[0]) {
2244189251Ssam					wpa_printf(MSG_DEBUG, "Do not allow "
2245189251Ssam						   "key_data field to be "
2246189251Ssam						   "exposed");
2247189251Ssam					os_free(res);
2248189251Ssam					return os_strdup("*");
2249189251Ssam				}
2250189251Ssam
2251189251Ssam				os_free(res);
2252189251Ssam				return NULL;
2253189251Ssam			}
2254189251Ssam			return res;
2255189251Ssam		}
2256189251Ssam	}
2257189251Ssam
2258189251Ssam	return NULL;
2259189251Ssam}
2260189251Ssam#endif /* NO_CONFIG_WRITE */
2261189251Ssam
2262189251Ssam
2263189251Ssam/**
2264189251Ssam * wpa_config_update_psk - Update WPA PSK based on passphrase and SSID
2265189251Ssam * @ssid: Pointer to network configuration data
2266189251Ssam *
2267189251Ssam * This function must be called to update WPA PSK when either SSID or the
2268189251Ssam * passphrase has changed for the network configuration.
2269189251Ssam */
2270189251Ssamvoid wpa_config_update_psk(struct wpa_ssid *ssid)
2271189251Ssam{
2272189251Ssam#ifndef CONFIG_NO_PBKDF2
2273252726Srpaulo	pbkdf2_sha1(ssid->passphrase, ssid->ssid, ssid->ssid_len, 4096,
2274189251Ssam		    ssid->psk, PMK_LEN);
2275189251Ssam	wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
2276189251Ssam			ssid->psk, PMK_LEN);
2277189251Ssam	ssid->psk_set = 1;
2278189251Ssam#endif /* CONFIG_NO_PBKDF2 */
2279189251Ssam}
2280189251Ssam
2281189251Ssam
2282252726Srpauloint wpa_config_set_cred(struct wpa_cred *cred, const char *var,
2283252726Srpaulo			const char *value, int line)
2284252726Srpaulo{
2285252726Srpaulo	char *val;
2286252726Srpaulo	size_t len;
2287252726Srpaulo
2288252726Srpaulo	if (os_strcmp(var, "priority") == 0) {
2289252726Srpaulo		cred->priority = atoi(value);
2290252726Srpaulo		return 0;
2291252726Srpaulo	}
2292252726Srpaulo
2293252726Srpaulo	if (os_strcmp(var, "pcsc") == 0) {
2294252726Srpaulo		cred->pcsc = atoi(value);
2295252726Srpaulo		return 0;
2296252726Srpaulo	}
2297252726Srpaulo
2298252726Srpaulo	if (os_strcmp(var, "eap") == 0) {
2299252726Srpaulo		struct eap_method_type method;
2300252726Srpaulo		method.method = eap_peer_get_type(value, &method.vendor);
2301252726Srpaulo		if (method.vendor == EAP_VENDOR_IETF &&
2302252726Srpaulo		    method.method == EAP_TYPE_NONE) {
2303252726Srpaulo			wpa_printf(MSG_ERROR, "Line %d: unknown EAP type '%s' "
2304252726Srpaulo				   "for a credential", line, value);
2305252726Srpaulo			return -1;
2306252726Srpaulo		}
2307252726Srpaulo		os_free(cred->eap_method);
2308252726Srpaulo		cred->eap_method = os_malloc(sizeof(*cred->eap_method));
2309252726Srpaulo		if (cred->eap_method == NULL)
2310252726Srpaulo			return -1;
2311252726Srpaulo		os_memcpy(cred->eap_method, &method, sizeof(method));
2312252726Srpaulo		return 0;
2313252726Srpaulo	}
2314252726Srpaulo
2315252726Srpaulo	if (os_strcmp(var, "password") == 0 &&
2316252726Srpaulo	    os_strncmp(value, "ext:", 4) == 0) {
2317252726Srpaulo		os_free(cred->password);
2318252726Srpaulo		cred->password = os_strdup(value);
2319252726Srpaulo		cred->ext_password = 1;
2320252726Srpaulo		return 0;
2321252726Srpaulo	}
2322252726Srpaulo
2323252726Srpaulo	val = wpa_config_parse_string(value, &len);
2324252726Srpaulo	if (val == NULL) {
2325252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
2326252726Srpaulo			   "value '%s'.", line, var, value);
2327252726Srpaulo		return -1;
2328252726Srpaulo	}
2329252726Srpaulo
2330252726Srpaulo	if (os_strcmp(var, "realm") == 0) {
2331252726Srpaulo		os_free(cred->realm);
2332252726Srpaulo		cred->realm = val;
2333252726Srpaulo		return 0;
2334252726Srpaulo	}
2335252726Srpaulo
2336252726Srpaulo	if (os_strcmp(var, "username") == 0) {
2337252726Srpaulo		os_free(cred->username);
2338252726Srpaulo		cred->username = val;
2339252726Srpaulo		return 0;
2340252726Srpaulo	}
2341252726Srpaulo
2342252726Srpaulo	if (os_strcmp(var, "password") == 0) {
2343252726Srpaulo		os_free(cred->password);
2344252726Srpaulo		cred->password = val;
2345252726Srpaulo		cred->ext_password = 0;
2346252726Srpaulo		return 0;
2347252726Srpaulo	}
2348252726Srpaulo
2349252726Srpaulo	if (os_strcmp(var, "ca_cert") == 0) {
2350252726Srpaulo		os_free(cred->ca_cert);
2351252726Srpaulo		cred->ca_cert = val;
2352252726Srpaulo		return 0;
2353252726Srpaulo	}
2354252726Srpaulo
2355252726Srpaulo	if (os_strcmp(var, "client_cert") == 0) {
2356252726Srpaulo		os_free(cred->client_cert);
2357252726Srpaulo		cred->client_cert = val;
2358252726Srpaulo		return 0;
2359252726Srpaulo	}
2360252726Srpaulo
2361252726Srpaulo	if (os_strcmp(var, "private_key") == 0) {
2362252726Srpaulo		os_free(cred->private_key);
2363252726Srpaulo		cred->private_key = val;
2364252726Srpaulo		return 0;
2365252726Srpaulo	}
2366252726Srpaulo
2367252726Srpaulo	if (os_strcmp(var, "private_key_passwd") == 0) {
2368252726Srpaulo		os_free(cred->private_key_passwd);
2369252726Srpaulo		cred->private_key_passwd = val;
2370252726Srpaulo		return 0;
2371252726Srpaulo	}
2372252726Srpaulo
2373252726Srpaulo	if (os_strcmp(var, "imsi") == 0) {
2374252726Srpaulo		os_free(cred->imsi);
2375252726Srpaulo		cred->imsi = val;
2376252726Srpaulo		return 0;
2377252726Srpaulo	}
2378252726Srpaulo
2379252726Srpaulo	if (os_strcmp(var, "milenage") == 0) {
2380252726Srpaulo		os_free(cred->milenage);
2381252726Srpaulo		cred->milenage = val;
2382252726Srpaulo		return 0;
2383252726Srpaulo	}
2384252726Srpaulo
2385252726Srpaulo	if (os_strcmp(var, "domain") == 0) {
2386252726Srpaulo		os_free(cred->domain);
2387252726Srpaulo		cred->domain = val;
2388252726Srpaulo		return 0;
2389252726Srpaulo	}
2390252726Srpaulo
2391252726Srpaulo	if (os_strcmp(var, "phase1") == 0) {
2392252726Srpaulo		os_free(cred->phase1);
2393252726Srpaulo		cred->phase1 = val;
2394252726Srpaulo		return 0;
2395252726Srpaulo	}
2396252726Srpaulo
2397252726Srpaulo	if (os_strcmp(var, "phase2") == 0) {
2398252726Srpaulo		os_free(cred->phase2);
2399252726Srpaulo		cred->phase2 = val;
2400252726Srpaulo		return 0;
2401252726Srpaulo	}
2402252726Srpaulo
2403252726Srpaulo	if (os_strcmp(var, "roaming_consortium") == 0) {
2404252726Srpaulo		if (len < 3 || len > sizeof(cred->roaming_consortium)) {
2405252726Srpaulo			wpa_printf(MSG_ERROR, "Line %d: invalid "
2406252726Srpaulo				   "roaming_consortium length %d (3..15 "
2407252726Srpaulo				   "expected)", line, (int) len);
2408252726Srpaulo			os_free(val);
2409252726Srpaulo			return -1;
2410252726Srpaulo		}
2411252726Srpaulo		os_memcpy(cred->roaming_consortium, val, len);
2412252726Srpaulo		cred->roaming_consortium_len = len;
2413252726Srpaulo		os_free(val);
2414252726Srpaulo		return 0;
2415252726Srpaulo	}
2416252726Srpaulo
2417252726Srpaulo	if (os_strcmp(var, "excluded_ssid") == 0) {
2418252726Srpaulo		struct excluded_ssid *e;
2419252726Srpaulo
2420252726Srpaulo		if (len > MAX_SSID_LEN) {
2421252726Srpaulo			wpa_printf(MSG_ERROR, "Line %d: invalid "
2422252726Srpaulo				   "excluded_ssid length %d", line, (int) len);
2423252726Srpaulo			os_free(val);
2424252726Srpaulo			return -1;
2425252726Srpaulo		}
2426252726Srpaulo
2427252726Srpaulo		e = os_realloc_array(cred->excluded_ssid,
2428252726Srpaulo				     cred->num_excluded_ssid + 1,
2429252726Srpaulo				     sizeof(struct excluded_ssid));
2430252726Srpaulo		if (e == NULL) {
2431252726Srpaulo			os_free(val);
2432252726Srpaulo			return -1;
2433252726Srpaulo		}
2434252726Srpaulo		cred->excluded_ssid = e;
2435252726Srpaulo
2436252726Srpaulo		e = &cred->excluded_ssid[cred->num_excluded_ssid++];
2437252726Srpaulo		os_memcpy(e->ssid, val, len);
2438252726Srpaulo		e->ssid_len = len;
2439252726Srpaulo
2440252726Srpaulo		os_free(val);
2441252726Srpaulo
2442252726Srpaulo		return 0;
2443252726Srpaulo	}
2444252726Srpaulo
2445252726Srpaulo	if (line) {
2446252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.",
2447252726Srpaulo			   line, var);
2448252726Srpaulo	}
2449252726Srpaulo
2450252726Srpaulo	os_free(val);
2451252726Srpaulo
2452252726Srpaulo	return -1;
2453252726Srpaulo}
2454252726Srpaulo
2455252726Srpaulo
2456252726Srpaulostruct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id)
2457252726Srpaulo{
2458252726Srpaulo	struct wpa_cred *cred;
2459252726Srpaulo
2460252726Srpaulo	cred = config->cred;
2461252726Srpaulo	while (cred) {
2462252726Srpaulo		if (id == cred->id)
2463252726Srpaulo			break;
2464252726Srpaulo		cred = cred->next;
2465252726Srpaulo	}
2466252726Srpaulo
2467252726Srpaulo	return cred;
2468252726Srpaulo}
2469252726Srpaulo
2470252726Srpaulo
2471252726Srpaulostruct wpa_cred * wpa_config_add_cred(struct wpa_config *config)
2472252726Srpaulo{
2473252726Srpaulo	int id;
2474252726Srpaulo	struct wpa_cred *cred, *last = NULL;
2475252726Srpaulo
2476252726Srpaulo	id = -1;
2477252726Srpaulo	cred = config->cred;
2478252726Srpaulo	while (cred) {
2479252726Srpaulo		if (cred->id > id)
2480252726Srpaulo			id = cred->id;
2481252726Srpaulo		last = cred;
2482252726Srpaulo		cred = cred->next;
2483252726Srpaulo	}
2484252726Srpaulo	id++;
2485252726Srpaulo
2486252726Srpaulo	cred = os_zalloc(sizeof(*cred));
2487252726Srpaulo	if (cred == NULL)
2488252726Srpaulo		return NULL;
2489252726Srpaulo	cred->id = id;
2490252726Srpaulo	if (last)
2491252726Srpaulo		last->next = cred;
2492252726Srpaulo	else
2493252726Srpaulo		config->cred = cred;
2494252726Srpaulo
2495252726Srpaulo	return cred;
2496252726Srpaulo}
2497252726Srpaulo
2498252726Srpaulo
2499252726Srpauloint wpa_config_remove_cred(struct wpa_config *config, int id)
2500252726Srpaulo{
2501252726Srpaulo	struct wpa_cred *cred, *prev = NULL;
2502252726Srpaulo
2503252726Srpaulo	cred = config->cred;
2504252726Srpaulo	while (cred) {
2505252726Srpaulo		if (id == cred->id)
2506252726Srpaulo			break;
2507252726Srpaulo		prev = cred;
2508252726Srpaulo		cred = cred->next;
2509252726Srpaulo	}
2510252726Srpaulo
2511252726Srpaulo	if (cred == NULL)
2512252726Srpaulo		return -1;
2513252726Srpaulo
2514252726Srpaulo	if (prev)
2515252726Srpaulo		prev->next = cred->next;
2516252726Srpaulo	else
2517252726Srpaulo		config->cred = cred->next;
2518252726Srpaulo
2519252726Srpaulo	wpa_config_free_cred(cred);
2520252726Srpaulo	return 0;
2521252726Srpaulo}
2522252726Srpaulo
2523252726Srpaulo
2524189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS
2525189251Ssam/**
2526189251Ssam * wpa_config_get_blob - Get a named configuration blob
2527189251Ssam * @config: Configuration data from wpa_config_read()
2528189251Ssam * @name: Name of the blob
2529189251Ssam * Returns: Pointer to blob data or %NULL if not found
2530189251Ssam */
2531189251Ssamconst struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config,
2532189251Ssam						   const char *name)
2533189251Ssam{
2534189251Ssam	struct wpa_config_blob *blob = config->blobs;
2535189251Ssam
2536189251Ssam	while (blob) {
2537189251Ssam		if (os_strcmp(blob->name, name) == 0)
2538189251Ssam			return blob;
2539189251Ssam		blob = blob->next;
2540189251Ssam	}
2541189251Ssam	return NULL;
2542189251Ssam}
2543189251Ssam
2544189251Ssam
2545189251Ssam/**
2546189251Ssam * wpa_config_set_blob - Set or add a named configuration blob
2547189251Ssam * @config: Configuration data from wpa_config_read()
2548189251Ssam * @blob: New value for the blob
2549189251Ssam *
2550189251Ssam * Adds a new configuration blob or replaces the current value of an existing
2551189251Ssam * blob.
2552189251Ssam */
2553189251Ssamvoid wpa_config_set_blob(struct wpa_config *config,
2554189251Ssam			 struct wpa_config_blob *blob)
2555189251Ssam{
2556189251Ssam	wpa_config_remove_blob(config, blob->name);
2557189251Ssam	blob->next = config->blobs;
2558189251Ssam	config->blobs = blob;
2559189251Ssam}
2560189251Ssam
2561189251Ssam
2562189251Ssam/**
2563189251Ssam * wpa_config_free_blob - Free blob data
2564189251Ssam * @blob: Pointer to blob to be freed
2565189251Ssam */
2566189251Ssamvoid wpa_config_free_blob(struct wpa_config_blob *blob)
2567189251Ssam{
2568189251Ssam	if (blob) {
2569189251Ssam		os_free(blob->name);
2570189251Ssam		os_free(blob->data);
2571189251Ssam		os_free(blob);
2572189251Ssam	}
2573189251Ssam}
2574189251Ssam
2575189251Ssam
2576189251Ssam/**
2577189251Ssam * wpa_config_remove_blob - Remove a named configuration blob
2578189251Ssam * @config: Configuration data from wpa_config_read()
2579189251Ssam * @name: Name of the blob to remove
2580189251Ssam * Returns: 0 if blob was removed or -1 if blob was not found
2581189251Ssam */
2582189251Ssamint wpa_config_remove_blob(struct wpa_config *config, const char *name)
2583189251Ssam{
2584189251Ssam	struct wpa_config_blob *pos = config->blobs, *prev = NULL;
2585189251Ssam
2586189251Ssam	while (pos) {
2587189251Ssam		if (os_strcmp(pos->name, name) == 0) {
2588189251Ssam			if (prev)
2589189251Ssam				prev->next = pos->next;
2590189251Ssam			else
2591189251Ssam				config->blobs = pos->next;
2592189251Ssam			wpa_config_free_blob(pos);
2593189251Ssam			return 0;
2594189251Ssam		}
2595189251Ssam		prev = pos;
2596189251Ssam		pos = pos->next;
2597189251Ssam	}
2598189251Ssam
2599189251Ssam	return -1;
2600189251Ssam}
2601189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */
2602189251Ssam
2603189251Ssam
2604189251Ssam/**
2605189251Ssam * wpa_config_alloc_empty - Allocate an empty configuration
2606189251Ssam * @ctrl_interface: Control interface parameters, e.g., path to UNIX domain
2607189251Ssam * socket
2608189251Ssam * @driver_param: Driver parameters
2609189251Ssam * Returns: Pointer to allocated configuration data or %NULL on failure
2610189251Ssam */
2611189251Ssamstruct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
2612189251Ssam					   const char *driver_param)
2613189251Ssam{
2614189251Ssam	struct wpa_config *config;
2615252726Srpaulo	const int aCWmin = 4, aCWmax = 10;
2616252726Srpaulo	const struct hostapd_wmm_ac_params ac_bk =
2617252726Srpaulo		{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
2618252726Srpaulo	const struct hostapd_wmm_ac_params ac_be =
2619252726Srpaulo		{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
2620252726Srpaulo	const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
2621252726Srpaulo		{ aCWmin - 1, aCWmin, 2, 3000 / 32, 0 };
2622252726Srpaulo	const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
2623252726Srpaulo		{ aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
2624189251Ssam
2625189251Ssam	config = os_zalloc(sizeof(*config));
2626189251Ssam	if (config == NULL)
2627189251Ssam		return NULL;
2628189251Ssam	config->eapol_version = DEFAULT_EAPOL_VERSION;
2629189251Ssam	config->ap_scan = DEFAULT_AP_SCAN;
2630189251Ssam	config->fast_reauth = DEFAULT_FAST_REAUTH;
2631252726Srpaulo	config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
2632252726Srpaulo	config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
2633252726Srpaulo	config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
2634214734Srpaulo	config->bss_max_count = DEFAULT_BSS_MAX_COUNT;
2635252726Srpaulo	config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE;
2636252726Srpaulo	config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT;
2637252726Srpaulo	config->max_num_sta = DEFAULT_MAX_NUM_STA;
2638252726Srpaulo	config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE;
2639252726Srpaulo	config->wmm_ac_params[0] = ac_be;
2640252726Srpaulo	config->wmm_ac_params[1] = ac_bk;
2641252726Srpaulo	config->wmm_ac_params[2] = ac_vi;
2642252726Srpaulo	config->wmm_ac_params[3] = ac_vo;
2643189251Ssam
2644189251Ssam	if (ctrl_interface)
2645189251Ssam		config->ctrl_interface = os_strdup(ctrl_interface);
2646189251Ssam	if (driver_param)
2647189251Ssam		config->driver_param = os_strdup(driver_param);
2648189251Ssam
2649189251Ssam	return config;
2650189251Ssam}
2651189251Ssam
2652189251Ssam
2653189251Ssam#ifndef CONFIG_NO_STDOUT_DEBUG
2654189251Ssam/**
2655189251Ssam * wpa_config_debug_dump_networks - Debug dump of configured networks
2656189251Ssam * @config: Configuration data from wpa_config_read()
2657189251Ssam */
2658189251Ssamvoid wpa_config_debug_dump_networks(struct wpa_config *config)
2659189251Ssam{
2660189251Ssam	int prio;
2661189251Ssam	struct wpa_ssid *ssid;
2662189251Ssam
2663189251Ssam	for (prio = 0; prio < config->num_prio; prio++) {
2664189251Ssam		ssid = config->pssid[prio];
2665189251Ssam		wpa_printf(MSG_DEBUG, "Priority group %d",
2666189251Ssam			   ssid->priority);
2667189251Ssam		while (ssid) {
2668189251Ssam			wpa_printf(MSG_DEBUG, "   id=%d ssid='%s'",
2669189251Ssam				   ssid->id,
2670189251Ssam				   wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
2671189251Ssam			ssid = ssid->pnext;
2672189251Ssam		}
2673189251Ssam	}
2674189251Ssam}
2675189251Ssam#endif /* CONFIG_NO_STDOUT_DEBUG */
2676252726Srpaulo
2677252726Srpaulo
2678252726Srpaulostruct global_parse_data {
2679252726Srpaulo	char *name;
2680252726Srpaulo	int (*parser)(const struct global_parse_data *data,
2681252726Srpaulo		      struct wpa_config *config, int line, const char *value);
2682252726Srpaulo	void *param1, *param2, *param3;
2683252726Srpaulo	unsigned int changed_flag;
2684252726Srpaulo};
2685252726Srpaulo
2686252726Srpaulo
2687252726Srpaulostatic int wpa_global_config_parse_int(const struct global_parse_data *data,
2688252726Srpaulo				       struct wpa_config *config, int line,
2689252726Srpaulo				       const char *pos)
2690252726Srpaulo{
2691252726Srpaulo	int *dst;
2692252726Srpaulo	dst = (int *) (((u8 *) config) + (long) data->param1);
2693252726Srpaulo	*dst = atoi(pos);
2694252726Srpaulo	wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
2695252726Srpaulo
2696252726Srpaulo	if (data->param2 && *dst < (long) data->param2) {
2697252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
2698252726Srpaulo			   "min_value=%ld)", line, data->name, *dst,
2699252726Srpaulo			   (long) data->param2);
2700252726Srpaulo		*dst = (long) data->param2;
2701252726Srpaulo		return -1;
2702252726Srpaulo	}
2703252726Srpaulo
2704252726Srpaulo	if (data->param3 && *dst > (long) data->param3) {
2705252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
2706252726Srpaulo			   "max_value=%ld)", line, data->name, *dst,
2707252726Srpaulo			   (long) data->param3);
2708252726Srpaulo		*dst = (long) data->param3;
2709252726Srpaulo		return -1;
2710252726Srpaulo	}
2711252726Srpaulo
2712252726Srpaulo	return 0;
2713252726Srpaulo}
2714252726Srpaulo
2715252726Srpaulo
2716252726Srpaulostatic int wpa_global_config_parse_str(const struct global_parse_data *data,
2717252726Srpaulo				       struct wpa_config *config, int line,
2718252726Srpaulo				       const char *pos)
2719252726Srpaulo{
2720252726Srpaulo	size_t len;
2721252726Srpaulo	char **dst, *tmp;
2722252726Srpaulo
2723252726Srpaulo	len = os_strlen(pos);
2724252726Srpaulo	if (data->param2 && len < (size_t) data->param2) {
2725252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
2726252726Srpaulo			   "min_len=%ld)", line, data->name,
2727252726Srpaulo			   (unsigned long) len, (long) data->param2);
2728252726Srpaulo		return -1;
2729252726Srpaulo	}
2730252726Srpaulo
2731252726Srpaulo	if (data->param3 && len > (size_t) data->param3) {
2732252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
2733252726Srpaulo			   "max_len=%ld)", line, data->name,
2734252726Srpaulo			   (unsigned long) len, (long) data->param3);
2735252726Srpaulo		return -1;
2736252726Srpaulo	}
2737252726Srpaulo
2738252726Srpaulo	tmp = os_strdup(pos);
2739252726Srpaulo	if (tmp == NULL)
2740252726Srpaulo		return -1;
2741252726Srpaulo
2742252726Srpaulo	dst = (char **) (((u8 *) config) + (long) data->param1);
2743252726Srpaulo	os_free(*dst);
2744252726Srpaulo	*dst = tmp;
2745252726Srpaulo	wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst);
2746252726Srpaulo
2747252726Srpaulo	return 0;
2748252726Srpaulo}
2749252726Srpaulo
2750252726Srpaulo
2751252726Srpaulostatic int wpa_global_config_parse_bin(const struct global_parse_data *data,
2752252726Srpaulo				       struct wpa_config *config, int line,
2753252726Srpaulo				       const char *pos)
2754252726Srpaulo{
2755252726Srpaulo	size_t len;
2756252726Srpaulo	struct wpabuf **dst, *tmp;
2757252726Srpaulo
2758252726Srpaulo	len = os_strlen(pos);
2759252726Srpaulo	if (len & 0x01)
2760252726Srpaulo		return -1;
2761252726Srpaulo
2762252726Srpaulo	tmp = wpabuf_alloc(len / 2);
2763252726Srpaulo	if (tmp == NULL)
2764252726Srpaulo		return -1;
2765252726Srpaulo
2766252726Srpaulo	if (hexstr2bin(pos, wpabuf_put(tmp, len / 2), len / 2)) {
2767252726Srpaulo		wpabuf_free(tmp);
2768252726Srpaulo		return -1;
2769252726Srpaulo	}
2770252726Srpaulo
2771252726Srpaulo	dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1);
2772252726Srpaulo	wpabuf_free(*dst);
2773252726Srpaulo	*dst = tmp;
2774252726Srpaulo	wpa_printf(MSG_DEBUG, "%s", data->name);
2775252726Srpaulo
2776252726Srpaulo	return 0;
2777252726Srpaulo}
2778252726Srpaulo
2779252726Srpaulo
2780252726Srpaulostatic int wpa_config_process_country(const struct global_parse_data *data,
2781252726Srpaulo				      struct wpa_config *config, int line,
2782252726Srpaulo				      const char *pos)
2783252726Srpaulo{
2784252726Srpaulo	if (!pos[0] || !pos[1]) {
2785252726Srpaulo		wpa_printf(MSG_DEBUG, "Invalid country set");
2786252726Srpaulo		return -1;
2787252726Srpaulo	}
2788252726Srpaulo	config->country[0] = pos[0];
2789252726Srpaulo	config->country[1] = pos[1];
2790252726Srpaulo	wpa_printf(MSG_DEBUG, "country='%c%c'",
2791252726Srpaulo		   config->country[0], config->country[1]);
2792252726Srpaulo	return 0;
2793252726Srpaulo}
2794252726Srpaulo
2795252726Srpaulo
2796252726Srpaulostatic int wpa_config_process_load_dynamic_eap(
2797252726Srpaulo	const struct global_parse_data *data, struct wpa_config *config,
2798252726Srpaulo	int line, const char *so)
2799252726Srpaulo{
2800252726Srpaulo	int ret;
2801252726Srpaulo	wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so);
2802252726Srpaulo	ret = eap_peer_method_load(so);
2803252726Srpaulo	if (ret == -2) {
2804252726Srpaulo		wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not "
2805252726Srpaulo			   "reloading.");
2806252726Srpaulo	} else if (ret) {
2807252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP "
2808252726Srpaulo			   "method '%s'.", line, so);
2809252726Srpaulo		return -1;
2810252726Srpaulo	}
2811252726Srpaulo
2812252726Srpaulo	return 0;
2813252726Srpaulo}
2814252726Srpaulo
2815252726Srpaulo
2816252726Srpaulo#ifdef CONFIG_WPS
2817252726Srpaulo
2818252726Srpaulostatic int wpa_config_process_uuid(const struct global_parse_data *data,
2819252726Srpaulo				   struct wpa_config *config, int line,
2820252726Srpaulo				   const char *pos)
2821252726Srpaulo{
2822252726Srpaulo	char buf[40];
2823252726Srpaulo	if (uuid_str2bin(pos, config->uuid)) {
2824252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
2825252726Srpaulo		return -1;
2826252726Srpaulo	}
2827252726Srpaulo	uuid_bin2str(config->uuid, buf, sizeof(buf));
2828252726Srpaulo	wpa_printf(MSG_DEBUG, "uuid=%s", buf);
2829252726Srpaulo	return 0;
2830252726Srpaulo}
2831252726Srpaulo
2832252726Srpaulo
2833252726Srpaulostatic int wpa_config_process_device_type(
2834252726Srpaulo	const struct global_parse_data *data,
2835252726Srpaulo	struct wpa_config *config, int line, const char *pos)
2836252726Srpaulo{
2837252726Srpaulo	return wps_dev_type_str2bin(pos, config->device_type);
2838252726Srpaulo}
2839252726Srpaulo
2840252726Srpaulo
2841252726Srpaulostatic int wpa_config_process_os_version(const struct global_parse_data *data,
2842252726Srpaulo					 struct wpa_config *config, int line,
2843252726Srpaulo					 const char *pos)
2844252726Srpaulo{
2845252726Srpaulo	if (hexstr2bin(pos, config->os_version, 4)) {
2846252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line);
2847252726Srpaulo		return -1;
2848252726Srpaulo	}
2849252726Srpaulo	wpa_printf(MSG_DEBUG, "os_version=%08x",
2850252726Srpaulo		   WPA_GET_BE32(config->os_version));
2851252726Srpaulo	return 0;
2852252726Srpaulo}
2853252726Srpaulo
2854252726Srpaulo
2855252726Srpaulostatic int wpa_config_process_wps_vendor_ext_m1(
2856252726Srpaulo	const struct global_parse_data *data,
2857252726Srpaulo	struct wpa_config *config, int line, const char *pos)
2858252726Srpaulo{
2859252726Srpaulo	struct wpabuf *tmp;
2860252726Srpaulo	int len = os_strlen(pos) / 2;
2861252726Srpaulo	u8 *p;
2862252726Srpaulo
2863252726Srpaulo	if (!len) {
2864252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: "
2865252726Srpaulo			   "invalid wps_vendor_ext_m1", line);
2866252726Srpaulo		return -1;
2867252726Srpaulo	}
2868252726Srpaulo
2869252726Srpaulo	tmp = wpabuf_alloc(len);
2870252726Srpaulo	if (tmp) {
2871252726Srpaulo		p = wpabuf_put(tmp, len);
2872252726Srpaulo
2873252726Srpaulo		if (hexstr2bin(pos, p, len)) {
2874252726Srpaulo			wpa_printf(MSG_ERROR, "Line %d: "
2875252726Srpaulo				   "invalid wps_vendor_ext_m1", line);
2876252726Srpaulo			wpabuf_free(tmp);
2877252726Srpaulo			return -1;
2878252726Srpaulo		}
2879252726Srpaulo
2880252726Srpaulo		wpabuf_free(config->wps_vendor_ext_m1);
2881252726Srpaulo		config->wps_vendor_ext_m1 = tmp;
2882252726Srpaulo	} else {
2883252726Srpaulo		wpa_printf(MSG_ERROR, "Can not allocate "
2884252726Srpaulo			   "memory for wps_vendor_ext_m1");
2885252726Srpaulo		return -1;
2886252726Srpaulo	}
2887252726Srpaulo
2888252726Srpaulo	return 0;
2889252726Srpaulo}
2890252726Srpaulo
2891252726Srpaulo#endif /* CONFIG_WPS */
2892252726Srpaulo
2893252726Srpaulo#ifdef CONFIG_P2P
2894252726Srpaulostatic int wpa_config_process_sec_device_type(
2895252726Srpaulo	const struct global_parse_data *data,
2896252726Srpaulo	struct wpa_config *config, int line, const char *pos)
2897252726Srpaulo{
2898252726Srpaulo	int idx;
2899252726Srpaulo
2900252726Srpaulo	if (config->num_sec_device_types >= MAX_SEC_DEVICE_TYPES) {
2901252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: too many sec_device_type "
2902252726Srpaulo			   "items", line);
2903252726Srpaulo		return -1;
2904252726Srpaulo	}
2905252726Srpaulo
2906252726Srpaulo	idx = config->num_sec_device_types;
2907252726Srpaulo
2908252726Srpaulo	if (wps_dev_type_str2bin(pos, config->sec_device_type[idx]))
2909252726Srpaulo		return -1;
2910252726Srpaulo
2911252726Srpaulo	config->num_sec_device_types++;
2912252726Srpaulo	return 0;
2913252726Srpaulo}
2914252726Srpaulo
2915252726Srpaulo
2916252726Srpaulostatic int wpa_config_process_p2p_pref_chan(
2917252726Srpaulo	const struct global_parse_data *data,
2918252726Srpaulo	struct wpa_config *config, int line, const char *pos)
2919252726Srpaulo{
2920252726Srpaulo	struct p2p_channel *pref = NULL, *n;
2921252726Srpaulo	unsigned int num = 0;
2922252726Srpaulo	const char *pos2;
2923252726Srpaulo	u8 op_class, chan;
2924252726Srpaulo
2925252726Srpaulo	/* format: class:chan,class:chan,... */
2926252726Srpaulo
2927252726Srpaulo	while (*pos) {
2928252726Srpaulo		op_class = atoi(pos);
2929252726Srpaulo		pos2 = os_strchr(pos, ':');
2930252726Srpaulo		if (pos2 == NULL)
2931252726Srpaulo			goto fail;
2932252726Srpaulo		pos2++;
2933252726Srpaulo		chan = atoi(pos2);
2934252726Srpaulo
2935252726Srpaulo		n = os_realloc_array(pref, num + 1,
2936252726Srpaulo				     sizeof(struct p2p_channel));
2937252726Srpaulo		if (n == NULL)
2938252726Srpaulo			goto fail;
2939252726Srpaulo		pref = n;
2940252726Srpaulo		pref[num].op_class = op_class;
2941252726Srpaulo		pref[num].chan = chan;
2942252726Srpaulo		num++;
2943252726Srpaulo
2944252726Srpaulo		pos = os_strchr(pos2, ',');
2945252726Srpaulo		if (pos == NULL)
2946252726Srpaulo			break;
2947252726Srpaulo		pos++;
2948252726Srpaulo	}
2949252726Srpaulo
2950252726Srpaulo	os_free(config->p2p_pref_chan);
2951252726Srpaulo	config->p2p_pref_chan = pref;
2952252726Srpaulo	config->num_p2p_pref_chan = num;
2953252726Srpaulo	wpa_hexdump(MSG_DEBUG, "P2P: Preferred class/channel pairs",
2954252726Srpaulo		    (u8 *) config->p2p_pref_chan,
2955252726Srpaulo		    config->num_p2p_pref_chan * sizeof(struct p2p_channel));
2956252726Srpaulo
2957252726Srpaulo	return 0;
2958252726Srpaulo
2959252726Srpaulofail:
2960252726Srpaulo	os_free(pref);
2961252726Srpaulo	wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line);
2962252726Srpaulo	return -1;
2963252726Srpaulo}
2964252726Srpaulo#endif /* CONFIG_P2P */
2965252726Srpaulo
2966252726Srpaulo
2967252726Srpaulostatic int wpa_config_process_hessid(
2968252726Srpaulo	const struct global_parse_data *data,
2969252726Srpaulo	struct wpa_config *config, int line, const char *pos)
2970252726Srpaulo{
2971252726Srpaulo	if (hwaddr_aton2(pos, config->hessid) < 0) {
2972252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: Invalid hessid '%s'",
2973252726Srpaulo			   line, pos);
2974252726Srpaulo		return -1;
2975252726Srpaulo	}
2976252726Srpaulo
2977252726Srpaulo	return 0;
2978252726Srpaulo}
2979252726Srpaulo
2980252726Srpaulo
2981252726Srpaulo#ifdef OFFSET
2982252726Srpaulo#undef OFFSET
2983252726Srpaulo#endif /* OFFSET */
2984252726Srpaulo/* OFFSET: Get offset of a variable within the wpa_config structure */
2985252726Srpaulo#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v)
2986252726Srpaulo
2987252726Srpaulo#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL
2988252726Srpaulo#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL
2989252726Srpaulo#define _INT(f) #f, wpa_global_config_parse_int, OFFSET(f)
2990252726Srpaulo#define INT(f) _INT(f), NULL, NULL
2991252726Srpaulo#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
2992252726Srpaulo#define _STR(f) #f, wpa_global_config_parse_str, OFFSET(f)
2993252726Srpaulo#define STR(f) _STR(f), NULL, NULL
2994252726Srpaulo#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
2995252726Srpaulo#define BIN(f) #f, wpa_global_config_parse_bin, OFFSET(f), NULL, NULL
2996252726Srpaulo
2997252726Srpaulostatic const struct global_parse_data global_fields[] = {
2998252726Srpaulo#ifdef CONFIG_CTRL_IFACE
2999252726Srpaulo	{ STR(ctrl_interface), 0 },
3000252726Srpaulo	{ STR(ctrl_interface_group), 0 } /* deprecated */,
3001252726Srpaulo#endif /* CONFIG_CTRL_IFACE */
3002252726Srpaulo	{ INT_RANGE(eapol_version, 1, 2), 0 },
3003252726Srpaulo	{ INT(ap_scan), 0 },
3004252726Srpaulo	{ INT(disable_scan_offload), 0 },
3005252726Srpaulo	{ INT(fast_reauth), 0 },
3006252726Srpaulo	{ STR(opensc_engine_path), 0 },
3007252726Srpaulo	{ STR(pkcs11_engine_path), 0 },
3008252726Srpaulo	{ STR(pkcs11_module_path), 0 },
3009252726Srpaulo	{ STR(pcsc_reader), 0 },
3010252726Srpaulo	{ STR(pcsc_pin), 0 },
3011252726Srpaulo	{ STR(driver_param), 0 },
3012252726Srpaulo	{ INT(dot11RSNAConfigPMKLifetime), 0 },
3013252726Srpaulo	{ INT(dot11RSNAConfigPMKReauthThreshold), 0 },
3014252726Srpaulo	{ INT(dot11RSNAConfigSATimeout), 0 },
3015252726Srpaulo#ifndef CONFIG_NO_CONFIG_WRITE
3016252726Srpaulo	{ INT(update_config), 0 },
3017252726Srpaulo#endif /* CONFIG_NO_CONFIG_WRITE */
3018252726Srpaulo	{ FUNC_NO_VAR(load_dynamic_eap), 0 },
3019252726Srpaulo#ifdef CONFIG_WPS
3020252726Srpaulo	{ FUNC(uuid), CFG_CHANGED_UUID },
3021252726Srpaulo	{ STR_RANGE(device_name, 0, 32), CFG_CHANGED_DEVICE_NAME },
3022252726Srpaulo	{ STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING },
3023252726Srpaulo	{ STR_RANGE(model_name, 0, 32), CFG_CHANGED_WPS_STRING },
3024252726Srpaulo	{ STR_RANGE(model_number, 0, 32), CFG_CHANGED_WPS_STRING },
3025252726Srpaulo	{ STR_RANGE(serial_number, 0, 32), CFG_CHANGED_WPS_STRING },
3026252726Srpaulo	{ FUNC(device_type), CFG_CHANGED_DEVICE_TYPE },
3027252726Srpaulo	{ FUNC(os_version), CFG_CHANGED_OS_VERSION },
3028252726Srpaulo	{ STR(config_methods), CFG_CHANGED_CONFIG_METHODS },
3029252726Srpaulo	{ INT_RANGE(wps_cred_processing, 0, 2), 0 },
3030252726Srpaulo	{ FUNC(wps_vendor_ext_m1), CFG_CHANGED_VENDOR_EXTENSION },
3031252726Srpaulo#endif /* CONFIG_WPS */
3032252726Srpaulo#ifdef CONFIG_P2P
3033252726Srpaulo	{ FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
3034252726Srpaulo	{ INT(p2p_listen_reg_class), 0 },
3035252726Srpaulo	{ INT(p2p_listen_channel), 0 },
3036252726Srpaulo	{ INT(p2p_oper_reg_class), 0 },
3037252726Srpaulo	{ INT(p2p_oper_channel), 0 },
3038252726Srpaulo	{ INT_RANGE(p2p_go_intent, 0, 15), 0 },
3039252726Srpaulo	{ STR(p2p_ssid_postfix), CFG_CHANGED_P2P_SSID_POSTFIX },
3040252726Srpaulo	{ INT_RANGE(persistent_reconnect, 0, 1), 0 },
3041252726Srpaulo	{ INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
3042252726Srpaulo	{ INT(p2p_group_idle), 0 },
3043252726Srpaulo	{ FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
3044252726Srpaulo	{ INT(p2p_go_ht40), 0 },
3045252726Srpaulo	{ INT(p2p_disabled), 0 },
3046252726Srpaulo	{ INT(p2p_no_group_iface), 0 },
3047252726Srpaulo#endif /* CONFIG_P2P */
3048252726Srpaulo	{ FUNC(country), CFG_CHANGED_COUNTRY },
3049252726Srpaulo	{ INT(bss_max_count), 0 },
3050252726Srpaulo	{ INT(bss_expiration_age), 0 },
3051252726Srpaulo	{ INT(bss_expiration_scan_count), 0 },
3052252726Srpaulo	{ INT_RANGE(filter_ssids, 0, 1), 0 },
3053252726Srpaulo	{ INT_RANGE(filter_rssi, -100, 0), 0 },
3054252726Srpaulo	{ INT(max_num_sta), 0 },
3055252726Srpaulo	{ INT_RANGE(disassoc_low_ack, 0, 1), 0 },
3056252726Srpaulo#ifdef CONFIG_HS20
3057252726Srpaulo	{ INT_RANGE(hs20, 0, 1), 0 },
3058252726Srpaulo#endif /* CONFIG_HS20 */
3059252726Srpaulo	{ INT_RANGE(interworking, 0, 1), 0 },
3060252726Srpaulo	{ FUNC(hessid), 0 },
3061252726Srpaulo	{ INT_RANGE(access_network_type, 0, 15), 0 },
3062252726Srpaulo	{ INT_RANGE(pbc_in_m1, 0, 1), 0 },
3063252726Srpaulo	{ STR(autoscan), 0 },
3064252726Srpaulo	{ INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff), 0 },
3065252726Srpaulo	{ BIN(wps_nfc_dh_pubkey), 0 },
3066252726Srpaulo	{ BIN(wps_nfc_dh_privkey), 0 },
3067252726Srpaulo	{ BIN(wps_nfc_dev_pw), 0 },
3068252726Srpaulo	{ STR(ext_password_backend), CFG_CHANGED_EXT_PW_BACKEND },
3069252726Srpaulo	{ INT(p2p_go_max_inactivity), 0 },
3070252726Srpaulo	{ INT_RANGE(auto_interworking, 0, 1), 0 },
3071252726Srpaulo	{ INT(okc), 0 },
3072252726Srpaulo	{ INT(pmf), 0 },
3073252726Srpaulo};
3074252726Srpaulo
3075252726Srpaulo#undef FUNC
3076252726Srpaulo#undef _INT
3077252726Srpaulo#undef INT
3078252726Srpaulo#undef INT_RANGE
3079252726Srpaulo#undef _STR
3080252726Srpaulo#undef STR
3081252726Srpaulo#undef STR_RANGE
3082252726Srpaulo#undef BIN
3083252726Srpaulo#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0]))
3084252726Srpaulo
3085252726Srpaulo
3086252726Srpauloint wpa_config_process_global(struct wpa_config *config, char *pos, int line)
3087252726Srpaulo{
3088252726Srpaulo	size_t i;
3089252726Srpaulo	int ret = 0;
3090252726Srpaulo
3091252726Srpaulo	for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
3092252726Srpaulo		const struct global_parse_data *field = &global_fields[i];
3093252726Srpaulo		size_t flen = os_strlen(field->name);
3094252726Srpaulo		if (os_strncmp(pos, field->name, flen) != 0 ||
3095252726Srpaulo		    pos[flen] != '=')
3096252726Srpaulo			continue;
3097252726Srpaulo
3098252726Srpaulo		if (field->parser(field, config, line, pos + flen + 1)) {
3099252726Srpaulo			wpa_printf(MSG_ERROR, "Line %d: failed to "
3100252726Srpaulo				   "parse '%s'.", line, pos);
3101252726Srpaulo			ret = -1;
3102252726Srpaulo		}
3103252726Srpaulo		config->changed_parameters |= field->changed_flag;
3104252726Srpaulo		break;
3105252726Srpaulo	}
3106252726Srpaulo	if (i == NUM_GLOBAL_FIELDS) {
3107252726Srpaulo#ifdef CONFIG_AP
3108252726Srpaulo		if (os_strncmp(pos, "wmm_ac_", 7) == 0) {
3109252726Srpaulo			char *tmp = os_strchr(pos, '=');
3110252726Srpaulo			if (tmp == NULL) {
3111252726Srpaulo				if (line < 0)
3112252726Srpaulo					return -1;
3113252726Srpaulo				wpa_printf(MSG_ERROR, "Line %d: invalid line "
3114252726Srpaulo					   "'%s'", line, pos);
3115252726Srpaulo				return -1;
3116252726Srpaulo			}
3117252726Srpaulo			*tmp++ = '\0';
3118252726Srpaulo			if (hostapd_config_wmm_ac(config->wmm_ac_params, pos,
3119252726Srpaulo						  tmp)) {
3120252726Srpaulo				wpa_printf(MSG_ERROR, "Line %d: invalid WMM "
3121252726Srpaulo					   "AC item", line);
3122252726Srpaulo				return -1;
3123252726Srpaulo			}
3124252726Srpaulo		}
3125252726Srpaulo#endif /* CONFIG_AP */
3126252726Srpaulo		if (line < 0)
3127252726Srpaulo			return -1;
3128252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.",
3129252726Srpaulo			   line, pos);
3130252726Srpaulo		ret = -1;
3131252726Srpaulo	}
3132252726Srpaulo
3133252726Srpaulo	return ret;
3134252726Srpaulo}
3135