* *
* Mike Machado <mike@innercite.com> *
***************************************************************************/
+
+typedef struct sql_config {
+ char *sql_server;
+ char *sql_login;
+ char *sql_password;
+ char *sql_db;
+ char *sql_acct_table;
+ char *sql_acct_table2;
+ char *sql_authcheck_table;
+ char *sql_authreply_table;
+ char *sql_groupcheck_table;
+ char *sql_groupreply_table;
+ char *sql_usergroup_table;
+ char *sql_nas_table;
+ char *sql_dict_table;
+ char *authorize_query;
+ char *authorize_group_query;
+ char *authenticate_query;
+ char *accounting_onoff_query;
+ char *accounting_update_query;
+ char *accounting_start_query;
+ char *accounting_start_query_alt;
+ char *accounting_stop_query;
+ char *accounting_stop_query_alt;
+ int sqltrace;
+ char *tracefile;
+ int deletestalesessions;
+ int num_sql_socks;
+} SQL_CONFIG;
+
#define CHECKRAD1 "/usr/sbin/checkrad"
#define CHECKRAD2 "/usr/local/sbin/checkrad"
#define ASCEND_PORT_HACK
#define ASCEND_CHANNELS_PER_LINE 23
-
#define CISCO_ACCOUNTING_HACK
/* SQL defines */
-#define SQL_LOCK_LEN sizeof(SQLACCTREC)
+#define MAX_QUERY_LEN 4096
+#define SQL_LOCK_LEN MAX_QUERY_LEN
#define SQLTRACEFILE RADLOG_DIR "/sqltrace.sql"
-#define SQLBIGREC 32
-#define SQLLILREC 15
#define MAX_COMMUNITY_LEN 50
#define MAX_SQL_SOCKS 256
*
*************************************************************************/
void sql_finish_query(SQLSOCK *socket) {
+ sql_free_result(socket);
}
struct sql_socket *next;
} SQLSOCK;
-typedef struct sql_config {
- char *sql_server;
- char *sql_login;
- char *sql_password;
- char *sql_db;
- char *sql_acct_table;
- char *sql_authcheck_table;
- char *sql_authreply_table;
- char *sql_groupcheck_table;
- char *sql_groupreply_table;
- char *sql_usergroup_table;
- char *sql_realm_table;
- char *sql_realmgroup_table;
- char *sql_nas_table;
- char *sql_dict_table;
- int sensitiveusername;
- int sqltrace;
- char *tracefile;
- int deletestalesessions;
- int num_sql_socks;
-} SQL_CONFIG;
-
typedef struct sql_inst {
int used;
SQLSOCK *sqlpool;
*
*************************************************************************/
void sql_free_result(SQLSOCK *socket) {
- /* Nothing to do here for Oracle */
+ int i=0;
+
+ for(i=0; i<sql_num_fields(socket); i++) {
+ free(socket->results[i]);
+ }
+ free(socket->results);
+ socket->results=NULL;
}
void sql_finish_query(SQLSOCK *socket)
{
/* Nothing to do here for Oracle */
+ sql_free_result(socket);
}
int in_use;
struct timeval tv;
} SQLSOCK;
-
-typedef struct sql_config {
- char *sql_server;
- char *sql_login;
- char *sql_password;
- char *sql_db;
- char *sql_acct_table;
- char *sql_authcheck_table;
- char *sql_authreply_table;
- char *sql_groupcheck_table;
- char *sql_groupreply_table;
- char *sql_usergroup_table;
- char *sql_realm_table;
- char *sql_realmgroup_table;
- char *sql_nas_table;
- char *sql_dict_table;
- int sensitiveusername;
- int sqltrace;
- char *tracefile;
- int deletestalesessions;
- int num_sql_socks;
-} SQL_CONFIG;
typedef struct sql {
SQL_CONFIG *config;
* Copyright 2000 The FreeRADIUS server project
* Copyright 2000 Mike Machado <mike@innercite.com>
* Copyright 2000 Alan DeKok <aland@ox.org>
+ *
+ * If you want this code to look right, set your tabstop to 2 or 3
+ * for vi users - :set ts=3
+ *
*/
static const char rcsid[] =
-"$Id$";
+
+ "$Id$";
#include "autoconf.h"
{"password", PW_TYPE_STRING_PTR, &config.sql_password, ""},
{"radius_db", PW_TYPE_STRING_PTR, &config.sql_db, "radius"},
{"acct_table", PW_TYPE_STRING_PTR, &config.sql_acct_table, "radacct"},
- {"authcheck_table", PW_TYPE_STRING_PTR, &config.sql_authcheck_table, "radcheck"},
- {"authreply_table", PW_TYPE_STRING_PTR, &config.sql_authreply_table, "radreply"},
- {"groupcheck_table", PW_TYPE_STRING_PTR, &config.sql_groupcheck_table, "radgroupcheck"},
- {"groupreply_table", PW_TYPE_STRING_PTR, &config.sql_groupreply_table, "radgroupreply"},
- {"usergroup_table", PW_TYPE_STRING_PTR, &config.sql_usergroup_table, "usergroup"},
- {"realm_table", PW_TYPE_STRING_PTR, &config.sql_realm_table, "realms"},
- {"realmgroup_table", PW_TYPE_STRING_PTR, &config.sql_realmgroup_table, "realmgroup"},
+ {"acct_table2", PW_TYPE_STRING_PTR, &config.sql_acct_table2, "radacct"},
+ {"authcheck_table", PW_TYPE_STRING_PTR, &config.sql_authcheck_table,
+ "radcheck"},
+ {"authreply_table", PW_TYPE_STRING_PTR, &config.sql_authreply_table,
+ "radreply"},
+ {"groupcheck_table", PW_TYPE_STRING_PTR, &config.sql_groupcheck_table,
+ "radgroupcheck"},
+ {"groupreply_table", PW_TYPE_STRING_PTR, &config.sql_groupreply_table,
+ "radgroupreply"},
+ {"usergroup_table", PW_TYPE_STRING_PTR, &config.sql_usergroup_table,
+ "usergroup"},
{"nas_table", PW_TYPE_STRING_PTR, &config.sql_nas_table, "nas"},
{"dict_table", PW_TYPE_STRING_PTR, &config.sql_dict_table, "dictionary"},
- {"sensitiveusername", PW_TYPE_BOOLEAN, &config.sensitiveusername, "1"},
{"sqltrace", PW_TYPE_BOOLEAN, &config.sqltrace, "0"},
{"sqltracefile", PW_TYPE_STRING_PTR, &config.tracefile, SQLTRACEFILE},
{"deletestalesessions", PW_TYPE_BOOLEAN, &config.deletestalesessions, "0"},
{"num_sql_socks", PW_TYPE_INTEGER, &config.num_sql_socks, "5"},
+ {"authorize_query", PW_TYPE_STRING_PTR, &config.authorize_query, ""},
+ {"authorize_group_query", PW_TYPE_STRING_PTR,
+ &config.authorize_group_query, ""},
+ {"authenticate_query", PW_TYPE_STRING_PTR, &config.authenticate_query, ""},
+ {"accounting_onoff_query", PW_TYPE_STRING_PTR,
+ &config.accounting_onoff_query, ""},
+ {"accounting_update_query", PW_TYPE_STRING_PTR,
+ &config.accounting_update_query, ""},
+ {"accounting_start_query", PW_TYPE_STRING_PTR,
+ &config.accounting_start_query, ""},
+ {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
+ &config.accounting_start_query_alt, ""},
+ {"accounting_stop_query", PW_TYPE_STRING_PTR, &config.accounting_stop_query,
+ ""},
+ {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
+ &config.accounting_stop_query_alt, ""},
{NULL, -1, NULL, NULL}
};
/***********************************************************************
* start of main routines
***********************************************************************/
-static int rlm_sql_init(void) {
+static int
+rlm_sql_init(void)
+{
/*
* FIXME:
return 0;
}
-static int rlm_sql_instantiate(CONF_SECTION *conf, void **instance) {
+static int
+rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
+{
SQL_INST *inst;
memset(inst->config, 0, sizeof(SQL_CONFIG));
#if HAVE_PTHREAD_H
- inst->lock = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
+ inst->lock = (pthread_mutex_t *) malloc(sizeof(pthread_mutex_t));
if (inst->lock == NULL)
return -1;
pthread_mutex_init(inst->lock, NULL);
- inst->notfull = (pthread_cond_t *)malloc(sizeof(pthread_cond_t));
+ inst->notfull = (pthread_cond_t *) malloc(sizeof(pthread_cond_t));
pthread_cond_init(inst->notfull, NULL);
#endif
/*
* If the configuration parameters can't be parsed, then
* fail.
- */
+ */
if (cf_section_parse(conf, module_config) < 0) {
free(inst->config);
free(inst);
return -1;
}
- if(config.num_sql_socks > MAX_SQL_SOCKS) {
- radlog(L_ERR | L_CONS, "sql_instantiate: number of sqlsockets cannot exceed %d", MAX_SQL_SOCKS);
+ if (config.num_sql_socks > MAX_SQL_SOCKS) {
+ radlog(L_ERR | L_CONS,
+ "sql_instantiate: number of sqlsockets cannot exceed %d",
+ MAX_SQL_SOCKS);
free(inst->config);
free(inst);
return -1;
}
- inst->config->sql_server = config.sql_server;
- inst->config->sql_login = config.sql_login;
- inst->config->sql_password = config.sql_password;
- inst->config->sql_db = config.sql_db;
- inst->config->sql_acct_table = config.sql_acct_table;
- inst->config->sql_authcheck_table = config.sql_authcheck_table;
- inst->config->sql_authreply_table = config.sql_authreply_table;
- inst->config->sql_groupcheck_table = config.sql_groupcheck_table;
- inst->config->sql_groupreply_table = config.sql_groupreply_table;
- inst->config->sql_usergroup_table = config.sql_usergroup_table;
- inst->config->sql_realm_table = config.sql_realm_table;
- inst->config->sql_realmgroup_table = config.sql_realmgroup_table;
- inst->config->sql_nas_table = config.sql_nas_table;
- inst->config->sql_dict_table = config.sql_dict_table;
- inst->config->sensitiveusername = config.sensitiveusername;
- inst->config->sqltrace = config.sqltrace;
- inst->config->tracefile = config.tracefile;
- inst->config->deletestalesessions = config.deletestalesessions;
- inst->config->num_sql_socks = config.num_sql_socks;
-
- config.sql_server = NULL;
- config.sql_login = NULL;
- config.sql_password = NULL;
- config.sql_db = NULL;
- config.sql_acct_table = NULL;
- config.sql_authcheck_table = NULL;
- config.sql_authreply_table = NULL;
- config.sql_groupcheck_table = NULL;
- config.sql_groupreply_table = NULL;
- config.sql_usergroup_table = NULL;
- config.sql_realm_table = NULL;
- config.sql_realmgroup_table = NULL;
- config.sql_nas_table = NULL;
- config.sql_dict_table = NULL;
- config.tracefile = NULL;
+ inst->config->sql_server = config.sql_server;
+ inst->config->sql_login = config.sql_login;
+ inst->config->sql_password = config.sql_password;
+ inst->config->sql_db = config.sql_db;
+ inst->config->sql_acct_table = config.sql_acct_table;
+ inst->config->sql_acct_table2 = config.sql_acct_table2;
+ inst->config->sql_authcheck_table = config.sql_authcheck_table;
+ inst->config->sql_authreply_table = config.sql_authreply_table;
+ inst->config->sql_groupcheck_table = config.sql_groupcheck_table;
+ inst->config->sql_groupreply_table = config.sql_groupreply_table;
+ inst->config->sql_usergroup_table = config.sql_usergroup_table;
+ inst->config->sql_nas_table = config.sql_nas_table;
+ inst->config->sql_dict_table = config.sql_dict_table;
+ inst->config->sqltrace = config.sqltrace;
+ inst->config->tracefile = config.tracefile;
+ inst->config->deletestalesessions = config.deletestalesessions;
+ inst->config->num_sql_socks = config.num_sql_socks;
+ inst->config->authorize_query = config.authorize_query;
+ inst->config->authorize_group_query = config.authorize_group_query;
+ inst->config->authenticate_query = config.authenticate_query;
+ inst->config->accounting_onoff_query = config.accounting_onoff_query;
+ inst->config->accounting_update_query = config.accounting_update_query;
+ inst->config->accounting_start_query = config.accounting_start_query;
+ inst->config->accounting_start_query_alt =
+ config.accounting_start_query_alt;
+ inst->config->accounting_stop_query = config.accounting_stop_query;
+ inst->config->accounting_stop_query_alt = config.accounting_stop_query_alt;
+
+ config.sql_server = NULL;
+ config.sql_login = NULL;
+ config.sql_password = NULL;
+ config.sql_db = NULL;
+ config.sql_acct_table = NULL;
+ config.sql_acct_table2 = NULL;
+ config.sql_authcheck_table = NULL;
+ config.sql_authreply_table = NULL;
+ config.sql_groupcheck_table = NULL;
+ config.sql_groupreply_table = NULL;
+ config.sql_usergroup_table = NULL;
+ config.sql_nas_table = NULL;
+ config.sql_dict_table = NULL;
+ config.tracefile = NULL;
+ config.authorize_query = NULL;
+ config.authorize_group_query = NULL;
+ config.authenticate_query = NULL;
+ config.accounting_onoff_query = NULL;
+ config.accounting_update_query = NULL;
+ config.accounting_start_query = NULL;
+ config.accounting_start_query_alt = NULL;
+ config.accounting_stop_query = NULL;
+ config.accounting_stop_query_alt = NULL;
radlog(L_INFO, "rlm_sql: Attempting to connect to %s@%s:%s",
- inst->config->sql_login, inst->config->sql_server,
- inst->config->sql_db);
+ inst->config->sql_login, inst->config->sql_server,
+ inst->config->sql_db);
- if(sql_init_socketpool(inst) < 0) {
+ if (sql_init_socketpool(inst) < 0) {
free(inst->config);
free(inst);
return -1;
return RLM_MODULE_OK;
}
-static int rlm_sql_destroy(void) {
+static int
+rlm_sql_destroy(void)
+{
return 0;
}
-static int rlm_sql_detach(void *instance) {
+static int
+rlm_sql_detach(void *instance)
+{
SQL_INST *inst = instance;
}
-static int rlm_sql_authorize(void *instance, REQUEST * request) {
+static int
+rlm_sql_authorize(void *instance, REQUEST * request)
+{
- int nas_port = 0;
VALUE_PAIR *check_tmp = NULL;
VALUE_PAIR *reply_tmp = NULL;
- VALUE_PAIR *tmp;
int found = 0;
char *name;
SQLSOCK *sqlsocket;
SQL_INST *inst = instance;
+ char querystr[MAX_QUERY_LEN];
+ char saveuser[MAX_STRING_LEN];
+ int savelen = 0;
- name = request->username->strvalue;
+ VALUE_PAIR *uservp = NULL;
+
+ sqlsocket = sql_get_socket(inst);
/*
- * Check for valid input, zero length names not permitted
+ * Set, escape, and check the user attr here
*/
+ uservp = set_userattr(request->packet->vps, NULL, saveuser, &savelen);
+ name = uservp->strvalue;
if (name[0] == 0) {
radlog(L_ERR, "zero length username not permitted\n");
+ sql_release_socket(inst, sqlsocket);
+ restore_userattr(uservp, saveuser, savelen);
return -1;
}
- sqlsocket = sql_get_socket(inst);
-
- /*
- * Find the NAS port ID.
- */
- if ((tmp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL)
- nas_port = tmp->lvalue;
-
-
+ radius_xlat2(querystr, MAX_QUERY_LEN, inst->config->authorize_query,
+ request);
+ found =
+ sql_getvpdata(inst, sqlsocket, &check_tmp, &reply_tmp, querystr,
+ PW_VP_USERDATA);
/*
* Find the entry for the user.
*/
- if ((found = sql_getvpdata(inst, sqlsocket, inst->config->sql_authcheck_table, &check_tmp, name, PW_VP_USERDATA)) > 0) {
- sql_getvpdata(inst, sqlsocket, inst->config->sql_groupcheck_table, &check_tmp, name, PW_VP_GROUPDATA);
- sql_getvpdata(inst, sqlsocket, inst->config->sql_authreply_table, &reply_tmp, name, PW_VP_USERDATA);
- sql_getvpdata(inst, sqlsocket, inst->config->sql_groupreply_table, &reply_tmp, name, PW_VP_GROUPDATA);
- } else if(found < 0) {
+ if (found > 0) {
+ radius_xlat2(querystr, MAX_QUERY_LEN,
+ inst->config->authorize_group_query, request);
+ sql_getvpdata(inst, sqlsocket, &check_tmp, &reply_tmp,
+ querystr, PW_VP_GROUPDATA);
+ } else if (found < 0) {
radlog(L_ERR, "rlm_sql: SQL query error; rejecting user");
+ sql_release_socket(inst, sqlsocket);
+ restore_userattr(uservp, saveuser, savelen);
return -1;
} else {
- int gcheck, greply;
+ int gcheck;
- gcheck = sql_getvpdata(inst, sqlsocket, inst->config->sql_groupcheck_table, &check_tmp, "DEFAULT", PW_VP_GROUPDATA);
- greply = sql_getvpdata(inst, sqlsocket, inst->config->sql_groupreply_table, &reply_tmp, "DEFAULT", PW_VP_GROUPDATA);
- if (gcheck && greply)
+ /*
+ * We didn't find the user, so we try looking
+ * for a DEFAULT entry
+ */
+ set_userattr(uservp, "DEFAULT", NULL, NULL);
+ radius_xlat2(querystr, MAX_QUERY_LEN,
+ inst->config->authorize_group_query, request);
+ gcheck = sql_getvpdata(inst, sqlsocket, &check_tmp, &reply_tmp,
+ querystr, PW_VP_GROUPDATA);
+ if (gcheck)
found = 1;
}
+ restore_userattr(uservp, saveuser, savelen);
sql_release_socket(inst, sqlsocket);
if (!found) {
- radlog(L_DBG,"rlm_sql: User %s not found and DEFAULT not found", name);
+ radlog(L_DBG, "rlm_sql: User %s not found and DEFAULT not found", name);
return RLM_MODULE_NOTFOUND;
}
+ /*
+ * Uncomment these lines for debugging
+ * Recompile, and run 'radiusd -X'
+ *
+ DEBUG2("rlm_sql: check items");
+ vp_printlist(stderr, check_tmp);
+ DEBUG2("rlm_sql: reply items");
+ vp_printlist(stderr, reply_tmp);
+ */
+
if (paircmp(request->packet->vps, check_tmp, &reply_tmp) != 0) {
- radlog(L_INFO,"rlm_sql: Pairs do not match [%s]", name);
+ radlog(L_INFO, "rlm_sql: Pairs do not match [%s]", name);
return RLM_MODULE_FAIL;
}
}
static int
-rlm_sql_authenticate(void *instance, REQUEST *request)
+rlm_sql_authenticate(void *instance, REQUEST * request)
{
SQL_ROW row;
SQLSOCK *sqlsocket;
- char *querystr;
- char escaped_user[AUTH_STRING_LEN * 3];
- char *user;
- const char query[] = "SELECT Value FROM %s WHERE UserName = '%s' AND Attribute = 'Password'";
+ char querystr[MAX_QUERY_LEN];
+ VALUE_PAIR *uservp;
+ char saveuser[MAX_STRING_LEN];
+ int savelen = 0;
SQL_INST *inst = instance;
- user = request->username->strvalue;
-
/*
* Ensure that a password attribute exists.
*/
if ((request->password == NULL) ||
(request->password->length == 0) ||
(request->password->attribute != PW_PASSWORD)) {
- radlog(L_AUTH, "rlm_sql: Attribute \"Password\" is required for authentication.");
+ radlog(L_AUTH,
+ "rlm_sql: Attribute \"Password\" is required for authentication.");
return RLM_MODULE_INVALID;
}
- sql_escape_string(escaped_user, user, strlen(user));
-
/*
- * This should really be replaced with a static buffer...
+ * 1. Set username to escaped value
+ * 2. Translate vars in the query
+ * 3. Replace User-Name attr with saved value
*/
- if ((querystr = malloc(strlen(escaped_user) +
- strlen(inst->config->sql_authcheck_table) +
- sizeof(query))) == NULL) {
- radlog(L_ERR | L_CONS, "no memory");
- exit(1);
- }
+ uservp = set_userattr(request->packet->vps, NULL, saveuser, &savelen);
+ radius_xlat2(querystr, MAX_QUERY_LEN, inst->config->authenticate_query,
+ request);
+ restore_userattr(uservp, saveuser, savelen);
- sprintf(querystr, query, inst->config->sql_authcheck_table, escaped_user);
sqlsocket = sql_get_socket(inst);
if (sql_select_query(inst, sqlsocket, querystr) < 0) {
- radlog(L_ERR,"rlm_sql_authenticate: database query error");
+ radlog(L_ERR, "rlm_sql_authenticate: database query error");
sql_release_socket(inst, sqlsocket);
return RLM_MODULE_REJECT;
}
row = sql_fetch_row(sqlsocket);
sql_finish_select_query(sqlsocket);
sql_release_socket(inst, sqlsocket);
- sql_free_result(sqlsocket);
- free(querystr);
if (row == NULL) {
- radlog(L_ERR,"rlm_sql_authenticate: no rows returned from query (no such user)");
+ radlog(L_ERR,
+ "rlm_sql_authenticate: no rows returned from query (no such user)");
return RLM_MODULE_REJECT;
}
- /* Just compare the two */
+ /*
+ * Just compare the two
+ */
if (strncmp(request->password->strvalue,
- row[0],
- request->password->length) != 0) {
- return RLM_MODULE_REJECT;
+ row[0], request->password->length) != 0) {
+ return RLM_MODULE_REJECT;
}
return RLM_MODULE_OK;
/*
* Accounting: save the account data to our sql table
*/
-static int rlm_sql_accounting(void *instance, REQUEST * request) {
+static int
+rlm_sql_accounting(void *instance, REQUEST * request)
+{
- time_t nowtime;
- struct tm *tim;
- char datebuf[20];
- VALUE_PAIR *pair;
- SQLACCTREC *sqlrecord;
SQLSOCK *sqlsocket;
- DICT_VALUE *dval;
+ VALUE_PAIR *pair;
SQL_INST *inst = instance;
- int lentmp = 0;
+ int numaffected = 0;
+ int acctstatustype = 0;
+ char querystr[MAX_QUERY_LEN];
+ char logstr[MAX_QUERY_LEN];
+
+#ifdef CISCO_ACCOUNTING_HACK
+ int acctsessiontime = 0;
+#endif
+
+ sqlsocket = sql_get_socket(inst);
+ memset(querystr, 0, MAX_QUERY_LEN);
/*
- * FIXME: Should we really do this malloc?
- * Why not a static structure, because this malloc is
- * relatively expensive considering we do it for every
- * accounting packet
+ * Find the Acct Status Type
*/
- if ((sqlrecord = malloc(sizeof(SQLACCTREC))) == NULL) {
- radlog(L_ERR | L_CONS, "no memory");
- exit(1);
+ if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
+ acctstatustype = pair->lvalue;
+ } else {
+ radius_xlat2(logstr, MAX_QUERY_LEN,
+ "rlm_sql: packet has no account status"
+ "type. (user '%{User-Name}', nas '%{NAS-IP-Address}')",
+ request);
+ radlog(L_ERR, logstr);
+ return 0;
}
- memset(sqlrecord, 0, sizeof(SQLACCTREC));
-
- pair = request->packet->vps;
- while (pair != (VALUE_PAIR *) NULL) {
- /*
- * Check the pairs to see if they are anything we are interested in.
- */
- switch (pair->attribute) {
- case PW_ACCT_SESSION_ID:
- strncpy(sqlrecord->AcctSessionId, pair->strvalue, SQLBIGREC);
- break;
-
- case PW_ACCT_UNIQUE_SESSION_ID:
- strncpy(sqlrecord->AcctUniqueId, pair->strvalue, SQLBIGREC);
- break;
-
- case PW_USER_NAME:
- strncpy(sqlrecord->UserName, pair->strvalue, SQLBIGREC);
- break;
-
- case PW_NAS_IP_ADDRESS:
- ip_ntoa(sqlrecord->NASIPAddress, pair->lvalue);
- //ipaddr2str(sqlrecord->NASIPAddress, pair->lvalue);
- break;
-
- case PW_NAS_PORT_ID:
- sqlrecord->NASPortId = pair->lvalue;
- break;
-
- case PW_NAS_PORT_TYPE:
- dval = dict_valbyattr(PW_NAS_PORT_TYPE, pair->lvalue);
- if (dval != NULL) {
- strncpy(sqlrecord->NASPortType, dval->name, SQLBIGREC);
- }
- break;
-
- case PW_ACCT_STATUS_TYPE:
- sqlrecord->AcctStatusTypeId = pair->lvalue;
- dval = dict_valbyattr(PW_ACCT_STATUS_TYPE, pair->lvalue);
- if (dval != NULL) {
- strncpy(sqlrecord->AcctStatusType, dval->name, SQLBIGREC);
- }
- break;
-
- case PW_ACCT_SESSION_TIME:
- sqlrecord->AcctSessionTime = pair->lvalue;
- break;
-
- case PW_ACCT_AUTHENTIC:
- dval = dict_valbyattr(PW_ACCT_AUTHENTIC, pair->lvalue);
- if (dval != NULL) {
- strncpy(sqlrecord->AcctAuthentic, dval->name, SQLBIGREC);
- }
- break;
-
- case PW_CONNECT_INFO:
- strncpy(sqlrecord->ConnectInfo, pair->strvalue, SQLBIGREC);
- break;
-
- case PW_ACCT_INPUT_OCTETS:
- sqlrecord->AcctInputOctets = pair->lvalue;
- break;
-
- case PW_ACCT_OUTPUT_OCTETS:
- sqlrecord->AcctOutputOctets = pair->lvalue;
- break;
-
- case PW_CALLED_STATION_ID:
- strncpy(sqlrecord->CalledStationId, pair->strvalue, SQLLILREC);
- break;
-
- case PW_CALLING_STATION_ID:
- /* USR 00 workaround */
- lentmp = strlen(pair->strvalue);
- if(lentmp > 10) {
- strncpy(sqlrecord->CallingStationId, pair->strvalue+(lentmp-10), SQLLILREC);
- } else {
- strncpy(sqlrecord->CallingStationId, pair->strvalue, SQLLILREC);
- }
- break;
+#ifdef CISCO_ACCOUNTING_HACK
+ /*
+ * If stop but zero session length AND no previous
+ * session found, drop it as in invalid packet
+ * This is to fix CISCO's aaa from filling our
+ * table with bogus crap
+ */
+ if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
+ acctsessiontime = pair->lvalue;
+
+ if ((acctsessiontime <= 0) && (acctstatustype == PW_STATUS_STOP)) {
+ radius_xlat2(logstr, MAX_QUERY_LEN,
+ "rlm_sql: Stop packet with zero session"
+ "length. (user '%{User-Name}', nas '%{NAS-IP-Address}')",
+ request);
+ radlog(L_ERR, logstr);
+ return 0;
+ }
+#endif
- case PW_ACCT_TERMINATE_CAUSE:
- dval = dict_valbyattr(PW_ACCT_TERMINATE_CAUSE, pair->lvalue);
- if(dval != NULL) {
- strncpy(sqlrecord->AcctTerminateCause, dval->name, SQLBIGREC);
- }
- break;
+ switch (acctstatustype) {
+ /*
+ * The Terminal server informed us that it was rebooted
+ * STOP all records from this NAS
+ */
+ case PW_STATUS_ACCOUNTING_ON:
+ case PW_STATUS_ACCOUNTING_OFF:
+ radlog(L_INFO, "rlm_sql: received Acct On/Off packet");
+ radius_xlat2(querystr, MAX_QUERY_LEN,
+ inst->config->accounting_onoff_query, request);
+ query_log(inst, querystr);
+
+ if (querystr) {
+ if (sql_query(inst, sqlsocket, querystr) < 0)
+ radlog(L_ERR,
+ "rlm_sql: Couldn't update SQL accounting for ALIVE packet - %s",
+ sql_error(sqlsocket));
+ sql_finish_query(sqlsocket);
+ }
+
+ break;
+
+ /*
+ * Got an update accounting packet
+ */
+ case PW_STATUS_ALIVE:
- case PW_SERVICE_TYPE:
- dval = dict_valbyattr(PW_SERVICE_TYPE, pair->lvalue);
- if (dval != NULL) {
- strncpy(sqlrecord->ServiceType, dval->name, SQLBIGREC);
- }
- break;
+ radius_xlat2(querystr, MAX_QUERY_LEN,
+ inst->config->accounting_update_query, request);
+ query_log(inst, querystr);
- case PW_FRAMED_PROTOCOL:
- dval = dict_valbyattr(PW_FRAMED_PROTOCOL, pair->lvalue);
- if (dval != NULL) {
- strncpy(sqlrecord->FramedProtocol, dval->name, SQLBIGREC);
- }
- break;
+ if (querystr) {
+ if (sql_query(inst, sqlsocket, querystr) < 0)
+ radlog(L_ERR,
+ "rlm_sql: Couldn't update SQL accounting for ALIVE packet - %s",
+ sql_error(sqlsocket));
+ sql_finish_query(sqlsocket);
+ }
- case PW_FRAMED_IP_ADDRESS:
- ip_ntoa(sqlrecord->FramedIPAddress, pair->lvalue);
- //ipaddr2str(sqlrecord->FramedIPAddress, pair->lvalue);
- break;
+ break;
- case PW_ACCT_DELAY_TIME:
- sqlrecord->AcctDelayTime = pair->lvalue;
- break;
-
- /*
- * FIXME: USR VSA for: USR-Connect-Speed
- * Ugly hack. Will go away when conf-based
- * tables are implemented
+ /*
+ * Got accounting start packet
*/
- case 167971:
- dval = dict_valbyattr(167971, pair->lvalue);
- if(dval != NULL) {
- strncpy(sqlrecord->ConnectInfo, dval->name, SQLBIGREC);
+ case PW_STATUS_START:
+
+ radius_xlat2(querystr, MAX_QUERY_LEN,
+ inst->config->accounting_start_query, request);
+ query_log(inst, querystr);
+
+ if (querystr) {
+ if (sql_query(inst, sqlsocket, querystr) < 0) {
+ radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting"
+ "for ALIVE packet - %s", sql_error(sqlsocket));
+ sql_finish_query(sqlsocket);
+
+ /*
+ * We failed the insert above. It's probably because
+ * the stop record came before the start. We try an
+ * our alternate query now (typically an UPDATE)
+ */
+ radius_xlat2(querystr, MAX_QUERY_LEN,
+ inst->config->accounting_start_query_alt, request);
+ query_log(inst, querystr);
+
+ if (querystr) {
+ if (sql_query(inst, sqlsocket, querystr) < 0) {
+ radlog(L_ERR, "rlm_sql: Couldn't update SQL"
+ "accounting START record - %s", sql_error(sqlsocket));
+ }
+ sql_finish_query(sqlsocket);
+ }
}
- break;
+ }
+ break;
- /* Appears to be LE-Terminate-Detail */
- case 65538:
- strncpy(sqlrecord->AcctTerminateCause, pair->strvalue, SQLBIGREC);
- break;
+ /*
+ * Got accounting stop packet
+ */
+ case PW_STATUS_STOP:
- default:
- break;
- }
+ radius_xlat2(querystr, MAX_QUERY_LEN,
+ inst->config->accounting_stop_query, request);
+ query_log(inst, querystr);
- pair = pair->next;
+ if (querystr) {
+ if ((querystr) && sql_query(inst, sqlsocket, querystr) < 0) {
+ radlog(L_ERR,
+ "rlm_sql: Couldn't insert SQL accounting START record - %s",
+ sql_error(sqlsocket));
+ }
+ sql_finish_query(sqlsocket);
+ }
+
+ numaffected = sql_affected_rows(sqlsocket);
+ if (numaffected < 1) {
+ /*
+ * If our update above didn't match anything
+ * we assume it's because we haven't seen a
+ * matching Start record. So we have to
+ * insert this stop rather than do an update
+ */
+ radius_xlat2(querystr, MAX_QUERY_LEN,
+ inst->config->accounting_stop_query_alt, request);
+ query_log(inst, querystr);
+
+ if (querystr) {
+ if (sql_query(inst, sqlsocket, querystr) < 0) {
+ radlog(L_ERR,
+ "rlm_sql: Couldn't update SQL accounting START record - %s",
+ sql_error(sqlsocket));
+ }
+ sql_finish_query(sqlsocket);
+ }
+ }
+ break;
}
-
- nowtime = request->timestamp - sqlrecord->AcctDelayTime;
- tim = localtime(&nowtime);
- strftime(datebuf, sizeof(datebuf), "%Y%m%d%H%M%S", tim);
-
- strncpy(sqlrecord->AcctTimeStamp, datebuf, 20);
-
- sqlsocket = sql_get_socket(inst);
- sql_save_acct(inst, sqlsocket, sqlrecord);
sql_release_socket(inst, sqlsocket);
return RLM_MODULE_OK;
/* globally exported name */
module_t rlm_sql = {
"SQL",
- 0, /* type: reserved */
- rlm_sql_init, /* initialization */
- rlm_sql_instantiate, /* instantiation */
- rlm_sql_authorize, /* authorization */
- rlm_sql_authenticate, /* authentication */
- NULL, /* preaccounting */
- rlm_sql_accounting, /* accounting */
- NULL, /* checksimul */
- rlm_sql_detach, /* detach */
- rlm_sql_destroy, /* destroy */
+ 0, /* type: reserved */
+ rlm_sql_init, /* initialization */
+ rlm_sql_instantiate, /* instantiation */
+ rlm_sql_authorize, /* authorization */
+ rlm_sql_authenticate, /* authentication */
+ NULL, /* preaccounting */
+ rlm_sql_accounting, /* accounting */
+ NULL, /* checksimul */
+ rlm_sql_detach, /* detach */
+ rlm_sql_destroy, /* destroy */
};
-
/***************************************************************************
* rlm_sql.h rlm_sql - FreeRADIUS SQL Module *
* *
#define PW_VP_GROUPDATA 2
#define PW_VP_REALMDATA 3
-typedef struct sqlrec {
- char AcctSessionId[SQLBIGREC];
- char AcctUniqueId[SQLBIGREC];
- char UserName[SQLBIGREC];
- char Realm[SQLBIGREC];
- char NASIPAddress[SQLLILREC];
- unsigned long NASPortId;
- char NASPortType[SQLBIGREC];
- char AcctStatusType[SQLBIGREC];
- unsigned int AcctStatusTypeId;
- char AcctTimeStamp[20];
- unsigned long AcctSessionTime;
- char AcctAuthentic[SQLBIGREC];
- char ConnectInfo[SQLBIGREC];
- unsigned long AcctInputOctets;
- unsigned long AcctOutputOctets;
- char CalledStationId[SQLLILREC];
- char CallingStationId[SQLLILREC];
- char AcctTerminateCause[SQLBIGREC];
- char ServiceType[SQLBIGREC];
- char FramedProtocol[SQLBIGREC];
- char FramedIPAddress[SQLLILREC];
- unsigned long AcctDelayTime;
-} SQLACCTREC;
-
-int sql_init_socketpool(SQL_INST *inst);
-void sql_poolfree(SQL_INST *inst);
-int sql_close_socket(SQLSOCK *sqlsocket);
-SQLSOCK *sql_get_socket(SQL_INST *inst);
-int sql_release_socket(SQL_INST *inst, SQLSOCK *sqlsocket);
-int sql_save_acct(SQL_INST *inst, SQLSOCK *sqlsocket, SQLACCTREC * sqlrecord);
-int sql_userparse(VALUE_PAIR ** first_pair, SQL_ROW row, int mode);
-int sql_read_realms(SQLSOCK *sqlsocket);
-int sql_getvpdata(SQL_INST *inst, SQLSOCK *sqlsocket, char *table, VALUE_PAIR ** vp, char *user, int mode);
-int sql_check_multi(SQL_INST *inst, SQLSOCK *sqlsocket, char *name, VALUE_PAIR * request, int maxsimul);
-int sql_read_naslist(SQLSOCK *sqlsocket);
-int sql_read_clients(SQLSOCK *sqlsocket);
-int sql_dict_init(SQLSOCK *sqlsocket);
+#define PW_ITEM_CHECK 0
+#define PW_ITEM_REPLY 1
+int sql_init_socketpool(SQL_INST * inst);
+void sql_poolfree(SQL_INST * inst);
+int sql_close_socket(SQLSOCK * sqlsocket);
+SQLSOCK *sql_get_socket(SQL_INST * inst);
+int sql_release_socket(SQL_INST * inst, SQLSOCK * sqlsocket);
+int sql_userparse(VALUE_PAIR ** first_pair, SQL_ROW row,
+ int mode, int itemtype);
+int sql_read_realms(SQLSOCK * sqlsocket);
+int sql_getvpdata(SQL_INST * inst, SQLSOCK * sqlsocket,
+ VALUE_PAIR ** check, VALUE_PAIR ** reply, char *query,
+ int mode);
+int sql_check_multi(SQL_INST * inst, SQLSOCK * sqlsocket, char *name,
+ VALUE_PAIR * request, int maxsimul);
+int sql_read_naslist(SQLSOCK * sqlsocket);
+int sql_read_clients(SQLSOCK * sqlsocket);
+int sql_dict_init(SQLSOCK * sqlsocket);
+void query_log(SQL_INST * inst, char *querystr);
+VALUE_PAIR *set_userattr(VALUE_PAIR * first, char *username,
+ char *saveuser, int *savelen);
+void restore_userattr(VALUE_PAIR * uservp, char *saveuser, int savelen);
* Purpose: Connect to the sql server
*
*************************************************************************/
-int sql_init_socketpool(SQL_INST *inst) {
+int
+sql_init_socketpool(SQL_INST * inst)
+{
- SQLSOCK *sqlsocket;
- int i;
+ SQLSOCK *sqlsocket;
+ int i;
inst->used = 0;
inst->sqlpool = NULL;
} else {
sqlsocket->id = i;
#if HAVE_PTHREAD_H
- sqlsocket->semaphore = (sem_t *)malloc(sizeof(sem_t));
+ sqlsocket->semaphore = (sem_t *) malloc(sizeof(sem_t));
sem_init(sqlsocket->semaphore, 0, SQLSOCK_UNLOCKED);
#else
sqlsocket->in_use = 0;
* Purpose: Clean up and free sql pool
*
*************************************************************************/
-void sql_poolfree(SQL_INST *inst) {
+void
+sql_poolfree(SQL_INST * inst)
+{
SQLSOCK *cur;
* Purpose: Close and free a sql sqlsocket
*
*************************************************************************/
-int sql_close_socket(SQLSOCK *sqlsocket) {
+int
+sql_close_socket(SQLSOCK * sqlsocket)
+{
- radlog(L_DBG,"rlm_sql: Closing sqlsocket %d", sqlsocket->id);
+ radlog(L_DBG, "rlm_sql: Closing sqlsocket %d", sqlsocket->id);
sql_close(sqlsocket);
#if HAVE_PTHREAD_H
sem_destroy(sqlsocket->semaphore);
* Purpose: Return a SQL sqlsocket from the connection pool
*
*************************************************************************/
-SQLSOCK *sql_get_socket(SQL_INST *inst) {
+SQLSOCK *
+sql_get_socket(SQL_INST * inst)
+{
SQLSOCK *cur;
#if HAVE_PTHREAD_H
pthread_cond_wait(inst->notfull, inst->lock);
#else
- /* FIXME: Subsecond sleep needed here */
+ /*
+ * FIXME: Subsecond sleep needed here
+ */
sleep(1);
#endif
}
#else
cur->in_use = SQLSOCK_LOCKED;
#endif
- radlog(L_DBG,"rlm_sql: Reserved sql socket id: %d", cur->id);
+ radlog(L_DBG, "rlm_sql: Reserved sql socket id: %d", cur->id);
return cur;
}
}
pthread_mutex_unlock(inst->lock);
#endif
- /* Should never get here, but what the hey */
+ /*
+ * Should never get here, but what the hey
+ */
return NULL;
}
* Purpose: Frees a SQL sqlsocket back to the connection pool
*
*************************************************************************/
-int sql_release_socket(SQL_INST *inst, SQLSOCK *sqlsocket) {
+int
+sql_release_socket(SQL_INST * inst, SQLSOCK * sqlsocket)
+{
#if HAVE_PTHREAD_H
pthread_mutex_lock(inst->lock);
sqlsocket->in_use = 0;
#endif
- radlog(L_DBG,"rlm_sql: Released sql socket id: %d", sqlsocket->id);
+ radlog(L_DBG, "rlm_sql: Released sql socket id: %d", sqlsocket->id);
#if HAVE_PTHREAD_H
pthread_mutex_unlock(inst->lock);
/*************************************************************************
*
- * Function: sql_save_acct
- *
- * Purpose: Write data from the sqlrecord structure to the database
- *
- *************************************************************************/
-
-int sql_save_acct(SQL_INST *inst, SQLSOCK *sqlsocket, SQLACCTREC *sqlrecord) {
-
- char querystr[2048];
- FILE *sqlfile=0;
- int num = 0;
- int acctunique = 0;
-
-#ifdef NT_DOMAIN_HACK
- char *ptr;
- char newname[AUTH_STRING_LEN];
-#endif
-
- acctunique = strlen(sqlrecord->AcctUniqueId);
-
- if(inst->config->sqltrace) {
- if ((sqlfile = fopen(inst->config->tracefile, "a")) == (FILE *) NULL) {
- radlog(L_ERR, "rlm_sql: Couldn't open file %s", inst->config->tracefile);
- } else {
-#if defined(F_LOCK) && !defined(BSD)
- (void) lockf((int) sqlfile, (int) F_LOCK, (off_t) SQL_LOCK_LEN);
-#else
- (void) flock(sqlfile, SQL_LOCK_EX);
-#endif
- }
- }
-
-#ifdef NT_DOMAIN_HACK
- /*
- * Windows NT machines often authenticate themselves as
- * NT_DOMAIN\username. Try to be smart about this.
- *
- * FIXME: should we handle this as a REALM ?
- */
- if ((ptr = strchr(sqlrecord->UserName, '\\')) != NULL) {
- strncpy(newname, ptr + 1, sizeof(newname));
- newname[sizeof(newname) - 1] = 0;
- strcpy(sqlrecord->UserName, newname);
- }
-#endif /*
- * NT_DOMAIN_HACK
- */
-
- if (sqlrecord->AcctStatusTypeId == PW_STATUS_ACCOUNTING_ON ||
- sqlrecord->AcctStatusTypeId == PW_STATUS_ACCOUNTING_OFF) {
- radlog(L_INFO, "rlm_sql: Portmaster %s rebooted at %s", sqlrecord->NASIPAddress,
- sqlrecord->AcctTimeStamp);
-
- /*
- * The Terminal server informed us that it was rebooted
- * * STOP all records from this NAS
- */
-
- sprintf(querystr,
- "UPDATE %s SET AcctStopTime='%s', AcctSessionTime=unix_timestamp('%s') - unix_timestamp(AcctStartTime), AcctTerminateCause='%s', AcctStopDelay = %ld WHERE AcctSessionTime=0 AND AcctStopTime=0 AND NASIPAddress= '%s' AND AcctStartTime <= '%s'",
- inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
- sqlrecord->AcctTimeStamp, sqlrecord->AcctTerminateCause,
- sqlrecord->AcctDelayTime, sqlrecord->NASIPAddress,
- sqlrecord->AcctTimeStamp);
-
- if (sql_query(inst, sqlsocket, querystr) < 0)
- radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting after NAS reboot - %s",
- sql_error(sqlsocket));
- sql_finish_query(sqlsocket);
-
- if (sqlfile) {
- fputs(querystr, sqlfile);
- fputs(";\n", sqlfile);
- fclose(sqlfile);
- }
- return 0;
- }
-
- if (sqlrecord->AcctStatusTypeId == PW_STATUS_ALIVE) {
- /*
- * Use acct unique session identifier if present
- */
- if(acctunique) {
- sprintf(querystr, "UPDATE %s SET FramedIPAddress = '%s' WHERE AcctUniqueId = '%s'",
- inst->config->sql_acct_table, sqlrecord->FramedIPAddress,
- sqlrecord->AcctUniqueId);
-
- } else {
- sprintf(querystr, "UPDATE %s SET FramedIPAddress = '%s' WHERE AcctSessionId = '%s' AND UserName = '%s' AND NASIPAddress= '%s'",
- inst->config->sql_acct_table, sqlrecord->FramedIPAddress,
- sqlrecord->AcctSessionId, sqlrecord->UserName,
- sqlrecord->NASIPAddress);
- }
-
- if (sql_query(inst, sqlsocket, querystr) < 0)
- radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting for ALIVE packet - %s",
- sql_error(sqlsocket));
- sql_finish_query(sqlsocket);
-
- if (sqlfile) {
- fputs(querystr, sqlfile);
- fputs(";\n", sqlfile);
- fclose(sqlfile);
- }
- return 0;
- }
-
- /*
- * Got start record
- */
- if (sqlrecord->AcctStatusTypeId == PW_STATUS_START) {
-
- /*
- * Insert new record with blank stop time until stop record is got
- */
- snprintf(querystr, 2048,
- "INSERT INTO %s ("
- "radacctid,"
- "acctsessionid,"
- "acctuniqueid,"
- "username,"
- "realm,"
- "nasipaddress,"
- "nasportid,"
- "nasporttype,"
- "acctstarttime,"
- "acctstoptime,"
- "acctsessiontime,"
- "acctauthentic,"
- "connectinfo_start,"
- "connectinfo_stop,"
- "acctinputoctets,"
- "acctoutputoctets,"
- "calledstationid,"
- "callingstationid,"
- "acctterminatecause,"
- "servicetype,"
- "framedprotocol,"
- "framedipaddress,"
- "acctstartdelay,"
- "acctstopdelay) "
- "VALUES (0,"
- "'%s', '%s', '%s', '%s', '%s', %ld, '%s', '%s',0,"
- "0, '%s', '%s', '', 0, 0,"
- "'%s', '%s', '', '%s', '%s', '%s', %ld, 0)",
- inst->config->sql_acct_table, sqlrecord->AcctSessionId,
- sqlrecord->AcctUniqueId, sqlrecord->UserName,
- sqlrecord->Realm, sqlrecord->NASIPAddress,
- sqlrecord->NASPortId, sqlrecord->NASPortType,
- sqlrecord->AcctTimeStamp, sqlrecord->AcctAuthentic,
- sqlrecord->ConnectInfo, sqlrecord->CalledStationId,
- sqlrecord->CallingStationId, sqlrecord->ServiceType,
- sqlrecord->FramedProtocol, sqlrecord->FramedIPAddress,
- sqlrecord->AcctDelayTime);
-
-
- if (sql_query(inst, sqlsocket, querystr) < 0) {
- radlog(L_ERR, "rlm_sql: Couldn't insert SQL accounting START record - %s",
- sql_error(sqlsocket));
-
- /*
- * We failed the insert above. It's probably because
- * the stop record came before the start. We try an
- * update here to be sure
- */
- if(acctunique) {
- snprintf(querystr, 2048, "UPDATE %s SET AcctStartTime = '%s', AcctStartDelay = %ld, ConnectInfo_start = '%s' WHERE AcctUniqueId = '%s'",
- inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
- sqlrecord->AcctDelayTime, sqlrecord->ConnectInfo,
- sqlrecord->AcctUniqueId);
- } else {
- snprintf(querystr, 2048, "UPDATE %s SET AcctStartTime = '%s', AcctStartDelay = %ld, ConnectInfo_start = '%s' WHERE AcctSessionId = '%s' AND UserName = '%s' AND NASIPAddress = '%s'",
- inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
- sqlrecord->AcctDelayTime, sqlrecord->ConnectInfo,
- sqlrecord->AcctSessionId, sqlrecord->UserName,
- sqlrecord->NASIPAddress);
- }
- if (sql_query(inst, sqlsocket, querystr) < 0)
- radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting START record - %s",
- sql_error(sqlsocket));
-
- }
- sql_finish_query(sqlsocket);
-
- /*
- * Got stop record
- */
- } else {
-
- /*
- * Set stop time on matching record with start time
- */
- if(acctunique) {
- snprintf(querystr, 2048,
- "UPDATE %s SET AcctStopTime = '%s', AcctSessionTime = '%lu', AcctInputOctets = '%lu', AcctOutputOctets = '%lu', AcctTerminateCause = '%s', AcctStopDelay = %ld, ConnectInfo_stop = '%s' WHERE AcctUniqueId = '%s'",
- inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
- sqlrecord->AcctSessionTime, sqlrecord->AcctInputOctets,
- sqlrecord->AcctOutputOctets, sqlrecord->AcctTerminateCause,
- sqlrecord->AcctDelayTime, sqlrecord->ConnectInfo,
- sqlrecord->AcctUniqueId);
-
- } else {
- snprintf(querystr, 2048,
- "UPDATE %s SET AcctStopTime = '%s', AcctSessionTime = '%lu', AcctInputOctets = '%lu', AcctOutputOctets = '%lu', AcctTerminateCause = '%s', AcctStopDelay = %ld, ConnectInfo_stop = '%s' WHERE AcctSessionId = '%s' AND UserName = '%s' AND NASIPAddress = '%s'",
- inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
- sqlrecord->AcctSessionTime, sqlrecord->AcctInputOctets,
- sqlrecord->AcctOutputOctets, sqlrecord->AcctTerminateCause,
- sqlrecord->AcctDelayTime, sqlrecord->ConnectInfo,
- sqlrecord->AcctSessionId, sqlrecord->UserName,
- sqlrecord->NASIPAddress);
- }
-
-
- if (sql_query(inst, sqlsocket, querystr) < 0)
- radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting STOP record - %s",
- sql_error(sqlsocket));
- sql_finish_query(sqlsocket);
-
-
- /*
- * If our update above didn't match anything
- * we assume it's because we haven't seen a
- * matching Start record. So we have to
- * insert this stop rather than do an update
- */
- num = sql_affected_rows(sqlsocket);
- if(num < 1) {
-
-#ifdef CISCO_ACCOUNTING_HACK
- /*
- * If stop but zero session length AND no previous
- * session found, drop it as in invalid packet
- * This is to fix CISCO's aaa from filling our
- * table with bogus crap
- */
- if (sqlrecord->AcctSessionTime <= 0) {
- radlog(L_ERR, "rlm_sql: Invalid STOP record. [%s] STOP record but zero session length? (nas %s)",
- sqlrecord->UserName, sqlrecord->NASIPAddress);
- return 0;
- }
-#endif
-
- /*
- * Insert record with no start time until matching start record comes
- */
- snprintf(querystr, 2048,
- "INSERT INTO %s ("
- "radacctid,"
- "acctsessionid,"
- "acctuniqueid,"
- "username,"
- "realm,"
- "nasipaddress,"
- "nasportid,"
- "nasporttype,"
- "acctstarttime,"
- "acctstoptime,"
- "acctsessiontime,"
- "acctauthentic,"
- "connectinfo_start,"
- "connectinfo_stop,"
- "acctinputoctets,"
- "acctoutputoctets,"
- "calledstationid,"
- "callingstationid,"
- "acctterminatecause,"
- "servicetype,"
- "framedprotocol,"
- "framedipaddress,"
- "acctstartdelay,"
- "acctstopdelay) "
- "VALUES ("
- "0, '%s', '%s', '%s', '%s', '%s', %ld, '%s', 0,"
- "'%s', '%lu', '%s', '', '%s', '%lu', '%lu', '%s',"
- "'%s', '%s', '%s', '%s', '%s', 0, %ld)",
- inst->config->sql_acct_table, sqlrecord->AcctSessionId,
- sqlrecord->AcctUniqueId, sqlrecord->UserName,
- sqlrecord->Realm, sqlrecord->NASIPAddress,
- sqlrecord->NASPortId, sqlrecord->NASPortType,
- sqlrecord->AcctTimeStamp, sqlrecord->AcctSessionTime,
- sqlrecord->AcctAuthentic, sqlrecord->ConnectInfo,
- sqlrecord->AcctInputOctets, sqlrecord->AcctOutputOctets,
- sqlrecord->CalledStationId, sqlrecord->CallingStationId,
- sqlrecord->AcctTerminateCause, sqlrecord->ServiceType,
- sqlrecord->FramedProtocol, sqlrecord->FramedIPAddress,
- sqlrecord->AcctDelayTime);
-
-
- if (sql_query(inst, sqlsocket, querystr) < 0)
- radlog(L_ERR, "rlm_sql: Couldn't insert SQL accounting STOP record - %s",
- sql_error(sqlsocket));
- sql_finish_query(sqlsocket);
- }
-
- }
- if (sqlfile) {
- fputs(querystr, sqlfile);
- fputs(";\n", sqlfile);
- fflush(sqlfile);
- fclose(sqlfile);
- }
-
- return 0;
-
-}
-
-
-/*************************************************************************
- *
* Function: sql_userparse
*
* Purpose: Read entries from the database and fill VALUE_PAIR structures
*
*************************************************************************/
-int sql_userparse(VALUE_PAIR ** first_pair, SQL_ROW row, int mode) {
+int
+sql_userparse(VALUE_PAIR ** first_pair, SQL_ROW row, int mode, int itemtype)
+{
DICT_ATTR *attr;
VALUE_PAIR *pair, *check;
+ int i = 0;
+
+ if (itemtype == PW_ITEM_REPLY)
+ i = 2;
- if ((attr = dict_attrbyname(row[2])) == (DICT_ATTR *) NULL) {
- radlog(L_ERR | L_CONS, "rlm_sql: unknown attribute %s", row[2]);
+ if ((attr = dict_attrbyname(row[i])) == (DICT_ATTR *) NULL) {
+ radlog(L_ERR | L_CONS, "rlm_sql: unknown attribute %s", row[i]);
return (-1);
}
#if defined( BINARY_FILTERS )
attr->type != PW_TYPE_ABINARY &&
#endif
- mode == PW_VP_GROUPDATA) return 0;
+ mode == PW_VP_GROUPDATA)
+ return 0;
- pair = pairmake(row[2], row[3], T_OP_CMP_EQ);
+ pair = pairmake(row[i], row[i + 1], T_OP_CMP_EQ);
pairadd(first_pair, pair);
+ vp_printlist(stderr, *first_pair);
+
return 0;
}
* Purpose: Get any group check or reply pairs
*
*************************************************************************/
-int sql_getvpdata(SQL_INST *inst, SQLSOCK *sqlsocket, char *table, VALUE_PAIR ** vp, char *user, int mode)
+int
+sql_getvpdata(SQL_INST * inst, SQLSOCK * sqlsocket, VALUE_PAIR ** check,
+ VALUE_PAIR ** reply, char *query, int mode)
{
- char querystr[256];
- char authstr[256];
- char username[AUTH_STRING_LEN * 2 + 1];
SQL_ROW row;
- int rows=0;
- int length;
+ int rows = 0;
- if (strlen(user) > AUTH_STRING_LEN)
- length = AUTH_STRING_LEN;
- else
- length = strlen(user);
-
- /*
- * FIXME CHECK user for weird charactors!!
- */
- sql_escape_string(username, user, length);
-
- if (mode == PW_VP_USERDATA) {
- if (inst->config->sensitiveusername)
- sprintf(authstr, "STRCMP(Username, '%s') = 0", username);
- else
- sprintf(authstr, "UserName = '%s'", username);
- sprintf(querystr, "SELECT * FROM %s WHERE %s ORDER BY id", table,
- authstr);
- } else if (mode == PW_VP_GROUPDATA) {
- if (inst->config->sensitiveusername)
- sprintf(authstr, "STRCMP(%s.Username, '%s') = 0",
- inst->config->sql_usergroup_table, username);
- else
- sprintf(authstr, "%s.UserName = '%s'", inst->config->sql_usergroup_table,
- username);
- sprintf(querystr,
- "SELECT %s.* FROM %s, %s WHERE %s AND %s.GroupName = %s.GroupName ORDER BY %s.id",
- table, table, inst->config->sql_usergroup_table, authstr,
- inst->config->sql_usergroup_table, table, table);
- } else if (mode == PW_VP_REALMDATA)
- sprintf(querystr,
- "SELECT %s.* FROM %s, %s WHERE %s.RealmName = '%s' AND %s.GroupName = %s.GroupName ORDER BY %s.id",
- table, table, inst->config->sql_realmgroup_table,
- inst->config->sql_realmgroup_table, username,
- inst->config->sql_realmgroup_table, table, table);
- if (sql_select_query(inst, sqlsocket, querystr) < 0) {
- radlog(L_ERR,"get_vpdata: database query error");
+ if (sql_select_query(inst, sqlsocket, query) < 0) {
+ radlog(L_ERR, "rlm_sql_getvpdata: database query error");
return -1;
}
-
-
while ((row = sql_fetch_row(sqlsocket))) {
- if (sql_userparse(vp, row, mode) != 0) {
+ if (sql_userparse(check, row, mode, PW_ITEM_CHECK) != 0) {
+ radlog(L_ERR | L_CONS, "rlm_sql: Error getting data from database");
+ sql_finish_select_query(sqlsocket);
+ return -1;
+ }
+ if (sql_userparse(reply, row, mode, PW_ITEM_REPLY) != 0) {
radlog(L_ERR | L_CONS, "rlm_sql: Error getting data from database");
sql_finish_select_query(sqlsocket);
return -1;
rows++;
}
sql_finish_select_query(sqlsocket);
- sql_free_result(sqlsocket);
return rows;
-
}
* Purpose: Checks the terminal server for a spacific login entry
*
*************************************************************************/
-static int sql_check_ts(SQL_ROW row) {
+static int
+sql_check_ts(SQL_ROW row)
+{
int pid, st, e;
int n;
* Purpose: Check radius accounting for duplicate logins
*
*************************************************************************/
-int sql_check_multi(SQL_INST *inst, SQLSOCK *sqlsocket, char *name, VALUE_PAIR * request, int maxsimul) {
+int
+sql_check_multi(SQL_INST * inst, SQLSOCK * sqlsocket, char *name,
+ VALUE_PAIR * request, int maxsimul)
+{
- char querystr[256];
+ char querystr[MAX_QUERY_LEN];
char authstr[256];
VALUE_PAIR *fra;
SQL_ROW row;
uint32_t ipno = 0;
int mpp = 1;
- if (inst->config->sensitiveusername)
- sprintf(authstr, "STRCMP(UserName, '%s') = 0", name);
- else
- sprintf(authstr, "UserName = '%s'", name);
+ sprintf(authstr, "UserName = '%s'", name);
sprintf(querystr, "SELECT COUNT(*) FROM %s WHERE %s AND AcctStopTime = 0",
inst->config->sql_acct_table, authstr);
if (sql_select_query(inst, sqlsocket, querystr) < 0) {
- radlog(L_ERR,"sql_check_multi: database query error");
+ radlog(L_ERR, "sql_check_multi: database query error");
return -1;
}
row = sql_fetch_row(sqlsocket);
count = atoi(row[0]);
sql_finish_select_query(sqlsocket);
- sql_free_result(sqlsocket);
if (count < maxsimul)
return 0;
sprintf(querystr, "SELECT * FROM %s WHERE %s AND AcctStopTime = 0",
inst->config->sql_acct_table, authstr);
if (sql_select_query(inst, sqlsocket, querystr) < 0) {
- radlog(L_ERR,"sql_check_multi: database query error");
+ radlog(L_ERR, "sql_check_multi: database query error");
return -1;
}
while ((row = sql_fetch_row(sqlsocket))) {
mpp = 2;
} else if (check == 2)
- radlog(L_ERR, "rlm_sql: Problem with checkrad [%s] (from nas %s)", name, row[4]);
+ radlog(L_ERR, "rlm_sql: Problem with checkrad [%s] (from nas %s)",
+ name, row[4]);
else {
/*
* False record - zap it
if (inst->config->deletestalesessions) {
SQLSOCK *sqlsocket1;
- radlog(L_ERR, "rlm_sql: Deleteing stale session [%s] (from nas %s/%s)", row[2],
- row[4], row[5]);
+ radlog(L_ERR,
+ "rlm_sql: Deleteing stale session [%s] (from nas %s/%s)",
+ row[2], row[4], row[5]);
sqlsocket1 = sql_get_socket(inst);
sprintf(querystr, "DELETE FROM %s WHERE RadAcctId = '%s'",
inst->config->sql_acct_table, row[0]);
}
}
sql_finish_select_query(sqlsocket);
- sql_free_result(sqlsocket);
return (count < maxsimul) ? 0 : mpp;
+}
+
+void
+query_log(SQL_INST * inst, char *querystr)
+{
+ FILE *sqlfile = 0;
+
+ if (inst->config->sqltrace) {
+ if ((sqlfile = fopen(inst->config->tracefile, "a")) == (FILE *) NULL) {
+ radlog(L_ERR, "rlm_sql: Couldn't open file %s",
+ inst->config->tracefile);
+ } else {
+#if defined(F_LOCK) && !defined(BSD)
+ (void) lockf((int) sqlfile, (int) F_LOCK, (off_t) MAX_QUERY_LEN);
+#else
+ (void) flock(sqlfile, SQL_LOCK_EX);
+#endif
+ fputs(querystr, sqlfile);
+ fputs(";\n", sqlfile);
+ fclose(sqlfile);
+ }
+ }
+}
+
+VALUE_PAIR *
+set_userattr(VALUE_PAIR * first, char *username, char *saveuser, int *savelen)
+{
+
+ VALUE_PAIR *uservp = NULL;
+ uint8_t escaped_user[MAX_STRING_LEN];
+
+ if ((uservp = pairfind(first, PW_USER_NAME)) != NULL) {
+ if (saveuser)
+ strNcpy(saveuser, uservp->strvalue, MAX_STRING_LEN);
+ if (savelen)
+ *savelen = uservp->length;
+ if (username) {
+ sql_escape_string(escaped_user, username, strlen(username));
+ } else {
+ sql_escape_string(escaped_user, uservp->strvalue, uservp->length);
+ }
+ strNcpy(uservp->strvalue, escaped_user, MAX_STRING_LEN);
+ uservp->length = strlen(escaped_user);
+ }
+
+ return uservp;
+}
+
+void
+restore_userattr(VALUE_PAIR * uservp, char *saveuser, int savelen)
+{
+ strNcpy(uservp->strvalue, saveuser, MAX_STRING_LEN);
+ uservp->length = savelen;
}
* Purpose: Establish connection to the db
*
*************************************************************************/
-SQLSOCK *sql_create_socket(SQL_INST *inst) {
+SQLSOCK *
+sql_create_socket(SQL_INST * inst)
+{
SQLSOCK *sqlsocket;
if ((sqlsocket = malloc(sizeof(SQLSOCK))) == NULL) {
- radlog(L_CONS|L_ERR, "sql_create_socket: no memory");
+ radlog(L_CONS | L_ERR, "sql_create_socket: no memory");
exit(1);
}
mysql_init(&(sqlsocket->conn));
- if (!(sqlsocket->sock = mysql_real_connect(&(sqlsocket->conn), inst->config->sql_server, inst->config->sql_login, inst->config->sql_password, inst->config->sql_db, 0, NULL, CLIENT_FOUND_ROWS))) {
- radlog(L_ERR, "rlm_sql: Couldn't connect socket to MySQL server %s@%s:%s", inst->config->sql_login, inst->config->sql_server, inst->config->sql_db);
- radlog(L_ERR, "rlm_sql: Mysql error '%s'", mysql_error(&sqlsocket->conn));
+ if (!
+ (sqlsocket->sock =
+ mysql_real_connect(&(sqlsocket->conn), inst->config->sql_server,
+ inst->config->sql_login, inst->config->sql_password,
+ inst->config->sql_db, 0, NULL, CLIENT_FOUND_ROWS))) {
+ radlog(L_ERR, "rlm_sql: Couldn't connect socket to MySQL server %s@%s:%s",
+ inst->config->sql_login, inst->config->sql_server,
+ inst->config->sql_db);
+ radlog(L_ERR, "rlm_sql: Mysql error '%s'",
+ mysql_error(&sqlsocket->conn));
sqlsocket->sock = NULL;
return NULL;
}
* Purpose: Issue a query to the database
*
*************************************************************************/
-int sql_query(SQL_INST *inst, SQLSOCK *sqlsocket, char *querystr) {
+int
+sql_query(SQL_INST * inst, SQLSOCK * sqlsocket, char *querystr)
+{
if (inst->config->sqltrace)
- DEBUG(querystr);
- if (sqlsocket->sock == NULL) {
+ DEBUG("query: %s", querystr);
+ if (sqlsocket->sock == NULL) {
radlog(L_ERR, "Socket not connected");
return 0;
}
* Purpose: Issue a select query to the database
*
*************************************************************************/
-int sql_select_query(SQL_INST *inst, SQLSOCK *sqlsocket, char *querystr) {
+int
+sql_select_query(SQL_INST * inst, SQLSOCK * sqlsocket, char *querystr)
+{
if (inst->config->sqltrace)
DEBUG(querystr);
if (sqlsocket->sock == NULL) {
radlog(L_ERR, "Socket not connected");
- return 0;
+ return -1;
}
mysql_query(sqlsocket->sock, querystr);
- if (sql_store_result(sqlsocket) && sql_num_fields(sqlsocket))
- return 1;
- else
+ if (sql_store_result(sqlsocket) && sql_num_fields(sqlsocket))
return 0;
+ else
+ return -1;
}
* set for the query.
*
*************************************************************************/
-int sql_store_result(SQLSOCK *sqlsocket) {
+int
+sql_store_result(SQLSOCK * sqlsocket)
+{
if (sqlsocket->sock == NULL) {
radlog(L_ERR, "Socket not connected");
return 0;
}
if (!(sqlsocket->result = mysql_store_result(sqlsocket->sock))) {
- radlog(L_ERR,"MYSQL Error: Cannot get result");
- radlog(L_ERR,"MYSQL Error: %s",mysql_error(sqlsocket->sock));
+ radlog(L_ERR, "MYSQL Error: Cannot get result");
+ radlog(L_ERR, "MYSQL Error: %s", mysql_error(sqlsocket->sock));
return 0;
}
return 1;
* of columns from query
*
*************************************************************************/
-int sql_num_fields(SQLSOCK *sqlsocket) {
+int
+sql_num_fields(SQLSOCK * sqlsocket)
+{
+
+ int num = 0;
- int num = 0;
#if MYSQL_VERSION_ID >= 32224
if (!(num = mysql_field_count(sqlsocket->sock))) {
#else
if (!(num = mysql_num_fields(sqlsocket->sock))) {
#endif
- radlog(L_ERR,"MYSQL Error: Cannot get result");
- radlog(L_ERR,"MYSQL error: %s",mysql_error(sqlsocket->sock));
+ radlog(L_ERR, "MYSQL Error: Cannot get result");
+ radlog(L_ERR, "MYSQL error: %s", mysql_error(sqlsocket->sock));
}
return num;
}
* query
*
*************************************************************************/
-int sql_num_rows(SQLSOCK *sqlsocket) {
+int
+sql_num_rows(SQLSOCK * sqlsocket)
+{
return mysql_num_rows(sqlsocket->result);
}
* with all the data for the query
*
*************************************************************************/
-SQL_ROW sql_fetch_row(SQLSOCK *sqlsocket) {
+SQL_ROW
+sql_fetch_row(SQLSOCK * sqlsocket)
+{
return mysql_fetch_row(sqlsocket->result);
}
* for a result set
*
*************************************************************************/
-void sql_free_result(SQLSOCK *sqlsocket) {
+void
+sql_free_result(SQLSOCK * sqlsocket)
+{
- mysql_free_result(sqlsocket->result);
+ if (sqlsocket->result) {
+ mysql_free_result(sqlsocket->result);
+ }
}
* connection
*
*************************************************************************/
-char *sql_error(SQLSOCK *sqlsocket) {
+char *
+sql_error(SQLSOCK * sqlsocket)
+{
return mysql_error(sqlsocket->sock);
}
* connection
*
*************************************************************************/
-void sql_close(SQLSOCK *sqlsocket) {
+void
+sql_close(SQLSOCK * sqlsocket)
+{
mysql_close(sqlsocket->sock);
sqlsocket->sock = NULL;
* Purpose: End the query, such as freeing memory
*
*************************************************************************/
-void sql_finish_query(SQLSOCK *sqlsocket) {
+void
+sql_finish_query(SQLSOCK * sqlsocket)
+{
}
* Purpose: End the select query, such as freeing memory or result
*
*************************************************************************/
-void sql_finish_select_query(SQLSOCK *sqlsocket) {
+void
+sql_finish_select_query(SQLSOCK * sqlsocket)
+{
sql_free_result(sqlsocket);
}
* Purpose: End the select query, such as freeing memory or result
*
*************************************************************************/
-int sql_affected_rows(SQLSOCK *sqlsocket) {
+int
+sql_affected_rows(SQLSOCK * sqlsocket)
+{
return mysql_affected_rows(sqlsocket->sock);
}
* Purpose: Esacpe "'" and any other wierd charactors
*
*************************************************************************/
-int sql_escape_string(char *to, char *from, int length) {
+int
+sql_escape_string(char *to, char *from, int length)
+{
mysql_escape_string(to, from, length);
return 1;
typedef MYSQL_ROW SQL_ROW;
typedef struct sql_socket {
- int id;
+ int id;
#if HAVE_PTHREAD_H
- sem_t *semaphore;
+ sem_t *semaphore;
#else
- int in_use;
+ int in_use;
#endif
struct sql_socket *next;
- MYSQL *sock;
- MYSQL conn;
- MYSQL_RES *result;
+ MYSQL *sock;
+ MYSQL conn;
+ MYSQL_RES *result;
} SQLSOCK;
-typedef struct sql_config {
- char *sql_server;
- char *sql_login;
- char *sql_password;
- char *sql_db;
- char *sql_acct_table;
- char *sql_authcheck_table;
- char *sql_authreply_table;
- char *sql_groupcheck_table;
- char *sql_groupreply_table;
- char *sql_usergroup_table;
- char *sql_realm_table;
- char *sql_realmgroup_table;
- char *sql_nas_table;
- char *sql_dict_table;
- int sensitiveusername;
- int sqltrace;
- char *tracefile;
- int deletestalesessions;
- int num_sql_socks;
-} SQL_CONFIG;
-
typedef struct sql_inst {
- int used;
+ int used;
SQLSOCK *sqlpool;
SQL_CONFIG *config;
#if HAVE_PTHREAD_H
#endif
} SQL_INST;
-SQLSOCK *sql_create_socket(SQL_INST *inst);
+SQLSOCK *sql_create_socket(SQL_INST * inst);
int sql_checksocket(const char *facility);
-int sql_query(SQL_INST *inst, SQLSOCK *sqlsocket, char *querystr);
-int sql_select_query(SQL_INST *inst, SQLSOCK *sqlsocket, char *querystr);
-int sql_store_result(SQLSOCK *sqlsocket);
-int sql_num_fields(SQLSOCK *sqlsocket);
-int sql_num_rows(SQLSOCK *sqlsocket);
-SQL_ROW sql_fetch_row(SQLSOCK *sqlsocket);
-void sql_free_result(SQLSOCK *sqlsocket);
-char *sql_error(SQLSOCK *sqlsocket);
-void sql_close(SQLSOCK *sqlsocket);
-void sql_finish_query(SQLSOCK *sqlsocket);
-void sql_finish_select_query(SQLSOCK *sqlsocket);
-int sql_affected_rows(SQLSOCK *sqlsocket);
+int sql_query(SQL_INST * inst, SQLSOCK * sqlsocket, char *querystr);
+int sql_select_query(SQL_INST * inst, SQLSOCK * sqlsocket,
+ char *querystr);
+int sql_store_result(SQLSOCK * sqlsocket);
+int sql_num_fields(SQLSOCK * sqlsocket);
+int sql_num_rows(SQLSOCK * sqlsocket);
+SQL_ROW sql_fetch_row(SQLSOCK * sqlsocket);
+void sql_free_result(SQLSOCK * sqlsocket);
+char *sql_error(SQLSOCK * sqlsocket);
+void sql_close(SQLSOCK * sqlsocket);
+void sql_finish_query(SQLSOCK * sqlsocket);
+void sql_finish_select_query(SQLSOCK * sqlsocket);
+int sql_affected_rows(SQLSOCK * sqlsocket);
int sql_escape_string(char *to, char *from, int length);