Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
4946 lines (4459 sloc)
138 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* hostapd / Configuration file parser | |
* Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> | |
* | |
* This software may be distributed under the terms of the BSD license. | |
* See README for more details. | |
*/ | |
#include "utils/includes.h" | |
#ifndef CONFIG_NATIVE_WINDOWS | |
#include <grp.h> | |
#endif /* CONFIG_NATIVE_WINDOWS */ | |
#include "utils/common.h" | |
#include "utils/uuid.h" | |
#include "common/ieee802_11_defs.h" | |
#include "crypto/sha256.h" | |
#include "crypto/tls.h" | |
#include "drivers/driver.h" | |
#include "eap_server/eap.h" | |
#include "radius/radius_client.h" | |
#include "ap/wpa_auth.h" | |
#include "ap/ap_config.h" | |
#include "config_file.h" | |
#ifdef EAPHAMMER | |
#include "eaphammer_wpe/eaphammer_wpe.h" | |
#include "ap/eh_ssid_table_t.h" | |
#endif | |
#ifndef CONFIG_NO_RADIUS | |
#ifdef EAP_SERVER | |
static struct hostapd_radius_attr * | |
hostapd_parse_radius_attr(const char *value); | |
#endif /* EAP_SERVER */ | |
#endif /* CONFIG_NO_RADIUS */ | |
#ifndef CONFIG_NO_VLAN | |
static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, | |
const char *fname) | |
{ | |
FILE *f; | |
char buf[128], *pos, *pos2, *pos3; | |
int line = 0, vlan_id; | |
struct hostapd_vlan *vlan; | |
f = fopen(fname, "r"); | |
if (!f) { | |
wpa_printf(MSG_ERROR, "VLAN file '%s' not readable.", fname); | |
return -1; | |
} | |
while (fgets(buf, sizeof(buf), f)) { | |
line++; | |
if (buf[0] == '#') | |
continue; | |
pos = buf; | |
while (*pos != '\0') { | |
if (*pos == '\n') { | |
*pos = '\0'; | |
break; | |
} | |
pos++; | |
} | |
if (buf[0] == '\0') | |
continue; | |
if (buf[0] == '*') { | |
vlan_id = VLAN_ID_WILDCARD; | |
pos = buf + 1; | |
} else { | |
vlan_id = strtol(buf, &pos, 10); | |
if (buf == pos || vlan_id < 1 || | |
vlan_id > MAX_VLAN_ID) { | |
wpa_printf(MSG_ERROR, "Invalid VLAN ID at " | |
"line %d in '%s'", line, fname); | |
fclose(f); | |
return -1; | |
} | |
} | |
while (*pos == ' ' || *pos == '\t') | |
pos++; | |
pos2 = pos; | |
while (*pos2 != ' ' && *pos2 != '\t' && *pos2 != '\0') | |
pos2++; | |
if (*pos2 != '\0') | |
*(pos2++) = '\0'; | |
if (*pos == '\0' || os_strlen(pos) > IFNAMSIZ) { | |
wpa_printf(MSG_ERROR, "Invalid VLAN ifname at line %d " | |
"in '%s'", line, fname); | |
fclose(f); | |
return -1; | |
} | |
while (*pos2 == ' ' || *pos2 == '\t') | |
pos2++; | |
pos3 = pos2; | |
while (*pos3 != ' ' && *pos3 != '\t' && *pos3 != '\0') | |
pos3++; | |
*pos3 = '\0'; | |
vlan = os_zalloc(sizeof(*vlan)); | |
if (vlan == NULL) { | |
wpa_printf(MSG_ERROR, "Out of memory while reading " | |
"VLAN interfaces from '%s'", fname); | |
fclose(f); | |
return -1; | |
} | |
vlan->vlan_id = vlan_id; | |
vlan->vlan_desc.untagged = vlan_id; | |
vlan->vlan_desc.notempty = !!vlan_id; | |
os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname)); | |
os_strlcpy(vlan->bridge, pos2, sizeof(vlan->bridge)); | |
vlan->next = bss->vlan; | |
bss->vlan = vlan; | |
} | |
fclose(f); | |
return 0; | |
} | |
#endif /* CONFIG_NO_VLAN */ | |
int hostapd_acl_comp(const void *a, const void *b) | |
{ | |
const struct mac_acl_entry *aa = a; | |
const struct mac_acl_entry *bb = b; | |
return os_memcmp(aa->addr, bb->addr, sizeof(macaddr)); | |
} | |
#ifdef EAPHAMMER | |
int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num, | |
int vlan_id, const u8 *addr, const u8 *mask) | |
#else | |
int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num, | |
int vlan_id, const u8 *addr) | |
#endif | |
{ | |
struct mac_acl_entry *newacl; | |
newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl)); | |
if (!newacl) { | |
wpa_printf(MSG_ERROR, "MAC list reallocation failed"); | |
return -1; | |
} | |
*acl = newacl; | |
os_memcpy((*acl)[*num].addr, addr, ETH_ALEN); | |
os_memcpy((*acl)[*num].mask, mask, ETH_ALEN); | |
os_memset(&(*acl)[*num].vlan_id, 0, sizeof((*acl)[*num].vlan_id)); | |
(*acl)[*num].vlan_id.untagged = vlan_id; | |
(*acl)[*num].vlan_id.notempty = !!vlan_id; | |
(*num)++; | |
return 0; | |
} | |
void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num, | |
const u8 *addr) | |
{ | |
int i = 0; | |
while (i < *num) { | |
if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) { | |
os_remove_in_array(*acl, *num, sizeof(**acl), i); | |
(*num)--; | |
} else { | |
i++; | |
} | |
} | |
} | |
#ifdef EAPHAMMER | |
static int hostapd_config_read_ssidlist(const char *fname, | |
struct ssid_acl_node **ssid_acl, | |
size_t *ssid_acl_len) { | |
FILE *f; | |
char buf[128]; | |
char *pos; | |
int line = 0; | |
struct ssid_acl_node *next_ssid_acl; | |
f = fopen(fname, "r"); | |
if (!f) { | |
wpa_printf(MSG_DEBUG, "[eaphammer] SSID list file '%s' not found.", fname); | |
return -1; | |
} | |
bzero(buf, 128); | |
while (fgets(buf, sizeof(buf), f)) { | |
// increment linecount by 1 | |
line++; | |
// if the line is a comment, skip it | |
if (buf[0] == '#') { | |
continue; | |
} | |
// replace the ending newline with a null terminator | |
pos = buf; | |
while (*pos != '\0') { | |
if (*pos == '\n') { | |
*pos = '\0'; | |
break; | |
} | |
pos++; | |
} | |
// if line is blank, skip | |
if (buf[0] == '\0') { | |
continue; | |
} | |
pos = buf; | |
if (os_strlen(pos) > SSID_MAX_LEN) { | |
wpa_printf(MSG_DEBUG, "[eaphammer] On line %d of %s, length of SSID %s exceeds max length of %d. Skipping.\n", line, fname, pos, SSID_MAX_LEN); | |
continue; | |
} | |
next_ssid_acl = os_realloc_array(*ssid_acl, *ssid_acl_len+1, sizeof(**ssid_acl)); | |
if ( !next_ssid_acl ) { | |
wpa_printf(MSG_DEBUG, "[eaphammer] Unable to reallocate SSID filter. Aborting."); | |
fclose(f); | |
return -1; | |
} | |
*ssid_acl = next_ssid_acl; | |
os_memcpy( (*ssid_acl)[*ssid_acl_len].text, pos, strnlen(pos, SSID_MAX_LEN)); | |
(*ssid_acl_len)++; | |
wpa_printf(MSG_DEBUG, "[eaphammer] Successfully added SSID %s to ACL.\n", pos); | |
bzero(buf, 128); | |
} | |
fclose(f); | |
return 0; | |
} | |
#endif | |
static int hostapd_config_read_maclist(const char *fname, | |
struct mac_acl_entry **acl, int *num) | |
{ | |
FILE *f; | |
char buf[128], *pos; | |
int line = 0; | |
u8 addr[ETH_ALEN]; | |
int vlan_id; | |
char addr_str[18]; // 18 == length of ascii mac representation + null terminator | |
char mask_str[18]; // 18 == length of ascii mac representation + null terminator | |
int addr_str_index; | |
int mask_str_index; | |
u8 mask[ETH_ALEN]; | |
int i; | |
// open the file and bail if not found | |
f = fopen(fname, "r"); | |
if (!f) { | |
wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", fname); | |
return -1; | |
} | |
// assume we aren't dealing with wildcards | |
eaphammer_global_conf.acl_has_wildcards = 0; | |
while (fgets(buf, sizeof(buf), f)) { | |
// assume that we're not removing this entry | |
int rem = 0; | |
// increment linecount by 1 | |
line++; | |
// if the line is a comment, skip it | |
if (buf[0] == '#') | |
continue; | |
// replace newline with null terminator | |
pos = buf; | |
while (*pos != '\0') { | |
if (*pos == '\n') { | |
*pos = '\0'; | |
break; | |
} | |
pos++; | |
} | |
// if line is blank, skip | |
if (buf[0] == '\0') | |
continue; | |
// if the line starts with '-', then flag for removal and increment pos to point at | |
// second character in line | |
pos = buf; | |
if (buf[0] == '-') { | |
rem = 1; | |
pos++; | |
} | |
// START wildcard handling code | |
// increment to the first null terminator or instance of whitespace | |
// (whichever comes first) | |
addr_str[18] = ""; // 18 == length of ascii mac representation + null terminator | |
mask_str[18] = "ff:ff:ff:ff:ff:ff"; // 18 == length of ascii mac representation + null terminator | |
addr_str_index = 0; | |
mask_str_index = 0; | |
while (*pos != '\0' && *pos != ' ' && *pos != '\t') { | |
if (*pos == '*') { | |
eaphammer_global_conf.acl_has_wildcards = 1; | |
addr_str[addr_str_index] = '0'; | |
addr_str_index++; | |
addr_str[addr_str_index] = '0'; | |
addr_str_index++; | |
mask_str[mask_str_index] = '0'; | |
mask_str_index++; | |
mask_str[mask_str_index] = '0'; | |
mask_str_index++; | |
} | |
else if (*pos == ':') { | |
addr_str[addr_str_index] = *pos; | |
addr_str_index++; | |
mask_str[mask_str_index] = *pos; | |
mask_str_index++; | |
} | |
else { | |
addr_str[addr_str_index] = *pos; | |
addr_str_index++; | |
mask_str[mask_str_index] = 'f'; | |
mask_str_index++; | |
} | |
pos++; | |
} | |
addr_str[addr_str_index] = '\0'; | |
mask_str[mask_str_index] = '\0'; | |
// END wildcard handling code | |
// hwaddr_aton converts an ascii string to a 6 byte mac address | |
// returns 0 on success, -1 on failure | |
// | |
// hwaddr_aton calls hwaddr_parse, which only operates on the first 6 bytes of the string | |
// | |
// take address starting at position pointed to by pos (buf[1] if flagged for removal else buf[0]), | |
// convert to 6 byte MAC address, and store in addr. bail if operation fails | |
//if (hwaddr_aton(pos, addr)) { | |
if (hwaddr_aton(addr_str, addr)) { | |
wpa_printf(MSG_ERROR, "Invalid MAC address '%s' at " | |
"line %d in '%s'", pos, line, fname); | |
fclose(f); | |
return -1; | |
} | |
if (hwaddr_aton(mask_str, mask)) { | |
wpa_printf(MSG_ERROR, "Invalid MAC address mask '%s' at " | |
"line %d in '%s'", pos, line, fname); | |
fclose(f); | |
return -1; | |
} | |
// START transformation code | |
for (i = 0; i < ETH_ALEN; addr[i] &= mask[i++]); | |
// END transformation code | |
// if flagged for removal, remove addr from acl | |
if (rem) { | |
hostapd_remove_acl_mac(acl, num, addr); | |
continue; | |
} | |
// assume we're using the default vlan (vlan ID == 0) | |
vlan_id = 0; | |
// point pos to the beginner of buf | |
pos = buf; | |
// increment to the first null terminator or instance of whitespace | |
// (whichever comes first) | |
while (*pos != '\0' && *pos != ' ' && *pos != '\t') | |
pos++; | |
// if pos is pointing to whitespace, keep incrementing pos until it's no longer | |
// pointing to whitespace | |
while (*pos == ' ' || *pos == '\t') | |
pos++; | |
// at this point, pos is going to point to either a vlan ID or a null terminator | |
// (the latter indicates the end of a line). if it's not pointing to a null terminiator | |
// at this point then it must be pointing to a vlan ID, in which case we convert the vlan | |
// ID from an ASCII character to an integer and store it in vlan_id | |
if (*pos != '\0') | |
vlan_id = atoi(pos); | |
printf("addr_str: %s\n", addr_str); | |
printf("mask_str: %s\n", mask_str); | |
printf("vlan_id: %d\n", vlan_id); | |
// attempt to add the MAC address and its vlan ID to our ACL, and bail if this fails | |
//if (hostapd_add_acl_maclist(acl, num, vlan_id, addr, mask) < 0) { | |
if (hostapd_add_acl_maclist(acl, num, vlan_id, addr, mask) < 0) { | |
fclose(f); | |
return -1; | |
} | |
} | |
// close the input handle | |
fclose(f); | |
// our ACL isn't null, sort it | |
if (*acl) | |
qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); | |
// return success | |
return 0; | |
} | |
#ifdef EAP_SERVER | |
static int hostapd_config_eap_user_salted(struct hostapd_eap_user *user, | |
const char *hash, size_t len, | |
char **pos, int line, | |
const char *fname) | |
{ | |
char *pos2 = *pos; | |
while (*pos2 != '\0' && *pos2 != ' ' && *pos2 != '\t' && *pos2 != '#') | |
pos2++; | |
if (pos2 - *pos < (int) (2 * (len + 1))) { /* at least 1 byte of salt */ | |
wpa_printf(MSG_ERROR, | |
"Invalid salted %s hash on line %d in '%s'", | |
hash, line, fname); | |
return -1; | |
} | |
user->password = os_malloc(len); | |
if (!user->password) { | |
wpa_printf(MSG_ERROR, | |
"Failed to allocate memory for salted %s hash", | |
hash); | |
return -1; | |
} | |
if (hexstr2bin(*pos, user->password, len) < 0) { | |
wpa_printf(MSG_ERROR, | |
"Invalid salted password on line %d in '%s'", | |
line, fname); | |
return -1; | |
} | |
user->password_len = len; | |
*pos += 2 * len; | |
user->salt_len = (pos2 - *pos) / 2; | |
user->salt = os_malloc(user->salt_len); | |
if (!user->salt) { | |
wpa_printf(MSG_ERROR, | |
"Failed to allocate memory for salted %s hash", | |
hash); | |
return -1; | |
} | |
if (hexstr2bin(*pos, user->salt, user->salt_len) < 0) { | |
wpa_printf(MSG_ERROR, | |
"Invalid salt for password on line %d in '%s'", | |
line, fname); | |
return -1; | |
} | |
*pos = pos2; | |
return 0; | |
} | |
static int hostapd_config_read_eap_user(const char *fname, | |
struct hostapd_bss_config *conf) | |
{ | |
FILE *f; | |
char buf[512], *pos, *start, *pos2; | |
int line = 0, ret = 0, num_methods; | |
struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL; | |
if (os_strncmp(fname, "sqlite:", 7) == 0) { | |
#ifdef CONFIG_SQLITE | |
os_free(conf->eap_user_sqlite); | |
conf->eap_user_sqlite = os_strdup(fname + 7); | |
return 0; | |
#else /* CONFIG_SQLITE */ | |
wpa_printf(MSG_ERROR, | |
"EAP user file in SQLite DB, but CONFIG_SQLITE was not enabled in the build."); | |
return -1; | |
#endif /* CONFIG_SQLITE */ | |
} | |
f = fopen(fname, "r"); | |
if (!f) { | |
wpa_printf(MSG_ERROR, "EAP user file '%s' not found.", fname); | |
return -1; | |
} | |
/* Lines: "user" METHOD,METHOD2 "password" (password optional) */ | |
while (fgets(buf, sizeof(buf), f)) { | |
line++; | |
if (buf[0] == '#') | |
continue; | |
pos = buf; | |
while (*pos != '\0') { | |
if (*pos == '\n') { | |
*pos = '\0'; | |
break; | |
} | |
pos++; | |
} | |
if (buf[0] == '\0') | |
continue; | |
#ifndef CONFIG_NO_RADIUS | |
if (user && os_strncmp(buf, "radius_accept_attr=", 19) == 0) { | |
struct hostapd_radius_attr *attr, *a; | |
attr = hostapd_parse_radius_attr(buf + 19); | |
if (attr == NULL) { | |
wpa_printf(MSG_ERROR, "Invalid radius_auth_req_attr: %s", | |
buf + 19); | |
user = NULL; /* already in the BSS list */ | |
goto failed; | |
} | |
if (user->accept_attr == NULL) { | |
user->accept_attr = attr; | |
} else { | |
a = user->accept_attr; | |
while (a->next) | |
a = a->next; | |
a->next = attr; | |
} | |
continue; | |
} | |
#endif /* CONFIG_NO_RADIUS */ | |
user = NULL; | |
if (buf[0] != '"' && buf[0] != '*') { | |
wpa_printf(MSG_ERROR, "Invalid EAP identity (no \" in " | |
"start) on line %d in '%s'", line, fname); | |
goto failed; | |
} | |
user = os_zalloc(sizeof(*user)); | |
if (user == NULL) { | |
wpa_printf(MSG_ERROR, "EAP user allocation failed"); | |
goto failed; | |
} | |
user->force_version = -1; | |
if (buf[0] == '*') { | |
pos = buf; | |
} else { | |
pos = buf + 1; | |
start = pos; | |
while (*pos != '"' && *pos != '\0') | |
pos++; | |
if (*pos == '\0') { | |
wpa_printf(MSG_ERROR, "Invalid EAP identity " | |
"(no \" in end) on line %d in '%s'", | |
line, fname); | |
goto failed; | |
} | |
user->identity = os_memdup(start, pos - start); | |
if (user->identity == NULL) { | |
wpa_printf(MSG_ERROR, "Failed to allocate " | |
"memory for EAP identity"); | |
goto failed; | |
} | |
user->identity_len = pos - start; | |
if (pos[0] == '"' && pos[1] == '*') { | |
user->wildcard_prefix = 1; | |
pos++; | |
} | |
} | |
pos++; | |
while (*pos == ' ' || *pos == '\t') | |
pos++; | |
if (*pos == '\0') { | |
wpa_printf(MSG_ERROR, "No EAP method on line %d in " | |
"'%s'", line, fname); | |
goto failed; | |
} | |
start = pos; | |
while (*pos != ' ' && *pos != '\t' && *pos != '\0') | |
pos++; | |
if (*pos == '\0') { | |
pos = NULL; | |
} else { | |
*pos = '\0'; | |
pos++; | |
} | |
num_methods = 0; | |
while (*start) { | |
char *pos3 = os_strchr(start, ','); | |
if (pos3) { | |
*pos3++ = '\0'; | |
} | |
user->methods[num_methods].method = | |
eap_server_get_type( | |
start, | |
&user->methods[num_methods].vendor); | |
if (user->methods[num_methods].vendor == | |
EAP_VENDOR_IETF && | |
user->methods[num_methods].method == EAP_TYPE_NONE) | |
{ | |
if (os_strcmp(start, "TTLS-PAP") == 0) { | |
user->ttls_auth |= EAP_TTLS_AUTH_PAP; | |
goto skip_eap; | |
} | |
if (os_strcmp(start, "TTLS-CHAP") == 0) { | |
user->ttls_auth |= EAP_TTLS_AUTH_CHAP; | |
goto skip_eap; | |
} | |
if (os_strcmp(start, "TTLS-MSCHAP") == 0) { | |
user->ttls_auth |= | |
EAP_TTLS_AUTH_MSCHAP; | |
goto skip_eap; | |
} | |
if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) { | |
user->ttls_auth |= | |
EAP_TTLS_AUTH_MSCHAPV2; | |
goto skip_eap; | |
} | |
if (os_strcmp(start, "MACACL") == 0) { | |
user->macacl = 1; | |
goto skip_eap; | |
} | |
wpa_printf(MSG_ERROR, "Unsupported EAP type " | |
"'%s' on line %d in '%s'", | |
start, line, fname); | |
goto failed; | |
} | |
num_methods++; | |
if (num_methods >= EAP_MAX_METHODS) | |
break; | |
skip_eap: | |
if (pos3 == NULL) | |
break; | |
start = pos3; | |
} | |
if (num_methods == 0 && user->ttls_auth == 0 && !user->macacl) { | |
wpa_printf(MSG_ERROR, "No EAP types configured on " | |
"line %d in '%s'", line, fname); | |
goto failed; | |
} | |
if (pos == NULL) | |
goto done; | |
while (*pos == ' ' || *pos == '\t') | |
pos++; | |
if (*pos == '\0') | |
goto done; | |
if (os_strncmp(pos, "[ver=0]", 7) == 0) { | |
user->force_version = 0; | |
goto done; | |
} | |
if (os_strncmp(pos, "[ver=1]", 7) == 0) { | |
user->force_version = 1; | |
goto done; | |
} | |
if (os_strncmp(pos, "[2]", 3) == 0) { | |
user->phase2 = 1; | |
goto done; | |
} | |
if (*pos == '"') { | |
pos++; | |
start = pos; | |
while (*pos != '"' && *pos != '\0') | |
pos++; | |
if (*pos == '\0') { | |
wpa_printf(MSG_ERROR, "Invalid EAP password " | |
"(no \" in end) on line %d in '%s'", | |
line, fname); | |
goto failed; | |
} | |
user->password = os_memdup(start, pos - start); | |
if (user->password == NULL) { | |
wpa_printf(MSG_ERROR, "Failed to allocate " | |
"memory for EAP password"); | |
goto failed; | |
} | |
user->password_len = pos - start; | |
pos++; | |
} else if (os_strncmp(pos, "hash:", 5) == 0) { | |
pos += 5; | |
pos2 = pos; | |
while (*pos2 != '\0' && *pos2 != ' ' && | |
*pos2 != '\t' && *pos2 != '#') | |
pos2++; | |
if (pos2 - pos != 32) { | |
wpa_printf(MSG_ERROR, "Invalid password hash " | |
"on line %d in '%s'", line, fname); | |
goto failed; | |
} | |
user->password = os_malloc(16); | |
if (user->password == NULL) { | |
wpa_printf(MSG_ERROR, "Failed to allocate " | |
"memory for EAP password hash"); | |
goto failed; | |
} | |
if (hexstr2bin(pos, user->password, 16) < 0) { | |
wpa_printf(MSG_ERROR, "Invalid hash password " | |
"on line %d in '%s'", line, fname); | |
goto failed; | |
} | |
user->password_len = 16; | |
user->password_hash = 1; | |
pos = pos2; | |
} else if (os_strncmp(pos, "ssha1:", 6) == 0) { | |
pos += 6; | |
if (hostapd_config_eap_user_salted(user, "sha1", 20, | |
&pos, | |
line, fname) < 0) | |
goto failed; | |
} else if (os_strncmp(pos, "ssha256:", 8) == 0) { | |
pos += 8; | |
if (hostapd_config_eap_user_salted(user, "sha256", 32, | |
&pos, | |
line, fname) < 0) | |
goto failed; | |
} else if (os_strncmp(pos, "ssha512:", 8) == 0) { | |
pos += 8; | |
if (hostapd_config_eap_user_salted(user, "sha512", 64, | |
&pos, | |
line, fname) < 0) | |
goto failed; | |
} else { | |
pos2 = pos; | |
while (*pos2 != '\0' && *pos2 != ' ' && | |
*pos2 != '\t' && *pos2 != '#') | |
pos2++; | |
if ((pos2 - pos) & 1) { | |
wpa_printf(MSG_ERROR, "Invalid hex password " | |
"on line %d in '%s'", line, fname); | |
goto failed; | |
} | |
user->password = os_malloc((pos2 - pos) / 2); | |
if (user->password == NULL) { | |
wpa_printf(MSG_ERROR, "Failed to allocate " | |
"memory for EAP password"); | |
goto failed; | |
} | |
if (hexstr2bin(pos, user->password, | |
(pos2 - pos) / 2) < 0) { | |
wpa_printf(MSG_ERROR, "Invalid hex password " | |
"on line %d in '%s'", line, fname); | |
goto failed; | |
} | |
user->password_len = (pos2 - pos) / 2; | |
pos = pos2; | |
} | |
while (*pos == ' ' || *pos == '\t') | |
pos++; | |
if (os_strncmp(pos, "[2]", 3) == 0) { | |
user->phase2 = 1; | |
} | |
done: | |
if (tail == NULL) { | |
tail = new_user = user; | |
} else { | |
tail->next = user; | |
tail = user; | |
} | |
continue; | |
failed: | |
if (user) | |
hostapd_config_free_eap_user(user); | |
ret = -1; | |
break; | |
} | |
fclose(f); | |
if (ret == 0) { | |
hostapd_config_free_eap_users(conf->eap_user); | |
conf->eap_user = new_user; | |
} else { | |
hostapd_config_free_eap_users(new_user); | |
} | |
return ret; | |
} | |
#endif /* EAP_SERVER */ | |
#ifndef CONFIG_NO_RADIUS | |
static int | |
hostapd_config_read_radius_addr(struct hostapd_radius_server **server, | |
int *num_server, const char *val, int def_port, | |
struct hostapd_radius_server **curr_serv) | |
{ | |
struct hostapd_radius_server *nserv; | |
int ret; | |
static int server_index = 1; | |
nserv = os_realloc_array(*server, *num_server + 1, sizeof(*nserv)); | |
if (nserv == NULL) | |
return -1; | |
*server = nserv; | |
nserv = &nserv[*num_server]; | |
(*num_server)++; | |
(*curr_serv) = nserv; | |
os_memset(nserv, 0, sizeof(*nserv)); | |
nserv->port = def_port; | |
ret = hostapd_parse_ip_addr(val, &nserv->addr); | |
nserv->index = server_index++; | |
return ret; | |
} | |
static struct hostapd_radius_attr * | |
hostapd_parse_radius_attr(const char *value) | |
{ | |
const char *pos; | |
char syntax; | |
struct hostapd_radius_attr *attr; | |
size_t len; | |
attr = os_zalloc(sizeof(*attr)); | |
if (attr == NULL) | |
return NULL; | |
attr->type = atoi(value); | |
pos = os_strchr(value, ':'); | |
if (pos == NULL) { | |
attr->val = wpabuf_alloc(1); | |
if (attr->val == NULL) { | |
os_free(attr); | |
return NULL; | |
} | |
wpabuf_put_u8(attr->val, 0); | |
return attr; | |
} | |
pos++; | |
if (pos[0] == '\0' || pos[1] != ':') { | |
os_free(attr); | |
return NULL; | |
} | |
syntax = *pos++; | |
pos++; | |
switch (syntax) { | |
case 's': | |
attr->val = wpabuf_alloc_copy(pos, os_strlen(pos)); | |
break; | |
case 'x': | |
len = os_strlen(pos); | |
if (len & 1) | |
break; | |
len /= 2; | |
attr->val = wpabuf_alloc(len); | |
if (attr->val == NULL) | |
break; | |
if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) { | |
wpabuf_free(attr->val); | |
os_free(attr); | |
return NULL; | |
} | |
break; | |
case 'd': | |
attr->val = wpabuf_alloc(4); | |
if (attr->val) | |
wpabuf_put_be32(attr->val, atoi(pos)); | |
break; | |
default: | |
os_free(attr); | |
return NULL; | |
} | |
if (attr->val == NULL) { | |
os_free(attr); | |
return NULL; | |
} | |
return attr; | |
} | |
static int hostapd_parse_das_client(struct hostapd_bss_config *bss, char *val) | |
{ | |
char *secret; | |
secret = os_strchr(val, ' '); | |
if (secret == NULL) | |
return -1; | |
*secret++ = '\0'; | |
if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr)) | |
return -1; | |
os_free(bss->radius_das_shared_secret); | |
bss->radius_das_shared_secret = (u8 *) os_strdup(secret); | |
if (bss->radius_das_shared_secret == NULL) | |
return -1; | |
bss->radius_das_shared_secret_len = os_strlen(secret); | |
return 0; | |
} | |
#endif /* CONFIG_NO_RADIUS */ | |
static int hostapd_config_parse_key_mgmt(int line, const char *value) | |
{ | |
int val = 0, last; | |
char *start, *end, *buf; | |
buf = os_strdup(value); | |
if (buf == NULL) | |
return -1; | |
start = buf; | |
while (*start != '\0') { | |
while (*start == ' ' || *start == '\t') | |
start++; | |
if (*start == '\0') | |
break; | |
end = start; | |
while (*end != ' ' && *end != '\t' && *end != '\0') | |
end++; | |
last = *end == '\0'; | |
*end = '\0'; | |
if (os_strcmp(start, "WPA-PSK") == 0) | |
val |= WPA_KEY_MGMT_PSK; | |
else if (os_strcmp(start, "WPA-EAP") == 0) | |
val |= WPA_KEY_MGMT_IEEE8021X; | |
#ifdef CONFIG_IEEE80211R_AP | |
else if (os_strcmp(start, "FT-PSK") == 0) | |
val |= WPA_KEY_MGMT_FT_PSK; | |
else if (os_strcmp(start, "FT-EAP") == 0) | |
val |= WPA_KEY_MGMT_FT_IEEE8021X; | |
#ifdef CONFIG_SHA384 | |
else if (os_strcmp(start, "FT-EAP-SHA384") == 0) | |
val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384; | |
#endif /* CONFIG_SHA384 */ | |
#endif /* CONFIG_IEEE80211R_AP */ | |
#ifdef CONFIG_IEEE80211W | |
else if (os_strcmp(start, "WPA-PSK-SHA256") == 0) | |
val |= WPA_KEY_MGMT_PSK_SHA256; | |
else if (os_strcmp(start, "WPA-EAP-SHA256") == 0) | |
val |= WPA_KEY_MGMT_IEEE8021X_SHA256; | |
#endif /* CONFIG_IEEE80211W */ | |
#ifdef CONFIG_SAE | |
else if (os_strcmp(start, "SAE") == 0) | |
val |= WPA_KEY_MGMT_SAE; | |
else if (os_strcmp(start, "FT-SAE") == 0) | |
val |= WPA_KEY_MGMT_FT_SAE; | |
#endif /* CONFIG_SAE */ | |
#ifdef CONFIG_SUITEB | |
else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0) | |
val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B; | |
#endif /* CONFIG_SUITEB */ | |
#ifdef CONFIG_SUITEB192 | |
else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0) | |
val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; | |
#endif /* CONFIG_SUITEB192 */ | |
#ifdef CONFIG_FILS | |
else if (os_strcmp(start, "FILS-SHA256") == 0) | |
val |= WPA_KEY_MGMT_FILS_SHA256; | |
else if (os_strcmp(start, "FILS-SHA384") == 0) | |
val |= WPA_KEY_MGMT_FILS_SHA384; | |
#ifdef CONFIG_IEEE80211R_AP | |
else if (os_strcmp(start, "FT-FILS-SHA256") == 0) | |
val |= WPA_KEY_MGMT_FT_FILS_SHA256; | |
else if (os_strcmp(start, "FT-FILS-SHA384") == 0) | |
val |= WPA_KEY_MGMT_FT_FILS_SHA384; | |
#endif /* CONFIG_IEEE80211R_AP */ | |
#endif /* CONFIG_FILS */ | |
#ifdef CONFIG_OWE | |
else if (os_strcmp(start, "OWE") == 0) | |
val |= WPA_KEY_MGMT_OWE; | |
#endif /* CONFIG_OWE */ | |
#ifdef CONFIG_DPP | |
else if (os_strcmp(start, "DPP") == 0) | |
val |= WPA_KEY_MGMT_DPP; | |
#endif /* CONFIG_DPP */ | |
#ifdef CONFIG_HS20 | |
else if (os_strcmp(start, "OSEN") == 0) | |
val |= WPA_KEY_MGMT_OSEN; | |
#endif /* CONFIG_HS20 */ | |
else { | |
wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", | |
line, start); | |
os_free(buf); | |
return -1; | |
} | |
if (last) | |
break; | |
start = end + 1; | |
} | |
os_free(buf); | |
if (val == 0) { | |
wpa_printf(MSG_ERROR, "Line %d: no key_mgmt values " | |
"configured.", line); | |
return -1; | |
} | |
return val; | |
} | |
static int hostapd_config_parse_cipher(int line, const char *value) | |
{ | |
int val = wpa_parse_cipher(value); | |
if (val < 0) { | |
wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", | |
line, value); | |
return -1; | |
} | |
if (val == 0) { | |
wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.", | |
line); | |
return -1; | |
} | |
return val; | |
} | |
static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx, | |
char *val) | |
{ | |
size_t len = os_strlen(val); | |
if (keyidx < 0 || keyidx > 3) | |
return -1; | |
if (len == 0) { | |
int i, set = 0; | |
bin_clear_free(wep->key[keyidx], wep->len[keyidx]); | |
wep->key[keyidx] = NULL; | |
wep->len[keyidx] = 0; | |
for (i = 0; i < NUM_WEP_KEYS; i++) { | |
if (wep->key[i]) | |
set++; | |
} | |
if (!set) | |
wep->keys_set = 0; | |
return 0; | |
} | |
if (wep->key[keyidx] != NULL) | |
return -1; | |
if (val[0] == '"') { | |
if (len < 2 || val[len - 1] != '"') | |
return -1; | |
len -= 2; | |
wep->key[keyidx] = os_memdup(val + 1, len); | |
if (wep->key[keyidx] == NULL) | |
return -1; | |
wep->len[keyidx] = len; | |
} else { | |
if (len & 1) | |
return -1; | |
len /= 2; | |
wep->key[keyidx] = os_malloc(len); | |
if (wep->key[keyidx] == NULL) | |
return -1; | |
wep->len[keyidx] = len; | |
if (hexstr2bin(val, wep->key[keyidx], len) < 0) | |
return -1; | |
} | |
wep->keys_set++; | |
return 0; | |
} | |
static int hostapd_parse_chanlist(struct hostapd_config *conf, char *val) | |
{ | |
char *pos; | |
/* for backwards compatibility, translate ' ' in conf str to ',' */ | |
pos = val; | |
while (pos) { | |
pos = os_strchr(pos, ' '); | |
if (pos) | |
*pos++ = ','; | |
} | |
if (freq_range_list_parse(&conf->acs_ch_list, val)) | |
return -1; | |
return 0; | |
} | |
static int hostapd_parse_intlist(int **int_list, char *val) | |
{ | |
int *list; | |
int count; | |
char *pos, *end; | |
os_free(*int_list); | |
*int_list = NULL; | |
pos = val; | |
count = 0; | |
while (*pos != '\0') { | |
if (*pos == ' ') | |
count++; | |
pos++; | |
} | |
list = os_malloc(sizeof(int) * (count + 2)); | |
if (list == NULL) | |
return -1; | |
pos = val; | |
count = 0; | |
while (*pos != '\0') { | |
end = os_strchr(pos, ' '); | |
if (end) | |
*end = '\0'; | |
list[count++] = atoi(pos); | |
if (!end) | |
break; | |
pos = end + 1; | |
} | |
list[count] = -1; | |
*int_list = list; | |
return 0; | |
} | |
static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname) | |
{ | |
struct hostapd_bss_config **all, *bss; | |
if (*ifname == '\0') | |
return -1; | |
all = os_realloc_array(conf->bss, conf->num_bss + 1, | |
sizeof(struct hostapd_bss_config *)); | |
if (all == NULL) { | |
wpa_printf(MSG_ERROR, "Failed to allocate memory for " | |
"multi-BSS entry"); | |
return -1; | |
} | |
conf->bss = all; | |
bss = os_zalloc(sizeof(*bss)); | |
if (bss == NULL) | |
return -1; | |
bss->radius = os_zalloc(sizeof(*bss->radius)); | |
if (bss->radius == NULL) { | |
wpa_printf(MSG_ERROR, "Failed to allocate memory for " | |
"multi-BSS RADIUS data"); | |
os_free(bss); | |
return -1; | |
} | |
conf->bss[conf->num_bss++] = bss; | |
conf->last_bss = bss; | |
hostapd_config_defaults_bss(bss); | |
os_strlcpy(bss->iface, ifname, sizeof(bss->iface)); | |
os_memcpy(bss->ssid.vlan, bss->iface, IFNAMSIZ + 1); | |
return 0; | |
} | |
/* convert floats with one decimal place to value*10 int, i.e., | |
* "1.5" will return 15 */ | |
static int hostapd_config_read_int10(const char *value) | |
{ | |
int i, d; | |
char *pos; | |
i = atoi(value); | |
pos = os_strchr(value, '.'); | |
d = 0; | |
if (pos) { | |
pos++; | |
if (*pos >= '0' && *pos <= '9') | |
d = *pos - '0'; | |
} | |
return i * 10 + d; | |
} | |
static int valid_cw(int cw) | |
{ | |
return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 || | |
cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023 || | |
cw == 2047 || cw == 4095 || cw == 8191 || cw == 16383 || | |
cw == 32767); | |
} | |
enum { | |
IEEE80211_TX_QUEUE_DATA0 = 0, /* used for EDCA AC_VO data */ | |
IEEE80211_TX_QUEUE_DATA1 = 1, /* used for EDCA AC_VI data */ | |
IEEE80211_TX_QUEUE_DATA2 = 2, /* used for EDCA AC_BE data */ | |
IEEE80211_TX_QUEUE_DATA3 = 3 /* used for EDCA AC_BK data */ | |
}; | |
static int hostapd_config_tx_queue(struct hostapd_config *conf, | |
const char *name, const char *val) | |
{ | |
int num; | |
const char *pos; | |
struct hostapd_tx_queue_params *queue; | |
/* skip 'tx_queue_' prefix */ | |
pos = name + 9; | |
if (os_strncmp(pos, "data", 4) == 0 && | |
pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') { | |
num = pos[4] - '0'; | |
pos += 6; | |
} else if (os_strncmp(pos, "after_beacon_", 13) == 0 || | |
os_strncmp(pos, "beacon_", 7) == 0) { | |
wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name); | |
return 0; | |
} else { | |
wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos); | |
return -1; | |
} | |
if (num >= NUM_TX_QUEUES) { | |
/* for backwards compatibility, do not trigger failure */ | |
wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name); | |
return 0; | |
} | |
queue = &conf->tx_queue[num]; | |
if (os_strcmp(pos, "aifs") == 0) { | |
queue->aifs = atoi(val); | |
if (queue->aifs < 0 || queue->aifs > 255) { | |
wpa_printf(MSG_ERROR, "Invalid AIFS value %d", | |
queue->aifs); | |
return -1; | |
} | |
} else if (os_strcmp(pos, "cwmin") == 0) { | |
queue->cwmin = atoi(val); | |
if (!valid_cw(queue->cwmin)) { | |
wpa_printf(MSG_ERROR, "Invalid cwMin value %d", | |
queue->cwmin); | |
return -1; | |
} | |
} else if (os_strcmp(pos, "cwmax") == 0) { | |
queue->cwmax = atoi(val); | |
if (!valid_cw(queue->cwmax)) { | |
wpa_printf(MSG_ERROR, "Invalid cwMax value %d", | |
queue->cwmax); | |
return -1; | |
} | |
} else if (os_strcmp(pos, "burst") == 0) { | |
queue->burst = hostapd_config_read_int10(val); | |
} else { | |
wpa_printf(MSG_ERROR, "Unknown tx_queue field '%s'", pos); | |
return -1; | |
} | |
return 0; | |
} | |
#ifdef CONFIG_IEEE80211R_AP | |
static int rkh_derive_key(const char *pos, u8 *key, size_t key_len) | |
{ | |
u8 oldkey[16]; | |
int ret; | |
if (!hexstr2bin(pos, key, key_len)) | |
return 0; | |
/* Try to use old short key for backwards compatibility */ | |
if (hexstr2bin(pos, oldkey, sizeof(oldkey))) | |
return -1; | |
ret = hmac_sha256_kdf(oldkey, sizeof(oldkey), "FT OLDKEY", NULL, 0, | |
key, key_len); | |
os_memset(oldkey, 0, sizeof(oldkey)); | |
return ret; | |
} | |
static int add_r0kh(struct hostapd_bss_config *bss, char *value) | |
{ | |
struct ft_remote_r0kh *r0kh; | |
char *pos, *next; | |
r0kh = os_zalloc(sizeof(*r0kh)); | |
if (r0kh == NULL) | |
return -1; | |
/* 02:01:02:03:04:05 a.example.com 000102030405060708090a0b0c0d0e0f */ | |
pos = value; | |
next = os_strchr(pos, ' '); | |
if (next) | |
*next++ = '\0'; | |
if (next == NULL || hwaddr_aton(pos, r0kh->addr)) { | |
wpa_printf(MSG_ERROR, "Invalid R0KH MAC address: '%s'", pos); | |
os_free(r0kh); | |
return -1; | |
} | |
pos = next; | |
next = os_strchr(pos, ' '); | |
if (next) | |
*next++ = '\0'; | |
if (next == NULL || next - pos > FT_R0KH_ID_MAX_LEN) { | |
wpa_printf(MSG_ERROR, "Invalid R0KH-ID: '%s'", pos); | |
os_free(r0kh); | |
return -1; | |
} | |
r0kh->id_len = next - pos - 1; | |
os_memcpy(r0kh->id, pos, r0kh->id_len); | |
pos = next; | |
if (rkh_derive_key(pos, r0kh->key, sizeof(r0kh->key)) < 0) { | |
wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos); | |
os_free(r0kh); | |
return -1; | |
} | |
r0kh->next = bss->r0kh_list; | |
bss->r0kh_list = r0kh; | |
return 0; | |
} | |
static int add_r1kh(struct hostapd_bss_config *bss, char *value) | |
{ | |
struct ft_remote_r1kh *r1kh; | |
char *pos, *next; | |
r1kh = os_zalloc(sizeof(*r1kh)); | |
if (r1kh == NULL) | |
return -1; | |
/* 02:01:02:03:04:05 02:01:02:03:04:05 | |
* 000102030405060708090a0b0c0d0e0f */ | |
pos = value; | |
next = os_strchr(pos, ' '); | |
if (next) | |
*next++ = '\0'; | |
if (next == NULL || hwaddr_aton(pos, r1kh->addr)) { | |
wpa_printf(MSG_ERROR, "Invalid R1KH MAC address: '%s'", pos); | |
os_free(r1kh); | |
return -1; | |
} | |
pos = next; | |
next = os_strchr(pos, ' '); | |
if (next) | |
*next++ = '\0'; | |
if (next == NULL || hwaddr_aton(pos, r1kh->id)) { | |
wpa_printf(MSG_ERROR, "Invalid R1KH-ID: '%s'", pos); | |
os_free(r1kh); | |
return -1; | |
} | |
pos = next; | |
if (rkh_derive_key(pos, r1kh->key, sizeof(r1kh->key)) < 0) { | |
wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos); | |
os_free(r1kh); | |
return -1; | |
} | |
r1kh->next = bss->r1kh_list; | |
bss->r1kh_list = r1kh; | |
return 0; | |
} | |
#endif /* CONFIG_IEEE80211R_AP */ | |
#ifdef CONFIG_IEEE80211N | |
static int hostapd_config_ht_capab(struct hostapd_config *conf, | |
const char *capab) | |
{ | |
if (os_strstr(capab, "[LDPC]")) | |
conf->ht_capab |= HT_CAP_INFO_LDPC_CODING_CAP; | |
if (os_strstr(capab, "[HT40-]")) { | |
conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; | |
conf->secondary_channel = -1; | |
} | |
if (os_strstr(capab, "[HT40+]")) { | |
conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; | |
conf->secondary_channel = 1; | |
} | |
if (os_strstr(capab, "[HT40+]") && os_strstr(capab, "[HT40-]")) { | |
conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; | |
conf->ht40_plus_minus_allowed = 1; | |
} | |
if (!os_strstr(capab, "[HT40+]") && !os_strstr(capab, "[HT40-]")) | |
conf->secondary_channel = 0; | |
if (os_strstr(capab, "[SMPS-STATIC]")) { | |
conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK; | |
conf->ht_capab |= HT_CAP_INFO_SMPS_STATIC; | |
} | |
if (os_strstr(capab, "[SMPS-DYNAMIC]")) { | |
conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK; | |
conf->ht_capab |= HT_CAP_INFO_SMPS_DYNAMIC; | |
} | |
if (os_strstr(capab, "[GF]")) | |
conf->ht_capab |= HT_CAP_INFO_GREEN_FIELD; | |
if (os_strstr(capab, "[SHORT-GI-20]")) | |
conf->ht_capab |= HT_CAP_INFO_SHORT_GI20MHZ; | |
if (os_strstr(capab, "[SHORT-GI-40]")) | |
conf->ht_capab |= HT_CAP_INFO_SHORT_GI40MHZ; | |
if (os_strstr(capab, "[TX-STBC]")) | |
conf->ht_capab |= HT_CAP_INFO_TX_STBC; | |
if (os_strstr(capab, "[RX-STBC1]")) { | |
conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK; | |
conf->ht_capab |= HT_CAP_INFO_RX_STBC_1; | |
} | |
if (os_strstr(capab, "[RX-STBC12]")) { | |
conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK; | |
conf->ht_capab |= HT_CAP_INFO_RX_STBC_12; | |
} | |
if (os_strstr(capab, "[RX-STBC123]")) { | |
conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK; | |
conf->ht_capab |= HT_CAP_INFO_RX_STBC_123; | |
} | |
if (os_strstr(capab, "[DELAYED-BA]")) | |
conf->ht_capab |= HT_CAP_INFO_DELAYED_BA; | |
if (os_strstr(capab, "[MAX-AMSDU-7935]")) | |
conf->ht_capab |= HT_CAP_INFO_MAX_AMSDU_SIZE; | |
if (os_strstr(capab, "[DSSS_CCK-40]")) | |
conf->ht_capab |= HT_CAP_INFO_DSSS_CCK40MHZ; | |
if (os_strstr(capab, "[40-INTOLERANT]")) | |
conf->ht_capab |= HT_CAP_INFO_40MHZ_INTOLERANT; | |
if (os_strstr(capab, "[LSIG-TXOP-PROT]")) | |
conf->ht_capab |= HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT; | |
return 0; | |
} | |
#endif /* CONFIG_IEEE80211N */ | |
#ifdef CONFIG_IEEE80211AC | |
static int hostapd_config_vht_capab(struct hostapd_config *conf, | |
const char *capab) | |
{ | |
if (os_strstr(capab, "[MAX-MPDU-7991]")) | |
conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_7991; | |
if (os_strstr(capab, "[MAX-MPDU-11454]")) | |
conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_11454; | |
if (os_strstr(capab, "[VHT160]")) | |
conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; | |
if (os_strstr(capab, "[VHT160-80PLUS80]")) | |
conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; | |
if (os_strstr(capab, "[RXLDPC]")) | |
conf->vht_capab |= VHT_CAP_RXLDPC; | |
if (os_strstr(capab, "[SHORT-GI-80]")) | |
conf->vht_capab |= VHT_CAP_SHORT_GI_80; | |
if (os_strstr(capab, "[SHORT-GI-160]")) | |
conf->vht_capab |= VHT_CAP_SHORT_GI_160; | |
if (os_strstr(capab, "[TX-STBC-2BY1]")) | |
conf->vht_capab |= VHT_CAP_TXSTBC; | |
if (os_strstr(capab, "[RX-STBC-1]")) | |
conf->vht_capab |= VHT_CAP_RXSTBC_1; | |
if (os_strstr(capab, "[RX-STBC-12]")) | |
conf->vht_capab |= VHT_CAP_RXSTBC_2; | |
if (os_strstr(capab, "[RX-STBC-123]")) | |
conf->vht_capab |= VHT_CAP_RXSTBC_3; | |
if (os_strstr(capab, "[RX-STBC-1234]")) | |
conf->vht_capab |= VHT_CAP_RXSTBC_4; | |
if (os_strstr(capab, "[SU-BEAMFORMER]")) | |
conf->vht_capab |= VHT_CAP_SU_BEAMFORMER_CAPABLE; | |
if (os_strstr(capab, "[SU-BEAMFORMEE]")) | |
conf->vht_capab |= VHT_CAP_SU_BEAMFORMEE_CAPABLE; | |
if (os_strstr(capab, "[BF-ANTENNA-2]") && | |
(conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) | |
conf->vht_capab |= (1 << VHT_CAP_BEAMFORMEE_STS_OFFSET); | |
if (os_strstr(capab, "[BF-ANTENNA-3]") && | |
(conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) | |
conf->vht_capab |= (2 << VHT_CAP_BEAMFORMEE_STS_OFFSET); | |
if (os_strstr(capab, "[BF-ANTENNA-4]") && | |
(conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) | |
conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET); | |
if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") && | |
(conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) | |
conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); | |
if (os_strstr(capab, "[SOUNDING-DIMENSION-3]") && | |
(conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) | |
conf->vht_capab |= (2 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); | |
if (os_strstr(capab, "[SOUNDING-DIMENSION-4]") && | |
(conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) | |
conf->vht_capab |= (3 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); | |
if (os_strstr(capab, "[MU-BEAMFORMER]")) | |
conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE; | |
if (os_strstr(capab, "[VHT-TXOP-PS]")) | |
conf->vht_capab |= VHT_CAP_VHT_TXOP_PS; | |
if (os_strstr(capab, "[HTC-VHT]")) | |
conf->vht_capab |= VHT_CAP_HTC_VHT; | |
if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP7]")) | |
conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX; | |
else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP6]")) | |
conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6; | |
else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP5]")) | |
conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_5; | |
else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP4]")) | |
conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_4; | |
else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP3]")) | |
conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_3; | |
else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP2]")) | |
conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_2; | |
else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP1]")) | |
conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_1; | |
if (os_strstr(capab, "[VHT-LINK-ADAPT2]") && | |
(conf->vht_capab & VHT_CAP_HTC_VHT)) | |
conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB; | |
if (os_strstr(capab, "[VHT-LINK-ADAPT3]") && | |
(conf->vht_capab & VHT_CAP_HTC_VHT)) | |
conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB; | |
if (os_strstr(capab, "[RX-ANTENNA-PATTERN]")) | |
conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN; | |
if (os_strstr(capab, "[TX-ANTENNA-PATTERN]")) | |
conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN; | |
return 0; | |
} | |
#endif /* CONFIG_IEEE80211AC */ | |
#ifdef CONFIG_IEEE80211AX | |
static u8 find_bit_offset(u8 val) | |
{ | |
u8 res = 0; | |
for (; val; val >>= 1) { | |
if (val & 1) | |
break; | |
res++; | |
} | |
return res; | |
} | |
static u8 set_he_cap(int val, u8 mask) | |
{ | |
return (u8) (mask & (val << find_bit_offset(mask))); | |
} | |
#endif /* CONFIG_IEEE80211AX */ | |
#ifdef CONFIG_INTERWORKING | |
static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos, | |
int line) | |
{ | |
size_t len = os_strlen(pos); | |
u8 oi[MAX_ROAMING_CONSORTIUM_LEN]; | |
struct hostapd_roaming_consortium *rc; | |
if ((len & 1) || len < 2 * 3 || len / 2 > MAX_ROAMING_CONSORTIUM_LEN || | |
hexstr2bin(pos, oi, len / 2)) { | |
wpa_printf(MSG_ERROR, "Line %d: invalid roaming_consortium " | |
"'%s'", line, pos); | |
return -1; | |
} | |
len /= 2; | |
rc = os_realloc_array(bss->roaming_consortium, | |
bss->roaming_consortium_count + 1, | |
sizeof(struct hostapd_roaming_consortium)); | |
if (rc == NULL) | |
return -1; | |
os_memcpy(rc[bss->roaming_consortium_count].oi, oi, len); | |
rc[bss->roaming_consortium_count].len = len; | |
bss->roaming_consortium = rc; | |
bss->roaming_consortium_count++; | |
return 0; | |
} | |
static int parse_lang_string(struct hostapd_lang_string **array, | |
unsigned int *count, char *pos) | |
{ | |
char *sep, *str = NULL; | |
size_t clen, nlen, slen; | |
struct hostapd_lang_string *ls; | |
int ret = -1; | |
if (*pos == '"' || (*pos == 'P' && pos[1] == '"')) { | |
str = wpa_config_parse_string(pos, &slen); | |
if (!str) | |
return -1; | |
pos = str; | |
} | |
sep = os_strchr(pos, ':'); | |
if (sep == NULL) | |
goto fail; | |
*sep++ = '\0'; | |
clen = os_strlen(pos); | |
if (clen < 2 || clen > sizeof(ls->lang)) | |
goto fail; | |
nlen = os_strlen(sep); | |
if (nlen > 252) | |
goto fail; | |
ls = os_realloc_array(*array, *count + 1, | |
sizeof(struct hostapd_lang_string)); | |
if (ls == NULL) | |
goto fail; | |
*array = ls; | |
ls = &(*array)[*count]; | |
(*count)++; | |
os_memset(ls->lang, 0, sizeof(ls->lang)); | |
os_memcpy(ls->lang, pos, clen); | |
ls->name_len = nlen; | |
os_memcpy(ls->name, sep, nlen); | |
ret = 0; | |
fail: | |
os_free(str); | |
return ret; | |
} | |
static int parse_venue_name(struct hostapd_bss_config *bss, char *pos, | |
int line) | |
{ | |
if (parse_lang_string(&bss->venue_name, &bss->venue_name_count, pos)) { | |
wpa_printf(MSG_ERROR, "Line %d: Invalid venue_name '%s'", | |
line, pos); | |
return -1; | |
} | |
return 0; | |
} | |
static int parse_venue_url(struct hostapd_bss_config *bss, char *pos, | |
int line) | |
{ | |
char *sep; | |
size_t nlen; | |
struct hostapd_venue_url *url; | |
int ret = -1; | |
sep = os_strchr(pos, ':'); | |
if (!sep) | |
goto fail; | |
*sep++ = '\0'; | |
nlen = os_strlen(sep); | |
if (nlen > 254) | |
goto fail; | |
url = os_realloc_array(bss->venue_url, bss->venue_url_count + 1, | |
sizeof(struct hostapd_venue_url)); | |
if (!url) | |
goto fail; | |
bss->venue_url = url; | |
url = &bss->venue_url[bss->venue_url_count++]; | |
url->venue_number = atoi(pos); | |
url->url_len = nlen; | |
os_memcpy(url->url, sep, nlen); | |
ret = 0; | |
fail: | |
if (ret) | |
wpa_printf(MSG_ERROR, "Line %d: Invalid venue_url '%s'", | |
line, pos); | |
return ret; | |
} | |
static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf, | |
int line) | |
{ | |
size_t count; | |
char *pos; | |
u8 *info = NULL, *ipos; | |
/* format: <MCC1,MNC1>[;<MCC2,MNC2>][;...] */ | |
count = 1; | |
for (pos = buf; *pos; pos++) { | |
if ((*pos < '0' || *pos > '9') && *pos != ';' && *pos != ',') | |
goto fail; | |
if (*pos == ';') | |
count++; | |
} | |
if (1 + count * 3 > 0x7f) | |
goto fail; | |
info = os_zalloc(2 + 3 + count * 3); | |
if (info == NULL) | |
return -1; | |
ipos = info; | |
*ipos++ = 0; /* GUD - Version 1 */ | |
*ipos++ = 3 + count * 3; /* User Data Header Length (UDHL) */ | |
*ipos++ = 0; /* PLMN List IEI */ | |
/* ext(b8) | Length of PLMN List value contents(b7..1) */ | |
*ipos++ = 1 + count * 3; | |
*ipos++ = count; /* Number of PLMNs */ | |
pos = buf; | |
while (pos && *pos) { | |
char *mcc, *mnc; | |
size_t mnc_len; | |
mcc = pos; | |
mnc = os_strchr(pos, ','); | |
if (mnc == NULL) | |
goto fail; | |
*mnc++ = '\0'; | |
pos = os_strchr(mnc, ';'); | |
if (pos) | |
*pos++ = '\0'; | |
mnc_len = os_strlen(mnc); | |
if (os_strlen(mcc) != 3 || (mnc_len != 2 && mnc_len != 3)) | |
goto fail; | |
/* BC coded MCC,MNC */ | |
/* MCC digit 2 | MCC digit 1 */ | |
*ipos++ = ((mcc[1] - '0') << 4) | (mcc[0] - '0'); | |
/* MNC digit 3 | MCC digit 3 */ | |
*ipos++ = (((mnc_len == 2) ? 0xf0 : ((mnc[2] - '0') << 4))) | | |
(mcc[2] - '0'); | |
/* MNC digit 2 | MNC digit 1 */ | |
*ipos++ = ((mnc[1] - '0') << 4) | (mnc[0] - '0'); | |
} | |
os_free(bss->anqp_3gpp_cell_net); | |
bss->anqp_3gpp_cell_net = info; | |
bss->anqp_3gpp_cell_net_len = 2 + 3 + 3 * count; | |
wpa_hexdump(MSG_MSGDUMP, "3GPP Cellular Network information", | |
bss->anqp_3gpp_cell_net, bss->anqp_3gpp_cell_net_len); | |
return 0; | |
fail: | |
wpa_printf(MSG_ERROR, "Line %d: Invalid anqp_3gpp_cell_net: %s", | |
line, buf); | |
os_free(info); | |
return -1; | |
} | |
static int parse_nai_realm(struct hostapd_bss_config *bss, char *buf, int line) | |
{ | |
struct hostapd_nai_realm_data *realm; | |
size_t i, j, len; | |
int *offsets; | |
char *pos, *end, *rpos; | |
offsets = os_calloc(bss->nai_realm_count * MAX_NAI_REALMS, | |
sizeof(int)); | |
if (offsets == NULL) | |
return -1; | |
for (i = 0; i < bss->nai_realm_count; i++) { | |
realm = &bss->nai_realm_data[i]; | |
for (j = 0; j < MAX_NAI_REALMS; j++) { | |
offsets[i * MAX_NAI_REALMS + j] = | |
realm->realm[j] ? | |
realm->realm[j] - realm->realm_buf : -1; | |
} | |
} | |
realm = os_realloc_array(bss->nai_realm_data, bss->nai_realm_count + 1, | |
sizeof(struct hostapd_nai_realm_data)); | |
if (realm == NULL) { | |
os_free(offsets); | |
return -1; | |
} | |
bss->nai_realm_data = realm; | |
/* patch the pointers after realloc */ | |
for (i = 0; i < bss->nai_realm_count; i++) { | |
realm = &bss->nai_realm_data[i]; | |
for (j = 0; j < MAX_NAI_REALMS; j++) { | |
int offs = offsets[i * MAX_NAI_REALMS + j]; | |
if (offs >= 0) | |
realm->realm[j] = realm->realm_buf + offs; | |
else | |
realm->realm[j] = NULL; | |
} | |
} | |
os_free(offsets); | |
realm = &bss->nai_realm_data[bss->nai_realm_count]; | |
os_memset(realm, 0, sizeof(*realm)); | |
pos = buf; | |
realm->encoding = atoi(pos); | |
pos = os_strchr(pos, ','); | |
if (pos == NULL) | |
goto fail; | |
pos++; | |
end = os_strchr(pos, ','); | |
if (end) { | |
len = end - pos; | |
*end = '\0'; | |
} else { | |
len = os_strlen(pos); | |
} | |
if (len > MAX_NAI_REALMLEN) { | |
wpa_printf(MSG_ERROR, "Too long a realm string (%d > max %d " | |
"characters)", (int) len, MAX_NAI_REALMLEN); | |
goto fail; | |
} | |
os_memcpy(realm->realm_buf, pos, len); | |
if (end) | |
pos = end + 1; | |
else | |
pos = NULL; | |
while (pos && *pos) { | |
struct hostapd_nai_realm_eap *eap; | |
if (realm->eap_method_count >= MAX_NAI_EAP_METHODS) { | |
wpa_printf(MSG_ERROR, "Too many EAP methods"); | |
goto fail; | |
} | |
eap = &realm->eap_method[realm->eap_method_count]; | |
realm->eap_method_count++; | |
end = os_strchr(pos, ','); | |
if (end == NULL) | |
end = pos + os_strlen(pos); | |
eap->eap_method = atoi(pos); | |
for (;;) { | |
pos = os_strchr(pos, '['); | |
if (pos == NULL || pos > end) | |
break; | |
pos++; | |
if (eap->num_auths >= MAX_NAI_AUTH_TYPES) { | |
wpa_printf(MSG_ERROR, "Too many auth params"); | |
goto fail; | |
} | |
eap->auth_id[eap->num_auths] = atoi(pos); | |
pos = os_strchr(pos, ':'); | |
if (pos == NULL || pos > end) | |
goto fail; | |
pos++; | |
eap->auth_val[eap->num_auths] = atoi(pos); | |
pos = os_strchr(pos, ']'); | |
if (pos == NULL || pos > end) | |
goto fail; | |
pos++; | |
eap->num_auths++; | |
} | |
if (*end != ',') | |
break; | |
pos = end + 1; | |
} | |
/* Split realm list into null terminated realms */ | |
rpos = realm->realm_buf; | |
i = 0; | |
while (*rpos) { | |
if (i >= MAX_NAI_REALMS) { | |
wpa_printf(MSG_ERROR, "Too many realms"); | |
goto fail; | |
} | |
realm->realm[i++] = rpos; | |
rpos = os_strchr(rpos, ';'); | |
if (rpos == NULL) | |
break; | |
*rpos++ = '\0'; | |
} | |
bss->nai_realm_count++; | |
return 0; | |
fail: | |
wpa_printf(MSG_ERROR, "Line %d: invalid nai_realm '%s'", line, buf); | |
return -1; | |
} | |
static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line) | |
{ | |
char *delim; | |
u16 infoid; | |
size_t len; | |
struct wpabuf *payload; | |
struct anqp_element *elem; | |
delim = os_strchr(buf, ':'); | |
if (!delim) | |
return -1; | |
delim++; | |
infoid = atoi(buf); | |
len = os_strlen(delim); | |
if (len & 1) | |
return -1; | |
len /= 2; | |
payload = wpabuf_alloc(len); | |
if (!payload) | |
return -1; | |
if (hexstr2bin(delim, wpabuf_put(payload, len), len) < 0) { | |
wpabuf_free(payload); | |
return -1; | |
} | |
dl_list_for_each(elem, &bss->anqp_elem, struct anqp_element, list) { | |
if (elem->infoid == infoid) { | |
/* Update existing entry */ | |
wpabuf_free(elem->payload); | |
elem->payload = payload; | |
return 0; | |
} | |
} | |
/* Add a new entry */ | |
elem = os_zalloc(sizeof(*elem)); | |
if (!elem) { | |
wpabuf_free(payload); | |
return -1; | |
} | |
elem->infoid = infoid; | |
elem->payload = payload; | |
dl_list_add(&bss->anqp_elem, &elem->list); | |
return 0; | |
} | |
static int parse_qos_map_set(struct hostapd_bss_config *bss, | |
char *buf, int line) | |
{ | |
u8 qos_map_set[16 + 2 * 21], count = 0; | |
char *pos = buf; | |
int val; | |
for (;;) { | |
if (count == sizeof(qos_map_set)) { | |
wpa_printf(MSG_ERROR, "Line %d: Too many qos_map_set " | |
"parameters '%s'", line, buf); | |
return -1; | |
} | |
val = atoi(pos); | |
if (val > 255 || val < 0) { | |
wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set " | |
"'%s'", line, buf); | |
return -1; | |
} | |
qos_map_set[count++] = val; | |
pos = os_strchr(pos, ','); | |
if (!pos) | |
break; | |
pos++; | |
} | |
if (count < 16 || count & 1) { | |
wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set '%s'", | |
line, buf); | |
return -1; | |
} | |
os_memcpy(bss->qos_map_set, qos_map_set, count); | |
bss->qos_map_set_len = count; | |
return 0; | |
} | |
#endif /* CONFIG_INTERWORKING */ | |
#ifdef CONFIG_HS20 | |
static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf, | |
int line) | |
{ | |
u8 *conn_cap; | |
char *pos; | |
if (bss->hs20_connection_capability_len >= 0xfff0) | |
return -1; | |
conn_cap = os_realloc(bss->hs20_connection_capability, | |
bss->hs20_connection_capability_len + 4); | |
if (conn_cap == NULL) | |
return -1; | |
bss->hs20_connection_capability = conn_cap; | |
conn_cap += bss->hs20_connection_capability_len; | |
pos = buf; | |
conn_cap[0] = atoi(pos); | |
pos = os_strchr(pos, ':'); | |
if (pos == NULL) | |
return -1; | |
pos++; | |
WPA_PUT_LE16(conn_cap + 1, atoi(pos)); | |
pos = os_strchr(pos, ':'); | |
if (pos == NULL) | |
return -1; | |
pos++; | |
conn_cap[3] = atoi(pos); | |
bss->hs20_connection_capability_len += 4; | |
return 0; | |
} | |
static int hs20_parse_wan_metrics(struct hostapd_bss_config *bss, char *buf, | |
int line) | |
{ | |
u8 *wan_metrics; | |
char *pos; | |
/* <WAN Info>:<DL Speed>:<UL Speed>:<DL Load>:<UL Load>:<LMD> */ | |
wan_metrics = os_zalloc(13); | |
if (wan_metrics == NULL) | |
return -1; | |
pos = buf; | |
/* WAN Info */ | |
if (hexstr2bin(pos, wan_metrics, 1) < 0) | |
goto fail; | |
pos += 2; | |
if (*pos != ':') | |
goto fail; | |
pos++; | |
/* Downlink Speed */ | |
WPA_PUT_LE32(wan_metrics + 1, atoi(pos)); | |
pos = os_strchr(pos, ':'); | |
if (pos == NULL) | |
goto fail; | |
pos++; | |
/* Uplink Speed */ | |
WPA_PUT_LE32(wan_metrics + 5, atoi(pos)); | |
pos = os_strchr(pos, ':'); | |
if (pos == NULL) | |
goto fail; | |
pos++; | |
/* Downlink Load */ | |
wan_metrics[9] = atoi(pos); | |
pos = os_strchr(pos, ':'); | |
if (pos == NULL) | |
goto fail; | |
pos++; | |
/* Uplink Load */ | |
wan_metrics[10] = atoi(pos); | |
pos = os_strchr(pos, ':'); | |
if (pos == NULL) | |
goto fail; | |
pos++; | |
/* LMD */ | |
WPA_PUT_LE16(wan_metrics + 11, atoi(pos)); | |
os_free(bss->hs20_wan_metrics); | |
bss->hs20_wan_metrics = wan_metrics; | |
return 0; | |
fail: | |
wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_wan_metrics '%s'", | |
line, buf); | |
os_free(wan_metrics); | |
return -1; | |
} | |
static int hs20_parse_oper_friendly_name(struct hostapd_bss_config *bss, | |
char *pos, int line) | |
{ | |
if (parse_lang_string(&bss->hs20_oper_friendly_name, | |
&bss->hs20_oper_friendly_name_count, pos)) { | |
wpa_printf(MSG_ERROR, "Line %d: Invalid " | |
"hs20_oper_friendly_name '%s'", line, pos); | |
return -1; | |
} | |
return 0; | |
} | |
static int hs20_parse_icon(struct hostapd_bss_config *bss, char *pos) | |
{ | |
struct hs20_icon *icon; | |
char *end; | |
icon = os_realloc_array(bss->hs20_icons, bss->hs20_icons_count + 1, | |
sizeof(struct hs20_icon)); | |
if (icon == NULL) | |
return -1; | |
bss->hs20_icons = icon; | |
icon = &bss->hs20_icons[bss->hs20_icons_count]; | |
os_memset(icon, 0, sizeof(*icon)); | |
icon->width = atoi(pos); | |
pos = os_strchr(pos, ':'); | |
if (pos == NULL) | |
return -1; | |
pos++; | |
icon->height = atoi(pos); | |
pos = os_strchr(pos, ':'); | |
if (pos == NULL) | |
return -1; | |
pos++; | |
end = os_strchr(pos, ':'); | |
if (end == NULL || end - pos > 3) | |
return -1; | |
os_memcpy(icon->language, pos, end - pos); | |
pos = end + 1; | |
end = os_strchr(pos, ':'); | |
if (end == NULL || end - pos > 255) | |
return -1; | |
os_memcpy(icon->type, pos, end - pos); | |
pos = end + 1; | |
end = os_strchr(pos, ':'); | |
if (end == NULL || end - pos > 255) | |
return -1; | |
os_memcpy(icon->name, pos, end - pos); | |
pos = end + 1; | |
if (os_strlen(pos) > 255) | |
return -1; | |
os_memcpy(icon->file, pos, os_strlen(pos)); | |
bss->hs20_icons_count++; | |
return 0; | |
} | |
static int hs20_parse_osu_ssid(struct hostapd_bss_config *bss, | |
char *pos, int line) | |
{ | |
size_t slen; | |
char *str; | |
str = wpa_config_parse_string(pos, &slen); | |
if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) { | |
wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos); | |
os_free(str); | |
return -1; | |
} | |
os_memcpy(bss->osu_ssid, str, slen); | |
bss->osu_ssid_len = slen; | |
os_free(str); | |
return 0; | |
} | |
static int hs20_parse_osu_server_uri(struct hostapd_bss_config *bss, | |
char *pos, int line) | |
{ | |
struct hs20_osu_provider *p; | |
p = os_realloc_array(bss->hs20_osu_providers, | |
bss->hs20_osu_providers_count + 1, sizeof(*p)); | |
if (p == NULL) | |
return -1; | |
bss->hs20_osu_providers = p; | |
bss->last_osu = &bss->hs20_osu_providers[bss->hs20_osu_providers_count]; | |
bss->hs20_osu_providers_count++; | |
os_memset(bss->last_osu, 0, sizeof(*p)); | |
bss->last_osu->server_uri = os_strdup(pos); | |
return 0; | |
} | |
static int hs20_parse_osu_friendly_name(struct hostapd_bss_config *bss, | |
char *pos, int line) | |
{ | |
if (bss->last_osu == NULL) { | |
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); | |
return -1; | |
} | |
if (parse_lang_string(&bss->last_osu->friendly_name, | |
&bss->last_osu->friendly_name_count, pos)) { | |
wpa_printf(MSG_ERROR, "Line %d: Invalid osu_friendly_name '%s'", | |
line, pos); | |
return -1; | |
} | |
return 0; | |
} | |
static int hs20_parse_osu_nai(struct hostapd_bss_config *bss, | |
char *pos, int line) | |
{ | |
if (bss->last_osu == NULL) { | |
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); | |
return -1; | |
} | |
os_free(bss->last_osu->osu_nai); | |
bss->last_osu->osu_nai = os_strdup(pos); | |
if (bss->last_osu->osu_nai == NULL) | |
return -1; | |
return 0; | |
} | |
static int hs20_parse_osu_nai2(struct hostapd_bss_config *bss, | |
char *pos, int line) | |
{ | |
if (bss->last_osu == NULL) { | |
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); | |
return -1; | |
} | |
os_free(bss->last_osu->osu_nai2); | |
bss->last_osu->osu_nai2 = os_strdup(pos); | |
if (bss->last_osu->osu_nai2 == NULL) | |
return -1; | |
bss->hs20_osu_providers_nai_count++; | |
return 0; | |
} | |
static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos, | |
int line) | |
{ | |
if (bss->last_osu == NULL) { | |
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); | |
return -1; | |
} | |
if (hostapd_parse_intlist(&bss->last_osu->method_list, pos)) { | |
wpa_printf(MSG_ERROR, "Line %d: Invalid osu_method_list", line); | |
return -1; | |
} | |
return 0; | |
} | |
static int hs20_parse_osu_icon(struct hostapd_bss_config *bss, char *pos, | |
int line) | |
{ | |
char **n; | |
struct hs20_osu_provider *p = bss->last_osu; | |
if (p == NULL) { | |
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); | |
return -1; | |
} | |
n = os_realloc_array(p->icons, p->icons_count + 1, sizeof(char *)); | |
if (n == NULL) | |
return -1; | |
p->icons = n; | |
p->icons[p->icons_count] = os_strdup(pos); | |
if (p->icons[p->icons_count] == NULL) | |
return -1; | |
p->icons_count++; | |
return 0; | |
} | |
static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss, | |
char *pos, int line) | |
{ | |
if (bss->last_osu == NULL) { | |
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); | |
return -1; | |
} | |
if (parse_lang_string(&bss->last_osu->service_desc, | |
&bss->last_osu->service_desc_count, pos)) { | |
wpa_printf(MSG_ERROR, "Line %d: Invalid osu_service_desc '%s'", | |
line, pos); | |
return -1; | |
} | |
return 0; | |
} | |
static int hs20_parse_operator_icon(struct hostapd_bss_config *bss, char *pos, | |
int line) | |
{ | |
char **n; | |
n = os_realloc_array(bss->hs20_operator_icon, | |
bss->hs20_operator_icon_count + 1, sizeof(char *)); | |
if (!n) | |
return -1; | |
bss->hs20_operator_icon = n; | |
bss->hs20_operator_icon[bss->hs20_operator_icon_count] = os_strdup(pos); | |
if (!bss->hs20_operator_icon[bss->hs20_operator_icon_count]) | |
return -1; | |
bss->hs20_operator_icon_count++; | |
return 0; | |
} | |
#endif /* CONFIG_HS20 */ | |
#ifdef CONFIG_ACS | |
static int hostapd_config_parse_acs_chan_bias(struct hostapd_config *conf, | |
char *pos) | |
{ | |
struct acs_bias *bias = NULL, *tmp; | |
unsigned int num = 0; | |
char *end; | |
while (*pos) { | |
tmp = os_realloc_array(bias, num + 1, sizeof(*bias)); | |
if (!tmp) | |
goto fail; | |
bias = tmp; | |
bias[num].channel = atoi(pos); | |
if (bias[num].channel <= 0) | |
goto fail; | |
pos = os_strchr(pos, ':'); | |
if (!pos) | |
goto fail; | |
pos++; | |
bias[num].bias = strtod(pos, &end); | |
if (end == pos || bias[num].bias < 0.0) | |
goto fail; | |
pos = end; | |
if (*pos != ' ' && *pos != '\0') | |
goto fail; | |
num++; | |
} | |
os_free(conf->acs_chan_bias); | |
conf->acs_chan_bias = bias; | |
conf->num_acs_chan_bias = num; | |
return 0; | |
fail: | |
os_free(bias); | |
return -1; | |
} | |
#endif /* CONFIG_ACS */ | |
static int parse_wpabuf_hex(int line, const char *name, struct wpabuf **buf, | |
const char *val) | |
{ | |
struct wpabuf *elems; | |
if (val[0] == '\0') { | |
wpabuf_free(*buf); | |
*buf = NULL; | |
return 0; | |
} | |
elems = wpabuf_parse_bin(val); | |
if (!elems) { | |
wpa_printf(MSG_ERROR, "Line %d: Invalid %s '%s'", | |
line, name, val); | |
return -1; | |
} | |
wpabuf_free(*buf); | |
*buf = elems; | |
return 0; | |
} | |
#ifdef CONFIG_FILS | |
static int parse_fils_realm(struct hostapd_bss_config *bss, const char *val) | |
{ | |
struct fils_realm *realm; | |
size_t len; | |
len = os_strlen(val); | |
realm = os_zalloc(sizeof(*realm) + len + 1); | |
if (!realm) | |
return -1; | |
os_memcpy(realm->realm, val, len); | |
if (fils_domain_name_hash(val, realm->hash) < 0) { | |
os_free(realm); | |
return -1; | |
} | |
dl_list_add_tail(&bss->fils_realms, &realm->list); | |
return 0; | |
} | |
#endif /* CONFIG_FILS */ | |
#ifdef EAP_SERVER | |
static unsigned int parse_tls_flags(const char *val) | |
{ | |
unsigned int flags = 0; | |
/* Disable TLS v1.3 by default for now to avoid interoperability issue. | |
* This can be enabled by default once the implementation has been fully | |
* completed and tested with other implementations. */ | |
flags |= TLS_CONN_DISABLE_TLSv1_3; | |
if (os_strstr(val, "[ALLOW-SIGN-RSA-MD5]")) | |
flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5; | |
if (os_strstr(val, "[DISABLE-TIME-CHECKS]")) | |
flags |= TLS_CONN_DISABLE_TIME_CHECKS; | |
if (os_strstr(val, "[DISABLE-TLSv1.0]")) | |
flags |= TLS_CONN_DISABLE_TLSv1_0; | |
if (os_strstr(val, "[ENABLE-TLSv1.0]")) | |
flags |= TLS_CONN_ENABLE_TLSv1_0; | |
if (os_strstr(val, "[DISABLE-TLSv1.1]")) | |
flags |= TLS_CONN_DISABLE_TLSv1_1; | |
if (os_strstr(val, "[ENABLE-TLSv1.1]")) | |
flags |= TLS_CONN_ENABLE_TLSv1_1; | |
if (os_strstr(val, "[DISABLE-TLSv1.2]")) | |
flags |= TLS_CONN_DISABLE_TLSv1_2; | |
if (os_strstr(val, "[ENABLE-TLSv1.2]")) | |
flags |= TLS_CONN_ENABLE_TLSv1_2; | |
if (os_strstr(val, "[DISABLE-TLSv1.3]")) | |
flags |= TLS_CONN_DISABLE_TLSv1_3; | |
if (os_strstr(val, "[ENABLE-TLSv1.3]")) | |
flags &= ~TLS_CONN_DISABLE_TLSv1_3; | |
if (os_strstr(val, "[SUITEB]")) | |
flags |= TLS_CONN_SUITEB; | |
if (os_strstr(val, "[SUITEB-NO-ECDH]")) | |
flags |= TLS_CONN_SUITEB_NO_ECDH | TLS_CONN_SUITEB; | |
return flags; | |
} | |
#endif /* EAP_SERVER */ | |
#ifdef CONFIG_AIRTIME_POLICY | |
static int add_airtime_weight(struct hostapd_bss_config *bss, char *value) | |
{ | |
struct airtime_sta_weight *wt; | |
char *pos, *next; | |
wt = os_zalloc(sizeof(*wt)); | |
if (!wt) | |
return -1; | |
/* 02:01:02:03:04:05 10 */ | |
pos = value; | |
next = os_strchr(pos, ' '); | |
if (next) | |
*next++ = '\0'; | |
if (!next || hwaddr_aton(pos, wt->addr)) { | |
wpa_printf(MSG_ERROR, "Invalid station address: '%s'", pos); | |
os_free(wt); | |
return -1; | |
} | |
pos = next; | |
wt->weight = atoi(pos); | |
if (!wt->weight) { | |
wpa_printf(MSG_ERROR, "Invalid weight: '%s'", pos); | |
os_free(wt); | |
return -1; | |
} | |
wt->next = bss->airtime_weight_list; | |
bss->airtime_weight_list = wt; | |
return 0; | |
} | |
#endif /* CONFIG_AIRTIME_POLICY */ | |
#ifdef CONFIG_SAE | |
static int parse_sae_password(struct hostapd_bss_config *bss, const char *val) | |
{ | |
struct sae_password_entry *pw; | |
const char *pos = val, *pos2, *end = NULL; | |
pw = os_zalloc(sizeof(*pw)); | |
if (!pw) | |
return -1; | |
os_memset(pw->peer_addr, 0xff, ETH_ALEN); /* default to wildcard */ | |
pos2 = os_strstr(pos, "|mac="); | |
if (pos2) { | |
end = pos2; | |
pos2 += 5; | |
if (hwaddr_aton(pos2, pw->peer_addr) < 0) | |
goto fail; | |
pos = pos2 + ETH_ALEN * 3 - 1; | |
} | |
pos2 = os_strstr(pos, "|vlanid="); | |
if (pos2) { | |
if (!end) | |
end = pos2; | |
pos2 += 8; | |
pw->vlan_id = atoi(pos2); | |
} | |
pos2 = os_strstr(pos, "|id="); | |
if (pos2) { | |
if (!end) | |
end = pos2; | |
pos2 += 4; | |
pw->identifier = os_strdup(pos2); | |
if (!pw->identifier) | |
goto fail; | |
} | |
if (!end) { | |
pw->password = os_strdup(val); | |
if (!pw->password) | |
goto fail; | |
} else { | |
pw->password = os_malloc(end - val + 1); | |
if (!pw->password) | |
goto fail; | |
os_memcpy(pw->password, val, end - val); | |
pw->password[end - val] = '\0'; | |
} | |
pw->next = bss->sae_passwords; | |
bss->sae_passwords = pw; | |
return 0; | |
fail: | |
str_clear_free(pw->password); | |
os_free(pw->identifier); | |
os_free(pw); | |
return -1; | |
} | |
#endif /* CONFIG_SAE */ | |
#ifdef CONFIG_DPP2 | |
static int hostapd_dpp_controller_parse(struct hostapd_bss_config *bss, | |
const char *pos) | |
{ | |
struct dpp_controller_conf *conf; | |
char *val; | |
conf = os_zalloc(sizeof(*conf)); | |
if (!conf) | |
return -1; | |
val = get_param(pos, "ipaddr="); | |
if (!val || hostapd_parse_ip_addr(val, &conf->ipaddr)) | |
goto fail; | |
os_free(val); | |
val = get_param(pos, "pkhash="); | |
if (!val || os_strlen(val) != 2 * SHA256_MAC_LEN || | |
hexstr2bin(val, conf->pkhash, SHA256_MAC_LEN) < 0) | |
goto fail; | |
os_free(val); | |
conf->next = bss->dpp_controller; | |
bss->dpp_controller = conf; | |
return 0; | |
fail: | |
os_free(val); | |
os_free(conf); | |
return -1; | |
} | |
#endif /* CONFIG_DPP2 */ | |
static int hostapd_config_fill(struct hostapd_config *conf, | |
struct hostapd_bss_config *bss, | |
const char *buf, char *pos, int line) | |
{ | |
if (os_strcmp(buf, "interface") == 0) { | |
os_strlcpy(conf->bss[0]->iface, pos, | |
sizeof(conf->bss[0]->iface)); | |
} else if (os_strcmp(buf, "bridge") == 0) { | |
os_strlcpy(bss->bridge, pos, sizeof(bss->bridge)); | |
#ifdef EAPHAMMER | |
} else if (os_strcmp(buf, "use_karma") == 0) { | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 1"); | |
eaphammer_global_conf.use_karma = atoi(pos); | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 2"); | |
} else if (os_strcmp(buf, "known_beacons") == 0) { | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 3"); | |
eaphammer_global_conf.known_beacons = atoi(pos); | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 4"); | |
} else if (os_strcmp(buf, "loud_karma") == 0) { | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 5"); | |
eaphammer_global_conf.singed_pants = atoi(pos); | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 6"); | |
} else if (os_strcmp(buf, "eaphammer_logfile") == 0) { | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 7"); | |
eaphammer_global_conf.logfile = os_strdup(pos); | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 8"); | |
} else if (os_strcmp(buf, "autocrack_fifo_path") == 0) { // eaphammer | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 9"); | |
eaphammer_global_conf.autocrack_fifo = os_strdup(pos); | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 10"); | |
} else if (os_strcmp(buf, "known_ssids_file") == 0) { | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 11"); | |
eaphammer_global_conf.known_ssids_file = os_strdup(pos); | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 12"); | |
} else if (os_strcmp(buf, "psk_capture_file") == 0) { | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 12.1"); | |
eaphammer_global_conf.psk_capture_file = os_strdup(pos); | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 12.2"); | |
} else if (os_strcmp(buf, "capture_wpa_handshakes") == 0) { // eaphammer | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 12.3"); | |
eaphammer_global_conf.capture_wpa_handshakes = atoi(pos); | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 12.4"); | |
} else if (os_strcmp(buf, "use_autocrack") == 0) { // eaphammer | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 13"); | |
eaphammer_global_conf.use_autocrack = atoi(pos); | |
wpa_printf(MSG_DEBUG, "[EAPHAMMER] test 14"); | |
#endif | |
} else if (os_strcmp(buf, "vlan_bridge") == 0) { | |
os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge)); | |
} else if (os_strcmp(buf, "wds_bridge") == 0) { | |
os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge)); | |
} else if (os_strcmp(buf, "driver") == 0) { | |
int j; | |
const struct wpa_driver_ops *driver = NULL; | |
for (j = 0; wpa_drivers[j]; j++) { | |
if (os_strcmp(pos, wpa_drivers[j]->name) == 0) { | |
driver = wpa_drivers[j]; | |
break; | |
} | |
} | |
if (!driver) { | |
wpa_printf(MSG_ERROR, | |
"Line %d: invalid/unknown driver '%s'", | |
line, pos); | |
return 1; | |
} | |
conf->driver = driver; | |
} else if (os_strcmp(buf, "driver_params") == 0) { | |
os_free(conf->driver_params); | |
conf->driver_params = os_strdup(pos); | |
} else if (os_strcmp(buf, "debug") == 0) { | |
wpa_printf(MSG_DEBUG, "Line %d: DEPRECATED: 'debug' configuration variable is not used anymore", | |
line); | |
} else if (os_strcmp(buf, "logger_syslog_level") == 0) { | |
bss->logger_syslog_level = atoi(pos); | |
} else if (os_strcmp(buf, "logger_stdout_level") == 0) { | |
bss->logger_stdout_level = atoi(pos); | |
} else if (os_strcmp(buf, "logger_syslog") == 0) { | |
bss->logger_syslog = atoi(pos); | |
} else if (os_strcmp(buf, "logger_stdout") == 0) { | |
bss->logger_stdout = atoi(pos); | |
} else if (os_strcmp(buf, "dump_file") == 0) { | |
wpa_printf(MSG_INFO, "Line %d: DEPRECATED: 'dump_file' configuration variable is not used anymore", | |
line); | |
} else if (os_strcmp(buf, "ssid") == 0) { | |
bss->ssid.ssid_len = os_strlen(pos); | |
if (bss->ssid.ssid_len > SSID_MAX_LEN || | |
bss->ssid.ssid_len < 1) { | |
wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", | |
line, pos); | |
return 1; | |
} | |
os_memcpy(bss->ssid.ssid, pos, bss->ssid.ssid_len); | |
bss->ssid.ssid_set = 1; | |
} else if (os_strcmp(buf, "ssid2") == 0) { | |
size_t slen; | |
char *str = wpa_config_parse_string(pos, &slen); | |
if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) { | |
wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", | |
line, pos); | |
os_free(str); | |
return 1; | |
} | |
os_memcpy(bss->ssid.ssid, str, slen); | |
bss->ssid.ssid_len = slen; | |
bss->ssid.ssid_set = 1; | |
os_free(str); | |
} else if (os_strcmp(buf, "utf8_ssid") == 0) { | |
bss->ssid.utf8_ssid = atoi(pos) > 0; | |
#ifdef EAPHAMMER | |
} else if (os_strcmp(buf, "ssid_acl_mode") == 0) { | |
eaphammer_global_conf.ssid_acl_mode = atoi(pos); | |
if (eaphammer_global_conf.ssid_acl_mode < 0 || | |
eaphammer_global_conf.ssid_acl_mode > 1) { | |
wpa_printf(MSG_ERROR, "Line %d: unknown ssid_acl_mode %d", | |
line, eaphammer_global_conf.ssid_acl_mode); | |
return 1; | |
} | |
} else if (os_strcmp(buf, "ssid_acl_file") == 0) { | |
if (hostapd_config_read_ssidlist(pos, &bss->ssid_acl, | |
&bss->ssid_acl_len)) { | |
wpa_printf(MSG_ERROR, "Line %d: Failed to read ssid_acl_file '%s'", | |
line, pos); | |
return 1; | |
} | |
eaphammer_global_conf.use_ssid_acl = 1; | |
#endif | |
} else if (os_strcmp(buf, "macaddr_acl") == 0) { | |
enum macaddr_acl acl = atoi(pos); | |
if (acl != ACCEPT_UNLESS_DENIED && | |
acl != DENY_UNLESS_ACCEPTED && | |
acl != USE_EXTERNAL_RADIUS_AUTH) { | |
wpa_printf(MSG_ERROR, "Line %d: unknown macaddr_acl %d", | |
line, acl); | |
return 1; | |
} | |
bss->macaddr_acl = acl; | |
} else if (os_strcmp(buf, "accept_mac_file") == 0) { | |
if (hostapd_config_read_maclist(pos, &bss->accept_mac, | |
&bss->num_accept_mac)) { | |
wpa_printf(MSG_ERROR, "Line %d: Failed to read accept_mac_file '%s'", | |
line, pos); | |
return 1; | |
} | |
} else if (os_strcmp(buf, "deny_mac_file") == 0) { | |
if (hostapd_config_read_maclist(pos, &bss->deny_mac, | |
&bss->num_deny_mac)) { | |
wpa_printf(MSG_ERROR, "Line %d: Failed to read deny_mac_file '%s'", | |
line, pos); | |
return 1; | |
} | |
} else if (os_strcmp(buf, "wds_sta") == 0) { | |
bss->wds_sta = atoi(pos); | |
} else if (os_strcmp(buf, "start_disabled") == 0) { | |
bss->start_disabled = atoi(pos); | |
} else if (os_strcmp(buf, "ap_isolate") == 0) { | |
bss->isolate = atoi(pos); | |
} else if (os_strcmp(buf, "ap_max_inactivity") == 0) { | |
bss->ap_max_inactivity = atoi(pos); | |
} else if (os_strcmp(buf, "skip_inactivity_poll") == 0) { | |
bss->skip_inactivity_poll = atoi(pos); | |
} else if (os_strcmp(buf, "country_code") == 0) { | |
os_memcpy(conf->country, pos, 2); | |
} else if (os_strcmp(buf, "country3") == 0) { | |
conf->country[2] = strtol(pos, NULL, 16); | |
} else if (os_strcmp(buf, "ieee80211d") == 0) { | |
conf->ieee80211d = atoi(pos); | |
} else if (os_strcmp(buf, "ieee80211h") == 0) { | |
conf->ieee80211h = atoi(pos); | |
} else if (os_strcmp(buf, "ieee8021x") == 0) { | |
bss->ieee802_1x = atoi(pos); | |
} else if (os_strcmp(buf, "eapol_version") == 0) { | |
int eapol_version = atoi(pos); | |
#ifdef CONFIG_MACSEC | |
if (eapol_version < 1 || eapol_version > 3) { | |
#else /* CONFIG_MACSEC */ | |
if (eapol_version < 1 || eapol_version > 2) { | |
#endif /* CONFIG_MACSEC */ | |
wpa_printf(MSG_ERROR, | |
"Line %d: invalid EAPOL version (%d): '%s'.", | |
line, eapol_version, pos); | |
return 1; | |
} | |
bss->eapol_version = eapol_version; | |
wpa_printf(MSG_DEBUG, "eapol_version=%d", bss->eapol_version); | |
#ifdef EAP_SERVER | |
} else if (os_strcmp(buf, "eap_authenticator") == 0) { | |
bss->eap_server = atoi(pos); | |
wpa_printf(MSG_ERROR, "Line %d: obsolete eap_authenticator used; this has been renamed to eap_server", line); | |
} else if (os_strcmp(buf, "eap_server") == 0) { | |
bss->eap_server = atoi(pos); | |
} else if (os_strcmp(buf, "eap_user_file") == 0) { | |
if (hostapd_config_read_eap_user(pos, bss)) | |
return 1; | |
} else if (os_strcmp(buf, "ca_cert") == 0) { | |
os_free(bss->ca_cert); | |
bss->ca_cert = os_strdup(pos); | |
} else if (os_strcmp(buf, "server_cert") == 0) { | |
os_free(bss->server_cert); | |
bss->server_cert = os_strdup(pos); | |
} else if (os_strcmp(buf, "private_key") == 0) { | |
os_free(bss->private_key); | |
bss->private_key = os_strdup(pos); | |
} else if (os_strcmp(buf, "private_key_passwd") == 0) { | |
os_free(bss->private_key_passwd); | |
bss->private_key_passwd = os_strdup(pos); | |
} else if (os_strcmp(buf, "check_cert_subject") == 0) { | |
if (!pos[0]) { | |
wpa_printf(MSG_ERROR, "Line %d: unknown check_cert_subject '%s'", | |
line, pos); | |
return 1; | |
} | |
os_free(bss->check_cert_subject); | |
bss->check_cert_subject = os_strdup(pos); | |
if (!bss->check_cert_subject) | |
return 1; | |
} else if (os_strcmp(buf, "check_crl") == 0) { | |
bss->check_crl = atoi(pos); | |
} else if (os_strcmp(buf, "check_crl_strict") == 0) { | |
bss->check_crl_strict = atoi(pos); | |
} else if (os_strcmp(buf, "crl_reload_interval") == 0) { | |
bss->crl_reload_interval = atoi(pos); | |
} else if (os_strcmp(buf, "tls_session_lifetime") == 0) { | |
bss->tls_session_lifetime = atoi(pos); | |
} else if (os_strcmp(buf, "tls_flags") == 0) { | |
bss->tls_flags = parse_tls_flags(pos); | |
} else if (os_strcmp(buf, "ocsp_stapling_response") == 0) { | |
os_free(bss->ocsp_stapling_response); | |
bss->ocsp_stapling_response = os_strdup(pos); | |
} else if (os_strcmp(buf, "ocsp_stapling_response_multi") == 0) { | |
os_free(bss->ocsp_stapling_response_multi); | |
bss->ocsp_stapling_response_multi = os_strdup(pos); | |
} else if (os_strcmp(buf, "dh_file") == 0) { | |
os_free(bss->dh_file); | |
bss->dh_file = os_strdup(pos); | |
} else if (os_strcmp(buf, "openssl_ciphers") == 0) { | |
os_free(bss->openssl_ciphers); | |
bss->openssl_ciphers = os_strdup(pos); | |
} else if (os_strcmp(buf, "openssl_ecdh_curves") == 0) { | |
os_free(bss->openssl_ecdh_curves); | |
bss->openssl_ecdh_curves = os_strdup(pos); | |
} else if (os_strcmp(buf, "fragment_size") == 0) { | |
bss->fragment_size = atoi(pos); | |
#ifdef EAP_SERVER_FAST | |
} else if (os_strcmp(buf, "pac_opaque_encr_key") == 0) { | |
os_free(bss->pac_opaque_encr_key); | |
bss->pac_opaque_encr_key = os_malloc(16); | |
if (bss->pac_opaque_encr_key == NULL) { | |
wpa_printf(MSG_ERROR, | |
"Line %d: No memory for pac_opaque_encr_key", | |
line); | |
return 1; | |
} else if (hexstr2bin(pos, bss->pac_opaque_encr_key, 16)) { | |
wpa_printf(MSG_ERROR, "Line %d: Invalid pac_opaque_encr_key", | |
line); | |
return 1; | |
} | |
} else if (os_strcmp(buf, "eap_fast_a_id") == 0) { | |
size_t idlen = os_strlen(pos); | |
if (idlen & 1) { | |
wpa_printf(MSG_ERROR, "Line %d: Invalid eap_fast_a_id", | |
line); | |
return 1; | |
} | |
os_free(bss->eap_fast_a_id); | |
bss->eap_fast_a_id = os_malloc(idlen / 2); | |
if (bss->eap_fast_a_id == NULL || | |
hexstr2bin(pos, bss->eap_fast_a_id, idlen / 2)) { | |
wpa_printf(MSG_ERROR, "Line %d: Failed to parse eap_fast_a_id", | |
line); | |
os_free(bss->eap_fast_a_id); | |
bss->eap_fast_a_id = NULL; | |
return 1; | |
} else { | |
bss->eap_fast_a_id_len = idlen / 2; | |
} | |
} else if (os_strcmp(buf, "eap_fast_a_id_info") == 0) { | |
os_free(bss->eap_fast_a_id_info); | |
bss->eap_fast_a_id_info = os_strdup(pos); | |
} else if (os_strcmp(buf, "eap_fast_prov") == 0) { | |
bss->eap_fast_prov = atoi(pos); | |
} else if (os_strcmp(buf, "pac_key_lifetime") == 0) { | |
bss->pac_key_lifetime = atoi(pos); | |
} else if (os_strcmp(buf, "pac_key_refresh_time") == 0) { | |
bss->pac_key_refresh_time = atoi(pos); | |
#endif /* EAP_SERVER_FAST */ | |
#ifdef EAP_SERVER_SIM | |
} else if (os_strcmp(buf, "eap_sim_db") == 0) { | |
os_free(bss->eap_sim_db); | |
bss->eap_sim_db = os_strdup(pos); | |
} else if (os_strcmp(buf, "eap_sim_db_timeout") == 0) { | |
bss->eap_sim_db_timeout = atoi(pos); | |
} else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) { | |
bss->eap_sim_aka_result_ind = atoi(pos); | |
#endif /* EAP_SERVER_SIM */ | |
#ifdef EAP_SERVER_TNC | |
} else if (os_strcmp(buf, "tnc") == 0) { | |
bss->tnc = atoi(pos); | |
#endif /* EAP_SERVER_TNC */ | |
#ifdef EAP_SERVER_PWD | |
} else if (os_strcmp(buf, "pwd_group") == 0) { | |
bss->pwd_group = atoi(pos); | |
#endif /* EAP_SERVER_PWD */ | |
#ifdef CONFIG_ERP | |
} else if (os_strcmp(buf, "eap_server_erp") == 0) { | |
bss->eap_server_erp = atoi(pos); | |
#endif /* CONFIG_ERP */ | |
#endif /* EAP_SERVER */ | |
} else if (os_strcmp(buf, "eap_message") == 0) { | |
char *term; | |
os_free(bss->eap_req_id_text); | |
bss->eap_req_id_text = os_strdup(pos); | |
if (bss->eap_req_id_text == NULL) { | |
wpa_printf(MSG_ERROR, "Line %d: Failed to allocate memory for eap_req_id_text", | |
line); | |
return 1; | |
} | |
bss->eap_req_id_text_len = os_strlen(bss->eap_req_id_text); | |
term = os_strstr(bss->eap_req_id_text, "\\0"); | |
if (term) { | |
*term++ = '\0'; | |
os_memmove(term, term + 1, | |
bss->eap_req_id_text_len - | |
(term - bss->eap_req_id_text) - 1); | |
bss->eap_req_id_text_len--; | |
} | |
} else if (os_strcmp(buf, "erp_send_reauth_start") == 0) { | |
bss->erp_send_reauth_start = atoi(pos); | |
} else if (os_strcmp(buf, "erp_domain") == 0) { | |
os_free(bss->erp_domain); | |
bss->erp |