1189251Ssam/* 2189251Ssam * WPA Supplicant / Configuration backend: text file 3189251Ssam * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> 4189251Ssam * 5189251Ssam * This program is free software; you can redistribute it and/or modify 6189251Ssam * it under the terms of the GNU General Public License version 2 as 7189251Ssam * published by the Free Software Foundation. 8189251Ssam * 9189251Ssam * Alternatively, this software may be distributed under the terms of BSD 10189251Ssam * license. 11189251Ssam * 12189251Ssam * See README and COPYING for more details. 13189251Ssam * 14189251Ssam * This file implements a configuration backend for text files. All the 15189251Ssam * configuration information is stored in a text file that uses a format 16189251Ssam * described in the sample configuration file, wpa_supplicant.conf. 17189251Ssam */ 18189251Ssam 19189251Ssam#include "includes.h" 20189251Ssam 21189251Ssam#include "common.h" 22189251Ssam#include "config.h" 23189251Ssam#include "base64.h" 24189251Ssam#include "uuid.h" 25189251Ssam#include "eap_peer/eap_methods.h" 26189251Ssam 27189251Ssam 28189251Ssam/** 29189251Ssam * wpa_config_get_line - Read the next configuration file line 30189251Ssam * @s: Buffer for the line 31189251Ssam * @size: The buffer length 32189251Ssam * @stream: File stream to read from 33189251Ssam * @line: Pointer to a variable storing the file line number 34189251Ssam * @_pos: Buffer for the pointer to the beginning of data on the text line or 35189251Ssam * %NULL if not needed (returned value used instead) 36189251Ssam * Returns: Pointer to the beginning of data on the text line or %NULL if no 37189251Ssam * more text lines are available. 38189251Ssam * 39189251Ssam * This function reads the next non-empty line from the configuration file and 40189251Ssam * removes comments. The returned string is guaranteed to be null-terminated. 41189251Ssam */ 42189251Ssamstatic char * wpa_config_get_line(char *s, int size, FILE *stream, int *line, 43189251Ssam char **_pos) 44189251Ssam{ 45189251Ssam char *pos, *end, *sstart; 46189251Ssam 47189251Ssam while (fgets(s, size, stream)) { 48189251Ssam (*line)++; 49189251Ssam s[size - 1] = '\0'; 50189251Ssam pos = s; 51189251Ssam 52189251Ssam /* Skip white space from the beginning of line. */ 53189251Ssam while (*pos == ' ' || *pos == '\t' || *pos == '\r') 54189251Ssam pos++; 55189251Ssam 56189251Ssam /* Skip comment lines and empty lines */ 57189251Ssam if (*pos == '#' || *pos == '\n' || *pos == '\0') 58189251Ssam continue; 59189251Ssam 60189251Ssam /* 61189251Ssam * Remove # comments unless they are within a double quoted 62189251Ssam * string. 63189251Ssam */ 64189251Ssam sstart = os_strchr(pos, '"'); 65189251Ssam if (sstart) 66189251Ssam sstart = os_strrchr(sstart + 1, '"'); 67189251Ssam if (!sstart) 68189251Ssam sstart = pos; 69189251Ssam end = os_strchr(sstart, '#'); 70189251Ssam if (end) 71189251Ssam *end-- = '\0'; 72189251Ssam else 73189251Ssam end = pos + os_strlen(pos) - 1; 74189251Ssam 75189251Ssam /* Remove trailing white space. */ 76189251Ssam while (end > pos && 77189251Ssam (*end == '\n' || *end == ' ' || *end == '\t' || 78189251Ssam *end == '\r')) 79189251Ssam *end-- = '\0'; 80189251Ssam 81189251Ssam if (*pos == '\0') 82189251Ssam continue; 83189251Ssam 84189251Ssam if (_pos) 85189251Ssam *_pos = pos; 86189251Ssam return pos; 87189251Ssam } 88189251Ssam 89189251Ssam if (_pos) 90189251Ssam *_pos = NULL; 91189251Ssam return NULL; 92189251Ssam} 93189251Ssam 94189251Ssam 95189251Ssamstatic int wpa_config_validate_network(struct wpa_ssid *ssid, int line) 96189251Ssam{ 97189251Ssam int errors = 0; 98189251Ssam 99189251Ssam if (ssid->passphrase) { 100189251Ssam if (ssid->psk_set) { 101189251Ssam wpa_printf(MSG_ERROR, "Line %d: both PSK and " 102189251Ssam "passphrase configured.", line); 103189251Ssam errors++; 104189251Ssam } 105189251Ssam wpa_config_update_psk(ssid); 106189251Ssam } 107189251Ssam 108189251Ssam if ((ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | 109189251Ssam WPA_KEY_MGMT_PSK_SHA256)) && 110189251Ssam !ssid->psk_set) { 111189251Ssam wpa_printf(MSG_ERROR, "Line %d: WPA-PSK accepted for key " 112189251Ssam "management, but no PSK configured.", line); 113189251Ssam errors++; 114189251Ssam } 115189251Ssam 116189251Ssam if ((ssid->group_cipher & WPA_CIPHER_CCMP) && 117189251Ssam !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && 118189251Ssam !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) { 119189251Ssam /* Group cipher cannot be stronger than the pairwise cipher. */ 120189251Ssam wpa_printf(MSG_DEBUG, "Line %d: removed CCMP from group cipher" 121189251Ssam " list since it was not allowed for pairwise " 122189251Ssam "cipher", line); 123189251Ssam ssid->group_cipher &= ~WPA_CIPHER_CCMP; 124189251Ssam } 125189251Ssam 126189251Ssam return errors; 127189251Ssam} 128189251Ssam 129189251Ssam 130189251Ssamstatic struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id) 131189251Ssam{ 132189251Ssam struct wpa_ssid *ssid; 133189251Ssam int errors = 0, end = 0; 134189251Ssam char buf[256], *pos, *pos2; 135189251Ssam 136189251Ssam wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block", 137189251Ssam *line); 138189251Ssam ssid = os_zalloc(sizeof(*ssid)); 139189251Ssam if (ssid == NULL) 140189251Ssam return NULL; 141189251Ssam ssid->id = id; 142189251Ssam 143189251Ssam wpa_config_set_network_defaults(ssid); 144189251Ssam 145189251Ssam while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) { 146189251Ssam if (os_strcmp(pos, "}") == 0) { 147189251Ssam end = 1; 148189251Ssam break; 149189251Ssam } 150189251Ssam 151189251Ssam pos2 = os_strchr(pos, '='); 152189251Ssam if (pos2 == NULL) { 153189251Ssam wpa_printf(MSG_ERROR, "Line %d: Invalid SSID line " 154189251Ssam "'%s'.", *line, pos); 155189251Ssam errors++; 156189251Ssam continue; 157189251Ssam } 158189251Ssam 159189251Ssam *pos2++ = '\0'; 160189251Ssam if (*pos2 == '"') { 161189251Ssam if (os_strchr(pos2 + 1, '"') == NULL) { 162189251Ssam wpa_printf(MSG_ERROR, "Line %d: invalid " 163189251Ssam "quotation '%s'.", *line, pos2); 164189251Ssam errors++; 165189251Ssam continue; 166189251Ssam } 167189251Ssam } 168189251Ssam 169189251Ssam if (wpa_config_set(ssid, pos, pos2, *line) < 0) 170189251Ssam errors++; 171189251Ssam } 172189251Ssam 173189251Ssam if (!end) { 174189251Ssam wpa_printf(MSG_ERROR, "Line %d: network block was not " 175189251Ssam "terminated properly.", *line); 176189251Ssam errors++; 177189251Ssam } 178189251Ssam 179189251Ssam errors += wpa_config_validate_network(ssid, *line); 180189251Ssam 181189251Ssam if (errors) { 182189251Ssam wpa_config_free_ssid(ssid); 183189251Ssam ssid = NULL; 184189251Ssam } 185189251Ssam 186189251Ssam return ssid; 187189251Ssam} 188189251Ssam 189189251Ssam 190189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS 191189251Ssamstatic struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line, 192189251Ssam const char *name) 193189251Ssam{ 194189251Ssam struct wpa_config_blob *blob; 195189251Ssam char buf[256], *pos; 196189251Ssam unsigned char *encoded = NULL, *nencoded; 197189251Ssam int end = 0; 198189251Ssam size_t encoded_len = 0, len; 199189251Ssam 200189251Ssam wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new named blob '%s'", 201189251Ssam *line, name); 202189251Ssam 203189251Ssam while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) { 204189251Ssam if (os_strcmp(pos, "}") == 0) { 205189251Ssam end = 1; 206189251Ssam break; 207189251Ssam } 208189251Ssam 209189251Ssam len = os_strlen(pos); 210189251Ssam nencoded = os_realloc(encoded, encoded_len + len); 211189251Ssam if (nencoded == NULL) { 212189251Ssam wpa_printf(MSG_ERROR, "Line %d: not enough memory for " 213189251Ssam "blob", *line); 214189251Ssam os_free(encoded); 215189251Ssam return NULL; 216189251Ssam } 217189251Ssam encoded = nencoded; 218189251Ssam os_memcpy(encoded + encoded_len, pos, len); 219189251Ssam encoded_len += len; 220189251Ssam } 221189251Ssam 222189251Ssam if (!end) { 223189251Ssam wpa_printf(MSG_ERROR, "Line %d: blob was not terminated " 224189251Ssam "properly", *line); 225189251Ssam os_free(encoded); 226189251Ssam return NULL; 227189251Ssam } 228189251Ssam 229189251Ssam blob = os_zalloc(sizeof(*blob)); 230189251Ssam if (blob == NULL) { 231189251Ssam os_free(encoded); 232189251Ssam return NULL; 233189251Ssam } 234189251Ssam blob->name = os_strdup(name); 235189251Ssam blob->data = base64_decode(encoded, encoded_len, &blob->len); 236189251Ssam os_free(encoded); 237189251Ssam 238189251Ssam if (blob->name == NULL || blob->data == NULL) { 239189251Ssam wpa_config_free_blob(blob); 240189251Ssam return NULL; 241189251Ssam } 242189251Ssam 243189251Ssam return blob; 244189251Ssam} 245189251Ssam 246189251Ssam 247189251Ssamstatic int wpa_config_process_blob(struct wpa_config *config, FILE *f, 248189251Ssam int *line, char *bname) 249189251Ssam{ 250189251Ssam char *name_end; 251189251Ssam struct wpa_config_blob *blob; 252189251Ssam 253189251Ssam name_end = os_strchr(bname, '='); 254189251Ssam if (name_end == NULL) { 255189251Ssam wpa_printf(MSG_ERROR, "Line %d: no blob name terminator", 256189251Ssam *line); 257189251Ssam return -1; 258189251Ssam } 259189251Ssam *name_end = '\0'; 260189251Ssam 261189251Ssam blob = wpa_config_read_blob(f, line, bname); 262189251Ssam if (blob == NULL) { 263189251Ssam wpa_printf(MSG_ERROR, "Line %d: failed to read blob %s", 264189251Ssam *line, bname); 265189251Ssam return -1; 266189251Ssam } 267189251Ssam wpa_config_set_blob(config, blob); 268189251Ssam return 0; 269189251Ssam} 270189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */ 271189251Ssam 272189251Ssam 273189251Ssamstruct global_parse_data { 274189251Ssam char *name; 275189251Ssam int (*parser)(const struct global_parse_data *data, 276189251Ssam struct wpa_config *config, int line, const char *value); 277189251Ssam void *param1, *param2, *param3; 278189251Ssam}; 279189251Ssam 280189251Ssam 281189251Ssamstatic int wpa_config_parse_int(const struct global_parse_data *data, 282189251Ssam struct wpa_config *config, int line, 283189251Ssam const char *pos) 284189251Ssam{ 285189251Ssam int *dst; 286189251Ssam dst = (int *) (((u8 *) config) + (long) data->param1); 287189251Ssam *dst = atoi(pos); 288189251Ssam wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst); 289189251Ssam 290189251Ssam if (data->param2 && *dst < (long) data->param2) { 291189251Ssam wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d " 292189251Ssam "min_value=%ld)", line, data->name, *dst, 293189251Ssam (long) data->param2); 294189251Ssam *dst = (long) data->param2; 295189251Ssam return -1; 296189251Ssam } 297189251Ssam 298189251Ssam if (data->param3 && *dst > (long) data->param3) { 299189251Ssam wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d " 300189251Ssam "max_value=%ld)", line, data->name, *dst, 301189251Ssam (long) data->param3); 302189251Ssam *dst = (long) data->param3; 303189251Ssam return -1; 304189251Ssam } 305189251Ssam 306189251Ssam return 0; 307189251Ssam} 308189251Ssam 309189251Ssam 310189251Ssamstatic int wpa_config_parse_str(const struct global_parse_data *data, 311189251Ssam struct wpa_config *config, int line, 312189251Ssam const char *pos) 313189251Ssam{ 314189251Ssam size_t len; 315189251Ssam char **dst, *tmp; 316189251Ssam 317189251Ssam len = os_strlen(pos); 318189251Ssam if (data->param2 && len < (size_t) data->param2) { 319189251Ssam wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu " 320189251Ssam "min_len=%ld)", line, data->name, 321189251Ssam (unsigned long) len, (long) data->param2); 322189251Ssam return -1; 323189251Ssam } 324189251Ssam 325189251Ssam if (data->param3 && len > (size_t) data->param3) { 326189251Ssam wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu " 327189251Ssam "max_len=%ld)", line, data->name, 328189251Ssam (unsigned long) len, (long) data->param3); 329189251Ssam return -1; 330189251Ssam } 331189251Ssam 332189251Ssam tmp = os_strdup(pos); 333189251Ssam if (tmp == NULL) 334189251Ssam return -1; 335189251Ssam 336189251Ssam dst = (char **) (((u8 *) config) + (long) data->param1); 337189251Ssam os_free(*dst); 338189251Ssam *dst = tmp; 339189251Ssam wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst); 340189251Ssam 341189251Ssam return 0; 342189251Ssam} 343189251Ssam 344189251Ssam 345189251Ssamstatic int wpa_config_process_country(const struct global_parse_data *data, 346189251Ssam struct wpa_config *config, int line, 347189251Ssam const char *pos) 348189251Ssam{ 349189251Ssam if (!pos[0] || !pos[1]) { 350189251Ssam wpa_printf(MSG_DEBUG, "Invalid country set"); 351189251Ssam return -1; 352189251Ssam } 353189251Ssam config->country[0] = pos[0]; 354189251Ssam config->country[1] = pos[1]; 355189251Ssam wpa_printf(MSG_DEBUG, "country='%c%c'", 356189251Ssam config->country[0], config->country[1]); 357189251Ssam return 0; 358189251Ssam} 359189251Ssam 360189251Ssam 361189251Ssamstatic int wpa_config_process_load_dynamic_eap( 362189251Ssam const struct global_parse_data *data, struct wpa_config *config, 363189251Ssam int line, const char *so) 364189251Ssam{ 365189251Ssam int ret; 366189251Ssam wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so); 367189251Ssam ret = eap_peer_method_load(so); 368189251Ssam if (ret == -2) { 369189251Ssam wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not " 370189251Ssam "reloading."); 371189251Ssam } else if (ret) { 372189251Ssam wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP " 373189251Ssam "method '%s'.", line, so); 374189251Ssam return -1; 375189251Ssam } 376189251Ssam 377189251Ssam return 0; 378189251Ssam} 379189251Ssam 380189251Ssam 381189251Ssam#ifdef CONFIG_WPS 382189251Ssam 383189251Ssamstatic int wpa_config_process_uuid(const struct global_parse_data *data, 384189251Ssam struct wpa_config *config, int line, 385189251Ssam const char *pos) 386189251Ssam{ 387189251Ssam char buf[40]; 388189251Ssam if (uuid_str2bin(pos, config->uuid)) { 389189251Ssam wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line); 390189251Ssam return -1; 391189251Ssam } 392189251Ssam uuid_bin2str(config->uuid, buf, sizeof(buf)); 393189251Ssam wpa_printf(MSG_DEBUG, "uuid=%s", buf); 394189251Ssam return 0; 395189251Ssam} 396189251Ssam 397189251Ssam 398189251Ssamstatic int wpa_config_process_os_version(const struct global_parse_data *data, 399189251Ssam struct wpa_config *config, int line, 400189251Ssam const char *pos) 401189251Ssam{ 402189251Ssam if (hexstr2bin(pos, config->os_version, 4)) { 403189251Ssam wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line); 404189251Ssam return -1; 405189251Ssam } 406189251Ssam wpa_printf(MSG_DEBUG, "os_version=%08x", 407189251Ssam WPA_GET_BE32(config->os_version)); 408189251Ssam return 0; 409189251Ssam} 410189251Ssam 411189251Ssam#endif /* CONFIG_WPS */ 412189251Ssam 413189251Ssam 414189251Ssam#ifdef OFFSET 415189251Ssam#undef OFFSET 416189251Ssam#endif /* OFFSET */ 417189251Ssam/* OFFSET: Get offset of a variable within the wpa_config structure */ 418189251Ssam#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v) 419189251Ssam 420189251Ssam#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL 421189251Ssam#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL 422189251Ssam#define _INT(f) #f, wpa_config_parse_int, OFFSET(f) 423189251Ssam#define INT(f) _INT(f), NULL, NULL 424189251Ssam#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max 425189251Ssam#define _STR(f) #f, wpa_config_parse_str, OFFSET(f) 426189251Ssam#define STR(f) _STR(f), NULL, NULL 427189251Ssam#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max 428189251Ssam 429189251Ssamstatic const struct global_parse_data global_fields[] = { 430189251Ssam#ifdef CONFIG_CTRL_IFACE 431189251Ssam { STR(ctrl_interface) }, 432189251Ssam { STR(ctrl_interface_group) } /* deprecated */, 433189251Ssam#endif /* CONFIG_CTRL_IFACE */ 434189251Ssam { INT_RANGE(eapol_version, 1, 2) }, 435189251Ssam { INT(ap_scan) }, 436189251Ssam { INT(fast_reauth) }, 437189251Ssam { STR(opensc_engine_path) }, 438189251Ssam { STR(pkcs11_engine_path) }, 439189251Ssam { STR(pkcs11_module_path) }, 440189251Ssam { STR(driver_param) }, 441189251Ssam { INT(dot11RSNAConfigPMKLifetime) }, 442189251Ssam { INT(dot11RSNAConfigPMKReauthThreshold) }, 443189251Ssam { INT(dot11RSNAConfigSATimeout) }, 444189251Ssam#ifndef CONFIG_NO_CONFIG_WRITE 445189251Ssam { INT(update_config) }, 446189251Ssam#endif /* CONFIG_NO_CONFIG_WRITE */ 447189251Ssam { FUNC_NO_VAR(load_dynamic_eap) }, 448189251Ssam#ifdef CONFIG_WPS 449189251Ssam { FUNC(uuid) }, 450189251Ssam { STR_RANGE(device_name, 0, 32) }, 451189251Ssam { STR_RANGE(manufacturer, 0, 64) }, 452189251Ssam { STR_RANGE(model_name, 0, 32) }, 453189251Ssam { STR_RANGE(model_number, 0, 32) }, 454189251Ssam { STR_RANGE(serial_number, 0, 32) }, 455189251Ssam { STR(device_type) }, 456189251Ssam { FUNC(os_version) }, 457214734Srpaulo { STR(config_methods) }, 458189251Ssam { INT_RANGE(wps_cred_processing, 0, 2) }, 459189251Ssam#endif /* CONFIG_WPS */ 460214734Srpaulo { FUNC(country) }, 461214734Srpaulo { INT(bss_max_count) }, 462214734Srpaulo { INT_RANGE(filter_ssids, 0, 1) } 463189251Ssam}; 464189251Ssam 465189251Ssam#undef FUNC 466189251Ssam#undef _INT 467189251Ssam#undef INT 468189251Ssam#undef INT_RANGE 469189251Ssam#undef _STR 470189251Ssam#undef STR 471189251Ssam#undef STR_RANGE 472189251Ssam#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0])) 473189251Ssam 474189251Ssam 475189251Ssamstatic int wpa_config_process_global(struct wpa_config *config, char *pos, 476189251Ssam int line) 477189251Ssam{ 478189251Ssam size_t i; 479189251Ssam int ret = 0; 480189251Ssam 481189251Ssam for (i = 0; i < NUM_GLOBAL_FIELDS; i++) { 482189251Ssam const struct global_parse_data *field = &global_fields[i]; 483189251Ssam size_t flen = os_strlen(field->name); 484189251Ssam if (os_strncmp(pos, field->name, flen) != 0 || 485189251Ssam pos[flen] != '=') 486189251Ssam continue; 487189251Ssam 488189251Ssam if (field->parser(field, config, line, pos + flen + 1)) { 489189251Ssam wpa_printf(MSG_ERROR, "Line %d: failed to " 490189251Ssam "parse '%s'.", line, pos); 491189251Ssam ret = -1; 492189251Ssam } 493189251Ssam break; 494189251Ssam } 495189251Ssam if (i == NUM_GLOBAL_FIELDS) { 496189251Ssam wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.", 497189251Ssam line, pos); 498189251Ssam ret = -1; 499189251Ssam } 500189251Ssam 501189251Ssam return ret; 502189251Ssam} 503189251Ssam 504189251Ssam 505189251Ssamstruct wpa_config * wpa_config_read(const char *name) 506189251Ssam{ 507189251Ssam FILE *f; 508189251Ssam char buf[256], *pos; 509189251Ssam int errors = 0, line = 0; 510189251Ssam struct wpa_ssid *ssid, *tail = NULL, *head = NULL; 511189251Ssam struct wpa_config *config; 512189251Ssam int id = 0; 513189251Ssam 514189251Ssam config = wpa_config_alloc_empty(NULL, NULL); 515189251Ssam if (config == NULL) 516189251Ssam return NULL; 517189251Ssam wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name); 518189251Ssam f = fopen(name, "r"); 519189251Ssam if (f == NULL) { 520189251Ssam os_free(config); 521189251Ssam return NULL; 522189251Ssam } 523189251Ssam 524189251Ssam while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) { 525189251Ssam if (os_strcmp(pos, "network={") == 0) { 526189251Ssam ssid = wpa_config_read_network(f, &line, id++); 527189251Ssam if (ssid == NULL) { 528189251Ssam wpa_printf(MSG_ERROR, "Line %d: failed to " 529189251Ssam "parse network block.", line); 530189251Ssam errors++; 531189251Ssam continue; 532189251Ssam } 533189251Ssam if (head == NULL) { 534189251Ssam head = tail = ssid; 535189251Ssam } else { 536189251Ssam tail->next = ssid; 537189251Ssam tail = ssid; 538189251Ssam } 539189251Ssam if (wpa_config_add_prio_network(config, ssid)) { 540189251Ssam wpa_printf(MSG_ERROR, "Line %d: failed to add " 541189251Ssam "network block to priority list.", 542189251Ssam line); 543189251Ssam errors++; 544189251Ssam continue; 545189251Ssam } 546189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS 547189251Ssam } else if (os_strncmp(pos, "blob-base64-", 12) == 0) { 548189251Ssam if (wpa_config_process_blob(config, f, &line, pos + 12) 549189251Ssam < 0) { 550189251Ssam errors++; 551189251Ssam continue; 552189251Ssam } 553189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */ 554189251Ssam } else if (wpa_config_process_global(config, pos, line) < 0) { 555189251Ssam wpa_printf(MSG_ERROR, "Line %d: Invalid configuration " 556189251Ssam "line '%s'.", line, pos); 557189251Ssam errors++; 558189251Ssam continue; 559189251Ssam } 560189251Ssam } 561189251Ssam 562189251Ssam fclose(f); 563189251Ssam 564189251Ssam config->ssid = head; 565189251Ssam wpa_config_debug_dump_networks(config); 566189251Ssam 567189251Ssam if (errors) { 568189251Ssam wpa_config_free(config); 569189251Ssam config = NULL; 570189251Ssam head = NULL; 571189251Ssam } 572189251Ssam 573189251Ssam return config; 574189251Ssam} 575189251Ssam 576189251Ssam 577189251Ssam#ifndef CONFIG_NO_CONFIG_WRITE 578189251Ssam 579189251Ssamstatic void write_str(FILE *f, const char *field, struct wpa_ssid *ssid) 580189251Ssam{ 581189251Ssam char *value = wpa_config_get(ssid, field); 582189251Ssam if (value == NULL) 583189251Ssam return; 584189251Ssam fprintf(f, "\t%s=%s\n", field, value); 585189251Ssam os_free(value); 586189251Ssam} 587189251Ssam 588189251Ssam 589189251Ssamstatic void write_int(FILE *f, const char *field, int value, int def) 590189251Ssam{ 591189251Ssam if (value == def) 592189251Ssam return; 593189251Ssam fprintf(f, "\t%s=%d\n", field, value); 594189251Ssam} 595189251Ssam 596189251Ssam 597189251Ssamstatic void write_bssid(FILE *f, struct wpa_ssid *ssid) 598189251Ssam{ 599189251Ssam char *value = wpa_config_get(ssid, "bssid"); 600189251Ssam if (value == NULL) 601189251Ssam return; 602189251Ssam fprintf(f, "\tbssid=%s\n", value); 603189251Ssam os_free(value); 604189251Ssam} 605189251Ssam 606189251Ssam 607189251Ssamstatic void write_psk(FILE *f, struct wpa_ssid *ssid) 608189251Ssam{ 609189251Ssam char *value = wpa_config_get(ssid, "psk"); 610189251Ssam if (value == NULL) 611189251Ssam return; 612189251Ssam fprintf(f, "\tpsk=%s\n", value); 613189251Ssam os_free(value); 614189251Ssam} 615189251Ssam 616189251Ssam 617189251Ssamstatic void write_proto(FILE *f, struct wpa_ssid *ssid) 618189251Ssam{ 619189251Ssam char *value; 620189251Ssam 621189251Ssam if (ssid->proto == DEFAULT_PROTO) 622189251Ssam return; 623189251Ssam 624189251Ssam value = wpa_config_get(ssid, "proto"); 625189251Ssam if (value == NULL) 626189251Ssam return; 627189251Ssam if (value[0]) 628189251Ssam fprintf(f, "\tproto=%s\n", value); 629189251Ssam os_free(value); 630189251Ssam} 631189251Ssam 632189251Ssam 633189251Ssamstatic void write_key_mgmt(FILE *f, struct wpa_ssid *ssid) 634189251Ssam{ 635189251Ssam char *value; 636189251Ssam 637189251Ssam if (ssid->key_mgmt == DEFAULT_KEY_MGMT) 638189251Ssam return; 639189251Ssam 640189251Ssam value = wpa_config_get(ssid, "key_mgmt"); 641189251Ssam if (value == NULL) 642189251Ssam return; 643189251Ssam if (value[0]) 644189251Ssam fprintf(f, "\tkey_mgmt=%s\n", value); 645189251Ssam os_free(value); 646189251Ssam} 647189251Ssam 648189251Ssam 649189251Ssamstatic void write_pairwise(FILE *f, struct wpa_ssid *ssid) 650189251Ssam{ 651189251Ssam char *value; 652189251Ssam 653189251Ssam if (ssid->pairwise_cipher == DEFAULT_PAIRWISE) 654189251Ssam return; 655189251Ssam 656189251Ssam value = wpa_config_get(ssid, "pairwise"); 657189251Ssam if (value == NULL) 658189251Ssam return; 659189251Ssam if (value[0]) 660189251Ssam fprintf(f, "\tpairwise=%s\n", value); 661189251Ssam os_free(value); 662189251Ssam} 663189251Ssam 664189251Ssam 665189251Ssamstatic void write_group(FILE *f, struct wpa_ssid *ssid) 666189251Ssam{ 667189251Ssam char *value; 668189251Ssam 669189251Ssam if (ssid->group_cipher == DEFAULT_GROUP) 670189251Ssam return; 671189251Ssam 672189251Ssam value = wpa_config_get(ssid, "group"); 673189251Ssam if (value == NULL) 674189251Ssam return; 675189251Ssam if (value[0]) 676189251Ssam fprintf(f, "\tgroup=%s\n", value); 677189251Ssam os_free(value); 678189251Ssam} 679189251Ssam 680189251Ssam 681189251Ssamstatic void write_auth_alg(FILE *f, struct wpa_ssid *ssid) 682189251Ssam{ 683189251Ssam char *value; 684189251Ssam 685189251Ssam if (ssid->auth_alg == 0) 686189251Ssam return; 687189251Ssam 688189251Ssam value = wpa_config_get(ssid, "auth_alg"); 689189251Ssam if (value == NULL) 690189251Ssam return; 691189251Ssam if (value[0]) 692189251Ssam fprintf(f, "\tauth_alg=%s\n", value); 693189251Ssam os_free(value); 694189251Ssam} 695189251Ssam 696189251Ssam 697189251Ssam#ifdef IEEE8021X_EAPOL 698189251Ssamstatic void write_eap(FILE *f, struct wpa_ssid *ssid) 699189251Ssam{ 700189251Ssam char *value; 701189251Ssam 702189251Ssam value = wpa_config_get(ssid, "eap"); 703189251Ssam if (value == NULL) 704189251Ssam return; 705189251Ssam 706189251Ssam if (value[0]) 707189251Ssam fprintf(f, "\teap=%s\n", value); 708189251Ssam os_free(value); 709189251Ssam} 710189251Ssam#endif /* IEEE8021X_EAPOL */ 711189251Ssam 712189251Ssam 713189251Ssamstatic void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid) 714189251Ssam{ 715189251Ssam char field[20], *value; 716189251Ssam int res; 717189251Ssam 718189251Ssam res = os_snprintf(field, sizeof(field), "wep_key%d", idx); 719189251Ssam if (res < 0 || (size_t) res >= sizeof(field)) 720189251Ssam return; 721189251Ssam value = wpa_config_get(ssid, field); 722189251Ssam if (value) { 723189251Ssam fprintf(f, "\t%s=%s\n", field, value); 724189251Ssam os_free(value); 725189251Ssam } 726189251Ssam} 727189251Ssam 728189251Ssam 729189251Ssamstatic void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) 730189251Ssam{ 731189251Ssam int i; 732189251Ssam 733189251Ssam#define STR(t) write_str(f, #t, ssid) 734189251Ssam#define INT(t) write_int(f, #t, ssid->t, 0) 735189251Ssam#define INTe(t) write_int(f, #t, ssid->eap.t, 0) 736189251Ssam#define INT_DEF(t, def) write_int(f, #t, ssid->t, def) 737189251Ssam#define INT_DEFe(t, def) write_int(f, #t, ssid->eap.t, def) 738189251Ssam 739189251Ssam STR(ssid); 740189251Ssam INT(scan_ssid); 741189251Ssam write_bssid(f, ssid); 742189251Ssam write_psk(f, ssid); 743189251Ssam write_proto(f, ssid); 744189251Ssam write_key_mgmt(f, ssid); 745189251Ssam write_pairwise(f, ssid); 746189251Ssam write_group(f, ssid); 747189251Ssam write_auth_alg(f, ssid); 748189251Ssam#ifdef IEEE8021X_EAPOL 749189251Ssam write_eap(f, ssid); 750189251Ssam STR(identity); 751189251Ssam STR(anonymous_identity); 752189251Ssam STR(password); 753189251Ssam STR(ca_cert); 754189251Ssam STR(ca_path); 755189251Ssam STR(client_cert); 756189251Ssam STR(private_key); 757189251Ssam STR(private_key_passwd); 758189251Ssam STR(dh_file); 759189251Ssam STR(subject_match); 760189251Ssam STR(altsubject_match); 761189251Ssam STR(ca_cert2); 762189251Ssam STR(ca_path2); 763189251Ssam STR(client_cert2); 764189251Ssam STR(private_key2); 765189251Ssam STR(private_key2_passwd); 766189251Ssam STR(dh_file2); 767189251Ssam STR(subject_match2); 768189251Ssam STR(altsubject_match2); 769189251Ssam STR(phase1); 770189251Ssam STR(phase2); 771189251Ssam STR(pcsc); 772189251Ssam STR(pin); 773189251Ssam STR(engine_id); 774189251Ssam STR(key_id); 775189251Ssam STR(cert_id); 776189251Ssam STR(ca_cert_id); 777189251Ssam STR(key2_id); 778189251Ssam STR(pin2); 779189251Ssam STR(engine2_id); 780189251Ssam STR(cert2_id); 781189251Ssam STR(ca_cert2_id); 782189251Ssam INTe(engine); 783189251Ssam INTe(engine2); 784189251Ssam INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS); 785189251Ssam#endif /* IEEE8021X_EAPOL */ 786189251Ssam for (i = 0; i < 4; i++) 787189251Ssam write_wep_key(f, i, ssid); 788189251Ssam INT(wep_tx_keyidx); 789189251Ssam INT(priority); 790189251Ssam#ifdef IEEE8021X_EAPOL 791189251Ssam INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND); 792189251Ssam STR(pac_file); 793189251Ssam INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE); 794189251Ssam#endif /* IEEE8021X_EAPOL */ 795189251Ssam INT(mode); 796189251Ssam INT(proactive_key_caching); 797189251Ssam INT(disabled); 798189251Ssam INT(peerkey); 799189251Ssam#ifdef CONFIG_IEEE80211W 800189251Ssam INT(ieee80211w); 801189251Ssam#endif /* CONFIG_IEEE80211W */ 802189251Ssam STR(id_str); 803189251Ssam 804189251Ssam#undef STR 805189251Ssam#undef INT 806189251Ssam#undef INT_DEF 807189251Ssam} 808189251Ssam 809189251Ssam 810189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS 811189251Ssamstatic int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob) 812189251Ssam{ 813189251Ssam unsigned char *encoded; 814189251Ssam 815189251Ssam encoded = base64_encode(blob->data, blob->len, NULL); 816189251Ssam if (encoded == NULL) 817189251Ssam return -1; 818189251Ssam 819189251Ssam fprintf(f, "\nblob-base64-%s={\n%s}\n", blob->name, encoded); 820189251Ssam os_free(encoded); 821189251Ssam return 0; 822189251Ssam} 823189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */ 824189251Ssam 825189251Ssam 826189251Ssamstatic void wpa_config_write_global(FILE *f, struct wpa_config *config) 827189251Ssam{ 828189251Ssam#ifdef CONFIG_CTRL_IFACE 829189251Ssam if (config->ctrl_interface) 830189251Ssam fprintf(f, "ctrl_interface=%s\n", config->ctrl_interface); 831189251Ssam if (config->ctrl_interface_group) 832189251Ssam fprintf(f, "ctrl_interface_group=%s\n", 833189251Ssam config->ctrl_interface_group); 834189251Ssam#endif /* CONFIG_CTRL_IFACE */ 835189251Ssam if (config->eapol_version != DEFAULT_EAPOL_VERSION) 836189251Ssam fprintf(f, "eapol_version=%d\n", config->eapol_version); 837189251Ssam if (config->ap_scan != DEFAULT_AP_SCAN) 838189251Ssam fprintf(f, "ap_scan=%d\n", config->ap_scan); 839189251Ssam if (config->fast_reauth != DEFAULT_FAST_REAUTH) 840189251Ssam fprintf(f, "fast_reauth=%d\n", config->fast_reauth); 841189251Ssam if (config->opensc_engine_path) 842189251Ssam fprintf(f, "opensc_engine_path=%s\n", 843189251Ssam config->opensc_engine_path); 844189251Ssam if (config->pkcs11_engine_path) 845189251Ssam fprintf(f, "pkcs11_engine_path=%s\n", 846189251Ssam config->pkcs11_engine_path); 847189251Ssam if (config->pkcs11_module_path) 848189251Ssam fprintf(f, "pkcs11_module_path=%s\n", 849189251Ssam config->pkcs11_module_path); 850189251Ssam if (config->driver_param) 851189251Ssam fprintf(f, "driver_param=%s\n", config->driver_param); 852189251Ssam if (config->dot11RSNAConfigPMKLifetime) 853189251Ssam fprintf(f, "dot11RSNAConfigPMKLifetime=%d\n", 854189251Ssam config->dot11RSNAConfigPMKLifetime); 855189251Ssam if (config->dot11RSNAConfigPMKReauthThreshold) 856189251Ssam fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%d\n", 857189251Ssam config->dot11RSNAConfigPMKReauthThreshold); 858189251Ssam if (config->dot11RSNAConfigSATimeout) 859189251Ssam fprintf(f, "dot11RSNAConfigSATimeout=%d\n", 860189251Ssam config->dot11RSNAConfigSATimeout); 861189251Ssam if (config->update_config) 862189251Ssam fprintf(f, "update_config=%d\n", config->update_config); 863189251Ssam#ifdef CONFIG_WPS 864189251Ssam if (!is_nil_uuid(config->uuid)) { 865189251Ssam char buf[40]; 866189251Ssam uuid_bin2str(config->uuid, buf, sizeof(buf)); 867189251Ssam fprintf(f, "uuid=%s\n", buf); 868189251Ssam } 869189251Ssam if (config->device_name) 870189251Ssam fprintf(f, "device_name=%s\n", config->device_name); 871189251Ssam if (config->manufacturer) 872189251Ssam fprintf(f, "manufacturer=%s\n", config->manufacturer); 873189251Ssam if (config->model_name) 874189251Ssam fprintf(f, "model_name=%s\n", config->model_name); 875189251Ssam if (config->model_number) 876189251Ssam fprintf(f, "model_number=%s\n", config->model_number); 877189251Ssam if (config->serial_number) 878189251Ssam fprintf(f, "serial_number=%s\n", config->serial_number); 879189251Ssam if (config->device_type) 880189251Ssam fprintf(f, "device_type=%s\n", config->device_type); 881189251Ssam if (WPA_GET_BE32(config->os_version)) 882189251Ssam fprintf(f, "os_version=%08x\n", 883189251Ssam WPA_GET_BE32(config->os_version)); 884214734Srpaulo if (config->config_methods) 885214734Srpaulo fprintf(f, "config_methods=%s\n", config->config_methods); 886189251Ssam if (config->wps_cred_processing) 887189251Ssam fprintf(f, "wps_cred_processing=%d\n", 888189251Ssam config->wps_cred_processing); 889189251Ssam#endif /* CONFIG_WPS */ 890189251Ssam if (config->country[0] && config->country[1]) { 891189251Ssam fprintf(f, "country=%c%c\n", 892189251Ssam config->country[0], config->country[1]); 893189251Ssam } 894214734Srpaulo if (config->bss_max_count != DEFAULT_BSS_MAX_COUNT) 895214734Srpaulo fprintf(f, "bss_max_count=%u\n", config->bss_max_count); 896214734Srpaulo if (config->filter_ssids) 897214734Srpaulo fprintf(f, "filter_ssids=%d\n", config->filter_ssids); 898189251Ssam} 899189251Ssam 900189251Ssam#endif /* CONFIG_NO_CONFIG_WRITE */ 901189251Ssam 902189251Ssam 903189251Ssamint wpa_config_write(const char *name, struct wpa_config *config) 904189251Ssam{ 905189251Ssam#ifndef CONFIG_NO_CONFIG_WRITE 906189251Ssam FILE *f; 907189251Ssam struct wpa_ssid *ssid; 908189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS 909189251Ssam struct wpa_config_blob *blob; 910189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */ 911189251Ssam int ret = 0; 912189251Ssam 913189251Ssam wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name); 914189251Ssam 915189251Ssam f = fopen(name, "w"); 916189251Ssam if (f == NULL) { 917189251Ssam wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", name); 918189251Ssam return -1; 919189251Ssam } 920189251Ssam 921189251Ssam wpa_config_write_global(f, config); 922189251Ssam 923189251Ssam for (ssid = config->ssid; ssid; ssid = ssid->next) { 924189251Ssam if (ssid->key_mgmt == WPA_KEY_MGMT_WPS) 925189251Ssam continue; /* do not save temporary WPS networks */ 926189251Ssam fprintf(f, "\nnetwork={\n"); 927189251Ssam wpa_config_write_network(f, ssid); 928189251Ssam fprintf(f, "}\n"); 929189251Ssam } 930189251Ssam 931189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS 932189251Ssam for (blob = config->blobs; blob; blob = blob->next) { 933189251Ssam ret = wpa_config_write_blob(f, blob); 934189251Ssam if (ret) 935189251Ssam break; 936189251Ssam } 937189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */ 938189251Ssam 939189251Ssam fclose(f); 940189251Ssam 941189251Ssam wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully", 942189251Ssam name, ret ? "un" : ""); 943189251Ssam return ret; 944189251Ssam#else /* CONFIG_NO_CONFIG_WRITE */ 945189251Ssam return -1; 946189251Ssam#endif /* CONFIG_NO_CONFIG_WRITE */ 947189251Ssam} 948