Extra RADIUS request attributes from SQLite

Add an SQLite table for defining per station MAC address version of
radius_auth_req_attr/radius_acct_req_attr information. Create the
necessary table and index where this doesn't exist. Select attributes
from the table keyed by station MAC address and request type (auth or
acct), parse and apply to a RADIUS message.

Add radius_req_attr_sqlite hostapd config option for SQLite database
file. Open/close RADIUS attribute database for a lifetime of a BSS and
invoke functions to add extra attributes during RADIUS auth and
accounting request generation.

Signed-off-by: Terry Burton <tez@terryburton.co.uk>
This commit is contained in:
Terry Burton 2019-07-21 13:05:56 +01:00 committed by Jouni Malinen
parent 74707def8f
commit f4111ff3d1
9 changed files with 158 additions and 1 deletions

View file

@ -2832,6 +2832,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
a = a->next; a = a->next;
a->next = attr; 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) { } else if (os_strcmp(buf, "radius_das_port") == 0) {
bss->radius_das_port = atoi(pos); bss->radius_das_port = atoi(pos);
} else if (os_strcmp(buf, "radius_das_client") == 0) { } else if (os_strcmp(buf, "radius_das_client") == 0) {

View file

@ -1384,6 +1384,17 @@ own_ip_addr=127.0.0.1
# Operator-Name = "Operator" # Operator-Name = "Operator"
#radius_acct_req_attr=126:s: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) # Dynamic Authorization Extensions (RFC 5176)
# This mechanism can be used to allow dynamic changes to user session based on # 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 # commands from a RADIUS server (or some other disconnect client that has the

View file

@ -97,6 +97,9 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
msg) < 0) msg) < 0)
goto fail; goto fail;
if (sta && add_sqlite_radius_attr(hapd, sta, msg, 1) < 0)
goto fail;
if (sta) { if (sta) {
for (i = 0; ; i++) { for (i = 0; ; i++) {
val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,

View file

@ -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; 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_auth_req_attr);
hostapd_config_free_radius_attr(conf->radius_acct_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->rsn_preauth_interfaces);
os_free(conf->ctrl_interface); os_free(conf->ctrl_interface);
os_free(conf->ca_cert); os_free(conf->ca_cert);

View file

@ -301,6 +301,7 @@ struct hostapd_bss_config {
int radius_request_cui; int radius_request_cui;
struct hostapd_radius_attr *radius_auth_req_attr; struct hostapd_radius_attr *radius_auth_req_attr;
struct hostapd_radius_attr *radius_acct_req_attr; struct hostapd_radius_attr *radius_acct_req_attr;
char *radius_req_attr_sqlite;
int radius_das_port; int radius_das_port;
unsigned int radius_das_time_window; unsigned int radius_das_time_window;
int radius_das_require_event_timestamp; 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); int hostapd_mac_comp(const void *a, const void *b);
struct hostapd_config * hostapd_config_defaults(void); struct hostapd_config * hostapd_config_defaults(void);
void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); 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_user(struct hostapd_eap_user *user);
void hostapd_config_free_eap_users(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); void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);

View file

@ -7,6 +7,9 @@
*/ */
#include "utils/includes.h" #include "utils/includes.h"
#ifdef CONFIG_SQLITE
#include <sqlite3.h>
#endif /* CONFIG_SQLITE */
#include "utils/common.h" #include "utils/common.h"
#include "utils/eloop.h" #include "utils/eloop.h"
@ -1025,6 +1028,43 @@ hostapd_das_coa(void *ctx, struct radius_das_attrs *attr)
#define hostapd_das_coa NULL #define hostapd_das_coa NULL
#endif /* CONFIG_HS20 */ #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 */ #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) if (wpa_debug_level <= MSG_MSGDUMP)
conf->radius->msg_dumps = 1; conf->radius->msg_dumps = 1;
#ifndef CONFIG_NO_RADIUS #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); hapd->radius = radius_client_init(hapd, conf->radius);
if (hapd->radius == NULL) { if (hapd->radius == NULL) {
wpa_printf(MSG_ERROR, "RADIUS client initialization failed."); 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"); hapd->conf ? hapd->conf->iface : "N/A");
hostapd_bss_deinit_no_free(hapd); hostapd_bss_deinit_no_free(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); 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); hostapd_cleanup(hapd);
} }

View file

@ -9,6 +9,10 @@
#ifndef HOSTAPD_H #ifndef HOSTAPD_H
#define HOSTAPD_H #define HOSTAPD_H
#ifdef CONFIG_SQLITE
#include <sqlite3.h>
#endif /* CONFIG_SQLITE */
#include "common/defs.h" #include "common/defs.h"
#include "utils/list.h" #include "utils/list.h"
#include "ap_config.h" #include "ap_config.h"
@ -390,6 +394,10 @@ struct hostapd_data {
#endif /* CONFIG_AIRTIME_POLICY */ #endif /* CONFIG_AIRTIME_POLICY */
u8 last_1x_eapol_key_replay_counter[8]; u8 last_1x_eapol_key_replay_counter[8];
#ifdef CONFIG_SQLITE
sqlite3 *rad_attr_db;
#endif /* CONFIG_SQLITE */
}; };

View file

@ -7,6 +7,9 @@
*/ */
#include "utils/includes.h" #include "utils/includes.h"
#ifdef CONFIG_SQLITE
#include <sqlite3.h>
#endif /* CONFIG_SQLITE */
#include "utils/common.h" #include "utils/common.h"
#include "utils/eloop.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, void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
struct sta_info *sta, struct sta_info *sta,
const u8 *eap, size_t len) const u8 *eap, size_t len)
@ -654,6 +714,9 @@ void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
msg) < 0) msg) < 0)
goto fail; 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 /* 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 * IEEE 802.11, but use 1400 to avoid problems with too large packets
*/ */

View file

@ -59,6 +59,8 @@ int add_common_radius_attr(struct hostapd_data *hapd,
struct hostapd_radius_attr *req_attr, struct hostapd_radius_attr *req_attr,
struct sta_info *sta, struct sta_info *sta,
struct radius_msg *msg); 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, void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
struct sta_info *sta, struct sta_info *sta,
const u8 *eap, size_t len); const u8 *eap, size_t len);