1189251Ssam/*
2189251Ssam * WPA Supplicant / Configuration parser and common functions
3346981Scy * Copyright (c) 2003-2018, 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"
13281806Srpaulo#include "utils/ip_addr.h"
14346981Scy#include "common/ieee802_1x_defs.h"
15214734Srpaulo#include "crypto/sha1.h"
16214734Srpaulo#include "rsn_supp/wpa.h"
17189251Ssam#include "eap_peer/eap.h"
18252726Srpaulo#include "p2p/p2p.h"
19289549Srpaulo#include "fst/fst.h"
20189251Ssam#include "config.h"
21189251Ssam
22189251Ssam
23189251Ssam#if !defined(CONFIG_CTRL_IFACE) && defined(CONFIG_NO_CONFIG_WRITE)
24189251Ssam#define NO_CONFIG_WRITE
25189251Ssam#endif
26189251Ssam
27189251Ssam/*
28189251Ssam * Structure for network configuration parsing. This data is used to implement
29189251Ssam * a generic parser for each network block variable. The table of configuration
30189251Ssam * variables is defined below in this file (ssid_fields[]).
31189251Ssam */
32189251Ssamstruct parse_data {
33189251Ssam	/* Configuration variable name */
34189251Ssam	char *name;
35189251Ssam
36337817Scy	/* Parser function for this variable. The parser functions return 0 or 1
37337817Scy	 * to indicate success. Value 0 indicates that the parameter value may
38337817Scy	 * have changed while value 1 means that the value did not change.
39337817Scy	 * Error cases (failure to parse the string) are indicated by returning
40337817Scy	 * -1. */
41189251Ssam	int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid,
42189251Ssam		      int line, const char *value);
43189251Ssam
44189251Ssam#ifndef NO_CONFIG_WRITE
45189251Ssam	/* Writer function (i.e., to get the variable in text format from
46189251Ssam	 * internal presentation). */
47189251Ssam	char * (*writer)(const struct parse_data *data, struct wpa_ssid *ssid);
48189251Ssam#endif /* NO_CONFIG_WRITE */
49189251Ssam
50189251Ssam	/* Variable specific parameters for the parser. */
51189251Ssam	void *param1, *param2, *param3, *param4;
52189251Ssam
53189251Ssam	/* 0 = this variable can be included in debug output and ctrl_iface
54189251Ssam	 * 1 = this variable contains key/private data and it must not be
55189251Ssam	 *     included in debug output unless explicitly requested. In
56189251Ssam	 *     addition, this variable will not be readable through the
57189251Ssam	 *     ctrl_iface.
58189251Ssam	 */
59189251Ssam	int key_data;
60189251Ssam};
61189251Ssam
62189251Ssam
63189251Ssamstatic int wpa_config_parse_str(const struct parse_data *data,
64189251Ssam				struct wpa_ssid *ssid,
65189251Ssam				int line, const char *value)
66189251Ssam{
67337817Scy	size_t res_len, *dst_len, prev_len;
68189251Ssam	char **dst, *tmp;
69189251Ssam
70189251Ssam	if (os_strcmp(value, "NULL") == 0) {
71189251Ssam		wpa_printf(MSG_DEBUG, "Unset configuration string '%s'",
72189251Ssam			   data->name);
73189251Ssam		tmp = NULL;
74189251Ssam		res_len = 0;
75189251Ssam		goto set;
76189251Ssam	}
77189251Ssam
78189251Ssam	tmp = wpa_config_parse_string(value, &res_len);
79189251Ssam	if (tmp == NULL) {
80189251Ssam		wpa_printf(MSG_ERROR, "Line %d: failed to parse %s '%s'.",
81189251Ssam			   line, data->name,
82189251Ssam			   data->key_data ? "[KEY DATA REMOVED]" : value);
83189251Ssam		return -1;
84189251Ssam	}
85189251Ssam
86189251Ssam	if (data->key_data) {
87189251Ssam		wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
88189251Ssam				      (u8 *) tmp, res_len);
89189251Ssam	} else {
90189251Ssam		wpa_hexdump_ascii(MSG_MSGDUMP, data->name,
91189251Ssam				  (u8 *) tmp, res_len);
92189251Ssam	}
93189251Ssam
94189251Ssam	if (data->param3 && res_len < (size_t) data->param3) {
95189251Ssam		wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
96189251Ssam			   "min_len=%ld)", line, data->name,
97189251Ssam			   (unsigned long) res_len, (long) data->param3);
98189251Ssam		os_free(tmp);
99189251Ssam		return -1;
100189251Ssam	}
101189251Ssam
102189251Ssam	if (data->param4 && res_len > (size_t) data->param4) {
103189251Ssam		wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
104189251Ssam			   "max_len=%ld)", line, data->name,
105189251Ssam			   (unsigned long) res_len, (long) data->param4);
106189251Ssam		os_free(tmp);
107189251Ssam		return -1;
108189251Ssam	}
109189251Ssam
110189251Ssamset:
111189251Ssam	dst = (char **) (((u8 *) ssid) + (long) data->param1);
112189251Ssam	dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2);
113337817Scy
114337817Scy	if (data->param2)
115337817Scy		prev_len = *dst_len;
116337817Scy	else if (*dst)
117337817Scy		prev_len = os_strlen(*dst);
118337817Scy	else
119337817Scy		prev_len = 0;
120337817Scy	if ((*dst == NULL && tmp == NULL) ||
121337817Scy	    (*dst && tmp && prev_len == res_len &&
122337817Scy	     os_memcmp(*dst, tmp, res_len) == 0)) {
123337817Scy		/* No change to the previously configured value */
124337817Scy		os_free(tmp);
125337817Scy		return 1;
126337817Scy	}
127337817Scy
128189251Ssam	os_free(*dst);
129189251Ssam	*dst = tmp;
130189251Ssam	if (data->param2)
131189251Ssam		*dst_len = res_len;
132189251Ssam
133189251Ssam	return 0;
134189251Ssam}
135189251Ssam
136189251Ssam
137189251Ssam#ifndef NO_CONFIG_WRITE
138189251Ssamstatic char * wpa_config_write_string_ascii(const u8 *value, size_t len)
139189251Ssam{
140189251Ssam	char *buf;
141189251Ssam
142189251Ssam	buf = os_malloc(len + 3);
143189251Ssam	if (buf == NULL)
144189251Ssam		return NULL;
145189251Ssam	buf[0] = '"';
146189251Ssam	os_memcpy(buf + 1, value, len);
147189251Ssam	buf[len + 1] = '"';
148189251Ssam	buf[len + 2] = '\0';
149189251Ssam
150189251Ssam	return buf;
151189251Ssam}
152189251Ssam
153189251Ssam
154189251Ssamstatic char * wpa_config_write_string_hex(const u8 *value, size_t len)
155189251Ssam{
156189251Ssam	char *buf;
157189251Ssam
158189251Ssam	buf = os_zalloc(2 * len + 1);
159189251Ssam	if (buf == NULL)
160189251Ssam		return NULL;
161189251Ssam	wpa_snprintf_hex(buf, 2 * len + 1, value, len);
162189251Ssam
163189251Ssam	return buf;
164189251Ssam}
165189251Ssam
166189251Ssam
167189251Ssamstatic char * wpa_config_write_string(const u8 *value, size_t len)
168189251Ssam{
169189251Ssam	if (value == NULL)
170189251Ssam		return NULL;
171189251Ssam
172189251Ssam	if (is_hex(value, len))
173189251Ssam		return wpa_config_write_string_hex(value, len);
174189251Ssam	else
175189251Ssam		return wpa_config_write_string_ascii(value, len);
176189251Ssam}
177189251Ssam
178189251Ssam
179189251Ssamstatic char * wpa_config_write_str(const struct parse_data *data,
180189251Ssam				   struct wpa_ssid *ssid)
181189251Ssam{
182189251Ssam	size_t len;
183189251Ssam	char **src;
184189251Ssam
185189251Ssam	src = (char **) (((u8 *) ssid) + (long) data->param1);
186189251Ssam	if (*src == NULL)
187189251Ssam		return NULL;
188189251Ssam
189189251Ssam	if (data->param2)
190189251Ssam		len = *((size_t *) (((u8 *) ssid) + (long) data->param2));
191189251Ssam	else
192189251Ssam		len = os_strlen(*src);
193189251Ssam
194189251Ssam	return wpa_config_write_string((const u8 *) *src, len);
195189251Ssam}
196189251Ssam#endif /* NO_CONFIG_WRITE */
197189251Ssam
198189251Ssam
199189251Ssamstatic int wpa_config_parse_int(const struct parse_data *data,
200189251Ssam				struct wpa_ssid *ssid,
201189251Ssam				int line, const char *value)
202189251Ssam{
203281806Srpaulo	int val, *dst;
204281806Srpaulo	char *end;
205189251Ssam
206189251Ssam	dst = (int *) (((u8 *) ssid) + (long) data->param1);
207281806Srpaulo	val = strtol(value, &end, 0);
208281806Srpaulo	if (*end) {
209281806Srpaulo		wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"",
210281806Srpaulo			   line, value);
211281806Srpaulo		return -1;
212281806Srpaulo	}
213337817Scy
214337817Scy	if (*dst == val)
215337817Scy		return 1;
216281806Srpaulo	*dst = val;
217189251Ssam	wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
218189251Ssam
219189251Ssam	if (data->param3 && *dst < (long) data->param3) {
220189251Ssam		wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
221189251Ssam			   "min_value=%ld)", line, data->name, *dst,
222189251Ssam			   (long) data->param3);
223189251Ssam		*dst = (long) data->param3;
224189251Ssam		return -1;
225189251Ssam	}
226189251Ssam
227189251Ssam	if (data->param4 && *dst > (long) data->param4) {
228189251Ssam		wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
229189251Ssam			   "max_value=%ld)", line, data->name, *dst,
230189251Ssam			   (long) data->param4);
231189251Ssam		*dst = (long) data->param4;
232189251Ssam		return -1;
233189251Ssam	}
234189251Ssam
235189251Ssam	return 0;
236189251Ssam}
237189251Ssam
238189251Ssam
239189251Ssam#ifndef NO_CONFIG_WRITE
240189251Ssamstatic char * wpa_config_write_int(const struct parse_data *data,
241189251Ssam				   struct wpa_ssid *ssid)
242189251Ssam{
243189251Ssam	int *src, res;
244189251Ssam	char *value;
245189251Ssam
246189251Ssam	src = (int *) (((u8 *) ssid) + (long) data->param1);
247189251Ssam
248189251Ssam	value = os_malloc(20);
249189251Ssam	if (value == NULL)
250189251Ssam		return NULL;
251189251Ssam	res = os_snprintf(value, 20, "%d", *src);
252281806Srpaulo	if (os_snprintf_error(20, res)) {
253189251Ssam		os_free(value);
254189251Ssam		return NULL;
255189251Ssam	}
256189251Ssam	value[20 - 1] = '\0';
257189251Ssam	return value;
258189251Ssam}
259189251Ssam#endif /* NO_CONFIG_WRITE */
260189251Ssam
261189251Ssam
262281806Srpaulostatic int wpa_config_parse_addr_list(const struct parse_data *data,
263281806Srpaulo				      int line, const char *value,
264281806Srpaulo				      u8 **list, size_t *num, char *name,
265281806Srpaulo				      u8 abort_on_error, u8 masked)
266281806Srpaulo{
267281806Srpaulo	const char *pos;
268281806Srpaulo	u8 *buf, *n, addr[2 * ETH_ALEN];
269281806Srpaulo	size_t count;
270281806Srpaulo
271281806Srpaulo	buf = NULL;
272281806Srpaulo	count = 0;
273281806Srpaulo
274281806Srpaulo	pos = value;
275281806Srpaulo	while (pos && *pos) {
276281806Srpaulo		while (*pos == ' ')
277281806Srpaulo			pos++;
278281806Srpaulo
279281806Srpaulo		if (hwaddr_masked_aton(pos, addr, &addr[ETH_ALEN], masked)) {
280281806Srpaulo			if (abort_on_error || count == 0) {
281281806Srpaulo				wpa_printf(MSG_ERROR,
282281806Srpaulo					   "Line %d: Invalid %s address '%s'",
283281806Srpaulo					   line, name, value);
284281806Srpaulo				os_free(buf);
285281806Srpaulo				return -1;
286281806Srpaulo			}
287281806Srpaulo			/* continue anyway since this could have been from a
288281806Srpaulo			 * truncated configuration file line */
289281806Srpaulo			wpa_printf(MSG_INFO,
290281806Srpaulo				   "Line %d: Ignore likely truncated %s address '%s'",
291281806Srpaulo				   line, name, pos);
292281806Srpaulo		} else {
293281806Srpaulo			n = os_realloc_array(buf, count + 1, 2 * ETH_ALEN);
294281806Srpaulo			if (n == NULL) {
295281806Srpaulo				os_free(buf);
296281806Srpaulo				return -1;
297281806Srpaulo			}
298281806Srpaulo			buf = n;
299281806Srpaulo			os_memmove(buf + 2 * ETH_ALEN, buf,
300281806Srpaulo				   count * 2 * ETH_ALEN);
301281806Srpaulo			os_memcpy(buf, addr, 2 * ETH_ALEN);
302281806Srpaulo			count++;
303281806Srpaulo			wpa_printf(MSG_MSGDUMP,
304281806Srpaulo				   "%s: addr=" MACSTR " mask=" MACSTR,
305281806Srpaulo				   name, MAC2STR(addr),
306281806Srpaulo				   MAC2STR(&addr[ETH_ALEN]));
307281806Srpaulo		}
308281806Srpaulo
309281806Srpaulo		pos = os_strchr(pos, ' ');
310281806Srpaulo	}
311281806Srpaulo
312281806Srpaulo	os_free(*list);
313281806Srpaulo	*list = buf;
314281806Srpaulo	*num = count;
315281806Srpaulo
316281806Srpaulo	return 0;
317281806Srpaulo}
318281806Srpaulo
319281806Srpaulo
320281806Srpaulo#ifndef NO_CONFIG_WRITE
321281806Srpaulostatic char * wpa_config_write_addr_list(const struct parse_data *data,
322281806Srpaulo					 const u8 *list, size_t num, char *name)
323281806Srpaulo{
324281806Srpaulo	char *value, *end, *pos;
325281806Srpaulo	int res;
326281806Srpaulo	size_t i;
327281806Srpaulo
328281806Srpaulo	if (list == NULL || num == 0)
329281806Srpaulo		return NULL;
330281806Srpaulo
331281806Srpaulo	value = os_malloc(2 * 20 * num);
332281806Srpaulo	if (value == NULL)
333281806Srpaulo		return NULL;
334281806Srpaulo	pos = value;
335281806Srpaulo	end = value + 2 * 20 * num;
336281806Srpaulo
337281806Srpaulo	for (i = num; i > 0; i--) {
338281806Srpaulo		const u8 *a = list + (i - 1) * 2 * ETH_ALEN;
339281806Srpaulo		const u8 *m = a + ETH_ALEN;
340281806Srpaulo
341281806Srpaulo		if (i < num)
342281806Srpaulo			*pos++ = ' ';
343281806Srpaulo		res = hwaddr_mask_txt(pos, end - pos, a, m);
344281806Srpaulo		if (res < 0) {
345281806Srpaulo			os_free(value);
346281806Srpaulo			return NULL;
347281806Srpaulo		}
348281806Srpaulo		pos += res;
349281806Srpaulo	}
350281806Srpaulo
351281806Srpaulo	return value;
352281806Srpaulo}
353281806Srpaulo#endif /* NO_CONFIG_WRITE */
354281806Srpaulo
355189251Ssamstatic int wpa_config_parse_bssid(const struct parse_data *data,
356189251Ssam				  struct wpa_ssid *ssid, int line,
357189251Ssam				  const char *value)
358189251Ssam{
359252726Srpaulo	if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
360252726Srpaulo	    os_strcmp(value, "any") == 0) {
361252726Srpaulo		ssid->bssid_set = 0;
362252726Srpaulo		wpa_printf(MSG_MSGDUMP, "BSSID any");
363252726Srpaulo		return 0;
364252726Srpaulo	}
365189251Ssam	if (hwaddr_aton(value, ssid->bssid)) {
366189251Ssam		wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.",
367189251Ssam			   line, value);
368189251Ssam		return -1;
369189251Ssam	}
370189251Ssam	ssid->bssid_set = 1;
371189251Ssam	wpa_hexdump(MSG_MSGDUMP, "BSSID", ssid->bssid, ETH_ALEN);
372189251Ssam	return 0;
373189251Ssam}
374189251Ssam
375189251Ssam
376189251Ssam#ifndef NO_CONFIG_WRITE
377189251Ssamstatic char * wpa_config_write_bssid(const struct parse_data *data,
378189251Ssam				     struct wpa_ssid *ssid)
379189251Ssam{
380189251Ssam	char *value;
381189251Ssam	int res;
382189251Ssam
383189251Ssam	if (!ssid->bssid_set)
384189251Ssam		return NULL;
385189251Ssam
386189251Ssam	value = os_malloc(20);
387189251Ssam	if (value == NULL)
388189251Ssam		return NULL;
389189251Ssam	res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid));
390281806Srpaulo	if (os_snprintf_error(20, res)) {
391189251Ssam		os_free(value);
392189251Ssam		return NULL;
393189251Ssam	}
394189251Ssam	value[20 - 1] = '\0';
395189251Ssam	return value;
396189251Ssam}
397189251Ssam#endif /* NO_CONFIG_WRITE */
398189251Ssam
399189251Ssam
400346981Scystatic int wpa_config_parse_bssid_hint(const struct parse_data *data,
401346981Scy				       struct wpa_ssid *ssid, int line,
402346981Scy				       const char *value)
403346981Scy{
404346981Scy	if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
405346981Scy	    os_strcmp(value, "any") == 0) {
406346981Scy		ssid->bssid_hint_set = 0;
407346981Scy		wpa_printf(MSG_MSGDUMP, "BSSID hint any");
408346981Scy		return 0;
409346981Scy	}
410346981Scy	if (hwaddr_aton(value, ssid->bssid_hint)) {
411346981Scy		wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID hint '%s'.",
412346981Scy			   line, value);
413346981Scy		return -1;
414346981Scy	}
415346981Scy	ssid->bssid_hint_set = 1;
416346981Scy	wpa_hexdump(MSG_MSGDUMP, "BSSID hint", ssid->bssid_hint, ETH_ALEN);
417346981Scy	return 0;
418346981Scy}
419346981Scy
420346981Scy
421346981Scy#ifndef NO_CONFIG_WRITE
422346981Scystatic char * wpa_config_write_bssid_hint(const struct parse_data *data,
423346981Scy					  struct wpa_ssid *ssid)
424346981Scy{
425346981Scy	char *value;
426346981Scy	int res;
427346981Scy
428346981Scy	if (!ssid->bssid_hint_set)
429346981Scy		return NULL;
430346981Scy
431346981Scy	value = os_malloc(20);
432346981Scy	if (!value)
433346981Scy		return NULL;
434346981Scy	res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid_hint));
435346981Scy	if (os_snprintf_error(20, res)) {
436346981Scy		os_free(value);
437346981Scy		return NULL;
438346981Scy	}
439346981Scy	return value;
440346981Scy}
441346981Scy#endif /* NO_CONFIG_WRITE */
442346981Scy
443346981Scy
444281806Srpaulostatic int wpa_config_parse_bssid_blacklist(const struct parse_data *data,
445281806Srpaulo					    struct wpa_ssid *ssid, int line,
446281806Srpaulo					    const char *value)
447281806Srpaulo{
448281806Srpaulo	return wpa_config_parse_addr_list(data, line, value,
449281806Srpaulo					  &ssid->bssid_blacklist,
450281806Srpaulo					  &ssid->num_bssid_blacklist,
451281806Srpaulo					  "bssid_blacklist", 1, 1);
452281806Srpaulo}
453281806Srpaulo
454281806Srpaulo
455281806Srpaulo#ifndef NO_CONFIG_WRITE
456281806Srpaulostatic char * wpa_config_write_bssid_blacklist(const struct parse_data *data,
457281806Srpaulo					       struct wpa_ssid *ssid)
458281806Srpaulo{
459281806Srpaulo	return wpa_config_write_addr_list(data, ssid->bssid_blacklist,
460281806Srpaulo					  ssid->num_bssid_blacklist,
461281806Srpaulo					  "bssid_blacklist");
462281806Srpaulo}
463281806Srpaulo#endif /* NO_CONFIG_WRITE */
464281806Srpaulo
465281806Srpaulo
466281806Srpaulostatic int wpa_config_parse_bssid_whitelist(const struct parse_data *data,
467281806Srpaulo					    struct wpa_ssid *ssid, int line,
468281806Srpaulo					    const char *value)
469281806Srpaulo{
470281806Srpaulo	return wpa_config_parse_addr_list(data, line, value,
471281806Srpaulo					  &ssid->bssid_whitelist,
472281806Srpaulo					  &ssid->num_bssid_whitelist,
473281806Srpaulo					  "bssid_whitelist", 1, 1);
474281806Srpaulo}
475281806Srpaulo
476281806Srpaulo
477281806Srpaulo#ifndef NO_CONFIG_WRITE
478281806Srpaulostatic char * wpa_config_write_bssid_whitelist(const struct parse_data *data,
479281806Srpaulo					       struct wpa_ssid *ssid)
480281806Srpaulo{
481281806Srpaulo	return wpa_config_write_addr_list(data, ssid->bssid_whitelist,
482281806Srpaulo					  ssid->num_bssid_whitelist,
483281806Srpaulo					  "bssid_whitelist");
484281806Srpaulo}
485281806Srpaulo#endif /* NO_CONFIG_WRITE */
486281806Srpaulo
487281806Srpaulo
488189251Ssamstatic int wpa_config_parse_psk(const struct parse_data *data,
489189251Ssam				struct wpa_ssid *ssid, int line,
490189251Ssam				const char *value)
491189251Ssam{
492252726Srpaulo#ifdef CONFIG_EXT_PASSWORD
493252726Srpaulo	if (os_strncmp(value, "ext:", 4) == 0) {
494281806Srpaulo		str_clear_free(ssid->passphrase);
495252726Srpaulo		ssid->passphrase = NULL;
496252726Srpaulo		ssid->psk_set = 0;
497252726Srpaulo		os_free(ssid->ext_psk);
498252726Srpaulo		ssid->ext_psk = os_strdup(value + 4);
499252726Srpaulo		if (ssid->ext_psk == NULL)
500252726Srpaulo			return -1;
501252726Srpaulo		wpa_printf(MSG_DEBUG, "PSK: External password '%s'",
502252726Srpaulo			   ssid->ext_psk);
503252726Srpaulo		return 0;
504252726Srpaulo	}
505252726Srpaulo#endif /* CONFIG_EXT_PASSWORD */
506252726Srpaulo
507189251Ssam	if (*value == '"') {
508189251Ssam#ifndef CONFIG_NO_PBKDF2
509189251Ssam		const char *pos;
510189251Ssam		size_t len;
511189251Ssam
512189251Ssam		value++;
513189251Ssam		pos = os_strrchr(value, '"');
514189251Ssam		if (pos)
515189251Ssam			len = pos - value;
516189251Ssam		else
517189251Ssam			len = os_strlen(value);
518189251Ssam		if (len < 8 || len > 63) {
519189251Ssam			wpa_printf(MSG_ERROR, "Line %d: Invalid passphrase "
520189251Ssam				   "length %lu (expected: 8..63) '%s'.",
521189251Ssam				   line, (unsigned long) len, value);
522189251Ssam			return -1;
523189251Ssam		}
524189251Ssam		wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)",
525189251Ssam				      (u8 *) value, len);
526337817Scy		if (has_ctrl_char((u8 *) value, len)) {
527337817Scy			wpa_printf(MSG_ERROR,
528337817Scy				   "Line %d: Invalid passphrase character",
529337817Scy				   line);
530337817Scy			return -1;
531337817Scy		}
532189251Ssam		if (ssid->passphrase && os_strlen(ssid->passphrase) == len &&
533337817Scy		    os_memcmp(ssid->passphrase, value, len) == 0) {
534337817Scy			/* No change to the previously configured value */
535337817Scy			return 1;
536337817Scy		}
537189251Ssam		ssid->psk_set = 0;
538281806Srpaulo		str_clear_free(ssid->passphrase);
539281806Srpaulo		ssid->passphrase = dup_binstr(value, len);
540189251Ssam		if (ssid->passphrase == NULL)
541189251Ssam			return -1;
542189251Ssam		return 0;
543189251Ssam#else /* CONFIG_NO_PBKDF2 */
544189251Ssam		wpa_printf(MSG_ERROR, "Line %d: ASCII passphrase not "
545189251Ssam			   "supported.", line);
546189251Ssam		return -1;
547189251Ssam#endif /* CONFIG_NO_PBKDF2 */
548189251Ssam	}
549189251Ssam
550189251Ssam	if (hexstr2bin(value, ssid->psk, PMK_LEN) ||
551189251Ssam	    value[PMK_LEN * 2] != '\0') {
552189251Ssam		wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
553189251Ssam			   line, value);
554189251Ssam		return -1;
555189251Ssam	}
556189251Ssam
557281806Srpaulo	str_clear_free(ssid->passphrase);
558189251Ssam	ssid->passphrase = NULL;
559189251Ssam
560189251Ssam	ssid->psk_set = 1;
561189251Ssam	wpa_hexdump_key(MSG_MSGDUMP, "PSK", ssid->psk, PMK_LEN);
562189251Ssam	return 0;
563189251Ssam}
564189251Ssam
565189251Ssam
566189251Ssam#ifndef NO_CONFIG_WRITE
567189251Ssamstatic char * wpa_config_write_psk(const struct parse_data *data,
568189251Ssam				   struct wpa_ssid *ssid)
569189251Ssam{
570252726Srpaulo#ifdef CONFIG_EXT_PASSWORD
571252726Srpaulo	if (ssid->ext_psk) {
572252726Srpaulo		size_t len = 4 + os_strlen(ssid->ext_psk) + 1;
573252726Srpaulo		char *buf = os_malloc(len);
574281806Srpaulo		int res;
575281806Srpaulo
576252726Srpaulo		if (buf == NULL)
577252726Srpaulo			return NULL;
578281806Srpaulo		res = os_snprintf(buf, len, "ext:%s", ssid->ext_psk);
579281806Srpaulo		if (os_snprintf_error(len, res)) {
580281806Srpaulo			os_free(buf);
581281806Srpaulo			buf = NULL;
582281806Srpaulo		}
583252726Srpaulo		return buf;
584252726Srpaulo	}
585252726Srpaulo#endif /* CONFIG_EXT_PASSWORD */
586252726Srpaulo
587189251Ssam	if (ssid->passphrase)
588189251Ssam		return wpa_config_write_string_ascii(
589189251Ssam			(const u8 *) ssid->passphrase,
590189251Ssam			os_strlen(ssid->passphrase));
591189251Ssam
592189251Ssam	if (ssid->psk_set)
593189251Ssam		return wpa_config_write_string_hex(ssid->psk, PMK_LEN);
594189251Ssam
595189251Ssam	return NULL;
596189251Ssam}
597189251Ssam#endif /* NO_CONFIG_WRITE */
598189251Ssam
599189251Ssam
600189251Ssamstatic int wpa_config_parse_proto(const struct parse_data *data,
601189251Ssam				  struct wpa_ssid *ssid, int line,
602189251Ssam				  const char *value)
603189251Ssam{
604189251Ssam	int val = 0, last, errors = 0;
605189251Ssam	char *start, *end, *buf;
606189251Ssam
607189251Ssam	buf = os_strdup(value);
608189251Ssam	if (buf == NULL)
609189251Ssam		return -1;
610189251Ssam	start = buf;
611189251Ssam
612189251Ssam	while (*start != '\0') {
613189251Ssam		while (*start == ' ' || *start == '\t')
614189251Ssam			start++;
615189251Ssam		if (*start == '\0')
616189251Ssam			break;
617189251Ssam		end = start;
618189251Ssam		while (*end != ' ' && *end != '\t' && *end != '\0')
619189251Ssam			end++;
620189251Ssam		last = *end == '\0';
621189251Ssam		*end = '\0';
622189251Ssam		if (os_strcmp(start, "WPA") == 0)
623189251Ssam			val |= WPA_PROTO_WPA;
624189251Ssam		else if (os_strcmp(start, "RSN") == 0 ||
625189251Ssam			 os_strcmp(start, "WPA2") == 0)
626189251Ssam			val |= WPA_PROTO_RSN;
627281806Srpaulo		else if (os_strcmp(start, "OSEN") == 0)
628281806Srpaulo			val |= WPA_PROTO_OSEN;
629189251Ssam		else {
630189251Ssam			wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'",
631189251Ssam				   line, start);
632189251Ssam			errors++;
633189251Ssam		}
634189251Ssam
635189251Ssam		if (last)
636189251Ssam			break;
637189251Ssam		start = end + 1;
638189251Ssam	}
639189251Ssam	os_free(buf);
640189251Ssam
641189251Ssam	if (val == 0) {
642189251Ssam		wpa_printf(MSG_ERROR,
643189251Ssam			   "Line %d: no proto values configured.", line);
644189251Ssam		errors++;
645189251Ssam	}
646189251Ssam
647337817Scy	if (!errors && ssid->proto == val)
648337817Scy		return 1;
649189251Ssam	wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val);
650189251Ssam	ssid->proto = val;
651189251Ssam	return errors ? -1 : 0;
652189251Ssam}
653189251Ssam
654189251Ssam
655189251Ssam#ifndef NO_CONFIG_WRITE
656189251Ssamstatic char * wpa_config_write_proto(const struct parse_data *data,
657189251Ssam				     struct wpa_ssid *ssid)
658189251Ssam{
659281806Srpaulo	int ret;
660189251Ssam	char *buf, *pos, *end;
661189251Ssam
662281806Srpaulo	pos = buf = os_zalloc(20);
663189251Ssam	if (buf == NULL)
664189251Ssam		return NULL;
665281806Srpaulo	end = buf + 20;
666189251Ssam
667189251Ssam	if (ssid->proto & WPA_PROTO_WPA) {
668281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sWPA",
669281806Srpaulo				  pos == buf ? "" : " ");
670281806Srpaulo		if (os_snprintf_error(end - pos, ret))
671189251Ssam			return buf;
672189251Ssam		pos += ret;
673189251Ssam	}
674189251Ssam
675189251Ssam	if (ssid->proto & WPA_PROTO_RSN) {
676281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sRSN",
677281806Srpaulo				  pos == buf ? "" : " ");
678281806Srpaulo		if (os_snprintf_error(end - pos, ret))
679189251Ssam			return buf;
680189251Ssam		pos += ret;
681189251Ssam	}
682189251Ssam
683281806Srpaulo	if (ssid->proto & WPA_PROTO_OSEN) {
684281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sOSEN",
685281806Srpaulo				  pos == buf ? "" : " ");
686281806Srpaulo		if (os_snprintf_error(end - pos, ret))
687281806Srpaulo			return buf;
688281806Srpaulo		pos += ret;
689281806Srpaulo	}
690281806Srpaulo
691281806Srpaulo	if (pos == buf) {
692281806Srpaulo		os_free(buf);
693281806Srpaulo		buf = NULL;
694281806Srpaulo	}
695281806Srpaulo
696189251Ssam	return buf;
697189251Ssam}
698189251Ssam#endif /* NO_CONFIG_WRITE */
699189251Ssam
700189251Ssam
701189251Ssamstatic int wpa_config_parse_key_mgmt(const struct parse_data *data,
702189251Ssam				     struct wpa_ssid *ssid, int line,
703189251Ssam				     const char *value)
704189251Ssam{
705189251Ssam	int val = 0, last, errors = 0;
706189251Ssam	char *start, *end, *buf;
707189251Ssam
708189251Ssam	buf = os_strdup(value);
709189251Ssam	if (buf == NULL)
710189251Ssam		return -1;
711189251Ssam	start = buf;
712189251Ssam
713189251Ssam	while (*start != '\0') {
714189251Ssam		while (*start == ' ' || *start == '\t')
715189251Ssam			start++;
716189251Ssam		if (*start == '\0')
717189251Ssam			break;
718189251Ssam		end = start;
719189251Ssam		while (*end != ' ' && *end != '\t' && *end != '\0')
720189251Ssam			end++;
721189251Ssam		last = *end == '\0';
722189251Ssam		*end = '\0';
723189251Ssam		if (os_strcmp(start, "WPA-PSK") == 0)
724189251Ssam			val |= WPA_KEY_MGMT_PSK;
725189251Ssam		else if (os_strcmp(start, "WPA-EAP") == 0)
726189251Ssam			val |= WPA_KEY_MGMT_IEEE8021X;
727189251Ssam		else if (os_strcmp(start, "IEEE8021X") == 0)
728189251Ssam			val |= WPA_KEY_MGMT_IEEE8021X_NO_WPA;
729189251Ssam		else if (os_strcmp(start, "NONE") == 0)
730189251Ssam			val |= WPA_KEY_MGMT_NONE;
731189251Ssam		else if (os_strcmp(start, "WPA-NONE") == 0)
732189251Ssam			val |= WPA_KEY_MGMT_WPA_NONE;
733189251Ssam#ifdef CONFIG_IEEE80211R
734189251Ssam		else if (os_strcmp(start, "FT-PSK") == 0)
735189251Ssam			val |= WPA_KEY_MGMT_FT_PSK;
736189251Ssam		else if (os_strcmp(start, "FT-EAP") == 0)
737189251Ssam			val |= WPA_KEY_MGMT_FT_IEEE8021X;
738346981Scy#ifdef CONFIG_SHA384
739346981Scy		else if (os_strcmp(start, "FT-EAP-SHA384") == 0)
740346981Scy			val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
741346981Scy#endif /* CONFIG_SHA384 */
742189251Ssam#endif /* CONFIG_IEEE80211R */
743189251Ssam#ifdef CONFIG_IEEE80211W
744189251Ssam		else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
745189251Ssam			val |= WPA_KEY_MGMT_PSK_SHA256;
746189251Ssam		else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
747189251Ssam			val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
748189251Ssam#endif /* CONFIG_IEEE80211W */
749189251Ssam#ifdef CONFIG_WPS
750189251Ssam		else if (os_strcmp(start, "WPS") == 0)
751189251Ssam			val |= WPA_KEY_MGMT_WPS;
752189251Ssam#endif /* CONFIG_WPS */
753252726Srpaulo#ifdef CONFIG_SAE
754252726Srpaulo		else if (os_strcmp(start, "SAE") == 0)
755252726Srpaulo			val |= WPA_KEY_MGMT_SAE;
756252726Srpaulo		else if (os_strcmp(start, "FT-SAE") == 0)
757252726Srpaulo			val |= WPA_KEY_MGMT_FT_SAE;
758252726Srpaulo#endif /* CONFIG_SAE */
759281806Srpaulo#ifdef CONFIG_HS20
760281806Srpaulo		else if (os_strcmp(start, "OSEN") == 0)
761281806Srpaulo			val |= WPA_KEY_MGMT_OSEN;
762281806Srpaulo#endif /* CONFIG_HS20 */
763281806Srpaulo#ifdef CONFIG_SUITEB
764281806Srpaulo		else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0)
765281806Srpaulo			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B;
766281806Srpaulo#endif /* CONFIG_SUITEB */
767281806Srpaulo#ifdef CONFIG_SUITEB192
768281806Srpaulo		else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
769281806Srpaulo			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
770281806Srpaulo#endif /* CONFIG_SUITEB192 */
771346981Scy#ifdef CONFIG_FILS
772346981Scy		else if (os_strcmp(start, "FILS-SHA256") == 0)
773346981Scy			val |= WPA_KEY_MGMT_FILS_SHA256;
774346981Scy		else if (os_strcmp(start, "FILS-SHA384") == 0)
775346981Scy			val |= WPA_KEY_MGMT_FILS_SHA384;
776346981Scy#ifdef CONFIG_IEEE80211R
777346981Scy		else if (os_strcmp(start, "FT-FILS-SHA256") == 0)
778346981Scy			val |= WPA_KEY_MGMT_FT_FILS_SHA256;
779346981Scy		else if (os_strcmp(start, "FT-FILS-SHA384") == 0)
780346981Scy			val |= WPA_KEY_MGMT_FT_FILS_SHA384;
781346981Scy#endif /* CONFIG_IEEE80211R */
782346981Scy#endif /* CONFIG_FILS */
783346981Scy#ifdef CONFIG_OWE
784346981Scy		else if (os_strcmp(start, "OWE") == 0)
785346981Scy			val |= WPA_KEY_MGMT_OWE;
786346981Scy#endif /* CONFIG_OWE */
787346981Scy#ifdef CONFIG_DPP
788346981Scy		else if (os_strcmp(start, "DPP") == 0)
789346981Scy			val |= WPA_KEY_MGMT_DPP;
790346981Scy#endif /* CONFIG_DPP */
791189251Ssam		else {
792189251Ssam			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
793189251Ssam				   line, start);
794189251Ssam			errors++;
795189251Ssam		}
796189251Ssam
797189251Ssam		if (last)
798189251Ssam			break;
799189251Ssam		start = end + 1;
800189251Ssam	}
801189251Ssam	os_free(buf);
802189251Ssam
803189251Ssam	if (val == 0) {
804189251Ssam		wpa_printf(MSG_ERROR,
805189251Ssam			   "Line %d: no key_mgmt values configured.", line);
806189251Ssam		errors++;
807189251Ssam	}
808189251Ssam
809337817Scy	if (!errors && ssid->key_mgmt == val)
810337817Scy		return 1;
811189251Ssam	wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val);
812189251Ssam	ssid->key_mgmt = val;
813189251Ssam	return errors ? -1 : 0;
814189251Ssam}
815189251Ssam
816189251Ssam
817189251Ssam#ifndef NO_CONFIG_WRITE
818189251Ssamstatic char * wpa_config_write_key_mgmt(const struct parse_data *data,
819189251Ssam					struct wpa_ssid *ssid)
820189251Ssam{
821189251Ssam	char *buf, *pos, *end;
822189251Ssam	int ret;
823189251Ssam
824281806Srpaulo	pos = buf = os_zalloc(100);
825189251Ssam	if (buf == NULL)
826189251Ssam		return NULL;
827281806Srpaulo	end = buf + 100;
828189251Ssam
829189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
830189251Ssam		ret = os_snprintf(pos, end - pos, "%sWPA-PSK",
831189251Ssam				  pos == buf ? "" : " ");
832281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
833189251Ssam			end[-1] = '\0';
834189251Ssam			return buf;
835189251Ssam		}
836189251Ssam		pos += ret;
837189251Ssam	}
838189251Ssam
839189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
840189251Ssam		ret = os_snprintf(pos, end - pos, "%sWPA-EAP",
841189251Ssam				  pos == buf ? "" : " ");
842281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
843189251Ssam			end[-1] = '\0';
844189251Ssam			return buf;
845189251Ssam		}
846189251Ssam		pos += ret;
847189251Ssam	}
848189251Ssam
849189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
850189251Ssam		ret = os_snprintf(pos, end - pos, "%sIEEE8021X",
851189251Ssam				  pos == buf ? "" : " ");
852281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
853189251Ssam			end[-1] = '\0';
854189251Ssam			return buf;
855189251Ssam		}
856189251Ssam		pos += ret;
857189251Ssam	}
858189251Ssam
859189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_NONE) {
860189251Ssam		ret = os_snprintf(pos, end - pos, "%sNONE",
861189251Ssam				  pos == buf ? "" : " ");
862281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
863189251Ssam			end[-1] = '\0';
864189251Ssam			return buf;
865189251Ssam		}
866189251Ssam		pos += ret;
867189251Ssam	}
868189251Ssam
869189251Ssam	if (ssid->key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
870189251Ssam		ret = os_snprintf(pos, end - pos, "%sWPA-NONE",
871189251Ssam				  pos == buf ? "" : " ");
872281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
873189251Ssam			end[-1] = '\0';
874189251Ssam			return buf;
875189251Ssam		}
876189251Ssam		pos += ret;
877189251Ssam	}
878189251Ssam
879189251Ssam#ifdef CONFIG_IEEE80211R
880281806Srpaulo	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK) {
881281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sFT-PSK",
882281806Srpaulo				  pos == buf ? "" : " ");
883281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
884281806Srpaulo			end[-1] = '\0';
885281806Srpaulo			return buf;
886281806Srpaulo		}
887281806Srpaulo		pos += ret;
888281806Srpaulo	}
889189251Ssam
890281806Srpaulo	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
891281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sFT-EAP",
892281806Srpaulo				  pos == buf ? "" : " ");
893281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
894281806Srpaulo			end[-1] = '\0';
895281806Srpaulo			return buf;
896281806Srpaulo		}
897281806Srpaulo		pos += ret;
898281806Srpaulo	}
899346981Scy
900346981Scy#ifdef CONFIG_SHA384
901346981Scy	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
902346981Scy		ret = os_snprintf(pos, end - pos, "%sFT-EAP-SHA384",
903346981Scy				  pos == buf ? "" : " ");
904346981Scy		if (os_snprintf_error(end - pos, ret)) {
905346981Scy			end[-1] = '\0';
906346981Scy			return buf;
907346981Scy		}
908346981Scy		pos += ret;
909346981Scy	}
910346981Scy#endif /* CONFIG_SHA384 */
911189251Ssam#endif /* CONFIG_IEEE80211R */
912189251Ssam
913189251Ssam#ifdef CONFIG_IEEE80211W
914281806Srpaulo	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
915281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
916281806Srpaulo				  pos == buf ? "" : " ");
917281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
918281806Srpaulo			end[-1] = '\0';
919281806Srpaulo			return buf;
920189251Ssam		}
921281806Srpaulo		pos += ret;
922189251Ssam	}
923189251Ssam
924281806Srpaulo	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
925281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256",
926281806Srpaulo				  pos == buf ? "" : " ");
927281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
928281806Srpaulo			end[-1] = '\0';
929281806Srpaulo			return buf;
930281806Srpaulo		}
931281806Srpaulo		pos += ret;
932189251Ssam	}
933281806Srpaulo#endif /* CONFIG_IEEE80211W */
934189251Ssam
935281806Srpaulo#ifdef CONFIG_WPS
936281806Srpaulo	if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
937281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sWPS",
938189251Ssam				  pos == buf ? "" : " ");
939281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
940189251Ssam			end[-1] = '\0';
941189251Ssam			return buf;
942189251Ssam		}
943189251Ssam		pos += ret;
944189251Ssam	}
945281806Srpaulo#endif /* CONFIG_WPS */
946189251Ssam
947281806Srpaulo#ifdef CONFIG_SAE
948281806Srpaulo	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
949281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sSAE",
950252726Srpaulo				  pos == buf ? "" : " ");
951281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
952252726Srpaulo			end[-1] = '\0';
953252726Srpaulo			return buf;
954252726Srpaulo		}
955252726Srpaulo		pos += ret;
956252726Srpaulo	}
957252726Srpaulo
958281806Srpaulo	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_SAE) {
959281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sFT-SAE",
960189251Ssam				  pos == buf ? "" : " ");
961281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
962189251Ssam			end[-1] = '\0';
963189251Ssam			return buf;
964189251Ssam		}
965189251Ssam		pos += ret;
966189251Ssam	}
967281806Srpaulo#endif /* CONFIG_SAE */
968189251Ssam
969281806Srpaulo#ifdef CONFIG_HS20
970281806Srpaulo	if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN) {
971281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sOSEN",
972189251Ssam				  pos == buf ? "" : " ");
973281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
974189251Ssam			end[-1] = '\0';
975189251Ssam			return buf;
976189251Ssam		}
977189251Ssam		pos += ret;
978189251Ssam	}
979281806Srpaulo#endif /* CONFIG_HS20 */
980189251Ssam
981281806Srpaulo#ifdef CONFIG_SUITEB
982281806Srpaulo	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
983281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B",
984189251Ssam				  pos == buf ? "" : " ");
985281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
986189251Ssam			end[-1] = '\0';
987189251Ssam			return buf;
988189251Ssam		}
989189251Ssam		pos += ret;
990189251Ssam	}
991281806Srpaulo#endif /* CONFIG_SUITEB */
992189251Ssam
993281806Srpaulo#ifdef CONFIG_SUITEB192
994281806Srpaulo	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
995281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B-192",
996189251Ssam				  pos == buf ? "" : " ");
997281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
998189251Ssam			end[-1] = '\0';
999189251Ssam			return buf;
1000189251Ssam		}
1001189251Ssam		pos += ret;
1002189251Ssam	}
1003281806Srpaulo#endif /* CONFIG_SUITEB192 */
1004189251Ssam
1005346981Scy#ifdef CONFIG_FILS
1006346981Scy	if (ssid->key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
1007346981Scy		ret = os_snprintf(pos, end - pos, "%sFILS-SHA256",
1008346981Scy				  pos == buf ? "" : " ");
1009346981Scy		if (os_snprintf_error(end - pos, ret)) {
1010346981Scy			end[-1] = '\0';
1011346981Scy			return buf;
1012346981Scy		}
1013346981Scy		pos += ret;
1014346981Scy	}
1015346981Scy	if (ssid->key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
1016346981Scy		ret = os_snprintf(pos, end - pos, "%sFILS-SHA384",
1017346981Scy				  pos == buf ? "" : " ");
1018346981Scy		if (os_snprintf_error(end - pos, ret)) {
1019346981Scy			end[-1] = '\0';
1020346981Scy			return buf;
1021346981Scy		}
1022346981Scy		pos += ret;
1023346981Scy	}
1024346981Scy#ifdef CONFIG_IEEE80211R
1025346981Scy	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
1026346981Scy		ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA256",
1027346981Scy				  pos == buf ? "" : " ");
1028346981Scy		if (os_snprintf_error(end - pos, ret)) {
1029346981Scy			end[-1] = '\0';
1030346981Scy			return buf;
1031346981Scy		}
1032346981Scy		pos += ret;
1033346981Scy	}
1034346981Scy	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
1035346981Scy		ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA384",
1036346981Scy				  pos == buf ? "" : " ");
1037346981Scy		if (os_snprintf_error(end - pos, ret)) {
1038346981Scy			end[-1] = '\0';
1039346981Scy			return buf;
1040346981Scy		}
1041346981Scy		pos += ret;
1042346981Scy	}
1043346981Scy#endif /* CONFIG_IEEE80211R */
1044346981Scy#endif /* CONFIG_FILS */
1045346981Scy
1046346981Scy#ifdef CONFIG_DPP
1047346981Scy	if (ssid->key_mgmt & WPA_KEY_MGMT_DPP) {
1048346981Scy		ret = os_snprintf(pos, end - pos, "%sDPP",
1049346981Scy				  pos == buf ? "" : " ");
1050346981Scy		if (os_snprintf_error(end - pos, ret)) {
1051346981Scy			end[-1] = '\0';
1052346981Scy			return buf;
1053346981Scy		}
1054346981Scy		pos += ret;
1055346981Scy	}
1056346981Scy#endif /* CONFIG_DPP */
1057346981Scy
1058346981Scy#ifdef CONFIG_OWE
1059346981Scy	if (ssid->key_mgmt & WPA_KEY_MGMT_OWE) {
1060346981Scy		ret = os_snprintf(pos, end - pos, "%sOWE",
1061346981Scy				  pos == buf ? "" : " ");
1062346981Scy		if (os_snprintf_error(end - pos, ret)) {
1063346981Scy			end[-1] = '\0';
1064346981Scy			return buf;
1065346981Scy		}
1066346981Scy		pos += ret;
1067346981Scy	}
1068346981Scy#endif /* CONFIG_OWE */
1069346981Scy
1070281806Srpaulo	if (pos == buf) {
1071281806Srpaulo		os_free(buf);
1072281806Srpaulo		buf = NULL;
1073281806Srpaulo	}
1074281806Srpaulo
1075189251Ssam	return buf;
1076189251Ssam}
1077189251Ssam#endif /* NO_CONFIG_WRITE */
1078189251Ssam
1079189251Ssam
1080281806Srpaulostatic int wpa_config_parse_cipher(int line, const char *value)
1081281806Srpaulo{
1082337817Scy#ifdef CONFIG_NO_WPA
1083337817Scy	return -1;
1084337817Scy#else /* CONFIG_NO_WPA */
1085281806Srpaulo	int val = wpa_parse_cipher(value);
1086281806Srpaulo	if (val < 0) {
1087281806Srpaulo		wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
1088281806Srpaulo			   line, value);
1089281806Srpaulo		return -1;
1090281806Srpaulo	}
1091281806Srpaulo	if (val == 0) {
1092281806Srpaulo		wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.",
1093281806Srpaulo			   line);
1094281806Srpaulo		return -1;
1095281806Srpaulo	}
1096281806Srpaulo	return val;
1097337817Scy#endif /* CONFIG_NO_WPA */
1098281806Srpaulo}
1099281806Srpaulo
1100281806Srpaulo
1101281806Srpaulo#ifndef NO_CONFIG_WRITE
1102281806Srpaulostatic char * wpa_config_write_cipher(int cipher)
1103281806Srpaulo{
1104337817Scy#ifdef CONFIG_NO_WPA
1105337817Scy	return NULL;
1106337817Scy#else /* CONFIG_NO_WPA */
1107281806Srpaulo	char *buf = os_zalloc(50);
1108281806Srpaulo	if (buf == NULL)
1109281806Srpaulo		return NULL;
1110281806Srpaulo
1111281806Srpaulo	if (wpa_write_ciphers(buf, buf + 50, cipher, " ") < 0) {
1112281806Srpaulo		os_free(buf);
1113281806Srpaulo		return NULL;
1114281806Srpaulo	}
1115281806Srpaulo
1116281806Srpaulo	return buf;
1117337817Scy#endif /* CONFIG_NO_WPA */
1118281806Srpaulo}
1119281806Srpaulo#endif /* NO_CONFIG_WRITE */
1120281806Srpaulo
1121281806Srpaulo
1122189251Ssamstatic int wpa_config_parse_pairwise(const struct parse_data *data,
1123189251Ssam				     struct wpa_ssid *ssid, int line,
1124189251Ssam				     const char *value)
1125189251Ssam{
1126189251Ssam	int val;
1127189251Ssam	val = wpa_config_parse_cipher(line, value);
1128189251Ssam	if (val == -1)
1129189251Ssam		return -1;
1130281806Srpaulo	if (val & ~WPA_ALLOWED_PAIRWISE_CIPHERS) {
1131189251Ssam		wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher "
1132189251Ssam			   "(0x%x).", line, val);
1133189251Ssam		return -1;
1134189251Ssam	}
1135189251Ssam
1136337817Scy	if (ssid->pairwise_cipher == val)
1137337817Scy		return 1;
1138189251Ssam	wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val);
1139189251Ssam	ssid->pairwise_cipher = val;
1140189251Ssam	return 0;
1141189251Ssam}
1142189251Ssam
1143189251Ssam
1144189251Ssam#ifndef NO_CONFIG_WRITE
1145189251Ssamstatic char * wpa_config_write_pairwise(const struct parse_data *data,
1146189251Ssam					struct wpa_ssid *ssid)
1147189251Ssam{
1148189251Ssam	return wpa_config_write_cipher(ssid->pairwise_cipher);
1149189251Ssam}
1150189251Ssam#endif /* NO_CONFIG_WRITE */
1151189251Ssam
1152189251Ssam
1153189251Ssamstatic int wpa_config_parse_group(const struct parse_data *data,
1154189251Ssam				  struct wpa_ssid *ssid, int line,
1155189251Ssam				  const char *value)
1156189251Ssam{
1157189251Ssam	int val;
1158189251Ssam	val = wpa_config_parse_cipher(line, value);
1159189251Ssam	if (val == -1)
1160189251Ssam		return -1;
1161289549Srpaulo
1162289549Srpaulo	/*
1163289549Srpaulo	 * Backwards compatibility - filter out WEP ciphers that were previously
1164289549Srpaulo	 * allowed.
1165289549Srpaulo	 */
1166289549Srpaulo	val &= ~(WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40);
1167289549Srpaulo
1168281806Srpaulo	if (val & ~WPA_ALLOWED_GROUP_CIPHERS) {
1169189251Ssam		wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher "
1170189251Ssam			   "(0x%x).", line, val);
1171189251Ssam		return -1;
1172189251Ssam	}
1173189251Ssam
1174337817Scy	if (ssid->group_cipher == val)
1175337817Scy		return 1;
1176189251Ssam	wpa_printf(MSG_MSGDUMP, "group: 0x%x", val);
1177189251Ssam	ssid->group_cipher = val;
1178189251Ssam	return 0;
1179189251Ssam}
1180189251Ssam
1181189251Ssam
1182189251Ssam#ifndef NO_CONFIG_WRITE
1183189251Ssamstatic char * wpa_config_write_group(const struct parse_data *data,
1184189251Ssam				     struct wpa_ssid *ssid)
1185189251Ssam{
1186189251Ssam	return wpa_config_write_cipher(ssid->group_cipher);
1187189251Ssam}
1188189251Ssam#endif /* NO_CONFIG_WRITE */
1189189251Ssam
1190189251Ssam
1191346981Scystatic int wpa_config_parse_group_mgmt(const struct parse_data *data,
1192346981Scy				       struct wpa_ssid *ssid, int line,
1193346981Scy				       const char *value)
1194346981Scy{
1195346981Scy	int val;
1196346981Scy
1197346981Scy	val = wpa_config_parse_cipher(line, value);
1198346981Scy	if (val == -1)
1199346981Scy		return -1;
1200346981Scy
1201346981Scy	if (val & ~WPA_ALLOWED_GROUP_MGMT_CIPHERS) {
1202346981Scy		wpa_printf(MSG_ERROR,
1203346981Scy			   "Line %d: not allowed group management cipher (0x%x).",
1204346981Scy			   line, val);
1205346981Scy		return -1;
1206346981Scy	}
1207346981Scy
1208346981Scy	if (ssid->group_mgmt_cipher == val)
1209346981Scy		return 1;
1210346981Scy	wpa_printf(MSG_MSGDUMP, "group_mgmt: 0x%x", val);
1211346981Scy	ssid->group_mgmt_cipher = val;
1212346981Scy	return 0;
1213346981Scy}
1214346981Scy
1215346981Scy
1216346981Scy#ifndef NO_CONFIG_WRITE
1217346981Scystatic char * wpa_config_write_group_mgmt(const struct parse_data *data,
1218346981Scy					  struct wpa_ssid *ssid)
1219346981Scy{
1220346981Scy	return wpa_config_write_cipher(ssid->group_mgmt_cipher);
1221346981Scy}
1222346981Scy#endif /* NO_CONFIG_WRITE */
1223346981Scy
1224346981Scy
1225189251Ssamstatic int wpa_config_parse_auth_alg(const struct parse_data *data,
1226189251Ssam				     struct wpa_ssid *ssid, int line,
1227189251Ssam				     const char *value)
1228189251Ssam{
1229189251Ssam	int val = 0, last, errors = 0;
1230189251Ssam	char *start, *end, *buf;
1231189251Ssam
1232189251Ssam	buf = os_strdup(value);
1233189251Ssam	if (buf == NULL)
1234189251Ssam		return -1;
1235189251Ssam	start = buf;
1236189251Ssam
1237189251Ssam	while (*start != '\0') {
1238189251Ssam		while (*start == ' ' || *start == '\t')
1239189251Ssam			start++;
1240189251Ssam		if (*start == '\0')
1241189251Ssam			break;
1242189251Ssam		end = start;
1243189251Ssam		while (*end != ' ' && *end != '\t' && *end != '\0')
1244189251Ssam			end++;
1245189251Ssam		last = *end == '\0';
1246189251Ssam		*end = '\0';
1247189251Ssam		if (os_strcmp(start, "OPEN") == 0)
1248189251Ssam			val |= WPA_AUTH_ALG_OPEN;
1249189251Ssam		else if (os_strcmp(start, "SHARED") == 0)
1250189251Ssam			val |= WPA_AUTH_ALG_SHARED;
1251189251Ssam		else if (os_strcmp(start, "LEAP") == 0)
1252189251Ssam			val |= WPA_AUTH_ALG_LEAP;
1253189251Ssam		else {
1254189251Ssam			wpa_printf(MSG_ERROR, "Line %d: invalid auth_alg '%s'",
1255189251Ssam				   line, start);
1256189251Ssam			errors++;
1257189251Ssam		}
1258189251Ssam
1259189251Ssam		if (last)
1260189251Ssam			break;
1261189251Ssam		start = end + 1;
1262189251Ssam	}
1263189251Ssam	os_free(buf);
1264189251Ssam
1265189251Ssam	if (val == 0) {
1266189251Ssam		wpa_printf(MSG_ERROR,
1267189251Ssam			   "Line %d: no auth_alg values configured.", line);
1268189251Ssam		errors++;
1269189251Ssam	}
1270189251Ssam
1271337817Scy	if (!errors && ssid->auth_alg == val)
1272337817Scy		return 1;
1273189251Ssam	wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val);
1274189251Ssam	ssid->auth_alg = val;
1275189251Ssam	return errors ? -1 : 0;
1276189251Ssam}
1277189251Ssam
1278189251Ssam
1279189251Ssam#ifndef NO_CONFIG_WRITE
1280189251Ssamstatic char * wpa_config_write_auth_alg(const struct parse_data *data,
1281189251Ssam					struct wpa_ssid *ssid)
1282189251Ssam{
1283189251Ssam	char *buf, *pos, *end;
1284189251Ssam	int ret;
1285189251Ssam
1286189251Ssam	pos = buf = os_zalloc(30);
1287189251Ssam	if (buf == NULL)
1288189251Ssam		return NULL;
1289189251Ssam	end = buf + 30;
1290189251Ssam
1291189251Ssam	if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) {
1292189251Ssam		ret = os_snprintf(pos, end - pos, "%sOPEN",
1293189251Ssam				  pos == buf ? "" : " ");
1294281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
1295189251Ssam			end[-1] = '\0';
1296189251Ssam			return buf;
1297189251Ssam		}
1298189251Ssam		pos += ret;
1299189251Ssam	}
1300189251Ssam
1301189251Ssam	if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) {
1302189251Ssam		ret = os_snprintf(pos, end - pos, "%sSHARED",
1303189251Ssam				  pos == buf ? "" : " ");
1304281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
1305189251Ssam			end[-1] = '\0';
1306189251Ssam			return buf;
1307189251Ssam		}
1308189251Ssam		pos += ret;
1309189251Ssam	}
1310189251Ssam
1311189251Ssam	if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) {
1312189251Ssam		ret = os_snprintf(pos, end - pos, "%sLEAP",
1313189251Ssam				  pos == buf ? "" : " ");
1314281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
1315189251Ssam			end[-1] = '\0';
1316189251Ssam			return buf;
1317189251Ssam		}
1318189251Ssam		pos += ret;
1319189251Ssam	}
1320189251Ssam
1321281806Srpaulo	if (pos == buf) {
1322281806Srpaulo		os_free(buf);
1323281806Srpaulo		buf = NULL;
1324281806Srpaulo	}
1325281806Srpaulo
1326189251Ssam	return buf;
1327189251Ssam}
1328189251Ssam#endif /* NO_CONFIG_WRITE */
1329189251Ssam
1330189251Ssam
1331281806Srpaulostatic int * wpa_config_parse_int_array(const char *value)
1332214734Srpaulo{
1333214734Srpaulo	int *freqs;
1334214734Srpaulo	size_t used, len;
1335214734Srpaulo	const char *pos;
1336214734Srpaulo
1337214734Srpaulo	used = 0;
1338214734Srpaulo	len = 10;
1339252726Srpaulo	freqs = os_calloc(len + 1, sizeof(int));
1340214734Srpaulo	if (freqs == NULL)
1341214734Srpaulo		return NULL;
1342214734Srpaulo
1343214734Srpaulo	pos = value;
1344214734Srpaulo	while (pos) {
1345214734Srpaulo		while (*pos == ' ')
1346214734Srpaulo			pos++;
1347214734Srpaulo		if (used == len) {
1348214734Srpaulo			int *n;
1349214734Srpaulo			size_t i;
1350252726Srpaulo			n = os_realloc_array(freqs, len * 2 + 1, sizeof(int));
1351214734Srpaulo			if (n == NULL) {
1352214734Srpaulo				os_free(freqs);
1353214734Srpaulo				return NULL;
1354214734Srpaulo			}
1355214734Srpaulo			for (i = len; i <= len * 2; i++)
1356214734Srpaulo				n[i] = 0;
1357214734Srpaulo			freqs = n;
1358214734Srpaulo			len *= 2;
1359214734Srpaulo		}
1360214734Srpaulo
1361214734Srpaulo		freqs[used] = atoi(pos);
1362214734Srpaulo		if (freqs[used] == 0)
1363214734Srpaulo			break;
1364214734Srpaulo		used++;
1365214734Srpaulo		pos = os_strchr(pos + 1, ' ');
1366214734Srpaulo	}
1367214734Srpaulo
1368214734Srpaulo	return freqs;
1369214734Srpaulo}
1370214734Srpaulo
1371214734Srpaulo
1372214734Srpaulostatic int wpa_config_parse_scan_freq(const struct parse_data *data,
1373214734Srpaulo				      struct wpa_ssid *ssid, int line,
1374214734Srpaulo				      const char *value)
1375214734Srpaulo{
1376214734Srpaulo	int *freqs;
1377214734Srpaulo
1378281806Srpaulo	freqs = wpa_config_parse_int_array(value);
1379214734Srpaulo	if (freqs == NULL)
1380214734Srpaulo		return -1;
1381281806Srpaulo	if (freqs[0] == 0) {
1382281806Srpaulo		os_free(freqs);
1383281806Srpaulo		freqs = NULL;
1384281806Srpaulo	}
1385214734Srpaulo	os_free(ssid->scan_freq);
1386214734Srpaulo	ssid->scan_freq = freqs;
1387214734Srpaulo
1388214734Srpaulo	return 0;
1389214734Srpaulo}
1390214734Srpaulo
1391214734Srpaulo
1392214734Srpaulostatic int wpa_config_parse_freq_list(const struct parse_data *data,
1393214734Srpaulo				      struct wpa_ssid *ssid, int line,
1394214734Srpaulo				      const char *value)
1395214734Srpaulo{
1396214734Srpaulo	int *freqs;
1397214734Srpaulo
1398281806Srpaulo	freqs = wpa_config_parse_int_array(value);
1399214734Srpaulo	if (freqs == NULL)
1400214734Srpaulo		return -1;
1401281806Srpaulo	if (freqs[0] == 0) {
1402281806Srpaulo		os_free(freqs);
1403281806Srpaulo		freqs = NULL;
1404281806Srpaulo	}
1405214734Srpaulo	os_free(ssid->freq_list);
1406214734Srpaulo	ssid->freq_list = freqs;
1407214734Srpaulo
1408214734Srpaulo	return 0;
1409214734Srpaulo}
1410214734Srpaulo
1411214734Srpaulo
1412214734Srpaulo#ifndef NO_CONFIG_WRITE
1413214734Srpaulostatic char * wpa_config_write_freqs(const struct parse_data *data,
1414214734Srpaulo				     const int *freqs)
1415214734Srpaulo{
1416214734Srpaulo	char *buf, *pos, *end;
1417214734Srpaulo	int i, ret;
1418214734Srpaulo	size_t count;
1419214734Srpaulo
1420214734Srpaulo	if (freqs == NULL)
1421214734Srpaulo		return NULL;
1422214734Srpaulo
1423214734Srpaulo	count = 0;
1424214734Srpaulo	for (i = 0; freqs[i]; i++)
1425214734Srpaulo		count++;
1426214734Srpaulo
1427214734Srpaulo	pos = buf = os_zalloc(10 * count + 1);
1428214734Srpaulo	if (buf == NULL)
1429214734Srpaulo		return NULL;
1430214734Srpaulo	end = buf + 10 * count + 1;
1431214734Srpaulo
1432214734Srpaulo	for (i = 0; freqs[i]; i++) {
1433214734Srpaulo		ret = os_snprintf(pos, end - pos, "%s%u",
1434214734Srpaulo				  i == 0 ? "" : " ", freqs[i]);
1435281806Srpaulo		if (os_snprintf_error(end - pos, ret)) {
1436214734Srpaulo			end[-1] = '\0';
1437214734Srpaulo			return buf;
1438214734Srpaulo		}
1439214734Srpaulo		pos += ret;
1440214734Srpaulo	}
1441214734Srpaulo
1442214734Srpaulo	return buf;
1443214734Srpaulo}
1444214734Srpaulo
1445214734Srpaulo
1446214734Srpaulostatic char * wpa_config_write_scan_freq(const struct parse_data *data,
1447214734Srpaulo					 struct wpa_ssid *ssid)
1448214734Srpaulo{
1449214734Srpaulo	return wpa_config_write_freqs(data, ssid->scan_freq);
1450214734Srpaulo}
1451214734Srpaulo
1452214734Srpaulo
1453214734Srpaulostatic char * wpa_config_write_freq_list(const struct parse_data *data,
1454214734Srpaulo					 struct wpa_ssid *ssid)
1455214734Srpaulo{
1456214734Srpaulo	return wpa_config_write_freqs(data, ssid->freq_list);
1457214734Srpaulo}
1458214734Srpaulo#endif /* NO_CONFIG_WRITE */
1459214734Srpaulo
1460214734Srpaulo
1461189251Ssam#ifdef IEEE8021X_EAPOL
1462189251Ssamstatic int wpa_config_parse_eap(const struct parse_data *data,
1463189251Ssam				struct wpa_ssid *ssid, int line,
1464189251Ssam				const char *value)
1465189251Ssam{
1466189251Ssam	int last, errors = 0;
1467189251Ssam	char *start, *end, *buf;
1468189251Ssam	struct eap_method_type *methods = NULL, *tmp;
1469189251Ssam	size_t num_methods = 0;
1470189251Ssam
1471189251Ssam	buf = os_strdup(value);
1472189251Ssam	if (buf == NULL)
1473189251Ssam		return -1;
1474189251Ssam	start = buf;
1475189251Ssam
1476189251Ssam	while (*start != '\0') {
1477189251Ssam		while (*start == ' ' || *start == '\t')
1478189251Ssam			start++;
1479189251Ssam		if (*start == '\0')
1480189251Ssam			break;
1481189251Ssam		end = start;
1482189251Ssam		while (*end != ' ' && *end != '\t' && *end != '\0')
1483189251Ssam			end++;
1484189251Ssam		last = *end == '\0';
1485189251Ssam		*end = '\0';
1486189251Ssam		tmp = methods;
1487252726Srpaulo		methods = os_realloc_array(methods, num_methods + 1,
1488252726Srpaulo					   sizeof(*methods));
1489189251Ssam		if (methods == NULL) {
1490189251Ssam			os_free(tmp);
1491189251Ssam			os_free(buf);
1492189251Ssam			return -1;
1493189251Ssam		}
1494189251Ssam		methods[num_methods].method = eap_peer_get_type(
1495189251Ssam			start, &methods[num_methods].vendor);
1496189251Ssam		if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
1497189251Ssam		    methods[num_methods].method == EAP_TYPE_NONE) {
1498189251Ssam			wpa_printf(MSG_ERROR, "Line %d: unknown EAP method "
1499189251Ssam				   "'%s'", line, start);
1500189251Ssam			wpa_printf(MSG_ERROR, "You may need to add support for"
1501189251Ssam				   " this EAP method during wpa_supplicant\n"
1502189251Ssam				   "build time configuration.\n"
1503189251Ssam				   "See README for more information.");
1504189251Ssam			errors++;
1505189251Ssam		} else if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
1506189251Ssam			   methods[num_methods].method == EAP_TYPE_LEAP)
1507189251Ssam			ssid->leap++;
1508189251Ssam		else
1509189251Ssam			ssid->non_leap++;
1510189251Ssam		num_methods++;
1511189251Ssam		if (last)
1512189251Ssam			break;
1513189251Ssam		start = end + 1;
1514189251Ssam	}
1515189251Ssam	os_free(buf);
1516189251Ssam
1517189251Ssam	tmp = methods;
1518252726Srpaulo	methods = os_realloc_array(methods, num_methods + 1, sizeof(*methods));
1519189251Ssam	if (methods == NULL) {
1520189251Ssam		os_free(tmp);
1521189251Ssam		return -1;
1522189251Ssam	}
1523189251Ssam	methods[num_methods].vendor = EAP_VENDOR_IETF;
1524189251Ssam	methods[num_methods].method = EAP_TYPE_NONE;
1525189251Ssam	num_methods++;
1526189251Ssam
1527337817Scy	if (!errors && ssid->eap.eap_methods) {
1528337817Scy		struct eap_method_type *prev_m;
1529337817Scy		size_t i, j, prev_methods, match = 0;
1530337817Scy
1531337817Scy		prev_m = ssid->eap.eap_methods;
1532337817Scy		for (i = 0; prev_m[i].vendor != EAP_VENDOR_IETF ||
1533337817Scy			     prev_m[i].method != EAP_TYPE_NONE; i++) {
1534337817Scy			/* Count the methods */
1535337817Scy		}
1536337817Scy		prev_methods = i + 1;
1537337817Scy
1538337817Scy		for (i = 0; prev_methods == num_methods && i < prev_methods;
1539337817Scy		     i++) {
1540337817Scy			for (j = 0; j < num_methods; j++) {
1541337817Scy				if (prev_m[i].vendor == methods[j].vendor &&
1542337817Scy				    prev_m[i].method == methods[j].method) {
1543337817Scy					match++;
1544337817Scy					break;
1545337817Scy				}
1546337817Scy			}
1547337817Scy		}
1548337817Scy		if (match == num_methods) {
1549337817Scy			os_free(methods);
1550337817Scy			return 1;
1551337817Scy		}
1552337817Scy	}
1553189251Ssam	wpa_hexdump(MSG_MSGDUMP, "eap methods",
1554189251Ssam		    (u8 *) methods, num_methods * sizeof(*methods));
1555252726Srpaulo	os_free(ssid->eap.eap_methods);
1556189251Ssam	ssid->eap.eap_methods = methods;
1557189251Ssam	return errors ? -1 : 0;
1558189251Ssam}
1559189251Ssam
1560189251Ssam
1561289549Srpaulo#ifndef NO_CONFIG_WRITE
1562189251Ssamstatic char * wpa_config_write_eap(const struct parse_data *data,
1563189251Ssam				   struct wpa_ssid *ssid)
1564189251Ssam{
1565189251Ssam	int i, ret;
1566189251Ssam	char *buf, *pos, *end;
1567189251Ssam	const struct eap_method_type *eap_methods = ssid->eap.eap_methods;
1568189251Ssam	const char *name;
1569189251Ssam
1570189251Ssam	if (eap_methods == NULL)
1571189251Ssam		return NULL;
1572189251Ssam
1573189251Ssam	pos = buf = os_zalloc(100);
1574189251Ssam	if (buf == NULL)
1575189251Ssam		return NULL;
1576189251Ssam	end = buf + 100;
1577189251Ssam
1578189251Ssam	for (i = 0; eap_methods[i].vendor != EAP_VENDOR_IETF ||
1579189251Ssam		     eap_methods[i].method != EAP_TYPE_NONE; i++) {
1580189251Ssam		name = eap_get_name(eap_methods[i].vendor,
1581189251Ssam				    eap_methods[i].method);
1582189251Ssam		if (name) {
1583189251Ssam			ret = os_snprintf(pos, end - pos, "%s%s",
1584189251Ssam					  pos == buf ? "" : " ", name);
1585281806Srpaulo			if (os_snprintf_error(end - pos, ret))
1586189251Ssam				break;
1587189251Ssam			pos += ret;
1588189251Ssam		}
1589189251Ssam	}
1590189251Ssam
1591189251Ssam	end[-1] = '\0';
1592189251Ssam
1593189251Ssam	return buf;
1594189251Ssam}
1595289549Srpaulo#endif /* NO_CONFIG_WRITE */
1596189251Ssam
1597189251Ssam
1598189251Ssamstatic int wpa_config_parse_password(const struct parse_data *data,
1599189251Ssam				     struct wpa_ssid *ssid, int line,
1600189251Ssam				     const char *value)
1601189251Ssam{
1602189251Ssam	u8 *hash;
1603189251Ssam
1604189251Ssam	if (os_strcmp(value, "NULL") == 0) {
1605337817Scy		if (!ssid->eap.password)
1606337817Scy			return 1; /* Already unset */
1607189251Ssam		wpa_printf(MSG_DEBUG, "Unset configuration string 'password'");
1608281806Srpaulo		bin_clear_free(ssid->eap.password, ssid->eap.password_len);
1609189251Ssam		ssid->eap.password = NULL;
1610189251Ssam		ssid->eap.password_len = 0;
1611189251Ssam		return 0;
1612189251Ssam	}
1613189251Ssam
1614252726Srpaulo#ifdef CONFIG_EXT_PASSWORD
1615252726Srpaulo	if (os_strncmp(value, "ext:", 4) == 0) {
1616252726Srpaulo		char *name = os_strdup(value + 4);
1617252726Srpaulo		if (name == NULL)
1618252726Srpaulo			return -1;
1619281806Srpaulo		bin_clear_free(ssid->eap.password, ssid->eap.password_len);
1620252726Srpaulo		ssid->eap.password = (u8 *) name;
1621252726Srpaulo		ssid->eap.password_len = os_strlen(name);
1622252726Srpaulo		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
1623252726Srpaulo		ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_PASSWORD;
1624252726Srpaulo		return 0;
1625252726Srpaulo	}
1626252726Srpaulo#endif /* CONFIG_EXT_PASSWORD */
1627252726Srpaulo
1628189251Ssam	if (os_strncmp(value, "hash:", 5) != 0) {
1629189251Ssam		char *tmp;
1630189251Ssam		size_t res_len;
1631189251Ssam
1632189251Ssam		tmp = wpa_config_parse_string(value, &res_len);
1633189251Ssam		if (tmp == NULL) {
1634189251Ssam			wpa_printf(MSG_ERROR, "Line %d: failed to parse "
1635189251Ssam				   "password.", line);
1636189251Ssam			return -1;
1637189251Ssam		}
1638189251Ssam		wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
1639189251Ssam				      (u8 *) tmp, res_len);
1640189251Ssam
1641281806Srpaulo		bin_clear_free(ssid->eap.password, ssid->eap.password_len);
1642189251Ssam		ssid->eap.password = (u8 *) tmp;
1643189251Ssam		ssid->eap.password_len = res_len;
1644189251Ssam		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
1645252726Srpaulo		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
1646189251Ssam
1647189251Ssam		return 0;
1648189251Ssam	}
1649189251Ssam
1650189251Ssam
1651189251Ssam	/* NtPasswordHash: hash:<32 hex digits> */
1652189251Ssam	if (os_strlen(value + 5) != 2 * 16) {
1653189251Ssam		wpa_printf(MSG_ERROR, "Line %d: Invalid password hash length "
1654189251Ssam			   "(expected 32 hex digits)", line);
1655189251Ssam		return -1;
1656189251Ssam	}
1657189251Ssam
1658189251Ssam	hash = os_malloc(16);
1659189251Ssam	if (hash == NULL)
1660189251Ssam		return -1;
1661189251Ssam
1662189251Ssam	if (hexstr2bin(value + 5, hash, 16)) {
1663189251Ssam		os_free(hash);
1664189251Ssam		wpa_printf(MSG_ERROR, "Line %d: Invalid password hash", line);
1665189251Ssam		return -1;
1666189251Ssam	}
1667189251Ssam
1668189251Ssam	wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
1669189251Ssam
1670337817Scy	if (ssid->eap.password && ssid->eap.password_len == 16 &&
1671337817Scy	    os_memcmp(ssid->eap.password, hash, 16) == 0 &&
1672337817Scy	    (ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) {
1673337817Scy		bin_clear_free(hash, 16);
1674337817Scy		return 1;
1675337817Scy	}
1676281806Srpaulo	bin_clear_free(ssid->eap.password, ssid->eap.password_len);
1677189251Ssam	ssid->eap.password = hash;
1678189251Ssam	ssid->eap.password_len = 16;
1679189251Ssam	ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
1680252726Srpaulo	ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
1681189251Ssam
1682189251Ssam	return 0;
1683189251Ssam}
1684189251Ssam
1685189251Ssam
1686289549Srpaulo#ifndef NO_CONFIG_WRITE
1687189251Ssamstatic char * wpa_config_write_password(const struct parse_data *data,
1688189251Ssam					struct wpa_ssid *ssid)
1689189251Ssam{
1690189251Ssam	char *buf;
1691189251Ssam
1692189251Ssam	if (ssid->eap.password == NULL)
1693189251Ssam		return NULL;
1694189251Ssam
1695252726Srpaulo#ifdef CONFIG_EXT_PASSWORD
1696252726Srpaulo	if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
1697252726Srpaulo		buf = os_zalloc(4 + ssid->eap.password_len + 1);
1698252726Srpaulo		if (buf == NULL)
1699252726Srpaulo			return NULL;
1700252726Srpaulo		os_memcpy(buf, "ext:", 4);
1701252726Srpaulo		os_memcpy(buf + 4, ssid->eap.password, ssid->eap.password_len);
1702252726Srpaulo		return buf;
1703252726Srpaulo	}
1704252726Srpaulo#endif /* CONFIG_EXT_PASSWORD */
1705252726Srpaulo
1706189251Ssam	if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) {
1707189251Ssam		return wpa_config_write_string(
1708189251Ssam			ssid->eap.password, ssid->eap.password_len);
1709189251Ssam	}
1710189251Ssam
1711189251Ssam	buf = os_malloc(5 + 32 + 1);
1712189251Ssam	if (buf == NULL)
1713189251Ssam		return NULL;
1714189251Ssam
1715189251Ssam	os_memcpy(buf, "hash:", 5);
1716189251Ssam	wpa_snprintf_hex(buf + 5, 32 + 1, ssid->eap.password, 16);
1717189251Ssam
1718189251Ssam	return buf;
1719189251Ssam}
1720289549Srpaulo#endif /* NO_CONFIG_WRITE */
1721189251Ssam#endif /* IEEE8021X_EAPOL */
1722189251Ssam
1723189251Ssam
1724189251Ssamstatic int wpa_config_parse_wep_key(u8 *key, size_t *len, int line,
1725189251Ssam				    const char *value, int idx)
1726189251Ssam{
1727189251Ssam	char *buf, title[20];
1728189251Ssam	int res;
1729189251Ssam
1730189251Ssam	buf = wpa_config_parse_string(value, len);
1731189251Ssam	if (buf == NULL) {
1732189251Ssam		wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key %d '%s'.",
1733189251Ssam			   line, idx, value);
1734189251Ssam		return -1;
1735189251Ssam	}
1736189251Ssam	if (*len > MAX_WEP_KEY_LEN) {
1737189251Ssam		wpa_printf(MSG_ERROR, "Line %d: Too long WEP key %d '%s'.",
1738189251Ssam			   line, idx, value);
1739189251Ssam		os_free(buf);
1740189251Ssam		return -1;
1741189251Ssam	}
1742252726Srpaulo	if (*len && *len != 5 && *len != 13 && *len != 16) {
1743252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key length %u - "
1744252726Srpaulo			   "this network block will be ignored",
1745252726Srpaulo			   line, (unsigned int) *len);
1746252726Srpaulo	}
1747189251Ssam	os_memcpy(key, buf, *len);
1748281806Srpaulo	str_clear_free(buf);
1749189251Ssam	res = os_snprintf(title, sizeof(title), "wep_key%d", idx);
1750281806Srpaulo	if (!os_snprintf_error(sizeof(title), res))
1751189251Ssam		wpa_hexdump_key(MSG_MSGDUMP, title, key, *len);
1752189251Ssam	return 0;
1753189251Ssam}
1754189251Ssam
1755189251Ssam
1756189251Ssamstatic int wpa_config_parse_wep_key0(const struct parse_data *data,
1757189251Ssam				     struct wpa_ssid *ssid, int line,
1758189251Ssam				     const char *value)
1759189251Ssam{
1760189251Ssam	return wpa_config_parse_wep_key(ssid->wep_key[0],
1761189251Ssam					&ssid->wep_key_len[0], line,
1762189251Ssam					value, 0);
1763189251Ssam}
1764189251Ssam
1765189251Ssam
1766189251Ssamstatic int wpa_config_parse_wep_key1(const struct parse_data *data,
1767189251Ssam				     struct wpa_ssid *ssid, int line,
1768189251Ssam				     const char *value)
1769189251Ssam{
1770189251Ssam	return wpa_config_parse_wep_key(ssid->wep_key[1],
1771189251Ssam					&ssid->wep_key_len[1], line,
1772189251Ssam					value, 1);
1773189251Ssam}
1774189251Ssam
1775189251Ssam
1776189251Ssamstatic int wpa_config_parse_wep_key2(const struct parse_data *data,
1777189251Ssam				     struct wpa_ssid *ssid, int line,
1778189251Ssam				     const char *value)
1779189251Ssam{
1780189251Ssam	return wpa_config_parse_wep_key(ssid->wep_key[2],
1781189251Ssam					&ssid->wep_key_len[2], line,
1782189251Ssam					value, 2);
1783189251Ssam}
1784189251Ssam
1785189251Ssam
1786189251Ssamstatic int wpa_config_parse_wep_key3(const struct parse_data *data,
1787189251Ssam				     struct wpa_ssid *ssid, int line,
1788189251Ssam				     const char *value)
1789189251Ssam{
1790189251Ssam	return wpa_config_parse_wep_key(ssid->wep_key[3],
1791189251Ssam					&ssid->wep_key_len[3], line,
1792189251Ssam					value, 3);
1793189251Ssam}
1794189251Ssam
1795189251Ssam
1796189251Ssam#ifndef NO_CONFIG_WRITE
1797189251Ssamstatic char * wpa_config_write_wep_key(struct wpa_ssid *ssid, int idx)
1798189251Ssam{
1799189251Ssam	if (ssid->wep_key_len[idx] == 0)
1800189251Ssam		return NULL;
1801189251Ssam	return wpa_config_write_string(ssid->wep_key[idx],
1802189251Ssam				       ssid->wep_key_len[idx]);
1803189251Ssam}
1804189251Ssam
1805189251Ssam
1806189251Ssamstatic char * wpa_config_write_wep_key0(const struct parse_data *data,
1807189251Ssam					struct wpa_ssid *ssid)
1808189251Ssam{
1809189251Ssam	return wpa_config_write_wep_key(ssid, 0);
1810189251Ssam}
1811189251Ssam
1812189251Ssam
1813189251Ssamstatic char * wpa_config_write_wep_key1(const struct parse_data *data,
1814189251Ssam					struct wpa_ssid *ssid)
1815189251Ssam{
1816189251Ssam	return wpa_config_write_wep_key(ssid, 1);
1817189251Ssam}
1818189251Ssam
1819189251Ssam
1820189251Ssamstatic char * wpa_config_write_wep_key2(const struct parse_data *data,
1821189251Ssam					struct wpa_ssid *ssid)
1822189251Ssam{
1823189251Ssam	return wpa_config_write_wep_key(ssid, 2);
1824189251Ssam}
1825189251Ssam
1826189251Ssam
1827189251Ssamstatic char * wpa_config_write_wep_key3(const struct parse_data *data,
1828189251Ssam					struct wpa_ssid *ssid)
1829189251Ssam{
1830189251Ssam	return wpa_config_write_wep_key(ssid, 3);
1831189251Ssam}
1832189251Ssam#endif /* NO_CONFIG_WRITE */
1833189251Ssam
1834189251Ssam
1835252726Srpaulo#ifdef CONFIG_P2P
1836252726Srpaulo
1837281806Srpaulostatic int wpa_config_parse_go_p2p_dev_addr(const struct parse_data *data,
1838252726Srpaulo					    struct wpa_ssid *ssid, int line,
1839252726Srpaulo					    const char *value)
1840252726Srpaulo{
1841281806Srpaulo	if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
1842281806Srpaulo	    os_strcmp(value, "any") == 0) {
1843281806Srpaulo		os_memset(ssid->go_p2p_dev_addr, 0, ETH_ALEN);
1844281806Srpaulo		wpa_printf(MSG_MSGDUMP, "GO P2P Device Address any");
1845281806Srpaulo		return 0;
1846281806Srpaulo	}
1847281806Srpaulo	if (hwaddr_aton(value, ssid->go_p2p_dev_addr)) {
1848281806Srpaulo		wpa_printf(MSG_ERROR, "Line %d: Invalid GO P2P Device Address '%s'.",
1849281806Srpaulo			   line, value);
1850281806Srpaulo		return -1;
1851281806Srpaulo	}
1852281806Srpaulo	ssid->bssid_set = 1;
1853281806Srpaulo	wpa_printf(MSG_MSGDUMP, "GO P2P Device Address " MACSTR,
1854281806Srpaulo		   MAC2STR(ssid->go_p2p_dev_addr));
1855281806Srpaulo	return 0;
1856281806Srpaulo}
1857252726Srpaulo
1858252726Srpaulo
1859281806Srpaulo#ifndef NO_CONFIG_WRITE
1860281806Srpaulostatic char * wpa_config_write_go_p2p_dev_addr(const struct parse_data *data,
1861281806Srpaulo					       struct wpa_ssid *ssid)
1862281806Srpaulo{
1863281806Srpaulo	char *value;
1864281806Srpaulo	int res;
1865252726Srpaulo
1866281806Srpaulo	if (is_zero_ether_addr(ssid->go_p2p_dev_addr))
1867281806Srpaulo		return NULL;
1868252726Srpaulo
1869281806Srpaulo	value = os_malloc(20);
1870281806Srpaulo	if (value == NULL)
1871281806Srpaulo		return NULL;
1872281806Srpaulo	res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->go_p2p_dev_addr));
1873281806Srpaulo	if (os_snprintf_error(20, res)) {
1874281806Srpaulo		os_free(value);
1875281806Srpaulo		return NULL;
1876252726Srpaulo	}
1877281806Srpaulo	value[20 - 1] = '\0';
1878281806Srpaulo	return value;
1879281806Srpaulo}
1880281806Srpaulo#endif /* NO_CONFIG_WRITE */
1881252726Srpaulo
1882252726Srpaulo
1883281806Srpaulostatic int wpa_config_parse_p2p_client_list(const struct parse_data *data,
1884281806Srpaulo					    struct wpa_ssid *ssid, int line,
1885281806Srpaulo					    const char *value)
1886281806Srpaulo{
1887281806Srpaulo	return wpa_config_parse_addr_list(data, line, value,
1888281806Srpaulo					  &ssid->p2p_client_list,
1889281806Srpaulo					  &ssid->num_p2p_clients,
1890281806Srpaulo					  "p2p_client_list", 0, 0);
1891252726Srpaulo}
1892252726Srpaulo
1893252726Srpaulo
1894252726Srpaulo#ifndef NO_CONFIG_WRITE
1895252726Srpaulostatic char * wpa_config_write_p2p_client_list(const struct parse_data *data,
1896252726Srpaulo					       struct wpa_ssid *ssid)
1897252726Srpaulo{
1898281806Srpaulo	return wpa_config_write_addr_list(data, ssid->p2p_client_list,
1899281806Srpaulo					  ssid->num_p2p_clients,
1900281806Srpaulo					  "p2p_client_list");
1901281806Srpaulo}
1902281806Srpaulo#endif /* NO_CONFIG_WRITE */
1903252726Srpaulo
1904252726Srpaulo
1905281806Srpaulostatic int wpa_config_parse_psk_list(const struct parse_data *data,
1906281806Srpaulo				     struct wpa_ssid *ssid, int line,
1907281806Srpaulo				     const char *value)
1908281806Srpaulo{
1909281806Srpaulo	struct psk_list_entry *p;
1910281806Srpaulo	const char *pos;
1911281806Srpaulo
1912281806Srpaulo	p = os_zalloc(sizeof(*p));
1913281806Srpaulo	if (p == NULL)
1914281806Srpaulo		return -1;
1915281806Srpaulo
1916252726Srpaulo	pos = value;
1917281806Srpaulo	if (os_strncmp(pos, "P2P-", 4) == 0) {
1918281806Srpaulo		p->p2p = 1;
1919281806Srpaulo		pos += 4;
1920281806Srpaulo	}
1921252726Srpaulo
1922281806Srpaulo	if (hwaddr_aton(pos, p->addr)) {
1923281806Srpaulo		wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list address '%s'",
1924281806Srpaulo			   line, pos);
1925281806Srpaulo		os_free(p);
1926281806Srpaulo		return -1;
1927252726Srpaulo	}
1928281806Srpaulo	pos += 17;
1929281806Srpaulo	if (*pos != '-') {
1930281806Srpaulo		wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list '%s'",
1931281806Srpaulo			   line, pos);
1932281806Srpaulo		os_free(p);
1933281806Srpaulo		return -1;
1934281806Srpaulo	}
1935281806Srpaulo	pos++;
1936252726Srpaulo
1937281806Srpaulo	if (hexstr2bin(pos, p->psk, PMK_LEN) || pos[PMK_LEN * 2] != '\0') {
1938281806Srpaulo		wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list PSK '%s'",
1939281806Srpaulo			   line, pos);
1940281806Srpaulo		os_free(p);
1941281806Srpaulo		return -1;
1942281806Srpaulo	}
1943252726Srpaulo
1944281806Srpaulo	dl_list_add(&ssid->psk_list, &p->list);
1945281806Srpaulo
1946281806Srpaulo	return 0;
1947252726Srpaulo}
1948281806Srpaulo
1949281806Srpaulo
1950281806Srpaulo#ifndef NO_CONFIG_WRITE
1951281806Srpaulostatic char * wpa_config_write_psk_list(const struct parse_data *data,
1952281806Srpaulo					struct wpa_ssid *ssid)
1953281806Srpaulo{
1954281806Srpaulo	return NULL;
1955281806Srpaulo}
1956252726Srpaulo#endif /* NO_CONFIG_WRITE */
1957252726Srpaulo
1958252726Srpaulo#endif /* CONFIG_P2P */
1959252726Srpaulo
1960281806Srpaulo
1961281806Srpaulo#ifdef CONFIG_MESH
1962281806Srpaulo
1963281806Srpaulostatic int wpa_config_parse_mesh_basic_rates(const struct parse_data *data,
1964281806Srpaulo					     struct wpa_ssid *ssid, int line,
1965281806Srpaulo					     const char *value)
1966281806Srpaulo{
1967281806Srpaulo	int *rates = wpa_config_parse_int_array(value);
1968281806Srpaulo
1969281806Srpaulo	if (rates == NULL) {
1970281806Srpaulo		wpa_printf(MSG_ERROR, "Line %d: Invalid mesh_basic_rates '%s'",
1971281806Srpaulo			   line, value);
1972281806Srpaulo		return -1;
1973281806Srpaulo	}
1974281806Srpaulo	if (rates[0] == 0) {
1975281806Srpaulo		os_free(rates);
1976281806Srpaulo		rates = NULL;
1977281806Srpaulo	}
1978281806Srpaulo
1979281806Srpaulo	os_free(ssid->mesh_basic_rates);
1980281806Srpaulo	ssid->mesh_basic_rates = rates;
1981281806Srpaulo
1982281806Srpaulo	return 0;
1983281806Srpaulo}
1984281806Srpaulo
1985281806Srpaulo
1986281806Srpaulo#ifndef NO_CONFIG_WRITE
1987281806Srpaulo
1988281806Srpaulostatic char * wpa_config_write_mesh_basic_rates(const struct parse_data *data,
1989281806Srpaulo						struct wpa_ssid *ssid)
1990281806Srpaulo{
1991281806Srpaulo	return wpa_config_write_freqs(data, ssid->mesh_basic_rates);
1992281806Srpaulo}
1993281806Srpaulo
1994281806Srpaulo#endif /* NO_CONFIG_WRITE */
1995281806Srpaulo
1996281806Srpaulo#endif /* CONFIG_MESH */
1997281806Srpaulo
1998281806Srpaulo
1999346981Scy#ifdef CONFIG_MACSEC
2000346981Scy
2001346981Scystatic int wpa_config_parse_mka_cak(const struct parse_data *data,
2002346981Scy				    struct wpa_ssid *ssid, int line,
2003346981Scy				    const char *value)
2004346981Scy{
2005346981Scy	size_t len;
2006346981Scy
2007346981Scy	len = os_strlen(value);
2008346981Scy	if (len > 2 * MACSEC_CAK_MAX_LEN ||
2009346981Scy	    (len != 2 * 16 && len != 2 * 32) ||
2010346981Scy	    hexstr2bin(value, ssid->mka_cak, len / 2)) {
2011346981Scy		wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CAK '%s'.",
2012346981Scy			   line, value);
2013346981Scy		return -1;
2014346981Scy	}
2015346981Scy	ssid->mka_cak_len = len / 2;
2016346981Scy	ssid->mka_psk_set |= MKA_PSK_SET_CAK;
2017346981Scy
2018346981Scy	wpa_hexdump_key(MSG_MSGDUMP, "MKA-CAK", ssid->mka_cak,
2019346981Scy			ssid->mka_cak_len);
2020346981Scy	return 0;
2021346981Scy}
2022346981Scy
2023346981Scy
2024346981Scystatic int wpa_config_parse_mka_ckn(const struct parse_data *data,
2025346981Scy				    struct wpa_ssid *ssid, int line,
2026346981Scy				    const char *value)
2027346981Scy{
2028346981Scy	size_t len;
2029346981Scy
2030346981Scy	len = os_strlen(value);
2031346981Scy	if (len > 2 * MACSEC_CKN_MAX_LEN || /* too long */
2032346981Scy	    len < 2 || /* too short */
2033346981Scy	    len % 2 != 0 /* not an integral number of bytes */) {
2034346981Scy		wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.",
2035346981Scy			   line, value);
2036346981Scy		return -1;
2037346981Scy	}
2038346981Scy	ssid->mka_ckn_len = len / 2;
2039346981Scy	if (hexstr2bin(value, ssid->mka_ckn, ssid->mka_ckn_len)) {
2040346981Scy		wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.",
2041346981Scy			   line, value);
2042346981Scy		return -1;
2043346981Scy	}
2044346981Scy
2045346981Scy	ssid->mka_psk_set |= MKA_PSK_SET_CKN;
2046346981Scy
2047346981Scy	wpa_hexdump_key(MSG_MSGDUMP, "MKA-CKN", ssid->mka_ckn,
2048346981Scy			ssid->mka_ckn_len);
2049346981Scy	return 0;
2050346981Scy}
2051346981Scy
2052346981Scy
2053346981Scy#ifndef NO_CONFIG_WRITE
2054346981Scy
2055346981Scystatic char * wpa_config_write_mka_cak(const struct parse_data *data,
2056346981Scy				       struct wpa_ssid *ssid)
2057346981Scy{
2058346981Scy	if (!(ssid->mka_psk_set & MKA_PSK_SET_CAK))
2059346981Scy		return NULL;
2060346981Scy
2061346981Scy	return wpa_config_write_string_hex(ssid->mka_cak, ssid->mka_cak_len);
2062346981Scy}
2063346981Scy
2064346981Scy
2065346981Scystatic char * wpa_config_write_mka_ckn(const struct parse_data *data,
2066346981Scy				       struct wpa_ssid *ssid)
2067346981Scy{
2068346981Scy	if (!(ssid->mka_psk_set & MKA_PSK_SET_CKN))
2069346981Scy		return NULL;
2070346981Scy	return wpa_config_write_string_hex(ssid->mka_ckn, ssid->mka_ckn_len);
2071346981Scy}
2072346981Scy
2073346981Scy#endif /* NO_CONFIG_WRITE */
2074346981Scy
2075346981Scy#endif /* CONFIG_MACSEC */
2076346981Scy
2077346981Scy
2078346981Scy#ifdef CONFIG_OCV
2079346981Scy
2080346981Scystatic int wpa_config_parse_ocv(const struct parse_data *data,
2081346981Scy				struct wpa_ssid *ssid, int line,
2082346981Scy				const char *value)
2083346981Scy{
2084346981Scy	char *end;
2085346981Scy
2086346981Scy	ssid->ocv = strtol(value, &end, 0);
2087346981Scy	if (*end || ssid->ocv < 0 || ssid->ocv > 1) {
2088346981Scy		wpa_printf(MSG_ERROR, "Line %d: Invalid ocv value '%s'.",
2089346981Scy			   line, value);
2090346981Scy		return -1;
2091346981Scy	}
2092346981Scy	if (ssid->ocv && ssid->ieee80211w == NO_MGMT_FRAME_PROTECTION)
2093346981Scy		ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
2094346981Scy	return 0;
2095346981Scy}
2096346981Scy
2097346981Scy
2098346981Scy#ifndef NO_CONFIG_WRITE
2099346981Scystatic char * wpa_config_write_ocv(const struct parse_data *data,
2100346981Scy				   struct wpa_ssid *ssid)
2101346981Scy{
2102346981Scy	char *value = os_malloc(20);
2103346981Scy
2104346981Scy	if (!value)
2105346981Scy		return NULL;
2106346981Scy	os_snprintf(value, 20, "%d", ssid->ocv);
2107346981Scy	value[20 - 1] = '\0';
2108346981Scy	return value;
2109346981Scy}
2110346981Scy#endif /* NO_CONFIG_WRITE */
2111346981Scy
2112346981Scy#endif /* CONFIG_OCV */
2113346981Scy
2114346981Scy
2115346981Scystatic int wpa_config_parse_peerkey(const struct parse_data *data,
2116346981Scy				    struct wpa_ssid *ssid, int line,
2117346981Scy				    const char *value)
2118346981Scy{
2119346981Scy	wpa_printf(MSG_INFO, "NOTE: Obsolete peerkey parameter ignored");
2120346981Scy	return 0;
2121346981Scy}
2122346981Scy
2123346981Scy
2124346981Scy#ifndef NO_CONFIG_WRITE
2125346981Scystatic char * wpa_config_write_peerkey(const struct parse_data *data,
2126346981Scy				       struct wpa_ssid *ssid)
2127346981Scy{
2128346981Scy	return NULL;
2129346981Scy}
2130346981Scy#endif /* NO_CONFIG_WRITE */
2131346981Scy
2132346981Scy
2133189251Ssam/* Helper macros for network block parser */
2134189251Ssam
2135189251Ssam#ifdef OFFSET
2136189251Ssam#undef OFFSET
2137189251Ssam#endif /* OFFSET */
2138189251Ssam/* OFFSET: Get offset of a variable within the wpa_ssid structure */
2139189251Ssam#define OFFSET(v) ((void *) &((struct wpa_ssid *) 0)->v)
2140189251Ssam
2141189251Ssam/* STR: Define a string variable for an ASCII string; f = field name */
2142189251Ssam#ifdef NO_CONFIG_WRITE
2143189251Ssam#define _STR(f) #f, wpa_config_parse_str, OFFSET(f)
2144189251Ssam#define _STRe(f) #f, wpa_config_parse_str, OFFSET(eap.f)
2145189251Ssam#else /* NO_CONFIG_WRITE */
2146189251Ssam#define _STR(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(f)
2147189251Ssam#define _STRe(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(eap.f)
2148189251Ssam#endif /* NO_CONFIG_WRITE */
2149189251Ssam#define STR(f) _STR(f), NULL, NULL, NULL, 0
2150189251Ssam#define STRe(f) _STRe(f), NULL, NULL, NULL, 0
2151189251Ssam#define STR_KEY(f) _STR(f), NULL, NULL, NULL, 1
2152189251Ssam#define STR_KEYe(f) _STRe(f), NULL, NULL, NULL, 1
2153189251Ssam
2154189251Ssam/* STR_LEN: Define a string variable with a separate variable for storing the
2155189251Ssam * data length. Unlike STR(), this can be used to store arbitrary binary data
2156189251Ssam * (i.e., even nul termination character). */
2157189251Ssam#define _STR_LEN(f) _STR(f), OFFSET(f ## _len)
2158189251Ssam#define _STR_LENe(f) _STRe(f), OFFSET(eap.f ## _len)
2159189251Ssam#define STR_LEN(f) _STR_LEN(f), NULL, NULL, 0
2160189251Ssam#define STR_LENe(f) _STR_LENe(f), NULL, NULL, 0
2161189251Ssam#define STR_LEN_KEY(f) _STR_LEN(f), NULL, NULL, 1
2162189251Ssam
2163189251Ssam/* STR_RANGE: Like STR_LEN(), but with minimum and maximum allowed length
2164189251Ssam * explicitly specified. */
2165189251Ssam#define _STR_RANGE(f, min, max) _STR_LEN(f), (void *) (min), (void *) (max)
2166189251Ssam#define STR_RANGE(f, min, max) _STR_RANGE(f, min, max), 0
2167189251Ssam#define STR_RANGE_KEY(f, min, max) _STR_RANGE(f, min, max), 1
2168189251Ssam
2169189251Ssam#ifdef NO_CONFIG_WRITE
2170189251Ssam#define _INT(f) #f, wpa_config_parse_int, OFFSET(f), (void *) 0
2171189251Ssam#define _INTe(f) #f, wpa_config_parse_int, OFFSET(eap.f), (void *) 0
2172189251Ssam#else /* NO_CONFIG_WRITE */
2173189251Ssam#define _INT(f) #f, wpa_config_parse_int, wpa_config_write_int, \
2174189251Ssam	OFFSET(f), (void *) 0
2175189251Ssam#define _INTe(f) #f, wpa_config_parse_int, wpa_config_write_int, \
2176189251Ssam	OFFSET(eap.f), (void *) 0
2177189251Ssam#endif /* NO_CONFIG_WRITE */
2178189251Ssam
2179189251Ssam/* INT: Define an integer variable */
2180189251Ssam#define INT(f) _INT(f), NULL, NULL, 0
2181189251Ssam#define INTe(f) _INTe(f), NULL, NULL, 0
2182189251Ssam
2183189251Ssam/* INT_RANGE: Define an integer variable with allowed value range */
2184189251Ssam#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0
2185189251Ssam
2186189251Ssam/* FUNC: Define a configuration variable that uses a custom function for
2187189251Ssam * parsing and writing the value. */
2188189251Ssam#ifdef NO_CONFIG_WRITE
2189189251Ssam#define _FUNC(f) #f, wpa_config_parse_ ## f, NULL, NULL, NULL, NULL
2190189251Ssam#else /* NO_CONFIG_WRITE */
2191189251Ssam#define _FUNC(f) #f, wpa_config_parse_ ## f, wpa_config_write_ ## f, \
2192189251Ssam	NULL, NULL, NULL, NULL
2193189251Ssam#endif /* NO_CONFIG_WRITE */
2194189251Ssam#define FUNC(f) _FUNC(f), 0
2195189251Ssam#define FUNC_KEY(f) _FUNC(f), 1
2196189251Ssam
2197189251Ssam/*
2198189251Ssam * Table of network configuration variables. This table is used to parse each
2199189251Ssam * network configuration variable, e.g., each line in wpa_supplicant.conf file
2200189251Ssam * that is inside a network block.
2201189251Ssam *
2202189251Ssam * This table is generated using the helper macros defined above and with
2203189251Ssam * generous help from the C pre-processor. The field name is stored as a string
2204189251Ssam * into .name and for STR and INT types, the offset of the target buffer within
2205189251Ssam * struct wpa_ssid is stored in .param1. .param2 (if not NULL) is similar
2206189251Ssam * offset to the field containing the length of the configuration variable.
2207189251Ssam * .param3 and .param4 can be used to mark the allowed range (length for STR
2208189251Ssam * and value for INT).
2209189251Ssam *
2210189251Ssam * For each configuration line in wpa_supplicant.conf, the parser goes through
2211189251Ssam * this table and select the entry that matches with the field name. The parser
2212189251Ssam * function (.parser) is then called to parse the actual value of the field.
2213189251Ssam *
2214189251Ssam * This kind of mechanism makes it easy to add new configuration parameters,
2215189251Ssam * since only one line needs to be added into this table and into the
2216189251Ssam * struct wpa_ssid definition if the new variable is either a string or
2217189251Ssam * integer. More complex types will need to use their own parser and writer
2218189251Ssam * functions.
2219189251Ssam */
2220189251Ssamstatic const struct parse_data ssid_fields[] = {
2221289549Srpaulo	{ STR_RANGE(ssid, 0, SSID_MAX_LEN) },
2222189251Ssam	{ INT_RANGE(scan_ssid, 0, 1) },
2223189251Ssam	{ FUNC(bssid) },
2224346981Scy	{ FUNC(bssid_hint) },
2225281806Srpaulo	{ FUNC(bssid_blacklist) },
2226281806Srpaulo	{ FUNC(bssid_whitelist) },
2227189251Ssam	{ FUNC_KEY(psk) },
2228289549Srpaulo	{ INT(mem_only_psk) },
2229346981Scy	{ STR_KEY(sae_password) },
2230346981Scy	{ STR(sae_password_id) },
2231189251Ssam	{ FUNC(proto) },
2232189251Ssam	{ FUNC(key_mgmt) },
2233252726Srpaulo	{ INT(bg_scan_period) },
2234189251Ssam	{ FUNC(pairwise) },
2235189251Ssam	{ FUNC(group) },
2236346981Scy	{ FUNC(group_mgmt) },
2237189251Ssam	{ FUNC(auth_alg) },
2238214734Srpaulo	{ FUNC(scan_freq) },
2239214734Srpaulo	{ FUNC(freq_list) },
2240346981Scy	{ INT_RANGE(ht, 0, 1) },
2241346981Scy	{ INT_RANGE(vht, 0, 1) },
2242346981Scy	{ INT_RANGE(ht40, -1, 1) },
2243351611Scy	{ INT_RANGE(max_oper_chwidth, CHANWIDTH_USE_HT,
2244351611Scy		    CHANWIDTH_80P80MHZ) },
2245346981Scy	{ INT(vht_center_freq1) },
2246346981Scy	{ INT(vht_center_freq2) },
2247189251Ssam#ifdef IEEE8021X_EAPOL
2248189251Ssam	{ FUNC(eap) },
2249189251Ssam	{ STR_LENe(identity) },
2250189251Ssam	{ STR_LENe(anonymous_identity) },
2251346981Scy	{ STR_LENe(imsi_identity) },
2252189251Ssam	{ FUNC_KEY(password) },
2253189251Ssam	{ STRe(ca_cert) },
2254189251Ssam	{ STRe(ca_path) },
2255189251Ssam	{ STRe(client_cert) },
2256189251Ssam	{ STRe(private_key) },
2257189251Ssam	{ STR_KEYe(private_key_passwd) },
2258189251Ssam	{ STRe(dh_file) },
2259189251Ssam	{ STRe(subject_match) },
2260346981Scy	{ STRe(check_cert_subject) },
2261189251Ssam	{ STRe(altsubject_match) },
2262281806Srpaulo	{ STRe(domain_suffix_match) },
2263281806Srpaulo	{ STRe(domain_match) },
2264189251Ssam	{ STRe(ca_cert2) },
2265189251Ssam	{ STRe(ca_path2) },
2266189251Ssam	{ STRe(client_cert2) },
2267189251Ssam	{ STRe(private_key2) },
2268189251Ssam	{ STR_KEYe(private_key2_passwd) },
2269189251Ssam	{ STRe(dh_file2) },
2270189251Ssam	{ STRe(subject_match2) },
2271346981Scy	{ STRe(check_cert_subject2) },
2272189251Ssam	{ STRe(altsubject_match2) },
2273281806Srpaulo	{ STRe(domain_suffix_match2) },
2274281806Srpaulo	{ STRe(domain_match2) },
2275189251Ssam	{ STRe(phase1) },
2276189251Ssam	{ STRe(phase2) },
2277189251Ssam	{ STRe(pcsc) },
2278189251Ssam	{ STR_KEYe(pin) },
2279189251Ssam	{ STRe(engine_id) },
2280189251Ssam	{ STRe(key_id) },
2281189251Ssam	{ STRe(cert_id) },
2282189251Ssam	{ STRe(ca_cert_id) },
2283189251Ssam	{ STR_KEYe(pin2) },
2284189251Ssam	{ STRe(engine2_id) },
2285189251Ssam	{ STRe(key2_id) },
2286189251Ssam	{ STRe(cert2_id) },
2287189251Ssam	{ STRe(ca_cert2_id) },
2288189251Ssam	{ INTe(engine) },
2289189251Ssam	{ INTe(engine2) },
2290189251Ssam	{ INT(eapol_flags) },
2291281806Srpaulo	{ INTe(sim_num) },
2292281806Srpaulo	{ STRe(openssl_ciphers) },
2293281806Srpaulo	{ INTe(erp) },
2294189251Ssam#endif /* IEEE8021X_EAPOL */
2295189251Ssam	{ FUNC_KEY(wep_key0) },
2296189251Ssam	{ FUNC_KEY(wep_key1) },
2297189251Ssam	{ FUNC_KEY(wep_key2) },
2298189251Ssam	{ FUNC_KEY(wep_key3) },
2299189251Ssam	{ INT(wep_tx_keyidx) },
2300189251Ssam	{ INT(priority) },
2301189251Ssam#ifdef IEEE8021X_EAPOL
2302189251Ssam	{ INT(eap_workaround) },
2303189251Ssam	{ STRe(pac_file) },
2304189251Ssam	{ INTe(fragment_size) },
2305281806Srpaulo	{ INTe(ocsp) },
2306189251Ssam#endif /* IEEE8021X_EAPOL */
2307281806Srpaulo#ifdef CONFIG_MESH
2308281806Srpaulo	{ INT_RANGE(mode, 0, 5) },
2309281806Srpaulo	{ INT_RANGE(no_auto_peer, 0, 1) },
2310346981Scy	{ INT_RANGE(mesh_rssi_threshold, -255, 1) },
2311281806Srpaulo#else /* CONFIG_MESH */
2312252726Srpaulo	{ INT_RANGE(mode, 0, 4) },
2313281806Srpaulo#endif /* CONFIG_MESH */
2314189251Ssam	{ INT_RANGE(proactive_key_caching, 0, 1) },
2315252726Srpaulo	{ INT_RANGE(disabled, 0, 2) },
2316189251Ssam	{ STR(id_str) },
2317189251Ssam#ifdef CONFIG_IEEE80211W
2318189251Ssam	{ INT_RANGE(ieee80211w, 0, 2) },
2319189251Ssam#endif /* CONFIG_IEEE80211W */
2320346981Scy#ifdef CONFIG_OCV
2321346981Scy	{ FUNC(ocv) },
2322346981Scy#endif /* CONFIG_OCV */
2323346981Scy	{ FUNC(peerkey) /* obsolete - removed */ },
2324189251Ssam	{ INT_RANGE(mixed_cell, 0, 1) },
2325252726Srpaulo	{ INT_RANGE(frequency, 0, 65000) },
2326281806Srpaulo	{ INT_RANGE(fixed_freq, 0, 1) },
2327337817Scy#ifdef CONFIG_ACS
2328337817Scy	{ INT_RANGE(acs, 0, 1) },
2329337817Scy#endif /* CONFIG_ACS */
2330281806Srpaulo#ifdef CONFIG_MESH
2331281806Srpaulo	{ FUNC(mesh_basic_rates) },
2332281806Srpaulo	{ INT(dot11MeshMaxRetries) },
2333281806Srpaulo	{ INT(dot11MeshRetryTimeout) },
2334281806Srpaulo	{ INT(dot11MeshConfirmTimeout) },
2335281806Srpaulo	{ INT(dot11MeshHoldingTimeout) },
2336281806Srpaulo#endif /* CONFIG_MESH */
2337214734Srpaulo	{ INT(wpa_ptk_rekey) },
2338337817Scy	{ INT(group_rekey) },
2339214734Srpaulo	{ STR(bgscan) },
2340252726Srpaulo	{ INT_RANGE(ignore_broadcast_ssid, 0, 2) },
2341252726Srpaulo#ifdef CONFIG_P2P
2342281806Srpaulo	{ FUNC(go_p2p_dev_addr) },
2343252726Srpaulo	{ FUNC(p2p_client_list) },
2344281806Srpaulo	{ FUNC(psk_list) },
2345252726Srpaulo#endif /* CONFIG_P2P */
2346252726Srpaulo#ifdef CONFIG_HT_OVERRIDES
2347252726Srpaulo	{ INT_RANGE(disable_ht, 0, 1) },
2348252726Srpaulo	{ INT_RANGE(disable_ht40, -1, 1) },
2349252726Srpaulo	{ INT_RANGE(disable_sgi, 0, 1) },
2350281806Srpaulo	{ INT_RANGE(disable_ldpc, 0, 1) },
2351281806Srpaulo	{ INT_RANGE(ht40_intolerant, 0, 1) },
2352346981Scy	{ INT_RANGE(tx_stbc, -1, 1) },
2353346981Scy	{ INT_RANGE(rx_stbc, -1, 3) },
2354252726Srpaulo	{ INT_RANGE(disable_max_amsdu, -1, 1) },
2355252726Srpaulo	{ INT_RANGE(ampdu_factor, -1, 3) },
2356252726Srpaulo	{ INT_RANGE(ampdu_density, -1, 7) },
2357252726Srpaulo	{ STR(ht_mcs) },
2358252726Srpaulo#endif /* CONFIG_HT_OVERRIDES */
2359281806Srpaulo#ifdef CONFIG_VHT_OVERRIDES
2360281806Srpaulo	{ INT_RANGE(disable_vht, 0, 1) },
2361281806Srpaulo	{ INT(vht_capa) },
2362281806Srpaulo	{ INT(vht_capa_mask) },
2363281806Srpaulo	{ INT_RANGE(vht_rx_mcs_nss_1, -1, 3) },
2364281806Srpaulo	{ INT_RANGE(vht_rx_mcs_nss_2, -1, 3) },
2365281806Srpaulo	{ INT_RANGE(vht_rx_mcs_nss_3, -1, 3) },
2366281806Srpaulo	{ INT_RANGE(vht_rx_mcs_nss_4, -1, 3) },
2367281806Srpaulo	{ INT_RANGE(vht_rx_mcs_nss_5, -1, 3) },
2368281806Srpaulo	{ INT_RANGE(vht_rx_mcs_nss_6, -1, 3) },
2369281806Srpaulo	{ INT_RANGE(vht_rx_mcs_nss_7, -1, 3) },
2370281806Srpaulo	{ INT_RANGE(vht_rx_mcs_nss_8, -1, 3) },
2371281806Srpaulo	{ INT_RANGE(vht_tx_mcs_nss_1, -1, 3) },
2372281806Srpaulo	{ INT_RANGE(vht_tx_mcs_nss_2, -1, 3) },
2373281806Srpaulo	{ INT_RANGE(vht_tx_mcs_nss_3, -1, 3) },
2374281806Srpaulo	{ INT_RANGE(vht_tx_mcs_nss_4, -1, 3) },
2375281806Srpaulo	{ INT_RANGE(vht_tx_mcs_nss_5, -1, 3) },
2376281806Srpaulo	{ INT_RANGE(vht_tx_mcs_nss_6, -1, 3) },
2377281806Srpaulo	{ INT_RANGE(vht_tx_mcs_nss_7, -1, 3) },
2378281806Srpaulo	{ INT_RANGE(vht_tx_mcs_nss_8, -1, 3) },
2379281806Srpaulo#endif /* CONFIG_VHT_OVERRIDES */
2380252726Srpaulo	{ INT(ap_max_inactivity) },
2381252726Srpaulo	{ INT(dtim_period) },
2382281806Srpaulo	{ INT(beacon_int) },
2383281806Srpaulo#ifdef CONFIG_MACSEC
2384281806Srpaulo	{ INT_RANGE(macsec_policy, 0, 1) },
2385346981Scy	{ INT_RANGE(macsec_integ_only, 0, 1) },
2386346981Scy	{ INT_RANGE(macsec_replay_protect, 0, 1) },
2387346981Scy	{ INT(macsec_replay_window) },
2388346981Scy	{ INT_RANGE(macsec_port, 1, 65534) },
2389346981Scy	{ INT_RANGE(mka_priority, 0, 255) },
2390346981Scy	{ FUNC_KEY(mka_cak) },
2391346981Scy	{ FUNC_KEY(mka_ckn) },
2392281806Srpaulo#endif /* CONFIG_MACSEC */
2393281806Srpaulo#ifdef CONFIG_HS20
2394281806Srpaulo	{ INT(update_identifier) },
2395346981Scy	{ STR_RANGE(roaming_consortium_selection, 0, MAX_ROAMING_CONS_OI_LEN) },
2396281806Srpaulo#endif /* CONFIG_HS20 */
2397281806Srpaulo	{ INT_RANGE(mac_addr, 0, 2) },
2398337817Scy	{ INT_RANGE(pbss, 0, 2) },
2399337817Scy	{ INT_RANGE(wps_disabled, 0, 1) },
2400346981Scy	{ INT_RANGE(fils_dh_group, 0, 65535) },
2401346981Scy#ifdef CONFIG_DPP
2402346981Scy	{ STR(dpp_connector) },
2403346981Scy	{ STR_LEN(dpp_netaccesskey) },
2404346981Scy	{ INT(dpp_netaccesskey_expiry) },
2405346981Scy	{ STR_LEN(dpp_csign) },
2406346981Scy#endif /* CONFIG_DPP */
2407346981Scy	{ INT_RANGE(owe_group, 0, 65535) },
2408346981Scy	{ INT_RANGE(owe_only, 0, 1) },
2409346981Scy	{ INT_RANGE(multi_ap_backhaul_sta, 0, 1) },
2410351611Scy	{ INT_RANGE(ft_eap_pmksa_caching, 0, 1) },
2411189251Ssam};
2412189251Ssam
2413189251Ssam#undef OFFSET
2414189251Ssam#undef _STR
2415189251Ssam#undef STR
2416189251Ssam#undef STR_KEY
2417189251Ssam#undef _STR_LEN
2418189251Ssam#undef STR_LEN
2419189251Ssam#undef STR_LEN_KEY
2420189251Ssam#undef _STR_RANGE
2421189251Ssam#undef STR_RANGE
2422189251Ssam#undef STR_RANGE_KEY
2423189251Ssam#undef _INT
2424189251Ssam#undef INT
2425189251Ssam#undef INT_RANGE
2426189251Ssam#undef _FUNC
2427189251Ssam#undef FUNC
2428189251Ssam#undef FUNC_KEY
2429281806Srpaulo#define NUM_SSID_FIELDS ARRAY_SIZE(ssid_fields)
2430189251Ssam
2431189251Ssam
2432189251Ssam/**
2433189251Ssam * wpa_config_add_prio_network - Add a network to priority lists
2434189251Ssam * @config: Configuration data from wpa_config_read()
2435189251Ssam * @ssid: Pointer to the network configuration to be added to the list
2436189251Ssam * Returns: 0 on success, -1 on failure
2437189251Ssam *
2438189251Ssam * This function is used to add a network block to the priority list of
2439189251Ssam * networks. This must be called for each network when reading in the full
2440189251Ssam * configuration. In addition, this can be used indirectly when updating
2441189251Ssam * priorities by calling wpa_config_update_prio_list().
2442189251Ssam */
2443189251Ssamint wpa_config_add_prio_network(struct wpa_config *config,
2444189251Ssam				struct wpa_ssid *ssid)
2445189251Ssam{
2446189251Ssam	int prio;
2447189251Ssam	struct wpa_ssid *prev, **nlist;
2448189251Ssam
2449189251Ssam	/*
2450189251Ssam	 * Add to an existing priority list if one is available for the
2451189251Ssam	 * configured priority level for this network.
2452189251Ssam	 */
2453189251Ssam	for (prio = 0; prio < config->num_prio; prio++) {
2454189251Ssam		prev = config->pssid[prio];
2455189251Ssam		if (prev->priority == ssid->priority) {
2456189251Ssam			while (prev->pnext)
2457189251Ssam				prev = prev->pnext;
2458189251Ssam			prev->pnext = ssid;
2459189251Ssam			return 0;
2460189251Ssam		}
2461189251Ssam	}
2462189251Ssam
2463189251Ssam	/* First network for this priority - add a new priority list */
2464252726Srpaulo	nlist = os_realloc_array(config->pssid, config->num_prio + 1,
2465252726Srpaulo				 sizeof(struct wpa_ssid *));
2466189251Ssam	if (nlist == NULL)
2467189251Ssam		return -1;
2468189251Ssam
2469189251Ssam	for (prio = 0; prio < config->num_prio; prio++) {
2470252726Srpaulo		if (nlist[prio]->priority < ssid->priority) {
2471252726Srpaulo			os_memmove(&nlist[prio + 1], &nlist[prio],
2472252726Srpaulo				   (config->num_prio - prio) *
2473252726Srpaulo				   sizeof(struct wpa_ssid *));
2474189251Ssam			break;
2475252726Srpaulo		}
2476189251Ssam	}
2477189251Ssam
2478189251Ssam	nlist[prio] = ssid;
2479189251Ssam	config->num_prio++;
2480189251Ssam	config->pssid = nlist;
2481189251Ssam
2482189251Ssam	return 0;
2483189251Ssam}
2484189251Ssam
2485189251Ssam
2486189251Ssam/**
2487189251Ssam * wpa_config_update_prio_list - Update network priority list
2488189251Ssam * @config: Configuration data from wpa_config_read()
2489189251Ssam * Returns: 0 on success, -1 on failure
2490189251Ssam *
2491189251Ssam * This function is called to update the priority list of networks in the
2492189251Ssam * configuration when a network is being added or removed. This is also called
2493189251Ssam * if a priority for a network is changed.
2494189251Ssam */
2495214734Srpauloint wpa_config_update_prio_list(struct wpa_config *config)
2496189251Ssam{
2497189251Ssam	struct wpa_ssid *ssid;
2498189251Ssam	int ret = 0;
2499189251Ssam
2500189251Ssam	os_free(config->pssid);
2501189251Ssam	config->pssid = NULL;
2502189251Ssam	config->num_prio = 0;
2503189251Ssam
2504189251Ssam	ssid = config->ssid;
2505189251Ssam	while (ssid) {
2506189251Ssam		ssid->pnext = NULL;
2507189251Ssam		if (wpa_config_add_prio_network(config, ssid) < 0)
2508189251Ssam			ret = -1;
2509189251Ssam		ssid = ssid->next;
2510189251Ssam	}
2511189251Ssam
2512189251Ssam	return ret;
2513189251Ssam}
2514189251Ssam
2515189251Ssam
2516189251Ssam#ifdef IEEE8021X_EAPOL
2517189251Ssamstatic void eap_peer_config_free(struct eap_peer_config *eap)
2518189251Ssam{
2519189251Ssam	os_free(eap->eap_methods);
2520281806Srpaulo	bin_clear_free(eap->identity, eap->identity_len);
2521189251Ssam	os_free(eap->anonymous_identity);
2522346981Scy	os_free(eap->imsi_identity);
2523281806Srpaulo	bin_clear_free(eap->password, eap->password_len);
2524189251Ssam	os_free(eap->ca_cert);
2525189251Ssam	os_free(eap->ca_path);
2526189251Ssam	os_free(eap->client_cert);
2527189251Ssam	os_free(eap->private_key);
2528281806Srpaulo	str_clear_free(eap->private_key_passwd);
2529189251Ssam	os_free(eap->dh_file);
2530189251Ssam	os_free(eap->subject_match);
2531346981Scy	os_free(eap->check_cert_subject);
2532189251Ssam	os_free(eap->altsubject_match);
2533281806Srpaulo	os_free(eap->domain_suffix_match);
2534281806Srpaulo	os_free(eap->domain_match);
2535189251Ssam	os_free(eap->ca_cert2);
2536189251Ssam	os_free(eap->ca_path2);
2537189251Ssam	os_free(eap->client_cert2);
2538189251Ssam	os_free(eap->private_key2);
2539281806Srpaulo	str_clear_free(eap->private_key2_passwd);
2540189251Ssam	os_free(eap->dh_file2);
2541189251Ssam	os_free(eap->subject_match2);
2542346981Scy	os_free(eap->check_cert_subject2);
2543189251Ssam	os_free(eap->altsubject_match2);
2544281806Srpaulo	os_free(eap->domain_suffix_match2);
2545281806Srpaulo	os_free(eap->domain_match2);
2546189251Ssam	os_free(eap->phase1);
2547189251Ssam	os_free(eap->phase2);
2548189251Ssam	os_free(eap->pcsc);
2549281806Srpaulo	str_clear_free(eap->pin);
2550189251Ssam	os_free(eap->engine_id);
2551189251Ssam	os_free(eap->key_id);
2552189251Ssam	os_free(eap->cert_id);
2553189251Ssam	os_free(eap->ca_cert_id);
2554189251Ssam	os_free(eap->key2_id);
2555189251Ssam	os_free(eap->cert2_id);
2556189251Ssam	os_free(eap->ca_cert2_id);
2557281806Srpaulo	str_clear_free(eap->pin2);
2558189251Ssam	os_free(eap->engine2_id);
2559189251Ssam	os_free(eap->otp);
2560189251Ssam	os_free(eap->pending_req_otp);
2561189251Ssam	os_free(eap->pac_file);
2562281806Srpaulo	bin_clear_free(eap->new_password, eap->new_password_len);
2563281806Srpaulo	str_clear_free(eap->external_sim_resp);
2564281806Srpaulo	os_free(eap->openssl_ciphers);
2565189251Ssam}
2566189251Ssam#endif /* IEEE8021X_EAPOL */
2567189251Ssam
2568189251Ssam
2569189251Ssam/**
2570189251Ssam * wpa_config_free_ssid - Free network/ssid configuration data
2571189251Ssam * @ssid: Configuration data for the network
2572189251Ssam *
2573189251Ssam * This function frees all resources allocated for the network configuration
2574189251Ssam * data.
2575189251Ssam */
2576189251Ssamvoid wpa_config_free_ssid(struct wpa_ssid *ssid)
2577189251Ssam{
2578281806Srpaulo	struct psk_list_entry *psk;
2579281806Srpaulo
2580189251Ssam	os_free(ssid->ssid);
2581281806Srpaulo	str_clear_free(ssid->passphrase);
2582252726Srpaulo	os_free(ssid->ext_psk);
2583346981Scy	str_clear_free(ssid->sae_password);
2584346981Scy	os_free(ssid->sae_password_id);
2585189251Ssam#ifdef IEEE8021X_EAPOL
2586189251Ssam	eap_peer_config_free(&ssid->eap);
2587189251Ssam#endif /* IEEE8021X_EAPOL */
2588189251Ssam	os_free(ssid->id_str);
2589214734Srpaulo	os_free(ssid->scan_freq);
2590214734Srpaulo	os_free(ssid->freq_list);
2591214734Srpaulo	os_free(ssid->bgscan);
2592252726Srpaulo	os_free(ssid->p2p_client_list);
2593281806Srpaulo	os_free(ssid->bssid_blacklist);
2594281806Srpaulo	os_free(ssid->bssid_whitelist);
2595252726Srpaulo#ifdef CONFIG_HT_OVERRIDES
2596252726Srpaulo	os_free(ssid->ht_mcs);
2597252726Srpaulo#endif /* CONFIG_HT_OVERRIDES */
2598281806Srpaulo#ifdef CONFIG_MESH
2599281806Srpaulo	os_free(ssid->mesh_basic_rates);
2600281806Srpaulo#endif /* CONFIG_MESH */
2601346981Scy#ifdef CONFIG_HS20
2602346981Scy	os_free(ssid->roaming_consortium_selection);
2603346981Scy#endif /* CONFIG_HS20 */
2604346981Scy	os_free(ssid->dpp_connector);
2605346981Scy	bin_clear_free(ssid->dpp_netaccesskey, ssid->dpp_netaccesskey_len);
2606346981Scy	os_free(ssid->dpp_csign);
2607281806Srpaulo	while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry,
2608281806Srpaulo				    list))) {
2609281806Srpaulo		dl_list_del(&psk->list);
2610281806Srpaulo		bin_clear_free(psk, sizeof(*psk));
2611281806Srpaulo	}
2612281806Srpaulo	bin_clear_free(ssid, sizeof(*ssid));
2613189251Ssam}
2614189251Ssam
2615189251Ssam
2616252726Srpaulovoid wpa_config_free_cred(struct wpa_cred *cred)
2617252726Srpaulo{
2618281806Srpaulo	size_t i;
2619281806Srpaulo
2620252726Srpaulo	os_free(cred->realm);
2621281806Srpaulo	str_clear_free(cred->username);
2622281806Srpaulo	str_clear_free(cred->password);
2623252726Srpaulo	os_free(cred->ca_cert);
2624252726Srpaulo	os_free(cred->client_cert);
2625252726Srpaulo	os_free(cred->private_key);
2626281806Srpaulo	str_clear_free(cred->private_key_passwd);
2627252726Srpaulo	os_free(cred->imsi);
2628281806Srpaulo	str_clear_free(cred->milenage);
2629281806Srpaulo	for (i = 0; i < cred->num_domain; i++)
2630281806Srpaulo		os_free(cred->domain[i]);
2631252726Srpaulo	os_free(cred->domain);
2632281806Srpaulo	os_free(cred->domain_suffix_match);
2633252726Srpaulo	os_free(cred->eap_method);
2634252726Srpaulo	os_free(cred->phase1);
2635252726Srpaulo	os_free(cred->phase2);
2636252726Srpaulo	os_free(cred->excluded_ssid);
2637281806Srpaulo	os_free(cred->roaming_partner);
2638281806Srpaulo	os_free(cred->provisioning_sp);
2639281806Srpaulo	for (i = 0; i < cred->num_req_conn_capab; i++)
2640281806Srpaulo		os_free(cred->req_conn_capab_port[i]);
2641281806Srpaulo	os_free(cred->req_conn_capab_port);
2642281806Srpaulo	os_free(cred->req_conn_capab_proto);
2643252726Srpaulo	os_free(cred);
2644252726Srpaulo}
2645252726Srpaulo
2646252726Srpaulo
2647281806Srpaulovoid wpa_config_flush_blobs(struct wpa_config *config)
2648281806Srpaulo{
2649281806Srpaulo#ifndef CONFIG_NO_CONFIG_BLOBS
2650281806Srpaulo	struct wpa_config_blob *blob, *prev;
2651281806Srpaulo
2652281806Srpaulo	blob = config->blobs;
2653281806Srpaulo	config->blobs = NULL;
2654281806Srpaulo	while (blob) {
2655281806Srpaulo		prev = blob;
2656281806Srpaulo		blob = blob->next;
2657281806Srpaulo		wpa_config_free_blob(prev);
2658281806Srpaulo	}
2659281806Srpaulo#endif /* CONFIG_NO_CONFIG_BLOBS */
2660281806Srpaulo}
2661281806Srpaulo
2662281806Srpaulo
2663189251Ssam/**
2664189251Ssam * wpa_config_free - Free configuration data
2665189251Ssam * @config: Configuration data from wpa_config_read()
2666189251Ssam *
2667189251Ssam * This function frees all resources allocated for the configuration data by
2668189251Ssam * wpa_config_read().
2669189251Ssam */
2670189251Ssamvoid wpa_config_free(struct wpa_config *config)
2671189251Ssam{
2672189251Ssam	struct wpa_ssid *ssid, *prev = NULL;
2673252726Srpaulo	struct wpa_cred *cred, *cprev;
2674281806Srpaulo	int i;
2675252726Srpaulo
2676189251Ssam	ssid = config->ssid;
2677189251Ssam	while (ssid) {
2678189251Ssam		prev = ssid;
2679189251Ssam		ssid = ssid->next;
2680189251Ssam		wpa_config_free_ssid(prev);
2681189251Ssam	}
2682189251Ssam
2683252726Srpaulo	cred = config->cred;
2684252726Srpaulo	while (cred) {
2685252726Srpaulo		cprev = cred;
2686252726Srpaulo		cred = cred->next;
2687252726Srpaulo		wpa_config_free_cred(cprev);
2688252726Srpaulo	}
2689252726Srpaulo
2690281806Srpaulo	wpa_config_flush_blobs(config);
2691189251Ssam
2692252726Srpaulo	wpabuf_free(config->wps_vendor_ext_m1);
2693281806Srpaulo	for (i = 0; i < MAX_WPS_VENDOR_EXT; i++)
2694281806Srpaulo		wpabuf_free(config->wps_vendor_ext[i]);
2695189251Ssam	os_free(config->ctrl_interface);
2696189251Ssam	os_free(config->ctrl_interface_group);
2697189251Ssam	os_free(config->opensc_engine_path);
2698189251Ssam	os_free(config->pkcs11_engine_path);
2699189251Ssam	os_free(config->pkcs11_module_path);
2700281806Srpaulo	os_free(config->openssl_ciphers);
2701252726Srpaulo	os_free(config->pcsc_reader);
2702281806Srpaulo	str_clear_free(config->pcsc_pin);
2703189251Ssam	os_free(config->driver_param);
2704189251Ssam	os_free(config->device_name);
2705189251Ssam	os_free(config->manufacturer);
2706189251Ssam	os_free(config->model_name);
2707189251Ssam	os_free(config->model_number);
2708189251Ssam	os_free(config->serial_number);
2709214734Srpaulo	os_free(config->config_methods);
2710252726Srpaulo	os_free(config->p2p_ssid_postfix);
2711189251Ssam	os_free(config->pssid);
2712252726Srpaulo	os_free(config->p2p_pref_chan);
2713281806Srpaulo	os_free(config->p2p_no_go_freq.range);
2714252726Srpaulo	os_free(config->autoscan);
2715281806Srpaulo	os_free(config->freq_list);
2716252726Srpaulo	wpabuf_free(config->wps_nfc_dh_pubkey);
2717252726Srpaulo	wpabuf_free(config->wps_nfc_dh_privkey);
2718252726Srpaulo	wpabuf_free(config->wps_nfc_dev_pw);
2719252726Srpaulo	os_free(config->ext_password_backend);
2720281806Srpaulo	os_free(config->sae_groups);
2721281806Srpaulo	wpabuf_free(config->ap_vendor_elements);
2722281806Srpaulo	os_free(config->osu_dir);
2723281806Srpaulo	os_free(config->bgscan);
2724281806Srpaulo	os_free(config->wowlan_triggers);
2725289549Srpaulo	os_free(config->fst_group_id);
2726337817Scy	os_free(config->sched_scan_plans);
2727337817Scy#ifdef CONFIG_MBO
2728337817Scy	os_free(config->non_pref_chan);
2729337817Scy#endif /* CONFIG_MBO */
2730337817Scy
2731189251Ssam	os_free(config);
2732189251Ssam}
2733189251Ssam
2734189251Ssam
2735189251Ssam/**
2736252726Srpaulo * wpa_config_foreach_network - Iterate over each configured network
2737252726Srpaulo * @config: Configuration data from wpa_config_read()
2738252726Srpaulo * @func: Callback function to process each network
2739252726Srpaulo * @arg: Opaque argument to pass to callback function
2740252726Srpaulo *
2741252726Srpaulo * Iterate over the set of configured networks calling the specified
2742252726Srpaulo * function for each item. We guard against callbacks removing the
2743252726Srpaulo * supplied network.
2744252726Srpaulo */
2745252726Srpaulovoid wpa_config_foreach_network(struct wpa_config *config,
2746252726Srpaulo				void (*func)(void *, struct wpa_ssid *),
2747252726Srpaulo				void *arg)
2748252726Srpaulo{
2749252726Srpaulo	struct wpa_ssid *ssid, *next;
2750252726Srpaulo
2751252726Srpaulo	ssid = config->ssid;
2752252726Srpaulo	while (ssid) {
2753252726Srpaulo		next = ssid->next;
2754252726Srpaulo		func(arg, ssid);
2755252726Srpaulo		ssid = next;
2756252726Srpaulo	}
2757252726Srpaulo}
2758252726Srpaulo
2759252726Srpaulo
2760252726Srpaulo/**
2761189251Ssam * wpa_config_get_network - Get configured network based on id
2762189251Ssam * @config: Configuration data from wpa_config_read()
2763189251Ssam * @id: Unique network id to search for
2764189251Ssam * Returns: Network configuration or %NULL if not found
2765189251Ssam */
2766189251Ssamstruct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id)
2767189251Ssam{
2768189251Ssam	struct wpa_ssid *ssid;
2769189251Ssam
2770189251Ssam	ssid = config->ssid;
2771189251Ssam	while (ssid) {
2772189251Ssam		if (id == ssid->id)
2773189251Ssam			break;
2774189251Ssam		ssid = ssid->next;
2775189251Ssam	}
2776189251Ssam
2777189251Ssam	return ssid;
2778189251Ssam}
2779189251Ssam
2780189251Ssam
2781189251Ssam/**
2782189251Ssam * wpa_config_add_network - Add a new network with empty configuration
2783189251Ssam * @config: Configuration data from wpa_config_read()
2784189251Ssam * Returns: The new network configuration or %NULL if operation failed
2785189251Ssam */
2786189251Ssamstruct wpa_ssid * wpa_config_add_network(struct wpa_config *config)
2787189251Ssam{
2788189251Ssam	int id;
2789189251Ssam	struct wpa_ssid *ssid, *last = NULL;
2790189251Ssam
2791189251Ssam	id = -1;
2792189251Ssam	ssid = config->ssid;
2793189251Ssam	while (ssid) {
2794189251Ssam		if (ssid->id > id)
2795189251Ssam			id = ssid->id;
2796189251Ssam		last = ssid;
2797189251Ssam		ssid = ssid->next;
2798189251Ssam	}
2799189251Ssam	id++;
2800189251Ssam
2801189251Ssam	ssid = os_zalloc(sizeof(*ssid));
2802189251Ssam	if (ssid == NULL)
2803189251Ssam		return NULL;
2804189251Ssam	ssid->id = id;
2805281806Srpaulo	dl_list_init(&ssid->psk_list);
2806189251Ssam	if (last)
2807189251Ssam		last->next = ssid;
2808189251Ssam	else
2809189251Ssam		config->ssid = ssid;
2810189251Ssam
2811189251Ssam	wpa_config_update_prio_list(config);
2812189251Ssam
2813189251Ssam	return ssid;
2814189251Ssam}
2815189251Ssam
2816189251Ssam
2817189251Ssam/**
2818189251Ssam * wpa_config_remove_network - Remove a configured network based on id
2819189251Ssam * @config: Configuration data from wpa_config_read()
2820189251Ssam * @id: Unique network id to search for
2821189251Ssam * Returns: 0 on success, or -1 if the network was not found
2822189251Ssam */
2823189251Ssamint wpa_config_remove_network(struct wpa_config *config, int id)
2824189251Ssam{
2825189251Ssam	struct wpa_ssid *ssid, *prev = NULL;
2826189251Ssam
2827189251Ssam	ssid = config->ssid;
2828189251Ssam	while (ssid) {
2829189251Ssam		if (id == ssid->id)
2830189251Ssam			break;
2831189251Ssam		prev = ssid;
2832189251Ssam		ssid = ssid->next;
2833189251Ssam	}
2834189251Ssam
2835189251Ssam	if (ssid == NULL)
2836189251Ssam		return -1;
2837189251Ssam
2838189251Ssam	if (prev)
2839189251Ssam		prev->next = ssid->next;
2840189251Ssam	else
2841189251Ssam		config->ssid = ssid->next;
2842189251Ssam
2843189251Ssam	wpa_config_update_prio_list(config);
2844189251Ssam	wpa_config_free_ssid(ssid);
2845189251Ssam	return 0;
2846189251Ssam}
2847189251Ssam
2848189251Ssam
2849189251Ssam/**
2850189251Ssam * wpa_config_set_network_defaults - Set network default values
2851189251Ssam * @ssid: Pointer to network configuration data
2852189251Ssam */
2853189251Ssamvoid wpa_config_set_network_defaults(struct wpa_ssid *ssid)
2854189251Ssam{
2855189251Ssam	ssid->proto = DEFAULT_PROTO;
2856189251Ssam	ssid->pairwise_cipher = DEFAULT_PAIRWISE;
2857189251Ssam	ssid->group_cipher = DEFAULT_GROUP;
2858189251Ssam	ssid->key_mgmt = DEFAULT_KEY_MGMT;
2859252726Srpaulo	ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
2860346981Scy	ssid->ht = 1;
2861189251Ssam#ifdef IEEE8021X_EAPOL
2862189251Ssam	ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
2863189251Ssam	ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
2864189251Ssam	ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;
2865281806Srpaulo	ssid->eap.sim_num = DEFAULT_USER_SELECTED_SIM;
2866189251Ssam#endif /* IEEE8021X_EAPOL */
2867281806Srpaulo#ifdef CONFIG_MESH
2868281806Srpaulo	ssid->dot11MeshMaxRetries = DEFAULT_MESH_MAX_RETRIES;
2869281806Srpaulo	ssid->dot11MeshRetryTimeout = DEFAULT_MESH_RETRY_TIMEOUT;
2870281806Srpaulo	ssid->dot11MeshConfirmTimeout = DEFAULT_MESH_CONFIRM_TIMEOUT;
2871281806Srpaulo	ssid->dot11MeshHoldingTimeout = DEFAULT_MESH_HOLDING_TIMEOUT;
2872346981Scy	ssid->mesh_rssi_threshold = DEFAULT_MESH_RSSI_THRESHOLD;
2873281806Srpaulo#endif /* CONFIG_MESH */
2874252726Srpaulo#ifdef CONFIG_HT_OVERRIDES
2875252726Srpaulo	ssid->disable_ht = DEFAULT_DISABLE_HT;
2876252726Srpaulo	ssid->disable_ht40 = DEFAULT_DISABLE_HT40;
2877252726Srpaulo	ssid->disable_sgi = DEFAULT_DISABLE_SGI;
2878281806Srpaulo	ssid->disable_ldpc = DEFAULT_DISABLE_LDPC;
2879346981Scy	ssid->tx_stbc = DEFAULT_TX_STBC;
2880346981Scy	ssid->rx_stbc = DEFAULT_RX_STBC;
2881252726Srpaulo	ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU;
2882252726Srpaulo	ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR;
2883252726Srpaulo	ssid->ampdu_density = DEFAULT_AMPDU_DENSITY;
2884252726Srpaulo#endif /* CONFIG_HT_OVERRIDES */
2885281806Srpaulo#ifdef CONFIG_VHT_OVERRIDES
2886281806Srpaulo	ssid->vht_rx_mcs_nss_1 = -1;
2887281806Srpaulo	ssid->vht_rx_mcs_nss_2 = -1;
2888281806Srpaulo	ssid->vht_rx_mcs_nss_3 = -1;
2889281806Srpaulo	ssid->vht_rx_mcs_nss_4 = -1;
2890281806Srpaulo	ssid->vht_rx_mcs_nss_5 = -1;
2891281806Srpaulo	ssid->vht_rx_mcs_nss_6 = -1;
2892281806Srpaulo	ssid->vht_rx_mcs_nss_7 = -1;
2893281806Srpaulo	ssid->vht_rx_mcs_nss_8 = -1;
2894281806Srpaulo	ssid->vht_tx_mcs_nss_1 = -1;
2895281806Srpaulo	ssid->vht_tx_mcs_nss_2 = -1;
2896281806Srpaulo	ssid->vht_tx_mcs_nss_3 = -1;
2897281806Srpaulo	ssid->vht_tx_mcs_nss_4 = -1;
2898281806Srpaulo	ssid->vht_tx_mcs_nss_5 = -1;
2899281806Srpaulo	ssid->vht_tx_mcs_nss_6 = -1;
2900281806Srpaulo	ssid->vht_tx_mcs_nss_7 = -1;
2901281806Srpaulo	ssid->vht_tx_mcs_nss_8 = -1;
2902281806Srpaulo#endif /* CONFIG_VHT_OVERRIDES */
2903252726Srpaulo	ssid->proactive_key_caching = -1;
2904252726Srpaulo#ifdef CONFIG_IEEE80211W
2905252726Srpaulo	ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT;
2906252726Srpaulo#endif /* CONFIG_IEEE80211W */
2907346981Scy#ifdef CONFIG_MACSEC
2908346981Scy	ssid->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
2909346981Scy#endif /* CONFIG_MACSEC */
2910281806Srpaulo	ssid->mac_addr = -1;
2911346981Scy	ssid->max_oper_chwidth = DEFAULT_MAX_OPER_CHWIDTH;
2912189251Ssam}
2913189251Ssam
2914189251Ssam
2915189251Ssam/**
2916189251Ssam * wpa_config_set - Set a variable in network configuration
2917189251Ssam * @ssid: Pointer to network configuration data
2918189251Ssam * @var: Variable name, e.g., "ssid"
2919189251Ssam * @value: Variable value
2920189251Ssam * @line: Line number in configuration file or 0 if not used
2921337817Scy * Returns: 0 on success with possible change in the value, 1 on success with
2922337817Scy * no change to previously configured value, or -1 on failure
2923189251Ssam *
2924189251Ssam * This function can be used to set network configuration variables based on
2925189251Ssam * both the configuration file and management interface input. The value
2926189251Ssam * parameter must be in the same format as the text-based configuration file is
2927189251Ssam * using. For example, strings are using double quotation marks.
2928189251Ssam */
2929189251Ssamint wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
2930189251Ssam		   int line)
2931189251Ssam{
2932189251Ssam	size_t i;
2933189251Ssam	int ret = 0;
2934189251Ssam
2935189251Ssam	if (ssid == NULL || var == NULL || value == NULL)
2936189251Ssam		return -1;
2937189251Ssam
2938189251Ssam	for (i = 0; i < NUM_SSID_FIELDS; i++) {
2939189251Ssam		const struct parse_data *field = &ssid_fields[i];
2940189251Ssam		if (os_strcmp(var, field->name) != 0)
2941189251Ssam			continue;
2942189251Ssam
2943337817Scy		ret = field->parser(field, ssid, line, value);
2944337817Scy		if (ret < 0) {
2945189251Ssam			if (line) {
2946189251Ssam				wpa_printf(MSG_ERROR, "Line %d: failed to "
2947189251Ssam					   "parse %s '%s'.", line, var, value);
2948189251Ssam			}
2949189251Ssam			ret = -1;
2950189251Ssam		}
2951189251Ssam		break;
2952189251Ssam	}
2953189251Ssam	if (i == NUM_SSID_FIELDS) {
2954189251Ssam		if (line) {
2955189251Ssam			wpa_printf(MSG_ERROR, "Line %d: unknown network field "
2956189251Ssam				   "'%s'.", line, var);
2957189251Ssam		}
2958189251Ssam		ret = -1;
2959189251Ssam	}
2960189251Ssam
2961189251Ssam	return ret;
2962189251Ssam}
2963189251Ssam
2964189251Ssam
2965252726Srpauloint wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
2966252726Srpaulo			  const char *value)
2967252726Srpaulo{
2968252726Srpaulo	size_t len;
2969252726Srpaulo	char *buf;
2970252726Srpaulo	int ret;
2971252726Srpaulo
2972252726Srpaulo	len = os_strlen(value);
2973252726Srpaulo	buf = os_malloc(len + 3);
2974252726Srpaulo	if (buf == NULL)
2975252726Srpaulo		return -1;
2976252726Srpaulo	buf[0] = '"';
2977252726Srpaulo	os_memcpy(buf + 1, value, len);
2978252726Srpaulo	buf[len + 1] = '"';
2979252726Srpaulo	buf[len + 2] = '\0';
2980252726Srpaulo	ret = wpa_config_set(ssid, var, buf, 0);
2981252726Srpaulo	os_free(buf);
2982252726Srpaulo	return ret;
2983252726Srpaulo}
2984252726Srpaulo
2985252726Srpaulo
2986214734Srpaulo/**
2987214734Srpaulo * wpa_config_get_all - Get all options from network configuration
2988214734Srpaulo * @ssid: Pointer to network configuration data
2989214734Srpaulo * @get_keys: Determines if keys/passwords will be included in returned list
2990252726Srpaulo *	(if they may be exported)
2991214734Srpaulo * Returns: %NULL terminated list of all set keys and their values in the form
2992214734Srpaulo * of [key1, val1, key2, val2, ... , NULL]
2993214734Srpaulo *
2994214734Srpaulo * This function can be used to get list of all configured network properties.
2995214734Srpaulo * The caller is responsible for freeing the returned list and all its
2996214734Srpaulo * elements.
2997214734Srpaulo */
2998214734Srpaulochar ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys)
2999214734Srpaulo{
3000289549Srpaulo#ifdef NO_CONFIG_WRITE
3001289549Srpaulo	return NULL;
3002289549Srpaulo#else /* NO_CONFIG_WRITE */
3003214734Srpaulo	const struct parse_data *field;
3004214734Srpaulo	char *key, *value;
3005214734Srpaulo	size_t i;
3006214734Srpaulo	char **props;
3007214734Srpaulo	int fields_num;
3008214734Srpaulo
3009252726Srpaulo	get_keys = get_keys && ssid->export_keys;
3010252726Srpaulo
3011252726Srpaulo	props = os_calloc(2 * NUM_SSID_FIELDS + 1, sizeof(char *));
3012214734Srpaulo	if (!props)
3013214734Srpaulo		return NULL;
3014214734Srpaulo
3015214734Srpaulo	fields_num = 0;
3016214734Srpaulo	for (i = 0; i < NUM_SSID_FIELDS; i++) {
3017214734Srpaulo		field = &ssid_fields[i];
3018214734Srpaulo		if (field->key_data && !get_keys)
3019214734Srpaulo			continue;
3020214734Srpaulo		value = field->writer(field, ssid);
3021214734Srpaulo		if (value == NULL)
3022214734Srpaulo			continue;
3023214734Srpaulo		if (os_strlen(value) == 0) {
3024214734Srpaulo			os_free(value);
3025214734Srpaulo			continue;
3026214734Srpaulo		}
3027214734Srpaulo
3028214734Srpaulo		key = os_strdup(field->name);
3029214734Srpaulo		if (key == NULL) {
3030214734Srpaulo			os_free(value);
3031214734Srpaulo			goto err;
3032214734Srpaulo		}
3033214734Srpaulo
3034214734Srpaulo		props[fields_num * 2] = key;
3035214734Srpaulo		props[fields_num * 2 + 1] = value;
3036214734Srpaulo
3037214734Srpaulo		fields_num++;
3038214734Srpaulo	}
3039214734Srpaulo
3040214734Srpaulo	return props;
3041214734Srpaulo
3042214734Srpauloerr:
3043337817Scy	for (i = 0; props[i]; i++)
3044337817Scy		os_free(props[i]);
3045214734Srpaulo	os_free(props);
3046214734Srpaulo	return NULL;
3047289549Srpaulo#endif /* NO_CONFIG_WRITE */
3048214734Srpaulo}
3049214734Srpaulo
3050214734Srpaulo
3051189251Ssam#ifndef NO_CONFIG_WRITE
3052189251Ssam/**
3053189251Ssam * wpa_config_get - Get a variable in network configuration
3054189251Ssam * @ssid: Pointer to network configuration data
3055189251Ssam * @var: Variable name, e.g., "ssid"
3056189251Ssam * Returns: Value of the variable or %NULL on failure
3057189251Ssam *
3058189251Ssam * This function can be used to get network configuration variables. The
3059189251Ssam * returned value is a copy of the configuration variable in text format, i.e,.
3060189251Ssam * the same format that the text-based configuration file and wpa_config_set()
3061189251Ssam * are using for the value. The caller is responsible for freeing the returned
3062189251Ssam * value.
3063189251Ssam */
3064189251Ssamchar * wpa_config_get(struct wpa_ssid *ssid, const char *var)
3065189251Ssam{
3066189251Ssam	size_t i;
3067189251Ssam
3068189251Ssam	if (ssid == NULL || var == NULL)
3069189251Ssam		return NULL;
3070189251Ssam
3071189251Ssam	for (i = 0; i < NUM_SSID_FIELDS; i++) {
3072189251Ssam		const struct parse_data *field = &ssid_fields[i];
3073337817Scy		if (os_strcmp(var, field->name) == 0) {
3074337817Scy			char *ret = field->writer(field, ssid);
3075337817Scy
3076337817Scy			if (ret && has_newline(ret)) {
3077337817Scy				wpa_printf(MSG_ERROR,
3078337817Scy					   "Found newline in value for %s; not returning it",
3079337817Scy					   var);
3080337817Scy				os_free(ret);
3081337817Scy				ret = NULL;
3082337817Scy			}
3083337817Scy
3084337817Scy			return ret;
3085337817Scy		}
3086189251Ssam	}
3087189251Ssam
3088189251Ssam	return NULL;
3089189251Ssam}
3090189251Ssam
3091189251Ssam
3092189251Ssam/**
3093189251Ssam * wpa_config_get_no_key - Get a variable in network configuration (no keys)
3094189251Ssam * @ssid: Pointer to network configuration data
3095189251Ssam * @var: Variable name, e.g., "ssid"
3096189251Ssam * Returns: Value of the variable or %NULL on failure
3097189251Ssam *
3098189251Ssam * This function can be used to get network configuration variable like
3099189251Ssam * wpa_config_get(). The only difference is that this functions does not expose
3100189251Ssam * key/password material from the configuration. In case a key/password field
3101189251Ssam * is requested, the returned value is an empty string or %NULL if the variable
3102189251Ssam * is not set or "*" if the variable is set (regardless of its value). The
3103189251Ssam * returned value is a copy of the configuration variable in text format, i.e,.
3104189251Ssam * the same format that the text-based configuration file and wpa_config_set()
3105189251Ssam * are using for the value. The caller is responsible for freeing the returned
3106189251Ssam * value.
3107189251Ssam */
3108189251Ssamchar * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var)
3109189251Ssam{
3110189251Ssam	size_t i;
3111189251Ssam
3112189251Ssam	if (ssid == NULL || var == NULL)
3113189251Ssam		return NULL;
3114189251Ssam
3115189251Ssam	for (i = 0; i < NUM_SSID_FIELDS; i++) {
3116189251Ssam		const struct parse_data *field = &ssid_fields[i];
3117189251Ssam		if (os_strcmp(var, field->name) == 0) {
3118189251Ssam			char *res = field->writer(field, ssid);
3119189251Ssam			if (field->key_data) {
3120189251Ssam				if (res && res[0]) {
3121189251Ssam					wpa_printf(MSG_DEBUG, "Do not allow "
3122189251Ssam						   "key_data field to be "
3123189251Ssam						   "exposed");
3124281806Srpaulo					str_clear_free(res);
3125189251Ssam					return os_strdup("*");
3126189251Ssam				}
3127189251Ssam
3128189251Ssam				os_free(res);
3129189251Ssam				return NULL;
3130189251Ssam			}
3131189251Ssam			return res;
3132189251Ssam		}
3133189251Ssam	}
3134189251Ssam
3135189251Ssam	return NULL;
3136189251Ssam}
3137189251Ssam#endif /* NO_CONFIG_WRITE */
3138189251Ssam
3139189251Ssam
3140189251Ssam/**
3141189251Ssam * wpa_config_update_psk - Update WPA PSK based on passphrase and SSID
3142189251Ssam * @ssid: Pointer to network configuration data
3143189251Ssam *
3144189251Ssam * This function must be called to update WPA PSK when either SSID or the
3145189251Ssam * passphrase has changed for the network configuration.
3146189251Ssam */
3147189251Ssamvoid wpa_config_update_psk(struct wpa_ssid *ssid)
3148189251Ssam{
3149189251Ssam#ifndef CONFIG_NO_PBKDF2
3150252726Srpaulo	pbkdf2_sha1(ssid->passphrase, ssid->ssid, ssid->ssid_len, 4096,
3151189251Ssam		    ssid->psk, PMK_LEN);
3152189251Ssam	wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
3153189251Ssam			ssid->psk, PMK_LEN);
3154189251Ssam	ssid->psk_set = 1;
3155189251Ssam#endif /* CONFIG_NO_PBKDF2 */
3156189251Ssam}
3157189251Ssam
3158189251Ssam
3159281806Srpaulostatic int wpa_config_set_cred_req_conn_capab(struct wpa_cred *cred,
3160281806Srpaulo					      const char *value)
3161281806Srpaulo{
3162281806Srpaulo	u8 *proto;
3163281806Srpaulo	int **port;
3164281806Srpaulo	int *ports, *nports;
3165281806Srpaulo	const char *pos;
3166281806Srpaulo	unsigned int num_ports;
3167281806Srpaulo
3168281806Srpaulo	proto = os_realloc_array(cred->req_conn_capab_proto,
3169281806Srpaulo				 cred->num_req_conn_capab + 1, sizeof(u8));
3170281806Srpaulo	if (proto == NULL)
3171281806Srpaulo		return -1;
3172281806Srpaulo	cred->req_conn_capab_proto = proto;
3173281806Srpaulo
3174281806Srpaulo	port = os_realloc_array(cred->req_conn_capab_port,
3175281806Srpaulo				cred->num_req_conn_capab + 1, sizeof(int *));
3176281806Srpaulo	if (port == NULL)
3177281806Srpaulo		return -1;
3178281806Srpaulo	cred->req_conn_capab_port = port;
3179281806Srpaulo
3180281806Srpaulo	proto[cred->num_req_conn_capab] = atoi(value);
3181281806Srpaulo
3182281806Srpaulo	pos = os_strchr(value, ':');
3183281806Srpaulo	if (pos == NULL) {
3184281806Srpaulo		port[cred->num_req_conn_capab] = NULL;
3185281806Srpaulo		cred->num_req_conn_capab++;
3186281806Srpaulo		return 0;
3187281806Srpaulo	}
3188281806Srpaulo	pos++;
3189281806Srpaulo
3190281806Srpaulo	ports = NULL;
3191281806Srpaulo	num_ports = 0;
3192281806Srpaulo
3193281806Srpaulo	while (*pos) {
3194281806Srpaulo		nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
3195281806Srpaulo		if (nports == NULL) {
3196281806Srpaulo			os_free(ports);
3197281806Srpaulo			return -1;
3198281806Srpaulo		}
3199281806Srpaulo		ports = nports;
3200281806Srpaulo		ports[num_ports++] = atoi(pos);
3201281806Srpaulo
3202281806Srpaulo		pos = os_strchr(pos, ',');
3203281806Srpaulo		if (pos == NULL)
3204281806Srpaulo			break;
3205281806Srpaulo		pos++;
3206281806Srpaulo	}
3207281806Srpaulo
3208281806Srpaulo	nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
3209281806Srpaulo	if (nports == NULL) {
3210281806Srpaulo		os_free(ports);
3211281806Srpaulo		return -1;
3212281806Srpaulo	}
3213281806Srpaulo	ports = nports;
3214281806Srpaulo	ports[num_ports] = -1;
3215281806Srpaulo
3216281806Srpaulo	port[cred->num_req_conn_capab] = ports;
3217281806Srpaulo	cred->num_req_conn_capab++;
3218281806Srpaulo	return 0;
3219281806Srpaulo}
3220281806Srpaulo
3221281806Srpaulo
3222346981Scystatic int wpa_config_set_cred_roaming_consortiums(struct wpa_cred *cred,
3223346981Scy						   const char *value)
3224346981Scy{
3225346981Scy	u8 roaming_consortiums[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
3226346981Scy	size_t roaming_consortiums_len[MAX_ROAMING_CONS];
3227346981Scy	unsigned int num_roaming_consortiums = 0;
3228346981Scy	const char *pos, *end;
3229346981Scy	size_t len;
3230346981Scy
3231346981Scy	os_memset(roaming_consortiums, 0, sizeof(roaming_consortiums));
3232346981Scy	os_memset(roaming_consortiums_len, 0, sizeof(roaming_consortiums_len));
3233346981Scy
3234346981Scy	for (pos = value;;) {
3235346981Scy		end = os_strchr(pos, ',');
3236346981Scy		len = end ? (size_t) (end - pos) : os_strlen(pos);
3237346981Scy		if (!end && len == 0)
3238346981Scy			break;
3239346981Scy		if (len == 0 || (len & 1) != 0 ||
3240346981Scy		    len / 2 > MAX_ROAMING_CONS_OI_LEN ||
3241346981Scy		    hexstr2bin(pos,
3242346981Scy			       roaming_consortiums[num_roaming_consortiums],
3243346981Scy			       len / 2) < 0) {
3244346981Scy			wpa_printf(MSG_INFO,
3245346981Scy				   "Invalid roaming_consortiums entry: %s",
3246346981Scy				   pos);
3247346981Scy			return -1;
3248346981Scy		}
3249346981Scy		roaming_consortiums_len[num_roaming_consortiums] = len / 2;
3250346981Scy		num_roaming_consortiums++;
3251346981Scy
3252346981Scy		if (!end)
3253346981Scy			break;
3254346981Scy
3255346981Scy		if (num_roaming_consortiums >= MAX_ROAMING_CONS) {
3256346981Scy			wpa_printf(MSG_INFO,
3257346981Scy				   "Too many roaming_consortiums OIs");
3258346981Scy			return -1;
3259346981Scy		}
3260346981Scy
3261346981Scy		pos = end + 1;
3262346981Scy	}
3263346981Scy
3264346981Scy	os_memcpy(cred->roaming_consortiums, roaming_consortiums,
3265346981Scy		  sizeof(roaming_consortiums));
3266346981Scy	os_memcpy(cred->roaming_consortiums_len, roaming_consortiums_len,
3267346981Scy		  sizeof(roaming_consortiums_len));
3268346981Scy	cred->num_roaming_consortiums = num_roaming_consortiums;
3269346981Scy
3270346981Scy	return 0;
3271346981Scy}
3272346981Scy
3273346981Scy
3274252726Srpauloint wpa_config_set_cred(struct wpa_cred *cred, const char *var,
3275252726Srpaulo			const char *value, int line)
3276252726Srpaulo{
3277252726Srpaulo	char *val;
3278252726Srpaulo	size_t len;
3279346981Scy	int res;
3280252726Srpaulo
3281281806Srpaulo	if (os_strcmp(var, "temporary") == 0) {
3282281806Srpaulo		cred->temporary = atoi(value);
3283281806Srpaulo		return 0;
3284281806Srpaulo	}
3285281806Srpaulo
3286252726Srpaulo	if (os_strcmp(var, "priority") == 0) {
3287252726Srpaulo		cred->priority = atoi(value);
3288252726Srpaulo		return 0;
3289252726Srpaulo	}
3290252726Srpaulo
3291281806Srpaulo	if (os_strcmp(var, "sp_priority") == 0) {
3292281806Srpaulo		int prio = atoi(value);
3293281806Srpaulo		if (prio < 0 || prio > 255)
3294281806Srpaulo			return -1;
3295281806Srpaulo		cred->sp_priority = prio;
3296281806Srpaulo		return 0;
3297281806Srpaulo	}
3298281806Srpaulo
3299252726Srpaulo	if (os_strcmp(var, "pcsc") == 0) {
3300252726Srpaulo		cred->pcsc = atoi(value);
3301252726Srpaulo		return 0;
3302252726Srpaulo	}
3303252726Srpaulo
3304252726Srpaulo	if (os_strcmp(var, "eap") == 0) {
3305252726Srpaulo		struct eap_method_type method;
3306252726Srpaulo		method.method = eap_peer_get_type(value, &method.vendor);
3307252726Srpaulo		if (method.vendor == EAP_VENDOR_IETF &&
3308252726Srpaulo		    method.method == EAP_TYPE_NONE) {
3309252726Srpaulo			wpa_printf(MSG_ERROR, "Line %d: unknown EAP type '%s' "
3310252726Srpaulo				   "for a credential", line, value);
3311252726Srpaulo			return -1;
3312252726Srpaulo		}
3313252726Srpaulo		os_free(cred->eap_method);
3314252726Srpaulo		cred->eap_method = os_malloc(sizeof(*cred->eap_method));
3315252726Srpaulo		if (cred->eap_method == NULL)
3316252726Srpaulo			return -1;
3317252726Srpaulo		os_memcpy(cred->eap_method, &method, sizeof(method));
3318252726Srpaulo		return 0;
3319252726Srpaulo	}
3320252726Srpaulo
3321252726Srpaulo	if (os_strcmp(var, "password") == 0 &&
3322252726Srpaulo	    os_strncmp(value, "ext:", 4) == 0) {
3323337817Scy		if (has_newline(value))
3324337817Scy			return -1;
3325281806Srpaulo		str_clear_free(cred->password);
3326252726Srpaulo		cred->password = os_strdup(value);
3327252726Srpaulo		cred->ext_password = 1;
3328252726Srpaulo		return 0;
3329252726Srpaulo	}
3330252726Srpaulo
3331281806Srpaulo	if (os_strcmp(var, "update_identifier") == 0) {
3332281806Srpaulo		cred->update_identifier = atoi(value);
3333281806Srpaulo		return 0;
3334281806Srpaulo	}
3335281806Srpaulo
3336281806Srpaulo	if (os_strcmp(var, "min_dl_bandwidth_home") == 0) {
3337281806Srpaulo		cred->min_dl_bandwidth_home = atoi(value);
3338281806Srpaulo		return 0;
3339281806Srpaulo	}
3340281806Srpaulo
3341281806Srpaulo	if (os_strcmp(var, "min_ul_bandwidth_home") == 0) {
3342281806Srpaulo		cred->min_ul_bandwidth_home = atoi(value);
3343281806Srpaulo		return 0;
3344281806Srpaulo	}
3345281806Srpaulo
3346281806Srpaulo	if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0) {
3347281806Srpaulo		cred->min_dl_bandwidth_roaming = atoi(value);
3348281806Srpaulo		return 0;
3349281806Srpaulo	}
3350281806Srpaulo
3351281806Srpaulo	if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0) {
3352281806Srpaulo		cred->min_ul_bandwidth_roaming = atoi(value);
3353281806Srpaulo		return 0;
3354281806Srpaulo	}
3355281806Srpaulo
3356281806Srpaulo	if (os_strcmp(var, "max_bss_load") == 0) {
3357281806Srpaulo		cred->max_bss_load = atoi(value);
3358281806Srpaulo		return 0;
3359281806Srpaulo	}
3360281806Srpaulo
3361281806Srpaulo	if (os_strcmp(var, "req_conn_capab") == 0)
3362281806Srpaulo		return wpa_config_set_cred_req_conn_capab(cred, value);
3363281806Srpaulo
3364281806Srpaulo	if (os_strcmp(var, "ocsp") == 0) {
3365281806Srpaulo		cred->ocsp = atoi(value);
3366281806Srpaulo		return 0;
3367281806Srpaulo	}
3368281806Srpaulo
3369281806Srpaulo	if (os_strcmp(var, "sim_num") == 0) {
3370281806Srpaulo		cred->sim_num = atoi(value);
3371281806Srpaulo		return 0;
3372281806Srpaulo	}
3373281806Srpaulo
3374252726Srpaulo	val = wpa_config_parse_string(value, &len);
3375337817Scy	if (val == NULL ||
3376337817Scy	    (os_strcmp(var, "excluded_ssid") != 0 &&
3377337817Scy	     os_strcmp(var, "roaming_consortium") != 0 &&
3378337817Scy	     os_strcmp(var, "required_roaming_consortium") != 0 &&
3379337817Scy	     has_newline(val))) {
3380252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
3381252726Srpaulo			   "value '%s'.", line, var, value);
3382337817Scy		os_free(val);
3383252726Srpaulo		return -1;
3384252726Srpaulo	}
3385252726Srpaulo
3386252726Srpaulo	if (os_strcmp(var, "realm") == 0) {
3387252726Srpaulo		os_free(cred->realm);
3388252726Srpaulo		cred->realm = val;
3389252726Srpaulo		return 0;
3390252726Srpaulo	}
3391252726Srpaulo
3392252726Srpaulo	if (os_strcmp(var, "username") == 0) {
3393281806Srpaulo		str_clear_free(cred->username);
3394252726Srpaulo		cred->username = val;
3395252726Srpaulo		return 0;
3396252726Srpaulo	}
3397252726Srpaulo
3398252726Srpaulo	if (os_strcmp(var, "password") == 0) {
3399281806Srpaulo		str_clear_free(cred->password);
3400252726Srpaulo		cred->password = val;
3401252726Srpaulo		cred->ext_password = 0;
3402252726Srpaulo		return 0;
3403252726Srpaulo	}
3404252726Srpaulo
3405252726Srpaulo	if (os_strcmp(var, "ca_cert") == 0) {
3406252726Srpaulo		os_free(cred->ca_cert);
3407252726Srpaulo		cred->ca_cert = val;
3408252726Srpaulo		return 0;
3409252726Srpaulo	}
3410252726Srpaulo
3411252726Srpaulo	if (os_strcmp(var, "client_cert") == 0) {
3412252726Srpaulo		os_free(cred->client_cert);
3413252726Srpaulo		cred->client_cert = val;
3414252726Srpaulo		return 0;
3415252726Srpaulo	}
3416252726Srpaulo
3417252726Srpaulo	if (os_strcmp(var, "private_key") == 0) {
3418252726Srpaulo		os_free(cred->private_key);
3419252726Srpaulo		cred->private_key = val;
3420252726Srpaulo		return 0;
3421252726Srpaulo	}
3422252726Srpaulo
3423252726Srpaulo	if (os_strcmp(var, "private_key_passwd") == 0) {
3424281806Srpaulo		str_clear_free(cred->private_key_passwd);
3425252726Srpaulo		cred->private_key_passwd = val;
3426252726Srpaulo		return 0;
3427252726Srpaulo	}
3428252726Srpaulo
3429252726Srpaulo	if (os_strcmp(var, "imsi") == 0) {
3430252726Srpaulo		os_free(cred->imsi);
3431252726Srpaulo		cred->imsi = val;
3432252726Srpaulo		return 0;
3433252726Srpaulo	}
3434252726Srpaulo
3435252726Srpaulo	if (os_strcmp(var, "milenage") == 0) {
3436281806Srpaulo		str_clear_free(cred->milenage);
3437252726Srpaulo		cred->milenage = val;
3438252726Srpaulo		return 0;
3439252726Srpaulo	}
3440252726Srpaulo
3441281806Srpaulo	if (os_strcmp(var, "domain_suffix_match") == 0) {
3442281806Srpaulo		os_free(cred->domain_suffix_match);
3443281806Srpaulo		cred->domain_suffix_match = val;
3444281806Srpaulo		return 0;
3445281806Srpaulo	}
3446281806Srpaulo
3447252726Srpaulo	if (os_strcmp(var, "domain") == 0) {
3448281806Srpaulo		char **new_domain;
3449281806Srpaulo		new_domain = os_realloc_array(cred->domain,
3450281806Srpaulo					      cred->num_domain + 1,
3451281806Srpaulo					      sizeof(char *));
3452281806Srpaulo		if (new_domain == NULL) {
3453281806Srpaulo			os_free(val);
3454281806Srpaulo			return -1;
3455281806Srpaulo		}
3456281806Srpaulo		new_domain[cred->num_domain++] = val;
3457281806Srpaulo		cred->domain = new_domain;
3458252726Srpaulo		return 0;
3459252726Srpaulo	}
3460252726Srpaulo
3461252726Srpaulo	if (os_strcmp(var, "phase1") == 0) {
3462252726Srpaulo		os_free(cred->phase1);
3463252726Srpaulo		cred->phase1 = val;
3464252726Srpaulo		return 0;
3465252726Srpaulo	}
3466252726Srpaulo
3467252726Srpaulo	if (os_strcmp(var, "phase2") == 0) {
3468252726Srpaulo		os_free(cred->phase2);
3469252726Srpaulo		cred->phase2 = val;
3470252726Srpaulo		return 0;
3471252726Srpaulo	}
3472252726Srpaulo
3473252726Srpaulo	if (os_strcmp(var, "roaming_consortium") == 0) {
3474252726Srpaulo		if (len < 3 || len > sizeof(cred->roaming_consortium)) {
3475252726Srpaulo			wpa_printf(MSG_ERROR, "Line %d: invalid "
3476252726Srpaulo				   "roaming_consortium length %d (3..15 "
3477252726Srpaulo				   "expected)", line, (int) len);
3478252726Srpaulo			os_free(val);
3479252726Srpaulo			return -1;
3480252726Srpaulo		}
3481252726Srpaulo		os_memcpy(cred->roaming_consortium, val, len);
3482252726Srpaulo		cred->roaming_consortium_len = len;
3483252726Srpaulo		os_free(val);
3484252726Srpaulo		return 0;
3485252726Srpaulo	}
3486252726Srpaulo
3487281806Srpaulo	if (os_strcmp(var, "required_roaming_consortium") == 0) {
3488281806Srpaulo		if (len < 3 || len > sizeof(cred->required_roaming_consortium))
3489281806Srpaulo		{
3490281806Srpaulo			wpa_printf(MSG_ERROR, "Line %d: invalid "
3491281806Srpaulo				   "required_roaming_consortium length %d "
3492281806Srpaulo				   "(3..15 expected)", line, (int) len);
3493281806Srpaulo			os_free(val);
3494281806Srpaulo			return -1;
3495281806Srpaulo		}
3496281806Srpaulo		os_memcpy(cred->required_roaming_consortium, val, len);
3497281806Srpaulo		cred->required_roaming_consortium_len = len;
3498281806Srpaulo		os_free(val);
3499281806Srpaulo		return 0;
3500281806Srpaulo	}
3501281806Srpaulo
3502346981Scy	if (os_strcmp(var, "roaming_consortiums") == 0) {
3503346981Scy		res = wpa_config_set_cred_roaming_consortiums(cred, val);
3504346981Scy		if (res < 0)
3505346981Scy			wpa_printf(MSG_ERROR,
3506346981Scy				   "Line %d: invalid roaming_consortiums",
3507346981Scy				   line);
3508346981Scy		os_free(val);
3509346981Scy		return res;
3510346981Scy	}
3511346981Scy
3512252726Srpaulo	if (os_strcmp(var, "excluded_ssid") == 0) {
3513252726Srpaulo		struct excluded_ssid *e;
3514252726Srpaulo
3515289549Srpaulo		if (len > SSID_MAX_LEN) {
3516252726Srpaulo			wpa_printf(MSG_ERROR, "Line %d: invalid "
3517252726Srpaulo				   "excluded_ssid length %d", line, (int) len);
3518252726Srpaulo			os_free(val);
3519252726Srpaulo			return -1;
3520252726Srpaulo		}
3521252726Srpaulo
3522252726Srpaulo		e = os_realloc_array(cred->excluded_ssid,
3523252726Srpaulo				     cred->num_excluded_ssid + 1,
3524252726Srpaulo				     sizeof(struct excluded_ssid));
3525252726Srpaulo		if (e == NULL) {
3526252726Srpaulo			os_free(val);
3527252726Srpaulo			return -1;
3528252726Srpaulo		}
3529252726Srpaulo		cred->excluded_ssid = e;
3530252726Srpaulo
3531252726Srpaulo		e = &cred->excluded_ssid[cred->num_excluded_ssid++];
3532252726Srpaulo		os_memcpy(e->ssid, val, len);
3533252726Srpaulo		e->ssid_len = len;
3534252726Srpaulo
3535252726Srpaulo		os_free(val);
3536252726Srpaulo
3537252726Srpaulo		return 0;
3538252726Srpaulo	}
3539252726Srpaulo
3540281806Srpaulo	if (os_strcmp(var, "roaming_partner") == 0) {
3541281806Srpaulo		struct roaming_partner *p;
3542281806Srpaulo		char *pos;
3543281806Srpaulo
3544281806Srpaulo		p = os_realloc_array(cred->roaming_partner,
3545281806Srpaulo				     cred->num_roaming_partner + 1,
3546281806Srpaulo				     sizeof(struct roaming_partner));
3547281806Srpaulo		if (p == NULL) {
3548281806Srpaulo			os_free(val);
3549281806Srpaulo			return -1;
3550281806Srpaulo		}
3551281806Srpaulo		cred->roaming_partner = p;
3552281806Srpaulo
3553281806Srpaulo		p = &cred->roaming_partner[cred->num_roaming_partner];
3554281806Srpaulo
3555281806Srpaulo		pos = os_strchr(val, ',');
3556281806Srpaulo		if (pos == NULL) {
3557281806Srpaulo			os_free(val);
3558281806Srpaulo			return -1;
3559281806Srpaulo		}
3560281806Srpaulo		*pos++ = '\0';
3561281806Srpaulo		if (pos - val - 1 >= (int) sizeof(p->fqdn)) {
3562281806Srpaulo			os_free(val);
3563281806Srpaulo			return -1;
3564281806Srpaulo		}
3565281806Srpaulo		os_memcpy(p->fqdn, val, pos - val);
3566281806Srpaulo
3567281806Srpaulo		p->exact_match = atoi(pos);
3568281806Srpaulo
3569281806Srpaulo		pos = os_strchr(pos, ',');
3570281806Srpaulo		if (pos == NULL) {
3571281806Srpaulo			os_free(val);
3572281806Srpaulo			return -1;
3573281806Srpaulo		}
3574281806Srpaulo		*pos++ = '\0';
3575281806Srpaulo
3576281806Srpaulo		p->priority = atoi(pos);
3577281806Srpaulo
3578281806Srpaulo		pos = os_strchr(pos, ',');
3579281806Srpaulo		if (pos == NULL) {
3580281806Srpaulo			os_free(val);
3581281806Srpaulo			return -1;
3582281806Srpaulo		}
3583281806Srpaulo		*pos++ = '\0';
3584281806Srpaulo
3585281806Srpaulo		if (os_strlen(pos) >= sizeof(p->country)) {
3586281806Srpaulo			os_free(val);
3587281806Srpaulo			return -1;
3588281806Srpaulo		}
3589281806Srpaulo		os_memcpy(p->country, pos, os_strlen(pos) + 1);
3590281806Srpaulo
3591281806Srpaulo		cred->num_roaming_partner++;
3592281806Srpaulo		os_free(val);
3593281806Srpaulo
3594281806Srpaulo		return 0;
3595281806Srpaulo	}
3596281806Srpaulo
3597281806Srpaulo	if (os_strcmp(var, "provisioning_sp") == 0) {
3598281806Srpaulo		os_free(cred->provisioning_sp);
3599281806Srpaulo		cred->provisioning_sp = val;
3600281806Srpaulo		return 0;
3601281806Srpaulo	}
3602281806Srpaulo
3603252726Srpaulo	if (line) {
3604252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.",
3605252726Srpaulo			   line, var);
3606252726Srpaulo	}
3607252726Srpaulo
3608252726Srpaulo	os_free(val);
3609252726Srpaulo
3610252726Srpaulo	return -1;
3611252726Srpaulo}
3612252726Srpaulo
3613252726Srpaulo
3614281806Srpaulostatic char * alloc_int_str(int val)
3615281806Srpaulo{
3616281806Srpaulo	const unsigned int bufsize = 20;
3617281806Srpaulo	char *buf;
3618281806Srpaulo	int res;
3619281806Srpaulo
3620281806Srpaulo	buf = os_malloc(bufsize);
3621281806Srpaulo	if (buf == NULL)
3622281806Srpaulo		return NULL;
3623281806Srpaulo	res = os_snprintf(buf, bufsize, "%d", val);
3624281806Srpaulo	if (os_snprintf_error(bufsize, res)) {
3625281806Srpaulo		os_free(buf);
3626281806Srpaulo		buf = NULL;
3627281806Srpaulo	}
3628281806Srpaulo	return buf;
3629281806Srpaulo}
3630281806Srpaulo
3631281806Srpaulo
3632281806Srpaulostatic char * alloc_strdup(const char *str)
3633281806Srpaulo{
3634281806Srpaulo	if (str == NULL)
3635281806Srpaulo		return NULL;
3636281806Srpaulo	return os_strdup(str);
3637281806Srpaulo}
3638281806Srpaulo
3639281806Srpaulo
3640281806Srpaulochar * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var)
3641281806Srpaulo{
3642281806Srpaulo	if (os_strcmp(var, "temporary") == 0)
3643281806Srpaulo		return alloc_int_str(cred->temporary);
3644281806Srpaulo
3645281806Srpaulo	if (os_strcmp(var, "priority") == 0)
3646281806Srpaulo		return alloc_int_str(cred->priority);
3647281806Srpaulo
3648281806Srpaulo	if (os_strcmp(var, "sp_priority") == 0)
3649281806Srpaulo		return alloc_int_str(cred->sp_priority);
3650281806Srpaulo
3651281806Srpaulo	if (os_strcmp(var, "pcsc") == 0)
3652281806Srpaulo		return alloc_int_str(cred->pcsc);
3653281806Srpaulo
3654281806Srpaulo	if (os_strcmp(var, "eap") == 0) {
3655281806Srpaulo		if (!cred->eap_method)
3656281806Srpaulo			return NULL;
3657281806Srpaulo		return alloc_strdup(eap_get_name(cred->eap_method[0].vendor,
3658281806Srpaulo						 cred->eap_method[0].method));
3659281806Srpaulo	}
3660281806Srpaulo
3661281806Srpaulo	if (os_strcmp(var, "update_identifier") == 0)
3662281806Srpaulo		return alloc_int_str(cred->update_identifier);
3663281806Srpaulo
3664281806Srpaulo	if (os_strcmp(var, "min_dl_bandwidth_home") == 0)
3665281806Srpaulo		return alloc_int_str(cred->min_dl_bandwidth_home);
3666281806Srpaulo
3667281806Srpaulo	if (os_strcmp(var, "min_ul_bandwidth_home") == 0)
3668281806Srpaulo		return alloc_int_str(cred->min_ul_bandwidth_home);
3669281806Srpaulo
3670281806Srpaulo	if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0)
3671281806Srpaulo		return alloc_int_str(cred->min_dl_bandwidth_roaming);
3672281806Srpaulo
3673281806Srpaulo	if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0)
3674281806Srpaulo		return alloc_int_str(cred->min_ul_bandwidth_roaming);
3675281806Srpaulo
3676281806Srpaulo	if (os_strcmp(var, "max_bss_load") == 0)
3677281806Srpaulo		return alloc_int_str(cred->max_bss_load);
3678281806Srpaulo
3679281806Srpaulo	if (os_strcmp(var, "req_conn_capab") == 0) {
3680281806Srpaulo		unsigned int i;
3681281806Srpaulo		char *buf, *end, *pos;
3682281806Srpaulo		int ret;
3683281806Srpaulo
3684281806Srpaulo		if (!cred->num_req_conn_capab)
3685281806Srpaulo			return NULL;
3686281806Srpaulo
3687281806Srpaulo		buf = os_malloc(4000);
3688281806Srpaulo		if (buf == NULL)
3689281806Srpaulo			return NULL;
3690281806Srpaulo		pos = buf;
3691281806Srpaulo		end = pos + 4000;
3692281806Srpaulo		for (i = 0; i < cred->num_req_conn_capab; i++) {
3693281806Srpaulo			int *ports;
3694281806Srpaulo
3695281806Srpaulo			ret = os_snprintf(pos, end - pos, "%s%u",
3696281806Srpaulo					  i > 0 ? "\n" : "",
3697281806Srpaulo					  cred->req_conn_capab_proto[i]);
3698281806Srpaulo			if (os_snprintf_error(end - pos, ret))
3699281806Srpaulo				return buf;
3700281806Srpaulo			pos += ret;
3701281806Srpaulo
3702281806Srpaulo			ports = cred->req_conn_capab_port[i];
3703281806Srpaulo			if (ports) {
3704281806Srpaulo				int j;
3705281806Srpaulo				for (j = 0; ports[j] != -1; j++) {
3706281806Srpaulo					ret = os_snprintf(pos, end - pos,
3707281806Srpaulo							  "%s%d",
3708281806Srpaulo							  j > 0 ? "," : ":",
3709281806Srpaulo							  ports[j]);
3710281806Srpaulo					if (os_snprintf_error(end - pos, ret))
3711281806Srpaulo						return buf;
3712281806Srpaulo					pos += ret;
3713281806Srpaulo				}
3714281806Srpaulo			}
3715281806Srpaulo		}
3716281806Srpaulo
3717281806Srpaulo		return buf;
3718281806Srpaulo	}
3719281806Srpaulo
3720281806Srpaulo	if (os_strcmp(var, "ocsp") == 0)
3721281806Srpaulo		return alloc_int_str(cred->ocsp);
3722281806Srpaulo
3723281806Srpaulo	if (os_strcmp(var, "realm") == 0)
3724281806Srpaulo		return alloc_strdup(cred->realm);
3725281806Srpaulo
3726281806Srpaulo	if (os_strcmp(var, "username") == 0)
3727281806Srpaulo		return alloc_strdup(cred->username);
3728281806Srpaulo
3729281806Srpaulo	if (os_strcmp(var, "password") == 0) {
3730281806Srpaulo		if (!cred->password)
3731281806Srpaulo			return NULL;
3732281806Srpaulo		return alloc_strdup("*");
3733281806Srpaulo	}
3734281806Srpaulo
3735281806Srpaulo	if (os_strcmp(var, "ca_cert") == 0)
3736281806Srpaulo		return alloc_strdup(cred->ca_cert);
3737281806Srpaulo
3738281806Srpaulo	if (os_strcmp(var, "client_cert") == 0)
3739281806Srpaulo		return alloc_strdup(cred->client_cert);
3740281806Srpaulo
3741281806Srpaulo	if (os_strcmp(var, "private_key") == 0)
3742281806Srpaulo		return alloc_strdup(cred->private_key);
3743281806Srpaulo
3744281806Srpaulo	if (os_strcmp(var, "private_key_passwd") == 0) {
3745281806Srpaulo		if (!cred->private_key_passwd)
3746281806Srpaulo			return NULL;
3747281806Srpaulo		return alloc_strdup("*");
3748281806Srpaulo	}
3749281806Srpaulo
3750281806Srpaulo	if (os_strcmp(var, "imsi") == 0)
3751281806Srpaulo		return alloc_strdup(cred->imsi);
3752281806Srpaulo
3753281806Srpaulo	if (os_strcmp(var, "milenage") == 0) {
3754281806Srpaulo		if (!(cred->milenage))
3755281806Srpaulo			return NULL;
3756281806Srpaulo		return alloc_strdup("*");
3757281806Srpaulo	}
3758281806Srpaulo
3759281806Srpaulo	if (os_strcmp(var, "domain_suffix_match") == 0)
3760281806Srpaulo		return alloc_strdup(cred->domain_suffix_match);
3761281806Srpaulo
3762281806Srpaulo	if (os_strcmp(var, "domain") == 0) {
3763281806Srpaulo		unsigned int i;
3764281806Srpaulo		char *buf, *end, *pos;
3765281806Srpaulo		int ret;
3766281806Srpaulo
3767281806Srpaulo		if (!cred->num_domain)
3768281806Srpaulo			return NULL;
3769281806Srpaulo
3770281806Srpaulo		buf = os_malloc(4000);
3771281806Srpaulo		if (buf == NULL)
3772281806Srpaulo			return NULL;
3773281806Srpaulo		pos = buf;
3774281806Srpaulo		end = pos + 4000;
3775281806Srpaulo
3776281806Srpaulo		for (i = 0; i < cred->num_domain; i++) {
3777281806Srpaulo			ret = os_snprintf(pos, end - pos, "%s%s",
3778281806Srpaulo					  i > 0 ? "\n" : "", cred->domain[i]);
3779281806Srpaulo			if (os_snprintf_error(end - pos, ret))
3780281806Srpaulo				return buf;
3781281806Srpaulo			pos += ret;
3782281806Srpaulo		}
3783281806Srpaulo
3784281806Srpaulo		return buf;
3785281806Srpaulo	}
3786281806Srpaulo
3787281806Srpaulo	if (os_strcmp(var, "phase1") == 0)
3788281806Srpaulo		return alloc_strdup(cred->phase1);
3789281806Srpaulo
3790281806Srpaulo	if (os_strcmp(var, "phase2") == 0)
3791281806Srpaulo		return alloc_strdup(cred->phase2);
3792281806Srpaulo
3793281806Srpaulo	if (os_strcmp(var, "roaming_consortium") == 0) {
3794281806Srpaulo		size_t buflen;
3795281806Srpaulo		char *buf;
3796281806Srpaulo
3797281806Srpaulo		if (!cred->roaming_consortium_len)
3798281806Srpaulo			return NULL;
3799281806Srpaulo		buflen = cred->roaming_consortium_len * 2 + 1;
3800281806Srpaulo		buf = os_malloc(buflen);
3801281806Srpaulo		if (buf == NULL)
3802281806Srpaulo			return NULL;
3803281806Srpaulo		wpa_snprintf_hex(buf, buflen, cred->roaming_consortium,
3804281806Srpaulo				 cred->roaming_consortium_len);
3805281806Srpaulo		return buf;
3806281806Srpaulo	}
3807281806Srpaulo
3808281806Srpaulo	if (os_strcmp(var, "required_roaming_consortium") == 0) {
3809281806Srpaulo		size_t buflen;
3810281806Srpaulo		char *buf;
3811281806Srpaulo
3812281806Srpaulo		if (!cred->required_roaming_consortium_len)
3813281806Srpaulo			return NULL;
3814281806Srpaulo		buflen = cred->required_roaming_consortium_len * 2 + 1;
3815281806Srpaulo		buf = os_malloc(buflen);
3816281806Srpaulo		if (buf == NULL)
3817281806Srpaulo			return NULL;
3818281806Srpaulo		wpa_snprintf_hex(buf, buflen, cred->required_roaming_consortium,
3819281806Srpaulo				 cred->required_roaming_consortium_len);
3820281806Srpaulo		return buf;
3821281806Srpaulo	}
3822281806Srpaulo
3823346981Scy	if (os_strcmp(var, "roaming_consortiums") == 0) {
3824346981Scy		size_t buflen;
3825346981Scy		char *buf, *pos;
3826346981Scy		size_t i;
3827346981Scy
3828346981Scy		if (!cred->num_roaming_consortiums)
3829346981Scy			return NULL;
3830346981Scy		buflen = cred->num_roaming_consortiums *
3831346981Scy			MAX_ROAMING_CONS_OI_LEN * 2 + 1;
3832346981Scy		buf = os_malloc(buflen);
3833346981Scy		if (!buf)
3834346981Scy			return NULL;
3835346981Scy		pos = buf;
3836346981Scy		for (i = 0; i < cred->num_roaming_consortiums; i++) {
3837346981Scy			if (i > 0)
3838346981Scy				*pos++ = ',';
3839346981Scy			pos += wpa_snprintf_hex(
3840346981Scy				pos, buf + buflen - pos,
3841346981Scy				cred->roaming_consortiums[i],
3842346981Scy				cred->roaming_consortiums_len[i]);
3843346981Scy		}
3844346981Scy		*pos = '\0';
3845346981Scy		return buf;
3846346981Scy	}
3847346981Scy
3848281806Srpaulo	if (os_strcmp(var, "excluded_ssid") == 0) {
3849281806Srpaulo		unsigned int i;
3850281806Srpaulo		char *buf, *end, *pos;
3851281806Srpaulo
3852281806Srpaulo		if (!cred->num_excluded_ssid)
3853281806Srpaulo			return NULL;
3854281806Srpaulo
3855281806Srpaulo		buf = os_malloc(4000);
3856281806Srpaulo		if (buf == NULL)
3857281806Srpaulo			return NULL;
3858281806Srpaulo		pos = buf;
3859281806Srpaulo		end = pos + 4000;
3860281806Srpaulo
3861281806Srpaulo		for (i = 0; i < cred->num_excluded_ssid; i++) {
3862281806Srpaulo			struct excluded_ssid *e;
3863281806Srpaulo			int ret;
3864281806Srpaulo
3865281806Srpaulo			e = &cred->excluded_ssid[i];
3866281806Srpaulo			ret = os_snprintf(pos, end - pos, "%s%s",
3867281806Srpaulo					  i > 0 ? "\n" : "",
3868281806Srpaulo					  wpa_ssid_txt(e->ssid, e->ssid_len));
3869281806Srpaulo			if (os_snprintf_error(end - pos, ret))
3870281806Srpaulo				return buf;
3871281806Srpaulo			pos += ret;
3872281806Srpaulo		}
3873281806Srpaulo
3874281806Srpaulo		return buf;
3875281806Srpaulo	}
3876281806Srpaulo
3877281806Srpaulo	if (os_strcmp(var, "roaming_partner") == 0) {
3878281806Srpaulo		unsigned int i;
3879281806Srpaulo		char *buf, *end, *pos;
3880281806Srpaulo
3881281806Srpaulo		if (!cred->num_roaming_partner)
3882281806Srpaulo			return NULL;
3883281806Srpaulo
3884281806Srpaulo		buf = os_malloc(4000);
3885281806Srpaulo		if (buf == NULL)
3886281806Srpaulo			return NULL;
3887281806Srpaulo		pos = buf;
3888281806Srpaulo		end = pos + 4000;
3889281806Srpaulo
3890281806Srpaulo		for (i = 0; i < cred->num_roaming_partner; i++) {
3891281806Srpaulo			struct roaming_partner *p;
3892281806Srpaulo			int ret;
3893281806Srpaulo
3894281806Srpaulo			p = &cred->roaming_partner[i];
3895281806Srpaulo			ret = os_snprintf(pos, end - pos, "%s%s,%d,%u,%s",
3896281806Srpaulo					  i > 0 ? "\n" : "",
3897281806Srpaulo					  p->fqdn, p->exact_match, p->priority,
3898281806Srpaulo					  p->country);
3899281806Srpaulo			if (os_snprintf_error(end - pos, ret))
3900281806Srpaulo				return buf;
3901281806Srpaulo			pos += ret;
3902281806Srpaulo		}
3903281806Srpaulo
3904281806Srpaulo		return buf;
3905281806Srpaulo	}
3906281806Srpaulo
3907281806Srpaulo	if (os_strcmp(var, "provisioning_sp") == 0)
3908281806Srpaulo		return alloc_strdup(cred->provisioning_sp);
3909281806Srpaulo
3910281806Srpaulo	return NULL;
3911281806Srpaulo}
3912281806Srpaulo
3913281806Srpaulo
3914252726Srpaulostruct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id)
3915252726Srpaulo{
3916252726Srpaulo	struct wpa_cred *cred;
3917252726Srpaulo
3918252726Srpaulo	cred = config->cred;
3919252726Srpaulo	while (cred) {
3920252726Srpaulo		if (id == cred->id)
3921252726Srpaulo			break;
3922252726Srpaulo		cred = cred->next;
3923252726Srpaulo	}
3924252726Srpaulo
3925252726Srpaulo	return cred;
3926252726Srpaulo}
3927252726Srpaulo
3928252726Srpaulo
3929252726Srpaulostruct wpa_cred * wpa_config_add_cred(struct wpa_config *config)
3930252726Srpaulo{
3931252726Srpaulo	int id;
3932252726Srpaulo	struct wpa_cred *cred, *last = NULL;
3933252726Srpaulo
3934252726Srpaulo	id = -1;
3935252726Srpaulo	cred = config->cred;
3936252726Srpaulo	while (cred) {
3937252726Srpaulo		if (cred->id > id)
3938252726Srpaulo			id = cred->id;
3939252726Srpaulo		last = cred;
3940252726Srpaulo		cred = cred->next;
3941252726Srpaulo	}
3942252726Srpaulo	id++;
3943252726Srpaulo
3944252726Srpaulo	cred = os_zalloc(sizeof(*cred));
3945252726Srpaulo	if (cred == NULL)
3946252726Srpaulo		return NULL;
3947252726Srpaulo	cred->id = id;
3948281806Srpaulo	cred->sim_num = DEFAULT_USER_SELECTED_SIM;
3949252726Srpaulo	if (last)
3950252726Srpaulo		last->next = cred;
3951252726Srpaulo	else
3952252726Srpaulo		config->cred = cred;
3953252726Srpaulo
3954252726Srpaulo	return cred;
3955252726Srpaulo}
3956252726Srpaulo
3957252726Srpaulo
3958252726Srpauloint wpa_config_remove_cred(struct wpa_config *config, int id)
3959252726Srpaulo{
3960252726Srpaulo	struct wpa_cred *cred, *prev = NULL;
3961252726Srpaulo
3962252726Srpaulo	cred = config->cred;
3963252726Srpaulo	while (cred) {
3964252726Srpaulo		if (id == cred->id)
3965252726Srpaulo			break;
3966252726Srpaulo		prev = cred;
3967252726Srpaulo		cred = cred->next;
3968252726Srpaulo	}
3969252726Srpaulo
3970252726Srpaulo	if (cred == NULL)
3971252726Srpaulo		return -1;
3972252726Srpaulo
3973252726Srpaulo	if (prev)
3974252726Srpaulo		prev->next = cred->next;
3975252726Srpaulo	else
3976252726Srpaulo		config->cred = cred->next;
3977252726Srpaulo
3978252726Srpaulo	wpa_config_free_cred(cred);
3979252726Srpaulo	return 0;
3980252726Srpaulo}
3981252726Srpaulo
3982252726Srpaulo
3983189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS
3984189251Ssam/**
3985189251Ssam * wpa_config_get_blob - Get a named configuration blob
3986189251Ssam * @config: Configuration data from wpa_config_read()
3987189251Ssam * @name: Name of the blob
3988189251Ssam * Returns: Pointer to blob data or %NULL if not found
3989189251Ssam */
3990189251Ssamconst struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config,
3991189251Ssam						   const char *name)
3992189251Ssam{
3993189251Ssam	struct wpa_config_blob *blob = config->blobs;
3994189251Ssam
3995189251Ssam	while (blob) {
3996189251Ssam		if (os_strcmp(blob->name, name) == 0)
3997189251Ssam			return blob;
3998189251Ssam		blob = blob->next;
3999189251Ssam	}
4000189251Ssam	return NULL;
4001189251Ssam}
4002189251Ssam
4003189251Ssam
4004189251Ssam/**
4005189251Ssam * wpa_config_set_blob - Set or add a named configuration blob
4006189251Ssam * @config: Configuration data from wpa_config_read()
4007189251Ssam * @blob: New value for the blob
4008189251Ssam *
4009189251Ssam * Adds a new configuration blob or replaces the current value of an existing
4010189251Ssam * blob.
4011189251Ssam */
4012189251Ssamvoid wpa_config_set_blob(struct wpa_config *config,
4013189251Ssam			 struct wpa_config_blob *blob)
4014189251Ssam{
4015189251Ssam	wpa_config_remove_blob(config, blob->name);
4016189251Ssam	blob->next = config->blobs;
4017189251Ssam	config->blobs = blob;
4018189251Ssam}
4019189251Ssam
4020189251Ssam
4021189251Ssam/**
4022189251Ssam * wpa_config_free_blob - Free blob data
4023189251Ssam * @blob: Pointer to blob to be freed
4024189251Ssam */
4025189251Ssamvoid wpa_config_free_blob(struct wpa_config_blob *blob)
4026189251Ssam{
4027189251Ssam	if (blob) {
4028189251Ssam		os_free(blob->name);
4029281806Srpaulo		bin_clear_free(blob->data, blob->len);
4030189251Ssam		os_free(blob);
4031189251Ssam	}
4032189251Ssam}
4033189251Ssam
4034189251Ssam
4035189251Ssam/**
4036189251Ssam * wpa_config_remove_blob - Remove a named configuration blob
4037189251Ssam * @config: Configuration data from wpa_config_read()
4038189251Ssam * @name: Name of the blob to remove
4039189251Ssam * Returns: 0 if blob was removed or -1 if blob was not found
4040189251Ssam */
4041189251Ssamint wpa_config_remove_blob(struct wpa_config *config, const char *name)
4042189251Ssam{
4043189251Ssam	struct wpa_config_blob *pos = config->blobs, *prev = NULL;
4044189251Ssam
4045189251Ssam	while (pos) {
4046189251Ssam		if (os_strcmp(pos->name, name) == 0) {
4047189251Ssam			if (prev)
4048189251Ssam				prev->next = pos->next;
4049189251Ssam			else
4050189251Ssam				config->blobs = pos->next;
4051189251Ssam			wpa_config_free_blob(pos);
4052189251Ssam			return 0;
4053189251Ssam		}
4054189251Ssam		prev = pos;
4055189251Ssam		pos = pos->next;
4056189251Ssam	}
4057189251Ssam
4058189251Ssam	return -1;
4059189251Ssam}
4060189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */
4061189251Ssam
4062189251Ssam
4063189251Ssam/**
4064189251Ssam * wpa_config_alloc_empty - Allocate an empty configuration
4065189251Ssam * @ctrl_interface: Control interface parameters, e.g., path to UNIX domain
4066189251Ssam * socket
4067189251Ssam * @driver_param: Driver parameters
4068189251Ssam * Returns: Pointer to allocated configuration data or %NULL on failure
4069189251Ssam */
4070189251Ssamstruct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
4071189251Ssam					   const char *driver_param)
4072189251Ssam{
4073189251Ssam	struct wpa_config *config;
4074252726Srpaulo	const int aCWmin = 4, aCWmax = 10;
4075252726Srpaulo	const struct hostapd_wmm_ac_params ac_bk =
4076252726Srpaulo		{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
4077252726Srpaulo	const struct hostapd_wmm_ac_params ac_be =
4078252726Srpaulo		{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
4079252726Srpaulo	const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
4080252726Srpaulo		{ aCWmin - 1, aCWmin, 2, 3000 / 32, 0 };
4081252726Srpaulo	const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
4082252726Srpaulo		{ aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
4083189251Ssam
4084189251Ssam	config = os_zalloc(sizeof(*config));
4085189251Ssam	if (config == NULL)
4086189251Ssam		return NULL;
4087189251Ssam	config->eapol_version = DEFAULT_EAPOL_VERSION;
4088189251Ssam	config->ap_scan = DEFAULT_AP_SCAN;
4089281806Srpaulo	config->user_mpm = DEFAULT_USER_MPM;
4090281806Srpaulo	config->max_peer_links = DEFAULT_MAX_PEER_LINKS;
4091281806Srpaulo	config->mesh_max_inactivity = DEFAULT_MESH_MAX_INACTIVITY;
4092289549Srpaulo	config->dot11RSNASAERetransPeriod =
4093289549Srpaulo		DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD;
4094189251Ssam	config->fast_reauth = DEFAULT_FAST_REAUTH;
4095252726Srpaulo	config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
4096252726Srpaulo	config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
4097289549Srpaulo	config->p2p_go_freq_change_policy = DEFAULT_P2P_GO_FREQ_MOVE;
4098252726Srpaulo	config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
4099281806Srpaulo	config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN;
4100281806Srpaulo	config->p2p_go_ctwindow = DEFAULT_P2P_GO_CTWINDOW;
4101214734Srpaulo	config->bss_max_count = DEFAULT_BSS_MAX_COUNT;
4102252726Srpaulo	config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE;
4103252726Srpaulo	config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT;
4104252726Srpaulo	config->max_num_sta = DEFAULT_MAX_NUM_STA;
4105346981Scy	config->ap_isolate = DEFAULT_AP_ISOLATE;
4106252726Srpaulo	config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE;
4107281806Srpaulo	config->scan_cur_freq = DEFAULT_SCAN_CUR_FREQ;
4108252726Srpaulo	config->wmm_ac_params[0] = ac_be;
4109252726Srpaulo	config->wmm_ac_params[1] = ac_bk;
4110252726Srpaulo	config->wmm_ac_params[2] = ac_vi;
4111252726Srpaulo	config->wmm_ac_params[3] = ac_vo;
4112281806Srpaulo	config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY;
4113281806Srpaulo	config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
4114281806Srpaulo	config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD;
4115281806Srpaulo	config->cert_in_cb = DEFAULT_CERT_IN_CB;
4116337817Scy	config->wpa_rsc_relaxation = DEFAULT_WPA_RSC_RELAXATION;
4117189251Ssam
4118337817Scy#ifdef CONFIG_MBO
4119337817Scy	config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA;
4120346981Scy	config->disassoc_imminent_rssi_threshold =
4121346981Scy		DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD;
4122346981Scy	config->oce = DEFAULT_OCE_SUPPORT;
4123337817Scy#endif /* CONFIG_MBO */
4124337817Scy
4125189251Ssam	if (ctrl_interface)
4126189251Ssam		config->ctrl_interface = os_strdup(ctrl_interface);
4127189251Ssam	if (driver_param)
4128189251Ssam		config->driver_param = os_strdup(driver_param);
4129346981Scy	config->gas_rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
4130189251Ssam
4131189251Ssam	return config;
4132189251Ssam}
4133189251Ssam
4134189251Ssam
4135189251Ssam#ifndef CONFIG_NO_STDOUT_DEBUG
4136189251Ssam/**
4137189251Ssam * wpa_config_debug_dump_networks - Debug dump of configured networks
4138189251Ssam * @config: Configuration data from wpa_config_read()
4139189251Ssam */
4140189251Ssamvoid wpa_config_debug_dump_networks(struct wpa_config *config)
4141189251Ssam{
4142189251Ssam	int prio;
4143189251Ssam	struct wpa_ssid *ssid;
4144189251Ssam
4145189251Ssam	for (prio = 0; prio < config->num_prio; prio++) {
4146189251Ssam		ssid = config->pssid[prio];
4147189251Ssam		wpa_printf(MSG_DEBUG, "Priority group %d",
4148189251Ssam			   ssid->priority);
4149189251Ssam		while (ssid) {
4150189251Ssam			wpa_printf(MSG_DEBUG, "   id=%d ssid='%s'",
4151189251Ssam				   ssid->id,
4152189251Ssam				   wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
4153189251Ssam			ssid = ssid->pnext;
4154189251Ssam		}
4155189251Ssam	}
4156189251Ssam}
4157189251Ssam#endif /* CONFIG_NO_STDOUT_DEBUG */
4158252726Srpaulo
4159252726Srpaulo
4160252726Srpaulostruct global_parse_data {
4161252726Srpaulo	char *name;
4162252726Srpaulo	int (*parser)(const struct global_parse_data *data,
4163252726Srpaulo		      struct wpa_config *config, int line, const char *value);
4164281806Srpaulo	int (*get)(const char *name, struct wpa_config *config, long offset,
4165281806Srpaulo		   char *buf, size_t buflen, int pretty_print);
4166252726Srpaulo	void *param1, *param2, *param3;
4167252726Srpaulo	unsigned int changed_flag;
4168252726Srpaulo};
4169252726Srpaulo
4170252726Srpaulo
4171252726Srpaulostatic int wpa_global_config_parse_int(const struct global_parse_data *data,
4172252726Srpaulo				       struct wpa_config *config, int line,
4173252726Srpaulo				       const char *pos)
4174252726Srpaulo{
4175281806Srpaulo	int val, *dst;
4176281806Srpaulo	char *end;
4177281806Srpaulo
4178252726Srpaulo	dst = (int *) (((u8 *) config) + (long) data->param1);
4179281806Srpaulo	val = strtol(pos, &end, 0);
4180281806Srpaulo	if (*end) {
4181281806Srpaulo		wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"",
4182281806Srpaulo			   line, pos);
4183281806Srpaulo		return -1;
4184281806Srpaulo	}
4185281806Srpaulo	*dst = val;
4186281806Srpaulo
4187252726Srpaulo	wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
4188252726Srpaulo
4189252726Srpaulo	if (data->param2 && *dst < (long) data->param2) {
4190252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
4191252726Srpaulo			   "min_value=%ld)", line, data->name, *dst,
4192252726Srpaulo			   (long) data->param2);
4193252726Srpaulo		*dst = (long) data->param2;
4194252726Srpaulo		return -1;
4195252726Srpaulo	}
4196252726Srpaulo
4197252726Srpaulo	if (data->param3 && *dst > (long) data->param3) {
4198252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
4199252726Srpaulo			   "max_value=%ld)", line, data->name, *dst,
4200252726Srpaulo			   (long) data->param3);
4201252726Srpaulo		*dst = (long) data->param3;
4202252726Srpaulo		return -1;
4203252726Srpaulo	}
4204252726Srpaulo
4205252726Srpaulo	return 0;
4206252726Srpaulo}
4207252726Srpaulo
4208252726Srpaulo
4209252726Srpaulostatic int wpa_global_config_parse_str(const struct global_parse_data *data,
4210252726Srpaulo				       struct wpa_config *config, int line,
4211252726Srpaulo				       const char *pos)
4212252726Srpaulo{
4213252726Srpaulo	size_t len;
4214252726Srpaulo	char **dst, *tmp;
4215252726Srpaulo
4216252726Srpaulo	len = os_strlen(pos);
4217252726Srpaulo	if (data->param2 && len < (size_t) data->param2) {
4218252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
4219252726Srpaulo			   "min_len=%ld)", line, data->name,
4220252726Srpaulo			   (unsigned long) len, (long) data->param2);
4221252726Srpaulo		return -1;
4222252726Srpaulo	}
4223252726Srpaulo
4224252726Srpaulo	if (data->param3 && len > (size_t) data->param3) {
4225252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
4226252726Srpaulo			   "max_len=%ld)", line, data->name,
4227252726Srpaulo			   (unsigned long) len, (long) data->param3);
4228252726Srpaulo		return -1;
4229252726Srpaulo	}
4230252726Srpaulo
4231337817Scy	if (has_newline(pos)) {
4232337817Scy		wpa_printf(MSG_ERROR, "Line %d: invalid %s value with newline",
4233337817Scy			   line, data->name);
4234337817Scy		return -1;
4235337817Scy	}
4236337817Scy
4237252726Srpaulo	tmp = os_strdup(pos);
4238252726Srpaulo	if (tmp == NULL)
4239252726Srpaulo		return -1;
4240252726Srpaulo
4241252726Srpaulo	dst = (char **) (((u8 *) config) + (long) data->param1);
4242252726Srpaulo	os_free(*dst);
4243252726Srpaulo	*dst = tmp;
4244252726Srpaulo	wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst);
4245252726Srpaulo
4246252726Srpaulo	return 0;
4247252726Srpaulo}
4248252726Srpaulo
4249252726Srpaulo
4250281806Srpaulostatic int wpa_config_process_bgscan(const struct global_parse_data *data,
4251281806Srpaulo				     struct wpa_config *config, int line,
4252281806Srpaulo				     const char *pos)
4253281806Srpaulo{
4254281806Srpaulo	size_t len;
4255281806Srpaulo	char *tmp;
4256281806Srpaulo	int res;
4257281806Srpaulo
4258281806Srpaulo	tmp = wpa_config_parse_string(pos, &len);
4259281806Srpaulo	if (tmp == NULL) {
4260281806Srpaulo		wpa_printf(MSG_ERROR, "Line %d: failed to parse %s",
4261281806Srpaulo			   line, data->name);
4262281806Srpaulo		return -1;
4263281806Srpaulo	}
4264281806Srpaulo
4265281806Srpaulo	res = wpa_global_config_parse_str(data, config, line, tmp);
4266281806Srpaulo	os_free(tmp);
4267281806Srpaulo	return res;
4268281806Srpaulo}
4269281806Srpaulo
4270281806Srpaulo
4271252726Srpaulostatic int wpa_global_config_parse_bin(const struct global_parse_data *data,
4272252726Srpaulo				       struct wpa_config *config, int line,
4273252726Srpaulo				       const char *pos)
4274252726Srpaulo{
4275252726Srpaulo	struct wpabuf **dst, *tmp;
4276252726Srpaulo
4277337817Scy	tmp = wpabuf_parse_bin(pos);
4278337817Scy	if (!tmp)
4279252726Srpaulo		return -1;
4280252726Srpaulo
4281252726Srpaulo	dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1);
4282252726Srpaulo	wpabuf_free(*dst);
4283252726Srpaulo	*dst = tmp;
4284252726Srpaulo	wpa_printf(MSG_DEBUG, "%s", data->name);
4285252726Srpaulo
4286252726Srpaulo	return 0;
4287252726Srpaulo}
4288252726Srpaulo
4289252726Srpaulo
4290281806Srpaulostatic int wpa_config_process_freq_list(const struct global_parse_data *data,
4291281806Srpaulo					struct wpa_config *config, int line,
4292281806Srpaulo					const char *value)
4293281806Srpaulo{
4294281806Srpaulo	int *freqs;
4295281806Srpaulo
4296281806Srpaulo	freqs = wpa_config_parse_int_array(value);
4297281806Srpaulo	if (freqs == NULL)
4298281806Srpaulo		return -1;
4299281806Srpaulo	if (freqs[0] == 0) {
4300281806Srpaulo		os_free(freqs);
4301281806Srpaulo		freqs = NULL;
4302281806Srpaulo	}
4303281806Srpaulo	os_free(config->freq_list);
4304281806Srpaulo	config->freq_list = freqs;
4305281806Srpaulo	return 0;
4306281806Srpaulo}
4307281806Srpaulo
4308281806Srpaulo
4309281806Srpaulo#ifdef CONFIG_P2P
4310281806Srpaulostatic int wpa_global_config_parse_ipv4(const struct global_parse_data *data,
4311281806Srpaulo					struct wpa_config *config, int line,
4312281806Srpaulo					const char *pos)
4313281806Srpaulo{
4314281806Srpaulo	u32 *dst;
4315281806Srpaulo	struct hostapd_ip_addr addr;
4316281806Srpaulo
4317281806Srpaulo	if (hostapd_parse_ip_addr(pos, &addr) < 0)
4318281806Srpaulo		return -1;
4319281806Srpaulo	if (addr.af != AF_INET)
4320281806Srpaulo		return -1;
4321281806Srpaulo
4322281806Srpaulo	dst = (u32 *) (((u8 *) config) + (long) data->param1);
4323281806Srpaulo	os_memcpy(dst, &addr.u.v4.s_addr, 4);
4324281806Srpaulo	wpa_printf(MSG_DEBUG, "%s = 0x%x", data->name,
4325281806Srpaulo		   WPA_GET_BE32((u8 *) dst));
4326281806Srpaulo
4327281806Srpaulo	return 0;
4328281806Srpaulo}
4329281806Srpaulo#endif /* CONFIG_P2P */
4330281806Srpaulo
4331281806Srpaulo
4332252726Srpaulostatic int wpa_config_process_country(const struct global_parse_data *data,
4333252726Srpaulo				      struct wpa_config *config, int line,
4334252726Srpaulo				      const char *pos)
4335252726Srpaulo{
4336252726Srpaulo	if (!pos[0] || !pos[1]) {
4337252726Srpaulo		wpa_printf(MSG_DEBUG, "Invalid country set");
4338252726Srpaulo		return -1;
4339252726Srpaulo	}
4340252726Srpaulo	config->country[0] = pos[0];
4341252726Srpaulo	config->country[1] = pos[1];
4342252726Srpaulo	wpa_printf(MSG_DEBUG, "country='%c%c'",
4343252726Srpaulo		   config->country[0], config->country[1]);
4344252726Srpaulo	return 0;
4345252726Srpaulo}
4346252726Srpaulo
4347252726Srpaulo
4348252726Srpaulostatic int wpa_config_process_load_dynamic_eap(
4349252726Srpaulo	const struct global_parse_data *data, struct wpa_config *config,
4350252726Srpaulo	int line, const char *so)
4351252726Srpaulo{
4352252726Srpaulo	int ret;
4353252726Srpaulo	wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so);
4354252726Srpaulo	ret = eap_peer_method_load(so);
4355252726Srpaulo	if (ret == -2) {
4356252726Srpaulo		wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not "
4357252726Srpaulo			   "reloading.");
4358252726Srpaulo	} else if (ret) {
4359252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP "
4360252726Srpaulo			   "method '%s'.", line, so);
4361252726Srpaulo		return -1;
4362252726Srpaulo	}
4363252726Srpaulo
4364252726Srpaulo	return 0;
4365252726Srpaulo}
4366252726Srpaulo
4367252726Srpaulo
4368252726Srpaulo#ifdef CONFIG_WPS
4369252726Srpaulo
4370252726Srpaulostatic int wpa_config_process_uuid(const struct global_parse_data *data,
4371252726Srpaulo				   struct wpa_config *config, int line,
4372252726Srpaulo				   const char *pos)
4373252726Srpaulo{
4374252726Srpaulo	char buf[40];
4375252726Srpaulo	if (uuid_str2bin(pos, config->uuid)) {
4376252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
4377252726Srpaulo		return -1;
4378252726Srpaulo	}
4379252726Srpaulo	uuid_bin2str(config->uuid, buf, sizeof(buf));
4380252726Srpaulo	wpa_printf(MSG_DEBUG, "uuid=%s", buf);
4381252726Srpaulo	return 0;
4382252726Srpaulo}
4383252726Srpaulo
4384252726Srpaulo
4385252726Srpaulostatic int wpa_config_process_device_type(
4386252726Srpaulo	const struct global_parse_data *data,
4387252726Srpaulo	struct wpa_config *config, int line, const char *pos)
4388252726Srpaulo{
4389252726Srpaulo	return wps_dev_type_str2bin(pos, config->device_type);
4390252726Srpaulo}
4391252726Srpaulo
4392252726Srpaulo
4393252726Srpaulostatic int wpa_config_process_os_version(const struct global_parse_data *data,
4394252726Srpaulo					 struct wpa_config *config, int line,
4395252726Srpaulo					 const char *pos)
4396252726Srpaulo{
4397252726Srpaulo	if (hexstr2bin(pos, config->os_version, 4)) {
4398252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line);
4399252726Srpaulo		return -1;
4400252726Srpaulo	}
4401252726Srpaulo	wpa_printf(MSG_DEBUG, "os_version=%08x",
4402252726Srpaulo		   WPA_GET_BE32(config->os_version));
4403252726Srpaulo	return 0;
4404252726Srpaulo}
4405252726Srpaulo
4406252726Srpaulo
4407252726Srpaulostatic int wpa_config_process_wps_vendor_ext_m1(
4408252726Srpaulo	const struct global_parse_data *data,
4409252726Srpaulo	struct wpa_config *config, int line, const char *pos)
4410252726Srpaulo{
4411252726Srpaulo	struct wpabuf *tmp;
4412252726Srpaulo	int len = os_strlen(pos) / 2;
4413252726Srpaulo	u8 *p;
4414252726Srpaulo
4415252726Srpaulo	if (!len) {
4416252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: "
4417252726Srpaulo			   "invalid wps_vendor_ext_m1", line);
4418252726Srpaulo		return -1;
4419252726Srpaulo	}
4420252726Srpaulo
4421252726Srpaulo	tmp = wpabuf_alloc(len);
4422252726Srpaulo	if (tmp) {
4423252726Srpaulo		p = wpabuf_put(tmp, len);
4424252726Srpaulo
4425252726Srpaulo		if (hexstr2bin(pos, p, len)) {
4426252726Srpaulo			wpa_printf(MSG_ERROR, "Line %d: "
4427252726Srpaulo				   "invalid wps_vendor_ext_m1", line);
4428252726Srpaulo			wpabuf_free(tmp);
4429252726Srpaulo			return -1;
4430252726Srpaulo		}
4431252726Srpaulo
4432252726Srpaulo		wpabuf_free(config->wps_vendor_ext_m1);
4433252726Srpaulo		config->wps_vendor_ext_m1 = tmp;
4434252726Srpaulo	} else {
4435252726Srpaulo		wpa_printf(MSG_ERROR, "Can not allocate "
4436252726Srpaulo			   "memory for wps_vendor_ext_m1");
4437252726Srpaulo		return -1;
4438252726Srpaulo	}
4439252726Srpaulo
4440252726Srpaulo	return 0;
4441252726Srpaulo}
4442252726Srpaulo
4443252726Srpaulo#endif /* CONFIG_WPS */
4444252726Srpaulo
4445252726Srpaulo#ifdef CONFIG_P2P
4446252726Srpaulostatic int wpa_config_process_sec_device_type(
4447252726Srpaulo	const struct global_parse_data *data,
4448252726Srpaulo	struct wpa_config *config, int line, const char *pos)
4449252726Srpaulo{
4450252726Srpaulo	int idx;
4451252726Srpaulo
4452252726Srpaulo	if (config->num_sec_device_types >= MAX_SEC_DEVICE_TYPES) {
4453252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: too many sec_device_type "
4454252726Srpaulo			   "items", line);
4455252726Srpaulo		return -1;
4456252726Srpaulo	}
4457252726Srpaulo
4458252726Srpaulo	idx = config->num_sec_device_types;
4459252726Srpaulo
4460252726Srpaulo	if (wps_dev_type_str2bin(pos, config->sec_device_type[idx]))
4461252726Srpaulo		return -1;
4462252726Srpaulo
4463252726Srpaulo	config->num_sec_device_types++;
4464252726Srpaulo	return 0;
4465252726Srpaulo}
4466252726Srpaulo
4467252726Srpaulo
4468252726Srpaulostatic int wpa_config_process_p2p_pref_chan(
4469252726Srpaulo	const struct global_parse_data *data,
4470252726Srpaulo	struct wpa_config *config, int line, const char *pos)
4471252726Srpaulo{
4472252726Srpaulo	struct p2p_channel *pref = NULL, *n;
4473252726Srpaulo	unsigned int num = 0;
4474252726Srpaulo	const char *pos2;
4475252726Srpaulo	u8 op_class, chan;
4476252726Srpaulo
4477252726Srpaulo	/* format: class:chan,class:chan,... */
4478252726Srpaulo
4479252726Srpaulo	while (*pos) {
4480252726Srpaulo		op_class = atoi(pos);
4481252726Srpaulo		pos2 = os_strchr(pos, ':');
4482252726Srpaulo		if (pos2 == NULL)
4483252726Srpaulo			goto fail;
4484252726Srpaulo		pos2++;
4485252726Srpaulo		chan = atoi(pos2);
4486252726Srpaulo
4487252726Srpaulo		n = os_realloc_array(pref, num + 1,
4488252726Srpaulo				     sizeof(struct p2p_channel));
4489252726Srpaulo		if (n == NULL)
4490252726Srpaulo			goto fail;
4491252726Srpaulo		pref = n;
4492252726Srpaulo		pref[num].op_class = op_class;
4493252726Srpaulo		pref[num].chan = chan;
4494252726Srpaulo		num++;
4495252726Srpaulo
4496252726Srpaulo		pos = os_strchr(pos2, ',');
4497252726Srpaulo		if (pos == NULL)
4498252726Srpaulo			break;
4499252726Srpaulo		pos++;
4500252726Srpaulo	}
4501252726Srpaulo
4502252726Srpaulo	os_free(config->p2p_pref_chan);
4503252726Srpaulo	config->p2p_pref_chan = pref;
4504252726Srpaulo	config->num_p2p_pref_chan = num;
4505252726Srpaulo	wpa_hexdump(MSG_DEBUG, "P2P: Preferred class/channel pairs",
4506252726Srpaulo		    (u8 *) config->p2p_pref_chan,
4507252726Srpaulo		    config->num_p2p_pref_chan * sizeof(struct p2p_channel));
4508252726Srpaulo
4509252726Srpaulo	return 0;
4510252726Srpaulo
4511252726Srpaulofail:
4512252726Srpaulo	os_free(pref);
4513252726Srpaulo	wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line);
4514252726Srpaulo	return -1;
4515252726Srpaulo}
4516281806Srpaulo
4517281806Srpaulo
4518281806Srpaulostatic int wpa_config_process_p2p_no_go_freq(
4519281806Srpaulo	const struct global_parse_data *data,
4520281806Srpaulo	struct wpa_config *config, int line, const char *pos)
4521281806Srpaulo{
4522281806Srpaulo	int ret;
4523281806Srpaulo
4524281806Srpaulo	ret = freq_range_list_parse(&config->p2p_no_go_freq, pos);
4525281806Srpaulo	if (ret < 0) {
4526281806Srpaulo		wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_no_go_freq", line);
4527281806Srpaulo		return -1;
4528281806Srpaulo	}
4529281806Srpaulo
4530281806Srpaulo	wpa_printf(MSG_DEBUG, "P2P: p2p_no_go_freq with %u items",
4531281806Srpaulo		   config->p2p_no_go_freq.num);
4532281806Srpaulo
4533281806Srpaulo	return 0;
4534281806Srpaulo}
4535281806Srpaulo
4536346981Scy
4537346981Scystatic int wpa_config_process_p2p_device_persistent_mac_addr(
4538346981Scy	const struct global_parse_data *data,
4539346981Scy	struct wpa_config *config, int line, const char *pos)
4540346981Scy{
4541346981Scy	if (hwaddr_aton2(pos, config->p2p_device_persistent_mac_addr) < 0) {
4542346981Scy		wpa_printf(MSG_ERROR,
4543346981Scy			   "Line %d: Invalid p2p_device_persistent_mac_addr '%s'",
4544346981Scy			   line, pos);
4545346981Scy		return -1;
4546346981Scy	}
4547346981Scy
4548346981Scy	return 0;
4549346981Scy}
4550346981Scy
4551252726Srpaulo#endif /* CONFIG_P2P */
4552252726Srpaulo
4553252726Srpaulo
4554252726Srpaulostatic int wpa_config_process_hessid(
4555252726Srpaulo	const struct global_parse_data *data,
4556252726Srpaulo	struct wpa_config *config, int line, const char *pos)
4557252726Srpaulo{
4558252726Srpaulo	if (hwaddr_aton2(pos, config->hessid) < 0) {
4559252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: Invalid hessid '%s'",
4560252726Srpaulo			   line, pos);
4561252726Srpaulo		return -1;
4562252726Srpaulo	}
4563252726Srpaulo
4564252726Srpaulo	return 0;
4565252726Srpaulo}
4566252726Srpaulo
4567252726Srpaulo
4568281806Srpaulostatic int wpa_config_process_sae_groups(
4569281806Srpaulo	const struct global_parse_data *data,
4570281806Srpaulo	struct wpa_config *config, int line, const char *pos)
4571281806Srpaulo{
4572281806Srpaulo	int *groups = wpa_config_parse_int_array(pos);
4573281806Srpaulo	if (groups == NULL) {
4574281806Srpaulo		wpa_printf(MSG_ERROR, "Line %d: Invalid sae_groups '%s'",
4575281806Srpaulo			   line, pos);
4576281806Srpaulo		return -1;
4577281806Srpaulo	}
4578281806Srpaulo
4579281806Srpaulo	os_free(config->sae_groups);
4580281806Srpaulo	config->sae_groups = groups;
4581281806Srpaulo
4582281806Srpaulo	return 0;
4583281806Srpaulo}
4584281806Srpaulo
4585281806Srpaulo
4586281806Srpaulostatic int wpa_config_process_ap_vendor_elements(
4587281806Srpaulo	const struct global_parse_data *data,
4588281806Srpaulo	struct wpa_config *config, int line, const char *pos)
4589281806Srpaulo{
4590281806Srpaulo	struct wpabuf *tmp;
4591281806Srpaulo	int len = os_strlen(pos) / 2;
4592281806Srpaulo	u8 *p;
4593281806Srpaulo
4594281806Srpaulo	if (!len) {
4595281806Srpaulo		wpa_printf(MSG_ERROR, "Line %d: invalid ap_vendor_elements",
4596281806Srpaulo			   line);
4597281806Srpaulo		return -1;
4598281806Srpaulo	}
4599281806Srpaulo
4600281806Srpaulo	tmp = wpabuf_alloc(len);
4601281806Srpaulo	if (tmp) {
4602281806Srpaulo		p = wpabuf_put(tmp, len);
4603281806Srpaulo
4604281806Srpaulo		if (hexstr2bin(pos, p, len)) {
4605281806Srpaulo			wpa_printf(MSG_ERROR, "Line %d: invalid "
4606281806Srpaulo				   "ap_vendor_elements", line);
4607281806Srpaulo			wpabuf_free(tmp);
4608281806Srpaulo			return -1;
4609281806Srpaulo		}
4610281806Srpaulo
4611281806Srpaulo		wpabuf_free(config->ap_vendor_elements);
4612281806Srpaulo		config->ap_vendor_elements = tmp;
4613281806Srpaulo	} else {
4614281806Srpaulo		wpa_printf(MSG_ERROR, "Cannot allocate memory for "
4615281806Srpaulo			   "ap_vendor_elements");
4616281806Srpaulo		return -1;
4617281806Srpaulo	}
4618281806Srpaulo
4619281806Srpaulo	return 0;
4620281806Srpaulo}
4621281806Srpaulo
4622281806Srpaulo
4623281806Srpaulo#ifdef CONFIG_CTRL_IFACE
4624281806Srpaulostatic int wpa_config_process_no_ctrl_interface(
4625281806Srpaulo	const struct global_parse_data *data,
4626281806Srpaulo	struct wpa_config *config, int line, const char *pos)
4627281806Srpaulo{
4628281806Srpaulo	wpa_printf(MSG_DEBUG, "no_ctrl_interface -> ctrl_interface=NULL");
4629281806Srpaulo	os_free(config->ctrl_interface);
4630281806Srpaulo	config->ctrl_interface = NULL;
4631281806Srpaulo	return 0;
4632281806Srpaulo}
4633281806Srpaulo#endif /* CONFIG_CTRL_IFACE */
4634281806Srpaulo
4635281806Srpaulo
4636281806Srpaulostatic int wpa_config_get_int(const char *name, struct wpa_config *config,
4637281806Srpaulo			      long offset, char *buf, size_t buflen,
4638281806Srpaulo			      int pretty_print)
4639281806Srpaulo{
4640281806Srpaulo	int *val = (int *) (((u8 *) config) + (long) offset);
4641281806Srpaulo
4642281806Srpaulo	if (pretty_print)
4643281806Srpaulo		return os_snprintf(buf, buflen, "%s=%d\n", name, *val);
4644281806Srpaulo	return os_snprintf(buf, buflen, "%d", *val);
4645281806Srpaulo}
4646281806Srpaulo
4647281806Srpaulo
4648281806Srpaulostatic int wpa_config_get_str(const char *name, struct wpa_config *config,
4649281806Srpaulo			      long offset, char *buf, size_t buflen,
4650281806Srpaulo			      int pretty_print)
4651281806Srpaulo{
4652281806Srpaulo	char **val = (char **) (((u8 *) config) + (long) offset);
4653281806Srpaulo	int res;
4654281806Srpaulo
4655281806Srpaulo	if (pretty_print)
4656281806Srpaulo		res = os_snprintf(buf, buflen, "%s=%s\n", name,
4657281806Srpaulo				  *val ? *val : "null");
4658281806Srpaulo	else if (!*val)
4659281806Srpaulo		return -1;
4660281806Srpaulo	else
4661281806Srpaulo		res = os_snprintf(buf, buflen, "%s", *val);
4662281806Srpaulo	if (os_snprintf_error(buflen, res))
4663281806Srpaulo		res = -1;
4664281806Srpaulo
4665281806Srpaulo	return res;
4666281806Srpaulo}
4667281806Srpaulo
4668281806Srpaulo
4669289549Srpaulo#ifdef CONFIG_P2P
4670289549Srpaulostatic int wpa_config_get_ipv4(const char *name, struct wpa_config *config,
4671289549Srpaulo			       long offset, char *buf, size_t buflen,
4672289549Srpaulo			       int pretty_print)
4673289549Srpaulo{
4674289549Srpaulo	void *val = ((u8 *) config) + (long) offset;
4675289549Srpaulo	int res;
4676289549Srpaulo	char addr[INET_ADDRSTRLEN];
4677289549Srpaulo
4678289549Srpaulo	if (!val || !inet_ntop(AF_INET, val, addr, sizeof(addr)))
4679289549Srpaulo		return -1;
4680289549Srpaulo
4681289549Srpaulo	if (pretty_print)
4682289549Srpaulo		res = os_snprintf(buf, buflen, "%s=%s\n", name, addr);
4683289549Srpaulo	else
4684289549Srpaulo		res = os_snprintf(buf, buflen, "%s", addr);
4685289549Srpaulo
4686289549Srpaulo	if (os_snprintf_error(buflen, res))
4687289549Srpaulo		res = -1;
4688289549Srpaulo
4689289549Srpaulo	return res;
4690289549Srpaulo}
4691289549Srpaulo#endif /* CONFIG_P2P */
4692289549Srpaulo
4693289549Srpaulo
4694252726Srpaulo#ifdef OFFSET
4695252726Srpaulo#undef OFFSET
4696252726Srpaulo#endif /* OFFSET */
4697252726Srpaulo/* OFFSET: Get offset of a variable within the wpa_config structure */
4698252726Srpaulo#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v)
4699252726Srpaulo
4700281806Srpaulo#define FUNC(f) #f, wpa_config_process_ ## f, NULL, OFFSET(f), NULL, NULL
4701281806Srpaulo#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL, NULL
4702281806Srpaulo#define _INT(f) #f, wpa_global_config_parse_int, wpa_config_get_int, OFFSET(f)
4703252726Srpaulo#define INT(f) _INT(f), NULL, NULL
4704252726Srpaulo#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
4705281806Srpaulo#define _STR(f) #f, wpa_global_config_parse_str, wpa_config_get_str, OFFSET(f)
4706252726Srpaulo#define STR(f) _STR(f), NULL, NULL
4707252726Srpaulo#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
4708281806Srpaulo#define BIN(f) #f, wpa_global_config_parse_bin, NULL, OFFSET(f), NULL, NULL
4709289549Srpaulo#define IPV4(f) #f, wpa_global_config_parse_ipv4, wpa_config_get_ipv4,  \
4710289549Srpaulo	OFFSET(f), NULL, NULL
4711252726Srpaulo
4712252726Srpaulostatic const struct global_parse_data global_fields[] = {
4713252726Srpaulo#ifdef CONFIG_CTRL_IFACE
4714252726Srpaulo	{ STR(ctrl_interface), 0 },
4715281806Srpaulo	{ FUNC_NO_VAR(no_ctrl_interface), 0 },
4716252726Srpaulo	{ STR(ctrl_interface_group), 0 } /* deprecated */,
4717252726Srpaulo#endif /* CONFIG_CTRL_IFACE */
4718281806Srpaulo#ifdef CONFIG_MACSEC
4719281806Srpaulo	{ INT_RANGE(eapol_version, 1, 3), 0 },
4720281806Srpaulo#else /* CONFIG_MACSEC */
4721252726Srpaulo	{ INT_RANGE(eapol_version, 1, 2), 0 },
4722281806Srpaulo#endif /* CONFIG_MACSEC */
4723252726Srpaulo	{ INT(ap_scan), 0 },
4724281806Srpaulo	{ FUNC(bgscan), 0 },
4725281806Srpaulo#ifdef CONFIG_MESH
4726281806Srpaulo	{ INT(user_mpm), 0 },
4727281806Srpaulo	{ INT_RANGE(max_peer_links, 0, 255), 0 },
4728281806Srpaulo	{ INT(mesh_max_inactivity), 0 },
4729289549Srpaulo	{ INT(dot11RSNASAERetransPeriod), 0 },
4730281806Srpaulo#endif /* CONFIG_MESH */
4731252726Srpaulo	{ INT(disable_scan_offload), 0 },
4732252726Srpaulo	{ INT(fast_reauth), 0 },
4733252726Srpaulo	{ STR(opensc_engine_path), 0 },
4734252726Srpaulo	{ STR(pkcs11_engine_path), 0 },
4735252726Srpaulo	{ STR(pkcs11_module_path), 0 },
4736281806Srpaulo	{ STR(openssl_ciphers), 0 },
4737252726Srpaulo	{ STR(pcsc_reader), 0 },
4738252726Srpaulo	{ STR(pcsc_pin), 0 },
4739281806Srpaulo	{ INT(external_sim), 0 },
4740252726Srpaulo	{ STR(driver_param), 0 },
4741252726Srpaulo	{ INT(dot11RSNAConfigPMKLifetime), 0 },
4742252726Srpaulo	{ INT(dot11RSNAConfigPMKReauthThreshold), 0 },
4743252726Srpaulo	{ INT(dot11RSNAConfigSATimeout), 0 },
4744252726Srpaulo#ifndef CONFIG_NO_CONFIG_WRITE
4745252726Srpaulo	{ INT(update_config), 0 },
4746252726Srpaulo#endif /* CONFIG_NO_CONFIG_WRITE */
4747252726Srpaulo	{ FUNC_NO_VAR(load_dynamic_eap), 0 },
4748252726Srpaulo#ifdef CONFIG_WPS
4749252726Srpaulo	{ FUNC(uuid), CFG_CHANGED_UUID },
4750346981Scy	{ INT_RANGE(auto_uuid, 0, 1), 0 },
4751289549Srpaulo	{ STR_RANGE(device_name, 0, WPS_DEV_NAME_MAX_LEN),
4752289549Srpaulo	  CFG_CHANGED_DEVICE_NAME },
4753252726Srpaulo	{ STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING },
4754252726Srpaulo	{ STR_RANGE(model_name, 0, 32), CFG_CHANGED_WPS_STRING },
4755252726Srpaulo	{ STR_RANGE(model_number, 0, 32), CFG_CHANGED_WPS_STRING },
4756252726Srpaulo	{ STR_RANGE(serial_number, 0, 32), CFG_CHANGED_WPS_STRING },
4757252726Srpaulo	{ FUNC(device_type), CFG_CHANGED_DEVICE_TYPE },
4758252726Srpaulo	{ FUNC(os_version), CFG_CHANGED_OS_VERSION },
4759252726Srpaulo	{ STR(config_methods), CFG_CHANGED_CONFIG_METHODS },
4760252726Srpaulo	{ INT_RANGE(wps_cred_processing, 0, 2), 0 },
4761346981Scy	{ INT_RANGE(wps_cred_add_sae, 0, 1), 0 },
4762252726Srpaulo	{ FUNC(wps_vendor_ext_m1), CFG_CHANGED_VENDOR_EXTENSION },
4763252726Srpaulo#endif /* CONFIG_WPS */
4764252726Srpaulo#ifdef CONFIG_P2P
4765252726Srpaulo	{ FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
4766289549Srpaulo	{ INT(p2p_listen_reg_class), CFG_CHANGED_P2P_LISTEN_CHANNEL },
4767289549Srpaulo	{ INT(p2p_listen_channel), CFG_CHANGED_P2P_LISTEN_CHANNEL },
4768281806Srpaulo	{ INT(p2p_oper_reg_class), CFG_CHANGED_P2P_OPER_CHANNEL },
4769281806Srpaulo	{ INT(p2p_oper_channel), CFG_CHANGED_P2P_OPER_CHANNEL },
4770252726Srpaulo	{ INT_RANGE(p2p_go_intent, 0, 15), 0 },
4771252726Srpaulo	{ STR(p2p_ssid_postfix), CFG_CHANGED_P2P_SSID_POSTFIX },
4772252726Srpaulo	{ INT_RANGE(persistent_reconnect, 0, 1), 0 },
4773252726Srpaulo	{ INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
4774252726Srpaulo	{ INT(p2p_group_idle), 0 },
4775289549Srpaulo	{ INT_RANGE(p2p_go_freq_change_policy, 0, P2P_GO_FREQ_MOVE_MAX), 0 },
4776281806Srpaulo	{ INT_RANGE(p2p_passphrase_len, 8, 63),
4777281806Srpaulo	  CFG_CHANGED_P2P_PASSPHRASE_LEN },
4778252726Srpaulo	{ FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
4779281806Srpaulo	{ FUNC(p2p_no_go_freq), CFG_CHANGED_P2P_PREF_CHAN },
4780281806Srpaulo	{ INT_RANGE(p2p_add_cli_chan, 0, 1), 0 },
4781281806Srpaulo	{ INT_RANGE(p2p_optimize_listen_chan, 0, 1), 0 },
4782252726Srpaulo	{ INT(p2p_go_ht40), 0 },
4783281806Srpaulo	{ INT(p2p_go_vht), 0 },
4784346981Scy	{ INT(p2p_go_he), 0 },
4785252726Srpaulo	{ INT(p2p_disabled), 0 },
4786281806Srpaulo	{ INT_RANGE(p2p_go_ctwindow, 0, 127), 0 },
4787252726Srpaulo	{ INT(p2p_no_group_iface), 0 },
4788281806Srpaulo	{ INT_RANGE(p2p_ignore_shared_freq, 0, 1), 0 },
4789281806Srpaulo	{ IPV4(ip_addr_go), 0 },
4790281806Srpaulo	{ IPV4(ip_addr_mask), 0 },
4791281806Srpaulo	{ IPV4(ip_addr_start), 0 },
4792281806Srpaulo	{ IPV4(ip_addr_end), 0 },
4793289549Srpaulo	{ INT_RANGE(p2p_cli_probe, 0, 1), 0 },
4794346981Scy	{ INT(p2p_device_random_mac_addr), 0 },
4795346981Scy	{ FUNC(p2p_device_persistent_mac_addr), 0 },
4796346981Scy	{ INT(p2p_interface_random_mac_addr), 0 },
4797252726Srpaulo#endif /* CONFIG_P2P */
4798252726Srpaulo	{ FUNC(country), CFG_CHANGED_COUNTRY },
4799252726Srpaulo	{ INT(bss_max_count), 0 },
4800252726Srpaulo	{ INT(bss_expiration_age), 0 },
4801252726Srpaulo	{ INT(bss_expiration_scan_count), 0 },
4802252726Srpaulo	{ INT_RANGE(filter_ssids, 0, 1), 0 },
4803252726Srpaulo	{ INT_RANGE(filter_rssi, -100, 0), 0 },
4804252726Srpaulo	{ INT(max_num_sta), 0 },
4805346981Scy	{ INT_RANGE(ap_isolate, 0, 1), 0 },
4806252726Srpaulo	{ INT_RANGE(disassoc_low_ack, 0, 1), 0 },
4807252726Srpaulo#ifdef CONFIG_HS20
4808252726Srpaulo	{ INT_RANGE(hs20, 0, 1), 0 },
4809252726Srpaulo#endif /* CONFIG_HS20 */
4810252726Srpaulo	{ INT_RANGE(interworking, 0, 1), 0 },
4811252726Srpaulo	{ FUNC(hessid), 0 },
4812252726Srpaulo	{ INT_RANGE(access_network_type, 0, 15), 0 },
4813346981Scy	{ INT_RANGE(go_interworking, 0, 1), 0 },
4814346981Scy	{ INT_RANGE(go_access_network_type, 0, 15), 0 },
4815346981Scy	{ INT_RANGE(go_internet, 0, 1), 0 },
4816346981Scy	{ INT_RANGE(go_venue_group, 0, 255), 0 },
4817346981Scy	{ INT_RANGE(go_venue_type, 0, 255), 0 },
4818252726Srpaulo	{ INT_RANGE(pbc_in_m1, 0, 1), 0 },
4819252726Srpaulo	{ STR(autoscan), 0 },
4820281806Srpaulo	{ INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff),
4821281806Srpaulo	  CFG_CHANGED_NFC_PASSWORD_TOKEN },
4822281806Srpaulo	{ BIN(wps_nfc_dh_pubkey), CFG_CHANGED_NFC_PASSWORD_TOKEN },
4823281806Srpaulo	{ BIN(wps_nfc_dh_privkey), CFG_CHANGED_NFC_PASSWORD_TOKEN },
4824281806Srpaulo	{ BIN(wps_nfc_dev_pw), CFG_CHANGED_NFC_PASSWORD_TOKEN },
4825252726Srpaulo	{ STR(ext_password_backend), CFG_CHANGED_EXT_PW_BACKEND },
4826252726Srpaulo	{ INT(p2p_go_max_inactivity), 0 },
4827252726Srpaulo	{ INT_RANGE(auto_interworking, 0, 1), 0 },
4828252726Srpaulo	{ INT(okc), 0 },
4829252726Srpaulo	{ INT(pmf), 0 },
4830281806Srpaulo	{ FUNC(sae_groups), 0 },
4831281806Srpaulo	{ INT(dtim_period), 0 },
4832281806Srpaulo	{ INT(beacon_int), 0 },
4833281806Srpaulo	{ FUNC(ap_vendor_elements), 0 },
4834281806Srpaulo	{ INT_RANGE(ignore_old_scan_res, 0, 1), 0 },
4835281806Srpaulo	{ FUNC(freq_list), 0 },
4836281806Srpaulo	{ INT(scan_cur_freq), 0 },
4837281806Srpaulo	{ INT(sched_scan_interval), 0 },
4838346981Scy	{ INT(sched_scan_start_delay), 0 },
4839281806Srpaulo	{ INT(tdls_external_control), 0},
4840281806Srpaulo	{ STR(osu_dir), 0 },
4841346981Scy	{ STR(wowlan_triggers), CFG_CHANGED_WOWLAN_TRIGGERS },
4842281806Srpaulo	{ INT(p2p_search_delay), 0},
4843281806Srpaulo	{ INT(mac_addr), 0 },
4844281806Srpaulo	{ INT(rand_addr_lifetime), 0 },
4845281806Srpaulo	{ INT(preassoc_mac_addr), 0 },
4846281806Srpaulo	{ INT(key_mgmt_offload), 0},
4847281806Srpaulo	{ INT(passive_scan), 0 },
4848281806Srpaulo	{ INT(reassoc_same_bss_optim), 0 },
4849289549Srpaulo	{ INT(wps_priority), 0},
4850289549Srpaulo#ifdef CONFIG_FST
4851289549Srpaulo	{ STR_RANGE(fst_group_id, 1, FST_MAX_GROUP_ID_LEN), 0 },
4852289549Srpaulo	{ INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 },
4853289549Srpaulo	{ INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 },
4854289549Srpaulo#endif /* CONFIG_FST */
4855346981Scy	{ INT_RANGE(cert_in_cb, 0, 1), 0 },
4856337817Scy	{ INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 },
4857337817Scy	{ STR(sched_scan_plans), CFG_CHANGED_SCHED_SCAN_PLANS },
4858337817Scy#ifdef CONFIG_MBO
4859337817Scy	{ STR(non_pref_chan), 0 },
4860337817Scy	{ INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE,
4861337817Scy		    MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
4862346981Scy	{ INT_RANGE(disassoc_imminent_rssi_threshold, -120, 0), 0 },
4863346981Scy	{ INT_RANGE(oce, 0, 3), 0 },
4864346981Scy#endif /* CONFIG_MBO */
4865337817Scy	{ INT(gas_address3), 0 },
4866337817Scy	{ INT_RANGE(ftm_responder, 0, 1), 0 },
4867337817Scy	{ INT_RANGE(ftm_initiator, 0, 1), 0 },
4868346981Scy	{ INT(gas_rand_addr_lifetime), 0 },
4869346981Scy	{ INT_RANGE(gas_rand_mac_addr, 0, 2), 0 },
4870346981Scy	{ INT_RANGE(dpp_config_processing, 0, 2), 0 },
4871346981Scy	{ INT_RANGE(coloc_intf_reporting, 0, 1), 0 },
4872351611Scy#ifdef CONFIG_WNM
4873351611Scy	{ INT_RANGE(disable_btm, 0, 1), CFG_CHANGED_DISABLE_BTM },
4874351611Scy#endif /* CONFIG_WNM */
4875252726Srpaulo};
4876252726Srpaulo
4877252726Srpaulo#undef FUNC
4878252726Srpaulo#undef _INT
4879252726Srpaulo#undef INT
4880252726Srpaulo#undef INT_RANGE
4881252726Srpaulo#undef _STR
4882252726Srpaulo#undef STR
4883252726Srpaulo#undef STR_RANGE
4884252726Srpaulo#undef BIN
4885281806Srpaulo#undef IPV4
4886281806Srpaulo#define NUM_GLOBAL_FIELDS ARRAY_SIZE(global_fields)
4887252726Srpaulo
4888252726Srpaulo
4889281806Srpauloint wpa_config_dump_values(struct wpa_config *config, char *buf, size_t buflen)
4890281806Srpaulo{
4891281806Srpaulo	int result = 0;
4892281806Srpaulo	size_t i;
4893281806Srpaulo
4894281806Srpaulo	for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
4895281806Srpaulo		const struct global_parse_data *field = &global_fields[i];
4896281806Srpaulo		int tmp;
4897281806Srpaulo
4898281806Srpaulo		if (!field->get)
4899281806Srpaulo			continue;
4900281806Srpaulo
4901281806Srpaulo		tmp = field->get(field->name, config, (long) field->param1,
4902281806Srpaulo				 buf, buflen, 1);
4903281806Srpaulo		if (tmp < 0)
4904281806Srpaulo			return -1;
4905281806Srpaulo		buf += tmp;
4906281806Srpaulo		buflen -= tmp;
4907281806Srpaulo		result += tmp;
4908281806Srpaulo	}
4909281806Srpaulo	return result;
4910281806Srpaulo}
4911281806Srpaulo
4912281806Srpaulo
4913281806Srpauloint wpa_config_get_value(const char *name, struct wpa_config *config,
4914281806Srpaulo			 char *buf, size_t buflen)
4915281806Srpaulo{
4916281806Srpaulo	size_t i;
4917281806Srpaulo
4918281806Srpaulo	for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
4919281806Srpaulo		const struct global_parse_data *field = &global_fields[i];
4920281806Srpaulo
4921281806Srpaulo		if (os_strcmp(name, field->name) != 0)
4922281806Srpaulo			continue;
4923281806Srpaulo		if (!field->get)
4924281806Srpaulo			break;
4925281806Srpaulo		return field->get(name, config, (long) field->param1,
4926281806Srpaulo				  buf, buflen, 0);
4927281806Srpaulo	}
4928281806Srpaulo
4929281806Srpaulo	return -1;
4930281806Srpaulo}
4931281806Srpaulo
4932281806Srpaulo
4933337817Scyint wpa_config_get_num_global_field_names(void)
4934337817Scy{
4935337817Scy	return NUM_GLOBAL_FIELDS;
4936337817Scy}
4937337817Scy
4938337817Scy
4939337817Scyconst char * wpa_config_get_global_field_name(unsigned int i, int *no_var)
4940337817Scy{
4941337817Scy	if (i >= NUM_GLOBAL_FIELDS)
4942337817Scy		return NULL;
4943337817Scy
4944337817Scy	if (no_var)
4945337817Scy		*no_var = !global_fields[i].param1;
4946337817Scy	return global_fields[i].name;
4947337817Scy}
4948337817Scy
4949337817Scy
4950252726Srpauloint wpa_config_process_global(struct wpa_config *config, char *pos, int line)
4951252726Srpaulo{
4952252726Srpaulo	size_t i;
4953252726Srpaulo	int ret = 0;
4954252726Srpaulo
4955252726Srpaulo	for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
4956252726Srpaulo		const struct global_parse_data *field = &global_fields[i];
4957252726Srpaulo		size_t flen = os_strlen(field->name);
4958252726Srpaulo		if (os_strncmp(pos, field->name, flen) != 0 ||
4959252726Srpaulo		    pos[flen] != '=')
4960252726Srpaulo			continue;
4961252726Srpaulo
4962252726Srpaulo		if (field->parser(field, config, line, pos + flen + 1)) {
4963252726Srpaulo			wpa_printf(MSG_ERROR, "Line %d: failed to "
4964252726Srpaulo				   "parse '%s'.", line, pos);
4965252726Srpaulo			ret = -1;
4966252726Srpaulo		}
4967281806Srpaulo		if (field->changed_flag == CFG_CHANGED_NFC_PASSWORD_TOKEN)
4968281806Srpaulo			config->wps_nfc_pw_from_config = 1;
4969252726Srpaulo		config->changed_parameters |= field->changed_flag;
4970252726Srpaulo		break;
4971252726Srpaulo	}
4972252726Srpaulo	if (i == NUM_GLOBAL_FIELDS) {
4973252726Srpaulo#ifdef CONFIG_AP
4974252726Srpaulo		if (os_strncmp(pos, "wmm_ac_", 7) == 0) {
4975252726Srpaulo			char *tmp = os_strchr(pos, '=');
4976252726Srpaulo			if (tmp == NULL) {
4977252726Srpaulo				if (line < 0)
4978252726Srpaulo					return -1;
4979252726Srpaulo				wpa_printf(MSG_ERROR, "Line %d: invalid line "
4980252726Srpaulo					   "'%s'", line, pos);
4981252726Srpaulo				return -1;
4982252726Srpaulo			}
4983252726Srpaulo			*tmp++ = '\0';
4984252726Srpaulo			if (hostapd_config_wmm_ac(config->wmm_ac_params, pos,
4985252726Srpaulo						  tmp)) {
4986252726Srpaulo				wpa_printf(MSG_ERROR, "Line %d: invalid WMM "
4987252726Srpaulo					   "AC item", line);
4988252726Srpaulo				return -1;
4989252726Srpaulo			}
4990252726Srpaulo		}
4991252726Srpaulo#endif /* CONFIG_AP */
4992252726Srpaulo		if (line < 0)
4993252726Srpaulo			return -1;
4994252726Srpaulo		wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.",
4995252726Srpaulo			   line, pos);
4996252726Srpaulo		ret = -1;
4997252726Srpaulo	}
4998252726Srpaulo
4999252726Srpaulo	return ret;
5000252726Srpaulo}
5001