diff --git a/hostapd/Makefile b/hostapd/Makefile index 6fe282c08..4cc38059b 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -43,6 +43,7 @@ OBJS += ../src/ap/utils.o OBJS += ../src/ap/authsrv.o OBJS += ../src/ap/ieee802_1x.o OBJS += ../src/ap/ap_config.o +OBJS += ../src/ap/eap_user_db.o OBJS += ../src/ap/ieee802_11_auth.o OBJS += ../src/ap/sta_info.o OBJS += ../src/ap/wpa_auth.o diff --git a/hostapd/config_file.c b/hostapd/config_file.c index f9b398431..7c9f941c2 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -200,6 +200,12 @@ static int hostapd_config_read_eap_user(const char *fname, if (!fname) return 0; + if (os_strncmp(fname, "sqlite:", 7) == 0) { + os_free(conf->eap_user_sqlite); + conf->eap_user_sqlite = os_strdup(fname + 7); + return 0; + } + f = fopen(fname, "r"); if (!f) { wpa_printf(MSG_ERROR, "EAP user file '%s' not found.", fname); diff --git a/hostapd/defconfig b/hostapd/defconfig index 204aa7683..b5ddca34c 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -265,5 +265,5 @@ CONFIG_IPV6=y # Hotspot 2.0 #CONFIG_HS20=y -# Enable SQLite database support in hlr_auc_gw +# Enable SQLite database support in hlr_auc_gw, EAP-SIM DB, and eap_user_file #CONFIG_SQLITE=y diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index edbd77234..5f0eb9666 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -629,6 +629,8 @@ eapol_key_index_workaround=0 eap_server=0 # Path for EAP server user database +# If SQLite support is included, this can be set to "sqlite:/path/to/sqlite.db" +# to use SQLite database instead of a text file. #eap_user_file=/etc/hostapd.eap_user # CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS diff --git a/hostapd/hostapd.eap_user_sqlite b/hostapd/hostapd.eap_user_sqlite new file mode 100644 index 000000000..f68832710 --- /dev/null +++ b/hostapd/hostapd.eap_user_sqlite @@ -0,0 +1,17 @@ +CREATE TABLE users( + identity TEXT PRIMARY KEY, + methods TEXT, + password TEXT, + phase2 INTEGER +); + +CREATE TABLE wildcards( + identity TEXT PRIMARY KEY, + methods TEXT +); + +INSERT INTO users(identity,methods,password,phase2) VALUES ('user','TTLS-MSCHAPV2','password',1); +INSERT INTO users(identity,methods,password,phase2) VALUES ('DOMAIN\mschapv2 user','TTLS-MSCHAPV2','password',1); + +INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS'); +INSERT INTO wildcards(identity,methods) VALUES ('0','AKA'); diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 31e1c19cc..3c699f71e 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -408,6 +408,7 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) user = user->next; hostapd_config_free_eap_user(prev_user); } + os_free(conf->eap_user_sqlite); os_free(conf->dump_log_name); os_free(conf->eap_req_id_text); @@ -619,57 +620,3 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, return NULL; } - - -const struct hostapd_eap_user * -hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, - size_t identity_len, int phase2) -{ - struct hostapd_eap_user *user = conf->eap_user; - -#ifdef CONFIG_WPS - if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN && - os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) { - static struct hostapd_eap_user wsc_enrollee; - os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee)); - wsc_enrollee.methods[0].method = eap_server_get_type( - "WSC", &wsc_enrollee.methods[0].vendor); - return &wsc_enrollee; - } - - if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN && - os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) { - static struct hostapd_eap_user wsc_registrar; - os_memset(&wsc_registrar, 0, sizeof(wsc_registrar)); - wsc_registrar.methods[0].method = eap_server_get_type( - "WSC", &wsc_registrar.methods[0].vendor); - wsc_registrar.password = (u8 *) conf->ap_pin; - wsc_registrar.password_len = conf->ap_pin ? - os_strlen(conf->ap_pin) : 0; - return &wsc_registrar; - } -#endif /* CONFIG_WPS */ - - while (user) { - if (!phase2 && user->identity == NULL) { - /* Wildcard match */ - break; - } - - if (user->phase2 == !!phase2 && user->wildcard_prefix && - identity_len >= user->identity_len && - os_memcmp(user->identity, identity, user->identity_len) == - 0) { - /* Wildcard prefix match */ - break; - } - - if (user->phase2 == !!phase2 && - user->identity_len == identity_len && - os_memcmp(user->identity, identity, identity_len) == 0) - break; - user = user->next; - } - - return user; -} diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index f5e4a6a59..0a83856d0 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -192,6 +192,7 @@ struct hostapd_bss_config { int eap_server; /* Use internal EAP server instead of external * RADIUS server */ struct hostapd_eap_user *eap_user; + char *eap_user_sqlite; char *eap_sim_db; struct hostapd_ip_addr own_ip_addr; char *nas_identifier; @@ -523,9 +524,6 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id); -const struct hostapd_eap_user * -hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, - size_t identity_len, int phase2); struct hostapd_radius_attr * hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type); diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 5c03f45c4..d66d97e4a 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -92,7 +92,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) os_memset(&srv, 0, sizeof(srv)); srv.client_file = conf->radius_server_clients; srv.auth_port = conf->radius_server_auth_port; - srv.conf_ctx = conf; + srv.conf_ctx = hapd; srv.eap_sim_db_priv = hapd->eap_sim_db_priv; srv.ssl_ctx = hapd->ssl_ctx; srv.msg_ctx = hapd->msg_ctx; diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c new file mode 100644 index 000000000..79d50e516 --- /dev/null +++ b/src/ap/eap_user_db.c @@ -0,0 +1,270 @@ +/* + * hostapd / EAP user database + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ + +#include "common.h" +#include "eap_common/eap_wsc_common.h" +#include "eap_server/eap_methods.h" +#include "eap_server/eap.h" +#include "ap_config.h" +#include "hostapd.h" + +#ifdef CONFIG_SQLITE + +static void set_user_methods(struct hostapd_eap_user *user, const char *methods) +{ + char *buf, *start; + int num_methods; + + buf = os_strdup(methods); + if (buf == NULL) + return; + + os_memset(&user->methods, 0, sizeof(user->methods)); + num_methods = 0; + start = buf; + 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; + } + wpa_printf(MSG_INFO, "DB: Unsupported EAP type '%s'", + start); + os_free(buf); + return; + } + + num_methods++; + if (num_methods >= EAP_MAX_METHODS) + break; + skip_eap: + if (pos3 == NULL) + break; + start = pos3; + } + + os_free(buf); +} + + +static int get_user_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct hostapd_eap_user *user = ctx; + int i; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "password") == 0 && argv[i]) { + os_free(user->password); + user->password_len = os_strlen(argv[i]); + user->password = (u8 *) os_strdup(argv[i]); + user->next = (void *) 1; + } else if (os_strcmp(col[i], "methods") == 0 && argv[i]) { + set_user_methods(user, argv[i]); + } + } + + return 0; +} + + +static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct hostapd_eap_user *user = ctx; + int i, id = -1, methods = -1; + size_t len; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "identity") == 0 && argv[i]) + id = i; + else if (os_strcmp(col[i], "methods") == 0 && argv[i]) + methods = i; + } + + if (id < 0 || methods < 0) + return 0; + + len = os_strlen(argv[id]); + if (len <= user->identity_len && + os_memcmp(argv[id], user->identity, len) == 0 && + (user->password == NULL || len > user->password_len)) { + os_free(user->password); + user->password_len = os_strlen(argv[id]); + user->password = (u8 *) os_strdup(argv[id]); + user->next = (void *) 1; + set_user_methods(user, argv[methods]); + } + + return 0; +} + + +static const struct hostapd_eap_user * +eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2) +{ + sqlite3 *db; + struct hostapd_eap_user *user = NULL; + char id_str[256], cmd[300]; + size_t i; + + if (identity_len >= sizeof(id_str)) + return NULL; + os_memcpy(id_str, identity, identity_len); + id_str[identity_len] = '\0'; + for (i = 0; i < identity_len; i++) { + if (id_str[i] >= 'a' && id_str[i] <= 'z') + continue; + if (id_str[i] >= 'A' && id_str[i] <= 'Z') + continue; + if (id_str[i] >= '0' && id_str[i] <= '9') + continue; + if (id_str[i] == '-' || id_str[i] == '_' || id_str[i] == '.' || + id_str[i] == ',' || id_str[i] == '@' || id_str[i] == '\\' || + id_str[i] == '!' || id_str[i] == '#' || id_str[i] == '%' || + id_str[i] == '=' || id_str[i] == ' ') + continue; + wpa_printf(MSG_INFO, "DB: Unsupported character in identity"); + return NULL; + } + + os_free(hapd->tmp_eap_user.identity); + os_free(hapd->tmp_eap_user.password); + os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user)); + hapd->tmp_eap_user.phase2 = phase2; + hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1); + if (hapd->tmp_eap_user.identity == NULL) + return NULL; + os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len); + + if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) { + wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s", + hapd->conf->eap_user_sqlite, sqlite3_errmsg(db)); + sqlite3_close(db); + return NULL; + } + + os_snprintf(cmd, sizeof(cmd), + "SELECT password,methods FROM users WHERE " + "identity='%s' AND phase2=%d;", id_str, phase2); + wpa_printf(MSG_DEBUG, "DB: %s", cmd); + if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != + SQLITE_OK) { + wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL operation"); + } else if (hapd->tmp_eap_user.next) + user = &hapd->tmp_eap_user; + + if (user == NULL && !phase2) { + os_snprintf(cmd, sizeof(cmd), + "SELECT identity,methods FROM wildcards;"); + wpa_printf(MSG_DEBUG, "DB: %s", cmd); + if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user, + NULL) != SQLITE_OK) { + wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL " + "operation"); + } else if (hapd->tmp_eap_user.next) { + user = &hapd->tmp_eap_user; + os_free(user->identity); + user->identity = user->password; + user->identity_len = user->password_len; + user->password = NULL; + user->password_len = 0; + } + } + + sqlite3_close(db); + + return user; +} + +#endif /* CONFIG_SQLITE */ + + +const struct hostapd_eap_user * +hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2) +{ + const struct hostapd_bss_config *conf = hapd->conf; + struct hostapd_eap_user *user = conf->eap_user; + +#ifdef CONFIG_WPS + if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) { + static struct hostapd_eap_user wsc_enrollee; + os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee)); + wsc_enrollee.methods[0].method = eap_server_get_type( + "WSC", &wsc_enrollee.methods[0].vendor); + return &wsc_enrollee; + } + + if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) { + static struct hostapd_eap_user wsc_registrar; + os_memset(&wsc_registrar, 0, sizeof(wsc_registrar)); + wsc_registrar.methods[0].method = eap_server_get_type( + "WSC", &wsc_registrar.methods[0].vendor); + wsc_registrar.password = (u8 *) conf->ap_pin; + wsc_registrar.password_len = conf->ap_pin ? + os_strlen(conf->ap_pin) : 0; + return &wsc_registrar; + } +#endif /* CONFIG_WPS */ + + while (user) { + if (!phase2 && user->identity == NULL) { + /* Wildcard match */ + break; + } + + if (user->phase2 == !!phase2 && user->wildcard_prefix && + identity_len >= user->identity_len && + os_memcmp(user->identity, identity, user->identity_len) == + 0) { + /* Wildcard prefix match */ + break; + } + + if (user->phase2 == !!phase2 && + user->identity_len == identity_len && + os_memcmp(user->identity, identity, identity_len) == 0) + break; + user = user->next; + } + +#ifdef CONFIG_SQLITE + if (user == NULL && conf->eap_user_sqlite) { + return eap_user_sqlite_get(hapd, identity, identity_len, + phase2); + } +#endif /* CONFIG_SQLITE */ + + return user; +} diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 1c968a7a7..cef9dafc5 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -273,6 +273,11 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) #ifdef CONFIG_INTERWORKING gas_serv_deinit(hapd); #endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_SQLITE + os_free(hapd->tmp_eap_user.identity); + os_free(hapd->tmp_eap_user.password); +#endif /* CONFIG_SQLITE */ } diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 71f476c0e..f1e7d9ff7 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -10,6 +10,7 @@ #define HOSTAPD_H #include "common/defs.h" +#include "ap_config.h" struct wpa_driver_ops; struct wpa_ctrl_dst; @@ -187,6 +188,10 @@ struct hostapd_data { #ifdef CONFIG_INTERWORKING size_t gas_frag_limit; #endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_SQLITE + struct hostapd_eap_user tmp_eap_user; +#endif /* CONFIG_SQLITE */ }; @@ -297,4 +302,8 @@ int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, int offset); +const struct hostapd_eap_user * +hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2); + #endif /* HOSTAPD_H */ diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index e87431ef1..7ac1d3e63 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -1686,8 +1686,7 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, const struct hostapd_eap_user *eap_user; int i; - eap_user = hostapd_get_eap_user(hapd->conf, identity, - identity_len, phase2); + eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2); if (eap_user == NULL) return -1;