diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 29ea92e0d..1f2c56583 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2832,6 +2832,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, a = a->next; a->next = attr; } + } else if (os_strcmp(buf, "radius_req_attr_sqlite") == 0) { + os_free(bss->radius_req_attr_sqlite); + bss->radius_req_attr_sqlite = os_strdup(pos); } else if (os_strcmp(buf, "radius_das_port") == 0) { bss->radius_das_port = atoi(pos); } else if (os_strcmp(buf, "radius_das_client") == 0) { diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index f2d587388..5138aeebc 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1384,6 +1384,17 @@ own_ip_addr=127.0.0.1 # Operator-Name = "Operator" #radius_acct_req_attr=126:s:Operator +# If SQLite support is included, path to a database from which additional +# RADIUS request attributes are extracted based on the station MAC address. +# +# The schema for the radius_attributes table is: +# id | sta | reqtype | attr : multi-key (sta, reqtype) +# id = autonumber +# sta = station MAC address in `11:22:33:44:55:66` format. +# type = `auth` | `acct` | NULL (match any) +# attr = existing config file format, e.g. `126:s:Test Operator` +#radius_req_attr_sqlite=radius_attr.sqlite + # Dynamic Authorization Extensions (RFC 5176) # This mechanism can be used to allow dynamic changes to user session based on # commands from a RADIUS server (or some other disconnect client that has the diff --git a/src/ap/accounting.c b/src/ap/accounting.c index 0aacc3c95..9fc1886a2 100644 --- a/src/ap/accounting.c +++ b/src/ap/accounting.c @@ -97,6 +97,9 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, msg) < 0) goto fail; + if (sta && add_sqlite_radius_attr(hapd, sta, msg, 1) < 0) + goto fail; + if (sta) { for (i = 0; ; i++) { val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 258a20572..968eb65a6 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -545,7 +545,7 @@ struct hostapd_radius_attr * hostapd_parse_radius_attr(const char *value) } -static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr) +void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr) { struct hostapd_radius_attr *prev; @@ -694,6 +694,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) } hostapd_config_free_radius_attr(conf->radius_auth_req_attr); hostapd_config_free_radius_attr(conf->radius_acct_req_attr); + os_free(conf->radius_req_attr_sqlite); os_free(conf->rsn_preauth_interfaces); os_free(conf->ctrl_interface); os_free(conf->ca_cert); diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index f42505e44..0a1d49b71 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -301,6 +301,7 @@ struct hostapd_bss_config { int radius_request_cui; struct hostapd_radius_attr *radius_auth_req_attr; struct hostapd_radius_attr *radius_acct_req_attr; + char *radius_req_attr_sqlite; int radius_das_port; unsigned int radius_das_time_window; int radius_das_require_event_timestamp; @@ -1074,6 +1075,7 @@ hostapd_set_oper_centr_freq_seg1_idx(struct hostapd_config *conf, int hostapd_mac_comp(const void *a, const void *b); struct hostapd_config * hostapd_config_defaults(void); void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); +void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr); void hostapd_config_free_eap_user(struct hostapd_eap_user *user); void hostapd_config_free_eap_users(struct hostapd_eap_user *user); void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p); diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index cc75a7765..bf1975fbd 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -7,6 +7,9 @@ */ #include "utils/includes.h" +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ #include "utils/common.h" #include "utils/eloop.h" @@ -1025,6 +1028,43 @@ hostapd_das_coa(void *ctx, struct radius_das_attrs *attr) #define hostapd_das_coa NULL #endif /* CONFIG_HS20 */ + +#ifdef CONFIG_SQLITE + +static int db_table_exists(sqlite3 *db, const char *name) +{ + char cmd[128]; + + os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); + return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; +} + + +static int db_table_create_radius_attributes(sqlite3 *db) +{ + char *err = NULL; + const char *sql = + "CREATE TABLE radius_attributes(" + " id INTEGER PRIMARY KEY," + " sta TEXT," + " reqtype TEXT," + " attr TEXT" + ");" + "CREATE INDEX idx_sta_reqtype ON radius_attributes(sta,reqtype);"; + + wpa_printf(MSG_DEBUG, + "Adding database table for RADIUS attribute information"); + if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { + wpa_printf(MSG_ERROR, "SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + +#endif /* CONFIG_SQLITE */ + #endif /* CONFIG_NO_RADIUS */ @@ -1178,6 +1218,24 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) if (wpa_debug_level <= MSG_MSGDUMP) conf->radius->msg_dumps = 1; #ifndef CONFIG_NO_RADIUS + +#ifdef CONFIG_SQLITE + if (conf->radius_req_attr_sqlite) { + if (sqlite3_open(conf->radius_req_attr_sqlite, + &hapd->rad_attr_db)) { + wpa_printf(MSG_ERROR, "Could not open SQLite file '%s'", + conf->radius_req_attr_sqlite); + return -1; + } + + wpa_printf(MSG_DEBUG, "Opening RADIUS attribute database: %s", + conf->radius_req_attr_sqlite); + if (!db_table_exists(hapd->rad_attr_db, "radius_attributes") && + db_table_create_radius_attributes(hapd->rad_attr_db) < 0) + return -1; + } +#endif /* CONFIG_SQLITE */ + hapd->radius = radius_client_init(hapd, conf->radius); if (hapd->radius == NULL) { wpa_printf(MSG_ERROR, "RADIUS client initialization failed."); @@ -2194,6 +2252,12 @@ static void hostapd_bss_deinit(struct hostapd_data *hapd) hapd->conf ? hapd->conf->iface : "N/A"); hostapd_bss_deinit_no_free(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); +#ifdef CONFIG_SQLITE + if (hapd->rad_attr_db) { + sqlite3_close(hapd->rad_attr_db); + hapd->rad_attr_db = NULL; + } +#endif /* CONFIG_SQLITE */ hostapd_cleanup(hapd); } diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index d90bae4e6..518c7f10b 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -9,6 +9,10 @@ #ifndef HOSTAPD_H #define HOSTAPD_H +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ + #include "common/defs.h" #include "utils/list.h" #include "ap_config.h" @@ -390,6 +394,10 @@ struct hostapd_data { #endif /* CONFIG_AIRTIME_POLICY */ u8 last_1x_eapol_key_replay_counter[8]; + +#ifdef CONFIG_SQLITE + sqlite3 *rad_attr_db; +#endif /* CONFIG_SQLITE */ }; diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index d62864136..ab6989b0d 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -7,6 +7,9 @@ */ #include "utils/includes.h" +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ #include "utils/common.h" #include "utils/eloop.h" @@ -615,6 +618,63 @@ int add_common_radius_attr(struct hostapd_data *hapd, } +int add_sqlite_radius_attr(struct hostapd_data *hapd, struct sta_info *sta, + struct radius_msg *msg, int acct) +{ +#ifdef CONFIG_SQLITE + const char *attrtxt; + char addrtxt[3 * ETH_ALEN]; + char *sql; + sqlite3_stmt *stmt = NULL; + + if (!hapd->rad_attr_db) + return 0; + + os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, MAC2STR(sta->addr)); + + sql = "SELECT attr FROM radius_attributes WHERE sta=? AND (reqtype=? OR reqtype IS NULL);"; + if (sqlite3_prepare_v2(hapd->rad_attr_db, sql, os_strlen(sql), &stmt, + NULL) != SQLITE_OK) { + wpa_printf(MSG_ERROR, "DB: Failed to prepare SQL statement: %s", + sqlite3_errmsg(hapd->rad_attr_db)); + return -1; + } + sqlite3_bind_text(stmt, 1, addrtxt, os_strlen(addrtxt), SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, acct ? "acct" : "auth", 4, SQLITE_STATIC); + while (sqlite3_step(stmt) == SQLITE_ROW) { + struct hostapd_radius_attr *attr; + struct radius_attr_hdr *hdr; + + attrtxt = (const char *) sqlite3_column_text(stmt, 0); + attr = hostapd_parse_radius_attr(attrtxt); + if (!attr) { + wpa_printf(MSG_ERROR, + "Skipping invalid attribute from SQL: %s", + attrtxt); + continue; + } + wpa_printf(MSG_DEBUG, "Adding RADIUS attribute from SQL: %s", + attrtxt); + hdr = radius_msg_add_attr(msg, attr->type, + wpabuf_head(attr->val), + wpabuf_len(attr->val)); + hostapd_config_free_radius_attr(attr); + if (!hdr) { + wpa_printf(MSG_ERROR, + "Could not add RADIUS attribute from SQL"); + continue; + } + } + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + sqlite3_finalize(stmt); +#endif /* CONFIG_SQLITE */ + + return 0; +} + + void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, struct sta_info *sta, const u8 *eap, size_t len) @@ -654,6 +714,9 @@ void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, msg) < 0) goto fail; + if (sta && add_sqlite_radius_attr(hapd, sta, msg, 0) < 0) + goto fail; + /* TODO: should probably check MTU from driver config; 2304 is max for * IEEE 802.11, but use 1400 to avoid problems with too large packets */ diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h index d771ba526..bb85b93d6 100644 --- a/src/ap/ieee802_1x.h +++ b/src/ap/ieee802_1x.h @@ -59,6 +59,8 @@ int add_common_radius_attr(struct hostapd_data *hapd, struct hostapd_radius_attr *req_attr, struct sta_info *sta, struct radius_msg *msg); +int add_sqlite_radius_attr(struct hostapd_data *hapd, struct sta_info *sta, + struct radius_msg *msg, int acct); void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, struct sta_info *sta, const u8 *eap, size_t len);