0d7e5a3a29
The nl80211 driver can report low ACK condition (in fact it reports complete loss right now only). Use that, along with a config option, to disconnect stations when the data connection is not working properly, e.g., due to the STA having went outside the range of the AP. This is disabled by default and can be enabled with disassoc_low_ack=1 in hostapd or wpa_supplicant configuration file. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
1015 lines
24 KiB
C
1015 lines
24 KiB
C
/*
|
|
* WPA Supplicant / Configuration backend: Windows registry
|
|
* Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* Alternatively, this software may be distributed under the terms of BSD
|
|
* license.
|
|
*
|
|
* See README and COPYING for more details.
|
|
*
|
|
* This file implements a configuration backend for Windows registry. All the
|
|
* configuration information is stored in the registry and the format for
|
|
* network configuration fields is same as described in the sample
|
|
* configuration file, wpa_supplicant.conf.
|
|
*
|
|
* Configuration data is in
|
|
* \a HKEY_LOCAL_MACHINE\\SOFTWARE\\%wpa_supplicant\\configs
|
|
* key. Each configuration profile has its own key under this. In terms of text
|
|
* files, each profile would map to a separate text file with possibly multiple
|
|
* networks. Under each profile, there is a networks key that lists all
|
|
* networks as a subkey. Each network has set of values in the same way as
|
|
* network block in the configuration file. In addition, blobs subkey has
|
|
* possible blobs as values.
|
|
*
|
|
* Example network configuration block:
|
|
* \verbatim
|
|
HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000
|
|
ssid="example"
|
|
key_mgmt=WPA-PSK
|
|
\endverbatim
|
|
*/
|
|
|
|
#include "includes.h"
|
|
|
|
#include "common.h"
|
|
#include "uuid.h"
|
|
#include "config.h"
|
|
|
|
#ifndef WPA_KEY_ROOT
|
|
#define WPA_KEY_ROOT HKEY_LOCAL_MACHINE
|
|
#endif
|
|
#ifndef WPA_KEY_PREFIX
|
|
#define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant")
|
|
#endif
|
|
|
|
#ifdef UNICODE
|
|
#define TSTR "%S"
|
|
#else /* UNICODE */
|
|
#define TSTR "%s"
|
|
#endif /* UNICODE */
|
|
|
|
|
|
static int wpa_config_read_blobs(struct wpa_config *config, HKEY hk)
|
|
{
|
|
struct wpa_config_blob *blob;
|
|
int errors = 0;
|
|
HKEY bhk;
|
|
LONG ret;
|
|
DWORD i;
|
|
|
|
ret = RegOpenKeyEx(hk, TEXT("blobs"), 0, KEY_QUERY_VALUE, &bhk);
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_DEBUG, "Could not open wpa_supplicant config "
|
|
"blobs key");
|
|
return 0; /* assume no blobs */
|
|
}
|
|
|
|
for (i = 0; ; i++) {
|
|
#define TNAMELEN 255
|
|
TCHAR name[TNAMELEN];
|
|
char data[4096];
|
|
DWORD namelen, datalen, type;
|
|
|
|
namelen = TNAMELEN;
|
|
datalen = sizeof(data);
|
|
ret = RegEnumValue(bhk, i, name, &namelen, NULL, &type,
|
|
(LPBYTE) data, &datalen);
|
|
|
|
if (ret == ERROR_NO_MORE_ITEMS)
|
|
break;
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_DEBUG, "RegEnumValue failed: 0x%x",
|
|
(unsigned int) ret);
|
|
break;
|
|
}
|
|
|
|
if (namelen >= TNAMELEN)
|
|
namelen = TNAMELEN - 1;
|
|
name[namelen] = TEXT('\0');
|
|
wpa_unicode2ascii_inplace(name);
|
|
|
|
if (datalen >= sizeof(data))
|
|
datalen = sizeof(data) - 1;
|
|
|
|
wpa_printf(MSG_MSGDUMP, "blob %d: field='%s' len %d",
|
|
(int) i, name, (int) datalen);
|
|
|
|
blob = os_zalloc(sizeof(*blob));
|
|
if (blob == NULL) {
|
|
errors++;
|
|
break;
|
|
}
|
|
blob->name = os_strdup((char *) name);
|
|
blob->data = os_malloc(datalen);
|
|
if (blob->name == NULL || blob->data == NULL) {
|
|
wpa_config_free_blob(blob);
|
|
errors++;
|
|
break;
|
|
}
|
|
os_memcpy(blob->data, data, datalen);
|
|
blob->len = datalen;
|
|
|
|
wpa_config_set_blob(config, blob);
|
|
}
|
|
|
|
RegCloseKey(bhk);
|
|
|
|
return errors ? -1 : 0;
|
|
}
|
|
|
|
|
|
static int wpa_config_read_reg_dword(HKEY hk, const TCHAR *name, int *_val)
|
|
{
|
|
DWORD val, buflen;
|
|
LONG ret;
|
|
|
|
buflen = sizeof(val);
|
|
ret = RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE) &val, &buflen);
|
|
if (ret == ERROR_SUCCESS && buflen == sizeof(val)) {
|
|
wpa_printf(MSG_DEBUG, TSTR "=%d", name, (int) val);
|
|
*_val = val;
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static char * wpa_config_read_reg_string(HKEY hk, const TCHAR *name)
|
|
{
|
|
DWORD buflen;
|
|
LONG ret;
|
|
TCHAR *val;
|
|
|
|
buflen = 0;
|
|
ret = RegQueryValueEx(hk, name, NULL, NULL, NULL, &buflen);
|
|
if (ret != ERROR_SUCCESS)
|
|
return NULL;
|
|
val = os_malloc(buflen);
|
|
if (val == NULL)
|
|
return NULL;
|
|
|
|
ret = RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE) val, &buflen);
|
|
if (ret != ERROR_SUCCESS) {
|
|
os_free(val);
|
|
return NULL;
|
|
}
|
|
|
|
wpa_unicode2ascii_inplace(val);
|
|
wpa_printf(MSG_DEBUG, TSTR "=%s", name, (char *) val);
|
|
return (char *) val;
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_WPS
|
|
static int wpa_config_read_global_uuid(struct wpa_config *config, HKEY hk)
|
|
{
|
|
char *str;
|
|
int ret = 0;
|
|
|
|
str = wpa_config_read_reg_string(hk, TEXT("uuid"));
|
|
if (str == NULL)
|
|
return 0;
|
|
|
|
if (uuid_str2bin(str, config->uuid))
|
|
ret = -1;
|
|
|
|
os_free(str);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int wpa_config_read_global_os_version(struct wpa_config *config,
|
|
HKEY hk)
|
|
{
|
|
char *str;
|
|
int ret = 0;
|
|
|
|
str = wpa_config_read_reg_string(hk, TEXT("os_version"));
|
|
if (str == NULL)
|
|
return 0;
|
|
|
|
if (hexstr2bin(str, config->os_version, 4))
|
|
ret = -1;
|
|
|
|
os_free(str);
|
|
|
|
return ret;
|
|
}
|
|
#endif /* CONFIG_WPS */
|
|
|
|
|
|
static int wpa_config_read_global(struct wpa_config *config, HKEY hk)
|
|
{
|
|
int errors = 0;
|
|
|
|
wpa_config_read_reg_dword(hk, TEXT("ap_scan"), &config->ap_scan);
|
|
wpa_config_read_reg_dword(hk, TEXT("fast_reauth"),
|
|
&config->fast_reauth);
|
|
wpa_config_read_reg_dword(hk, TEXT("dot11RSNAConfigPMKLifetime"),
|
|
(int *) &config->dot11RSNAConfigPMKLifetime);
|
|
wpa_config_read_reg_dword(hk,
|
|
TEXT("dot11RSNAConfigPMKReauthThreshold"),
|
|
(int *)
|
|
&config->dot11RSNAConfigPMKReauthThreshold);
|
|
wpa_config_read_reg_dword(hk, TEXT("dot11RSNAConfigSATimeout"),
|
|
(int *) &config->dot11RSNAConfigSATimeout);
|
|
wpa_config_read_reg_dword(hk, TEXT("update_config"),
|
|
&config->update_config);
|
|
|
|
if (wpa_config_read_reg_dword(hk, TEXT("eapol_version"),
|
|
&config->eapol_version) == 0) {
|
|
if (config->eapol_version < 1 ||
|
|
config->eapol_version > 2) {
|
|
wpa_printf(MSG_ERROR, "Invalid EAPOL version (%d)",
|
|
config->eapol_version);
|
|
errors++;
|
|
}
|
|
}
|
|
|
|
config->ctrl_interface = wpa_config_read_reg_string(
|
|
hk, TEXT("ctrl_interface"));
|
|
|
|
#ifdef CONFIG_WPS
|
|
if (wpa_config_read_global_uuid(config, hk))
|
|
errors++;
|
|
config->device_name = wpa_config_read_reg_string(
|
|
hk, TEXT("device_name"));
|
|
config->manufacturer = wpa_config_read_reg_string(
|
|
hk, TEXT("manufacturer"));
|
|
config->model_name = wpa_config_read_reg_string(
|
|
hk, TEXT("model_name"));
|
|
config->serial_number = wpa_config_read_reg_string(
|
|
hk, TEXT("serial_number"));
|
|
config->device_type = wpa_config_read_reg_string(
|
|
hk, TEXT("device_type"));
|
|
config->config_methods = wpa_config_read_reg_string(
|
|
hk, TEXT("config_methods"));
|
|
if (wpa_config_read_global_os_version(config, hk))
|
|
errors++;
|
|
wpa_config_read_reg_dword(hk, TEXT("wps_cred_processing"),
|
|
&config->wps_cred_processing);
|
|
#endif /* CONFIG_WPS */
|
|
#ifdef CONFIG_P2P
|
|
config->p2p_ssid_postfix = wpa_config_read_reg_string(
|
|
hk, TEXT("p2p_ssid_postfix"));
|
|
wpa_config_read_reg_dword(hk, TEXT("p2p_group_idle"),
|
|
(int *) &config->p2p_group_idle);
|
|
#endif /* CONFIG_P2P */
|
|
|
|
wpa_config_read_reg_dword(hk, TEXT("bss_max_count"),
|
|
(int *) &config->bss_max_count);
|
|
wpa_config_read_reg_dword(hk, TEXT("filter_ssids"),
|
|
&config->filter_ssids);
|
|
wpa_config_read_reg_dword(hk, TEXT("max_num_sta"),
|
|
(int *) &config->max_num_sta);
|
|
wpa_config_read_reg_dword(hk, TEXT("disassoc_low_ack"),
|
|
(int *) &config->disassoc_low_ack);
|
|
|
|
return errors ? -1 : 0;
|
|
}
|
|
|
|
|
|
static struct wpa_ssid * wpa_config_read_network(HKEY hk, const TCHAR *netw,
|
|
int id)
|
|
{
|
|
HKEY nhk;
|
|
LONG ret;
|
|
DWORD i;
|
|
struct wpa_ssid *ssid;
|
|
int errors = 0;
|
|
|
|
ret = RegOpenKeyEx(hk, netw, 0, KEY_QUERY_VALUE, &nhk);
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_DEBUG, "Could not open wpa_supplicant config "
|
|
"network '" TSTR "'", netw);
|
|
return NULL;
|
|
}
|
|
|
|
wpa_printf(MSG_MSGDUMP, "Start of a new network '" TSTR "'", netw);
|
|
ssid = os_zalloc(sizeof(*ssid));
|
|
if (ssid == NULL) {
|
|
RegCloseKey(nhk);
|
|
return NULL;
|
|
}
|
|
ssid->id = id;
|
|
|
|
wpa_config_set_network_defaults(ssid);
|
|
|
|
for (i = 0; ; i++) {
|
|
TCHAR name[255], data[1024];
|
|
DWORD namelen, datalen, type;
|
|
|
|
namelen = 255;
|
|
datalen = sizeof(data);
|
|
ret = RegEnumValue(nhk, i, name, &namelen, NULL, &type,
|
|
(LPBYTE) data, &datalen);
|
|
|
|
if (ret == ERROR_NO_MORE_ITEMS)
|
|
break;
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_ERROR, "RegEnumValue failed: 0x%x",
|
|
(unsigned int) ret);
|
|
break;
|
|
}
|
|
|
|
if (namelen >= 255)
|
|
namelen = 255 - 1;
|
|
name[namelen] = TEXT('\0');
|
|
|
|
if (datalen >= 1024)
|
|
datalen = 1024 - 1;
|
|
data[datalen] = TEXT('\0');
|
|
|
|
wpa_unicode2ascii_inplace(name);
|
|
wpa_unicode2ascii_inplace(data);
|
|
if (wpa_config_set(ssid, (char *) name, (char *) data, 0) < 0)
|
|
errors++;
|
|
}
|
|
|
|
RegCloseKey(nhk);
|
|
|
|
if (ssid->passphrase) {
|
|
if (ssid->psk_set) {
|
|
wpa_printf(MSG_ERROR, "Both PSK and passphrase "
|
|
"configured for network '" TSTR "'.", netw);
|
|
errors++;
|
|
}
|
|
wpa_config_update_psk(ssid);
|
|
}
|
|
|
|
if ((ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK |
|
|
WPA_KEY_MGMT_PSK_SHA256)) &&
|
|
!ssid->psk_set) {
|
|
wpa_printf(MSG_ERROR, "WPA-PSK accepted for key management, "
|
|
"but no PSK configured for network '" TSTR "'.",
|
|
netw);
|
|
errors++;
|
|
}
|
|
|
|
if ((ssid->group_cipher & WPA_CIPHER_CCMP) &&
|
|
!(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
|
|
!(ssid->pairwise_cipher & WPA_CIPHER_NONE)) {
|
|
/* Group cipher cannot be stronger than the pairwise cipher. */
|
|
wpa_printf(MSG_DEBUG, "Removed CCMP from group cipher "
|
|
"list since it was not allowed for pairwise "
|
|
"cipher for network '" TSTR "'.", netw);
|
|
ssid->group_cipher &= ~WPA_CIPHER_CCMP;
|
|
}
|
|
|
|
if (errors) {
|
|
wpa_config_free_ssid(ssid);
|
|
ssid = NULL;
|
|
}
|
|
|
|
return ssid;
|
|
}
|
|
|
|
|
|
static int wpa_config_read_networks(struct wpa_config *config, HKEY hk)
|
|
{
|
|
HKEY nhk;
|
|
struct wpa_ssid *ssid, *tail = NULL, *head = NULL;
|
|
int errors = 0;
|
|
LONG ret;
|
|
DWORD i;
|
|
|
|
ret = RegOpenKeyEx(hk, TEXT("networks"), 0, KEY_ENUMERATE_SUB_KEYS,
|
|
&nhk);
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_ERROR, "Could not open wpa_supplicant networks "
|
|
"registry key");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; ; i++) {
|
|
TCHAR name[255];
|
|
DWORD namelen;
|
|
|
|
namelen = 255;
|
|
ret = RegEnumKeyEx(nhk, i, name, &namelen, NULL, NULL, NULL,
|
|
NULL);
|
|
|
|
if (ret == ERROR_NO_MORE_ITEMS)
|
|
break;
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_DEBUG, "RegEnumKeyEx failed: 0x%x",
|
|
(unsigned int) ret);
|
|
break;
|
|
}
|
|
|
|
if (namelen >= 255)
|
|
namelen = 255 - 1;
|
|
name[namelen] = '\0';
|
|
|
|
ssid = wpa_config_read_network(nhk, name, i);
|
|
if (ssid == NULL) {
|
|
wpa_printf(MSG_ERROR, "Failed to parse network "
|
|
"profile '%s'.", name);
|
|
errors++;
|
|
continue;
|
|
}
|
|
if (head == NULL) {
|
|
head = tail = ssid;
|
|
} else {
|
|
tail->next = ssid;
|
|
tail = ssid;
|
|
}
|
|
if (wpa_config_add_prio_network(config, ssid)) {
|
|
wpa_printf(MSG_ERROR, "Failed to add network profile "
|
|
"'%s' to priority list.", name);
|
|
errors++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(nhk);
|
|
|
|
config->ssid = head;
|
|
|
|
return errors ? -1 : 0;
|
|
}
|
|
|
|
|
|
struct wpa_config * wpa_config_read(const char *name)
|
|
{
|
|
TCHAR buf[256];
|
|
int errors = 0;
|
|
struct wpa_config *config;
|
|
HKEY hk;
|
|
LONG ret;
|
|
|
|
config = wpa_config_alloc_empty(NULL, NULL);
|
|
if (config == NULL)
|
|
return NULL;
|
|
wpa_printf(MSG_DEBUG, "Reading configuration profile '%s'", name);
|
|
|
|
#ifdef UNICODE
|
|
_snwprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%S"), name);
|
|
#else /* UNICODE */
|
|
os_snprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%s"), name);
|
|
#endif /* UNICODE */
|
|
|
|
ret = RegOpenKeyEx(WPA_KEY_ROOT, buf, 0, KEY_QUERY_VALUE, &hk);
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_ERROR, "Could not open wpa_supplicant "
|
|
"configuration registry HKLM\\" TSTR, buf);
|
|
os_free(config);
|
|
return NULL;
|
|
}
|
|
|
|
if (wpa_config_read_global(config, hk))
|
|
errors++;
|
|
|
|
if (wpa_config_read_networks(config, hk))
|
|
errors++;
|
|
|
|
if (wpa_config_read_blobs(config, hk))
|
|
errors++;
|
|
|
|
wpa_config_debug_dump_networks(config);
|
|
|
|
RegCloseKey(hk);
|
|
|
|
if (errors) {
|
|
wpa_config_free(config);
|
|
config = NULL;
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
|
|
static int wpa_config_write_reg_dword(HKEY hk, const TCHAR *name, int val,
|
|
int def)
|
|
{
|
|
LONG ret;
|
|
DWORD _val = val;
|
|
|
|
if (val == def) {
|
|
RegDeleteValue(hk, name);
|
|
return 0;
|
|
}
|
|
|
|
ret = RegSetValueEx(hk, name, 0, REG_DWORD, (LPBYTE) &_val,
|
|
sizeof(_val));
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_ERROR, "WINREG: Failed to set %s=%d: error %d",
|
|
name, val, (int) GetLastError());
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_config_write_reg_string(HKEY hk, const char *name,
|
|
const char *val)
|
|
{
|
|
LONG ret;
|
|
TCHAR *_name, *_val;
|
|
|
|
_name = wpa_strdup_tchar(name);
|
|
if (_name == NULL)
|
|
return -1;
|
|
|
|
if (val == NULL) {
|
|
RegDeleteValue(hk, _name);
|
|
os_free(_name);
|
|
return 0;
|
|
}
|
|
|
|
_val = wpa_strdup_tchar(val);
|
|
if (_val == NULL) {
|
|
os_free(_name);
|
|
return -1;
|
|
}
|
|
ret = RegSetValueEx(hk, _name, 0, REG_SZ, (BYTE *) _val,
|
|
(os_strlen(val) + 1) * sizeof(TCHAR));
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_ERROR, "WINREG: Failed to set %s='%s': "
|
|
"error %d", name, val, (int) GetLastError());
|
|
os_free(_name);
|
|
os_free(_val);
|
|
return -1;
|
|
}
|
|
|
|
os_free(_name);
|
|
os_free(_val);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_config_write_global(struct wpa_config *config, HKEY hk)
|
|
{
|
|
#ifdef CONFIG_CTRL_IFACE
|
|
wpa_config_write_reg_string(hk, "ctrl_interface",
|
|
config->ctrl_interface);
|
|
#endif /* CONFIG_CTRL_IFACE */
|
|
|
|
wpa_config_write_reg_dword(hk, TEXT("eapol_version"),
|
|
config->eapol_version,
|
|
DEFAULT_EAPOL_VERSION);
|
|
wpa_config_write_reg_dword(hk, TEXT("ap_scan"), config->ap_scan,
|
|
DEFAULT_AP_SCAN);
|
|
wpa_config_write_reg_dword(hk, TEXT("fast_reauth"),
|
|
config->fast_reauth, DEFAULT_FAST_REAUTH);
|
|
wpa_config_write_reg_dword(hk, TEXT("dot11RSNAConfigPMKLifetime"),
|
|
config->dot11RSNAConfigPMKLifetime, 0);
|
|
wpa_config_write_reg_dword(hk,
|
|
TEXT("dot11RSNAConfigPMKReauthThreshold"),
|
|
config->dot11RSNAConfigPMKReauthThreshold,
|
|
0);
|
|
wpa_config_write_reg_dword(hk, TEXT("dot11RSNAConfigSATimeout"),
|
|
config->dot11RSNAConfigSATimeout, 0);
|
|
wpa_config_write_reg_dword(hk, TEXT("update_config"),
|
|
config->update_config,
|
|
0);
|
|
#ifdef CONFIG_WPS
|
|
if (!is_nil_uuid(config->uuid)) {
|
|
char buf[40];
|
|
uuid_bin2str(config->uuid, buf, sizeof(buf));
|
|
wpa_config_write_reg_string(hk, "uuid", buf);
|
|
}
|
|
wpa_config_write_reg_string(hk, "device_name", config->device_name);
|
|
wpa_config_write_reg_string(hk, "manufacturer", config->manufacturer);
|
|
wpa_config_write_reg_string(hk, "model_name", config->model_name);
|
|
wpa_config_write_reg_string(hk, "model_number", config->model_number);
|
|
wpa_config_write_reg_string(hk, "serial_number",
|
|
config->serial_number);
|
|
wpa_config_write_reg_string(hk, "device_type", config->device_type);
|
|
wpa_config_write_reg_string(hk, "config_methods",
|
|
config->config_methods);
|
|
if (WPA_GET_BE32(config->os_version)) {
|
|
char vbuf[10];
|
|
os_snprintf(vbuf, sizeof(vbuf), "%08x",
|
|
WPA_GET_BE32(config->os_version));
|
|
wpa_config_write_reg_string(hk, "os_version", vbuf);
|
|
}
|
|
wpa_config_write_reg_dword(hk, TEXT("wps_cred_processing"),
|
|
config->wps_cred_processing, 0);
|
|
#endif /* CONFIG_WPS */
|
|
#ifdef CONFIG_P2P
|
|
wpa_config_write_reg_string(hk, "p2p_ssid_postfix",
|
|
config->p2p_ssid_postfix);
|
|
wpa_config_write_reg_dword(hk, TEXT("p2p_group_idle"),
|
|
config->p2p_group_idle, 0);
|
|
#endif /* CONFIG_P2P */
|
|
|
|
wpa_config_write_reg_dword(hk, TEXT("bss_max_count"),
|
|
config->bss_max_count,
|
|
DEFAULT_BSS_MAX_COUNT);
|
|
wpa_config_write_reg_dword(hk, TEXT("filter_ssids"),
|
|
config->filter_ssids, 0);
|
|
wpa_config_write_reg_dword(hk, TEXT("max_num_sta"),
|
|
config->max_num_sta, DEFAULT_MAX_NUM_STA);
|
|
wpa_config_write_reg_dword(hk, TEXT("disassoc_low_ack"),
|
|
config->disassoc_low_ack, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_config_delete_subkeys(HKEY hk, const TCHAR *key)
|
|
{
|
|
HKEY nhk;
|
|
int i, errors = 0;
|
|
LONG ret;
|
|
|
|
ret = RegOpenKeyEx(hk, key, 0, KEY_ENUMERATE_SUB_KEYS | DELETE, &nhk);
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_DEBUG, "WINREG: Could not open key '" TSTR
|
|
"' for subkey deletion: error 0x%x (%d)", key,
|
|
(unsigned int) ret, (int) GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; ; i++) {
|
|
TCHAR name[255];
|
|
DWORD namelen;
|
|
|
|
namelen = 255;
|
|
ret = RegEnumKeyEx(nhk, i, name, &namelen, NULL, NULL, NULL,
|
|
NULL);
|
|
|
|
if (ret == ERROR_NO_MORE_ITEMS)
|
|
break;
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_DEBUG, "RegEnumKeyEx failed: 0x%x (%d)",
|
|
(unsigned int) ret, (int) GetLastError());
|
|
break;
|
|
}
|
|
|
|
if (namelen >= 255)
|
|
namelen = 255 - 1;
|
|
name[namelen] = TEXT('\0');
|
|
|
|
ret = RegDeleteKey(nhk, name);
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_DEBUG, "RegDeleteKey failed: 0x%x (%d)",
|
|
(unsigned int) ret, (int) GetLastError());
|
|
errors++;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(nhk);
|
|
|
|
return errors ? -1 : 0;
|
|
}
|
|
|
|
|
|
static void write_str(HKEY hk, const char *field, struct wpa_ssid *ssid)
|
|
{
|
|
char *value = wpa_config_get(ssid, field);
|
|
if (value == NULL)
|
|
return;
|
|
wpa_config_write_reg_string(hk, field, value);
|
|
os_free(value);
|
|
}
|
|
|
|
|
|
static void write_int(HKEY hk, const char *field, int value, int def)
|
|
{
|
|
char val[20];
|
|
if (value == def)
|
|
return;
|
|
os_snprintf(val, sizeof(val), "%d", value);
|
|
wpa_config_write_reg_string(hk, field, val);
|
|
}
|
|
|
|
|
|
static void write_bssid(HKEY hk, struct wpa_ssid *ssid)
|
|
{
|
|
char *value = wpa_config_get(ssid, "bssid");
|
|
if (value == NULL)
|
|
return;
|
|
wpa_config_write_reg_string(hk, "bssid", value);
|
|
os_free(value);
|
|
}
|
|
|
|
|
|
static void write_psk(HKEY hk, struct wpa_ssid *ssid)
|
|
{
|
|
char *value = wpa_config_get(ssid, "psk");
|
|
if (value == NULL)
|
|
return;
|
|
wpa_config_write_reg_string(hk, "psk", value);
|
|
os_free(value);
|
|
}
|
|
|
|
|
|
static void write_proto(HKEY hk, struct wpa_ssid *ssid)
|
|
{
|
|
char *value;
|
|
|
|
if (ssid->proto == DEFAULT_PROTO)
|
|
return;
|
|
|
|
value = wpa_config_get(ssid, "proto");
|
|
if (value == NULL)
|
|
return;
|
|
if (value[0])
|
|
wpa_config_write_reg_string(hk, "proto", value);
|
|
os_free(value);
|
|
}
|
|
|
|
|
|
static void write_key_mgmt(HKEY hk, struct wpa_ssid *ssid)
|
|
{
|
|
char *value;
|
|
|
|
if (ssid->key_mgmt == DEFAULT_KEY_MGMT)
|
|
return;
|
|
|
|
value = wpa_config_get(ssid, "key_mgmt");
|
|
if (value == NULL)
|
|
return;
|
|
if (value[0])
|
|
wpa_config_write_reg_string(hk, "key_mgmt", value);
|
|
os_free(value);
|
|
}
|
|
|
|
|
|
static void write_pairwise(HKEY hk, struct wpa_ssid *ssid)
|
|
{
|
|
char *value;
|
|
|
|
if (ssid->pairwise_cipher == DEFAULT_PAIRWISE)
|
|
return;
|
|
|
|
value = wpa_config_get(ssid, "pairwise");
|
|
if (value == NULL)
|
|
return;
|
|
if (value[0])
|
|
wpa_config_write_reg_string(hk, "pairwise", value);
|
|
os_free(value);
|
|
}
|
|
|
|
|
|
static void write_group(HKEY hk, struct wpa_ssid *ssid)
|
|
{
|
|
char *value;
|
|
|
|
if (ssid->group_cipher == DEFAULT_GROUP)
|
|
return;
|
|
|
|
value = wpa_config_get(ssid, "group");
|
|
if (value == NULL)
|
|
return;
|
|
if (value[0])
|
|
wpa_config_write_reg_string(hk, "group", value);
|
|
os_free(value);
|
|
}
|
|
|
|
|
|
static void write_auth_alg(HKEY hk, struct wpa_ssid *ssid)
|
|
{
|
|
char *value;
|
|
|
|
if (ssid->auth_alg == 0)
|
|
return;
|
|
|
|
value = wpa_config_get(ssid, "auth_alg");
|
|
if (value == NULL)
|
|
return;
|
|
if (value[0])
|
|
wpa_config_write_reg_string(hk, "auth_alg", value);
|
|
os_free(value);
|
|
}
|
|
|
|
|
|
#ifdef IEEE8021X_EAPOL
|
|
static void write_eap(HKEY hk, struct wpa_ssid *ssid)
|
|
{
|
|
char *value;
|
|
|
|
value = wpa_config_get(ssid, "eap");
|
|
if (value == NULL)
|
|
return;
|
|
|
|
if (value[0])
|
|
wpa_config_write_reg_string(hk, "eap", value);
|
|
os_free(value);
|
|
}
|
|
#endif /* IEEE8021X_EAPOL */
|
|
|
|
|
|
static void write_wep_key(HKEY hk, int idx, struct wpa_ssid *ssid)
|
|
{
|
|
char field[20], *value;
|
|
|
|
os_snprintf(field, sizeof(field), "wep_key%d", idx);
|
|
value = wpa_config_get(ssid, field);
|
|
if (value) {
|
|
wpa_config_write_reg_string(hk, field, value);
|
|
os_free(value);
|
|
}
|
|
}
|
|
|
|
|
|
static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id)
|
|
{
|
|
int i, errors = 0;
|
|
HKEY nhk, netw;
|
|
LONG ret;
|
|
TCHAR name[5];
|
|
|
|
ret = RegOpenKeyEx(hk, TEXT("networks"), 0, KEY_CREATE_SUB_KEY, &nhk);
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_DEBUG, "WINREG: Could not open networks key "
|
|
"for subkey addition: error 0x%x (%d)",
|
|
(unsigned int) ret, (int) GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
wsprintf(name, L"%04d", id);
|
|
#else /* UNICODE */
|
|
os_snprintf(name, sizeof(name), "%04d", id);
|
|
#endif /* UNICODE */
|
|
ret = RegCreateKeyEx(nhk, name, 0, NULL, 0, KEY_WRITE, NULL, &netw,
|
|
NULL);
|
|
RegCloseKey(nhk);
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_DEBUG, "WINREG: Could not add network key '%s':"
|
|
" error 0x%x (%d)",
|
|
name, (unsigned int) ret, (int) GetLastError());
|
|
return -1;
|
|
}
|
|
|
|
#define STR(t) write_str(netw, #t, ssid)
|
|
#define INT(t) write_int(netw, #t, ssid->t, 0)
|
|
#define INTe(t) write_int(netw, #t, ssid->eap.t, 0)
|
|
#define INT_DEF(t, def) write_int(netw, #t, ssid->t, def)
|
|
#define INT_DEFe(t, def) write_int(netw, #t, ssid->eap.t, def)
|
|
|
|
STR(ssid);
|
|
INT(scan_ssid);
|
|
write_bssid(netw, ssid);
|
|
write_psk(netw, ssid);
|
|
write_proto(netw, ssid);
|
|
write_key_mgmt(netw, ssid);
|
|
write_pairwise(netw, ssid);
|
|
write_group(netw, ssid);
|
|
write_auth_alg(netw, ssid);
|
|
#ifdef IEEE8021X_EAPOL
|
|
write_eap(netw, ssid);
|
|
STR(identity);
|
|
STR(anonymous_identity);
|
|
STR(password);
|
|
STR(ca_cert);
|
|
STR(ca_path);
|
|
STR(client_cert);
|
|
STR(private_key);
|
|
STR(private_key_passwd);
|
|
STR(dh_file);
|
|
STR(subject_match);
|
|
STR(altsubject_match);
|
|
STR(ca_cert2);
|
|
STR(ca_path2);
|
|
STR(client_cert2);
|
|
STR(private_key2);
|
|
STR(private_key2_passwd);
|
|
STR(dh_file2);
|
|
STR(subject_match2);
|
|
STR(altsubject_match2);
|
|
STR(phase1);
|
|
STR(phase2);
|
|
STR(pcsc);
|
|
STR(pin);
|
|
STR(engine_id);
|
|
STR(key_id);
|
|
STR(cert_id);
|
|
STR(ca_cert_id);
|
|
STR(key2_id);
|
|
STR(pin2);
|
|
STR(engine2_id);
|
|
STR(cert2_id);
|
|
STR(ca_cert2_id);
|
|
INTe(engine);
|
|
INTe(engine2);
|
|
INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS);
|
|
#endif /* IEEE8021X_EAPOL */
|
|
for (i = 0; i < 4; i++)
|
|
write_wep_key(netw, i, ssid);
|
|
INT(wep_tx_keyidx);
|
|
INT(priority);
|
|
#ifdef IEEE8021X_EAPOL
|
|
INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND);
|
|
STR(pac_file);
|
|
INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE);
|
|
#endif /* IEEE8021X_EAPOL */
|
|
INT(mode);
|
|
INT(proactive_key_caching);
|
|
INT(disabled);
|
|
INT(peerkey);
|
|
#ifdef CONFIG_IEEE80211W
|
|
INT(ieee80211w);
|
|
#endif /* CONFIG_IEEE80211W */
|
|
STR(id_str);
|
|
|
|
#undef STR
|
|
#undef INT
|
|
#undef INT_DEF
|
|
|
|
RegCloseKey(netw);
|
|
|
|
return errors ? -1 : 0;
|
|
}
|
|
|
|
|
|
static int wpa_config_write_blob(HKEY hk, struct wpa_config_blob *blob)
|
|
{
|
|
HKEY bhk;
|
|
LONG ret;
|
|
TCHAR *name;
|
|
|
|
ret = RegCreateKeyEx(hk, TEXT("blobs"), 0, NULL, 0, KEY_WRITE, NULL,
|
|
&bhk, NULL);
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_DEBUG, "WINREG: Could not add blobs key: "
|
|
"error 0x%x (%d)",
|
|
(unsigned int) ret, (int) GetLastError());
|
|
return -1;
|
|
}
|
|
|
|
name = wpa_strdup_tchar(blob->name);
|
|
ret = RegSetValueEx(bhk, name, 0, REG_BINARY, blob->data,
|
|
blob->len);
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_ERROR, "WINREG: Failed to set blob %s': "
|
|
"error 0x%x (%d)", blob->name, (unsigned int) ret,
|
|
(int) GetLastError());
|
|
RegCloseKey(bhk);
|
|
os_free(name);
|
|
return -1;
|
|
}
|
|
os_free(name);
|
|
|
|
RegCloseKey(bhk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int wpa_config_write(const char *name, struct wpa_config *config)
|
|
{
|
|
TCHAR buf[256];
|
|
HKEY hk;
|
|
LONG ret;
|
|
int errors = 0;
|
|
struct wpa_ssid *ssid;
|
|
struct wpa_config_blob *blob;
|
|
int id;
|
|
|
|
wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name);
|
|
|
|
#ifdef UNICODE
|
|
_snwprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%S"), name);
|
|
#else /* UNICODE */
|
|
os_snprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%s"), name);
|
|
#endif /* UNICODE */
|
|
|
|
ret = RegOpenKeyEx(WPA_KEY_ROOT, buf, 0, KEY_SET_VALUE | DELETE, &hk);
|
|
if (ret != ERROR_SUCCESS) {
|
|
wpa_printf(MSG_ERROR, "Could not open wpa_supplicant "
|
|
"configuration registry %s: error %d", buf,
|
|
(int) GetLastError());
|
|
return -1;
|
|
}
|
|
|
|
if (wpa_config_write_global(config, hk)) {
|
|
wpa_printf(MSG_ERROR, "Failed to write global configuration "
|
|
"data");
|
|
errors++;
|
|
}
|
|
|
|
wpa_config_delete_subkeys(hk, TEXT("networks"));
|
|
for (ssid = config->ssid, id = 0; ssid; ssid = ssid->next, id++) {
|
|
if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
|
|
continue; /* do not save temporary WPS networks */
|
|
if (wpa_config_write_network(hk, ssid, id))
|
|
errors++;
|
|
}
|
|
|
|
RegDeleteKey(hk, TEXT("blobs"));
|
|
for (blob = config->blobs; blob; blob = blob->next) {
|
|
if (wpa_config_write_blob(hk, blob))
|
|
errors++;
|
|
}
|
|
|
|
RegCloseKey(hk);
|
|
|
|
wpa_printf(MSG_DEBUG, "Configuration '%s' written %ssuccessfully",
|
|
name, errors ? "un" : "");
|
|
return errors ? -1 : 0;
|
|
}
|