*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
- * Copyright 2000 The FreeRADIUS server project
+ * Copyright 2000,2006 The FreeRADIUS server project
* Copyright 2000 Mike Machado <mike@innercite.com>
* Copyright 2000 Alan DeKok <aland@ox.org>
*/
-static const char rcsid[] =
- "$Id$";
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
-#include "autoconf.h"
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/rad_assert.h>
-#include <stdio.h>
#include <sys/stat.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <string.h>
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include "radiusd.h"
-#include "modules.h"
-#include "conffile.h"
#include "rlm_sql.h"
-#include "rad_assert.h"
static char *allowed_chars = NULL;
-static CONF_PARSER module_config[] = {
+static const CONF_PARSER module_config[] = {
{"driver",PW_TYPE_STRING_PTR,
offsetof(SQL_CONFIG,sql_driver), NULL, "mysql"},
{"server",PW_TYPE_STRING_PTR,
offsetof(SQL_CONFIG,sql_password), NULL, ""},
{"radius_db", PW_TYPE_STRING_PTR,
offsetof(SQL_CONFIG,sql_db), NULL, "radius"},
- {"acct_table", PW_TYPE_STRING_PTR,
- offsetof(SQL_CONFIG,sql_acct_table), NULL, "radacct"},
- {"acct_table2", PW_TYPE_STRING_PTR,
- offsetof(SQL_CONFIG,sql_acct_table2), NULL, "radacct"},
- {"authcheck_table", PW_TYPE_STRING_PTR,
- offsetof(SQL_CONFIG,sql_authcheck_table), NULL, "radcheck"},
- {"authreply_table", PW_TYPE_STRING_PTR,
- offsetof(SQL_CONFIG,sql_authreply_table), NULL, "radreply"},
- {"groupcheck_table", PW_TYPE_STRING_PTR,
- offsetof(SQL_CONFIG,sql_groupcheck_table), NULL, "radgroupcheck"},
- {"groupreply_table", PW_TYPE_STRING_PTR,
- offsetof(SQL_CONFIG,sql_groupreply_table), NULL, "radgroupreply"},
- {"usergroup_table", PW_TYPE_STRING_PTR,
- offsetof(SQL_CONFIG,sql_usergroup_table), NULL, "usergroup"},
- {"nas_table", PW_TYPE_STRING_PTR,
- offsetof(SQL_CONFIG,sql_nas_table), NULL, "nas"},
- {"dict_table", PW_TYPE_STRING_PTR,
- offsetof(SQL_CONFIG,sql_dict_table), NULL, "dictionary"},
+ {"filename", PW_TYPE_FILENAME, /* for sqlite */
+ offsetof(SQL_CONFIG,sql_file), NULL, NULL},
+ {"read_groups", PW_TYPE_BOOLEAN,
+ offsetof(SQL_CONFIG,read_groups), NULL, "yes"},
{"sqltrace", PW_TYPE_BOOLEAN,
offsetof(SQL_CONFIG,sqltrace), NULL, "no"},
{"sqltracefile", PW_TYPE_STRING_PTR,
{"readclients", PW_TYPE_BOOLEAN,
offsetof(SQL_CONFIG,do_clients), NULL, "no"},
{"deletestalesessions", PW_TYPE_BOOLEAN,
- offsetof(SQL_CONFIG,deletestalesessions), NULL, "no"},
+ offsetof(SQL_CONFIG,deletestalesessions), NULL, "yes"},
{"num_sql_socks", PW_TYPE_INTEGER,
offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"},
+ {"lifetime", PW_TYPE_INTEGER,
+ offsetof(SQL_CONFIG,lifetime), NULL, "0"},
+ {"max_queries", PW_TYPE_INTEGER,
+ offsetof(SQL_CONFIG,max_queries), NULL, "0"},
{"sql_user_name", PW_TYPE_STRING_PTR,
offsetof(SQL_CONFIG,query_user), NULL, ""},
{"default_user_profile", PW_TYPE_STRING_PTR,
offsetof(SQL_CONFIG,default_profile), NULL, ""},
- {"query_on_not_found", PW_TYPE_BOOLEAN,
- offsetof(SQL_CONFIG,query_on_not_found), NULL, "no"},
+ {"nas_query", PW_TYPE_STRING_PTR,
+ offsetof(SQL_CONFIG,nas_query), NULL, "SELECT id,nasname,shortname,type,secret FROM nas"},
{"authorize_check_query", PW_TYPE_STRING_PTR,
offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
{"authorize_reply_query", PW_TYPE_STRING_PTR,
- offsetof(SQL_CONFIG,authorize_reply_query), NULL, ""},
+ offsetof(SQL_CONFIG,authorize_reply_query), NULL, NULL},
{"authorize_group_check_query", PW_TYPE_STRING_PTR,
offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
{"authorize_group_reply_query", PW_TYPE_STRING_PTR,
{"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
{"group_membership_query", PW_TYPE_STRING_PTR,
- offsetof(SQL_CONFIG,groupmemb_query), NULL, ""},
+ offsetof(SQL_CONFIG,groupmemb_query), NULL, NULL},
{"connect_failure_retry_delay", PW_TYPE_INTEGER,
offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
{"simul_count_query", PW_TYPE_STRING_PTR,
offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
{"simul_verify_query", PW_TYPE_STRING_PTR,
offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
- {"postauth_table", PW_TYPE_STRING_PTR,
- offsetof(SQL_CONFIG,sql_postauth_table), NULL, "radpostauth"},
{"postauth_query", PW_TYPE_STRING_PTR,
offsetof(SQL_CONFIG,postauth_query), NULL, ""},
{"safe-characters", PW_TYPE_STRING_PTR,
- offsetof(SQL_CONFIG,allowed_chars), NULL,
+ offsetof(SQL_CONFIG,allowed_chars), NULL,
"@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
+ /*
+ * This only works for a few drivers.
+ */
+ {"query_timeout", PW_TYPE_INTEGER,
+ offsetof(SQL_CONFIG,query_timeout), NULL, NULL},
+
{NULL, -1, 0, NULL, NULL}
};
-/***********************************************************************
- * start of main routines
- ***********************************************************************/
-static int rlm_sql_init(void) {
+/*
+ * Fall-Through checking function from rlm_files.c
+ */
+static int fallthrough(VALUE_PAIR *vp)
+{
+ VALUE_PAIR *tmp;
+ tmp = pairfind(vp, PW_FALL_THROUGH, 0);
- /*
- * FIXME:
- * We should put the sqlsocket array here once
- * the module code is reworked to not unload
- * modules on HUP. This way we can have
- * persistant connections. -jcarneal
- */
- return 0;
+ return tmp ? tmp->vp_integer : 0;
}
+
+
/*
* Yucky prototype.
*/
-static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username);
static int generate_sql_clients(SQL_INST *inst);
-static int sql_escape_func(char *out, int outlen, const char *in);
+static size_t sql_escape_func(char *out, size_t outlen, const char *in);
/*
* sql xlat function. Right now only SELECTs are supported. Only
* the first element of the SELECT result will be used.
+ *
+ * For other statements (insert, update, delete, etc.), the
+ * number of affected rows will be returned.
*/
static int sql_xlat(void *instance, REQUEST *request,
char *fmt, char *out, size_t freespace,
- RADIUS_ESCAPE_STRING func)
+ UNUSED RADIUS_ESCAPE_STRING func)
{
SQLSOCK *sqlsocket;
SQL_ROW row;
SQL_INST *inst = instance;
char querystr[MAX_QUERY_LEN];
char sqlusername[MAX_STRING_LEN];
- int ret = 0;
+ size_t ret = 0;
+
+ RDEBUG("sql_xlat");
- DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name);
/*
* Add SQL-User-Name attribute just in case it is needed
* We could search the string fmt for SQL-User-Name to see if this is
sqlsocket = sql_get_socket(inst);
if (sqlsocket == NULL)
return 0;
+
+ /*
+ * If the query starts with any of the following prefixes,
+ * then return the number of rows affected
+ */
+ if ((strncasecmp(querystr, "insert", 6) == 0) ||
+ (strncasecmp(querystr, "update", 6) == 0) ||
+ (strncasecmp(querystr, "delete", 6) == 0)) {
+ int numaffected;
+ char buffer[21]; /* 64bit max is 20 decimal chars + null byte */
+
+ if (rlm_sql_query(sqlsocket,inst,querystr)) {
+ radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
+ inst->config->xlat_name, querystr,
+ (inst->module->sql_error)(sqlsocket,
+ inst->config));
+ sql_release_socket(inst,sqlsocket);
+ return 0;
+ }
+
+ numaffected = (inst->module->sql_affected_rows)(sqlsocket,
+ inst->config);
+ if (numaffected < 1) {
+ RDEBUG("rlm_sql (%s): SQL query affected no rows",
+ inst->config->xlat_name);
+ }
+
+ /*
+ * Don't chop the returned number if freespace is
+ * too small. This hack is necessary because
+ * some implementations of snprintf return the
+ * size of the written data, and others return
+ * the size of the data they *would* have written
+ * if the output buffer was large enough.
+ */
+ snprintf(buffer, sizeof(buffer), "%d", numaffected);
+ ret = strlen(buffer);
+ if (ret >= freespace){
+ RDEBUG("rlm_sql (%s): Can't write result, insufficient string space",
+ inst->config->xlat_name);
+ (inst->module->sql_finish_query)(sqlsocket,
+ inst->config);
+ sql_release_socket(inst,sqlsocket);
+ return 0;
+ }
+
+ memcpy(out, buffer, ret + 1); /* we did bounds checking above */
+
+ (inst->module->sql_finish_query)(sqlsocket, inst->config);
+ sql_release_socket(inst,sqlsocket);
+ return ret;
+ } /* else it's a SELECT statement */
+
if (rlm_sql_select_query(sqlsocket,inst,querystr)){
radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
inst->config->xlat_name,querystr,
- (char *)(inst->module->sql_error)(sqlsocket, inst->config));
+ (inst->module->sql_error)(sqlsocket, inst->config));
sql_release_socket(inst,sqlsocket);
return 0;
}
ret = rlm_sql_fetch_row(sqlsocket, inst);
if (ret) {
- DEBUG("rlm_sql (%s): SQL query did not succeed",
- inst->config->xlat_name);
+ RDEBUG("SQL query did not succeed");
(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
sql_release_socket(inst,sqlsocket);
return 0;
row = sqlsocket->row;
if (row == NULL) {
- DEBUG("rlm_sql (%s): SQL query did not return any results",
- inst->config->xlat_name);
+ RDEBUG("SQL query did not return any results");
(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
sql_release_socket(inst,sqlsocket);
return 0;
}
if (row[0] == NULL){
- DEBUG("rlm_sql (%s): row[0] returned NULL",
- inst->config->xlat_name);
+ RDEBUG("row[0] returned NULL");
(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
sql_release_socket(inst,sqlsocket);
return 0;
}
ret = strlen(row[0]);
- if (ret > freespace){
- DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space",
- inst->config->xlat_name);
+ if (ret >= freespace){
+ RDEBUG("Insufficient string space");
(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
sql_release_socket(inst,sqlsocket);
return 0;
}
- strncpy(out,row[0],ret);
+ strlcpy(out,row[0],freespace);
- DEBUG("rlm_sql (%s): - sql_xlat finished",
- inst->config->xlat_name);
+ RDEBUG("sql_xlat finished");
(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
sql_release_socket(inst,sqlsocket);
SQL_ROW row;
char querystr[MAX_QUERY_LEN];
RADCLIENT *c;
- char *netmask;
+ char *prefix_ptr = NULL;
unsigned int i = 0;
-
- DEBUG("rlm_sql (%s): - generate_sql_clients",inst->config->xlat_name);
+ int numf = 0;
- if (inst->config->sql_nas_table == NULL){
- radlog(L_ERR, "rlm_sql (%s): sql_nas_table is NULL.",inst->config->xlat_name);
- return -1;
- }
- snprintf(querystr,MAX_QUERY_LEN - 1,"SELECT * FROM %s",inst->config->sql_nas_table);
+ DEBUG("rlm_sql (%s): Processing generate_sql_clients",
+ inst->config->xlat_name);
+
+ /* NAS query isn't xlat'ed */
+ strlcpy(querystr, inst->config->nas_query, sizeof(querystr));
+ DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s",
+ inst->config->xlat_name, querystr);
- DEBUG("rlm_sql (%s): Query: %s",inst->config->xlat_name,querystr);
sqlsocket = sql_get_socket(inst);
if (sqlsocket == NULL)
return -1;
if (rlm_sql_select_query(sqlsocket,inst,querystr)){
radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
- inst->config->xlat_name,querystr,
- (char *)(inst->module->sql_error)(sqlsocket, inst->config));
+ inst->config->xlat_name,querystr,
+ (inst->module->sql_error)(sqlsocket, inst->config));
sql_release_socket(inst,sqlsocket);
return -1;
}
row = sqlsocket->row;
if (row == NULL)
break;
-/*
- * Format:
- * Row1 Row2 Row3 Row4 Row5 Row6 Row7 Row8
- *
- * id nasname shortname type ports secret community description
- *
- */
-
+ /*
+ * The return data for each row MUST be in the following order:
+ *
+ * 0. Row ID (currently unused)
+ * 1. Name (or IP address)
+ * 2. Shortname
+ * 3. Type
+ * 4. Secret
+ * 5. Virtual Server (optional)
+ */
if (!row[0]){
radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
continue;
radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
continue;
}
- if (strlen(row[1]) >= sizeof(c->longname)){
- radlog(L_ERR, "rlm_sql (%s): nasname of length %d is greater than the allowed maximum of %d",
- inst->config->xlat_name,strlen(row[1]),sizeof(c->longname) - 1);
- continue;
- }
-
if (!row[2]){
radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
continue;
}
- if (strlen(row[2]) >= sizeof(c->shortname)){
- radlog(L_ERR, "rlm_sql (%s): shortname of length %d is greater than the allowed maximum of %d",
- inst->config->xlat_name,strlen(row[2]),sizeof(c->shortname) - 1);
- continue;
- }
- if (row[3] && strlen(row[3]) >= sizeof(c->nastype)){
- radlog(L_ERR, "rlm_sql (%s): nastype of length %d is greater than the allowed maximum of %d",
- inst->config->xlat_name,strlen(row[3]),sizeof(c->nastype) - 1);
- continue;
- }
- if (!row[5]){
+ if (!row[4]){
radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
continue;
}
- if (strlen(row[5]) >= sizeof(c->secret)){
- radlog(L_ERR, "rlm_sql (%s): secret of length %d is greater than the allowed maximum of %d",
- inst->config->xlat_name,strlen(row[5]),sizeof(c->secret) - 1);
- continue;
- }
DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
- row[1],row[2],row[5]);
+ row[1],row[2],row[4]);
- c = rad_malloc(sizeof(RADCLIENT));
- memset(c, 0, sizeof(RADCLIENT));
+ c = rad_malloc(sizeof(*c));
+ memset(c, 0, sizeof(*c));
+
+#ifdef WITH_DYNAMIC_CLIENTS
+ c->dynamic = 1;
+#endif
- c->netmask = ~0;
- netmask = strchr(row[1], '/');
-
/*
- * Look for netmasks.
+ * Look for prefixes
*/
- c->netmask = ~0;
- if (netmask) {
- int mask_length;
-
- mask_length = atoi(netmask + 1);
- if ((mask_length < 0) || (mask_length > 32)) {
- radlog(L_ERR, "rlm_sql (%s): Invalid value '%s' for IP network mask for nasname %s.",
- inst->config->xlat_name, netmask + 1,row[1]);
+ c->prefix = -1;
+ prefix_ptr = strchr(row[1], '/');
+ if (prefix_ptr) {
+ c->prefix = atoi(prefix_ptr + 1);
+ if ((c->prefix < 0) || (c->prefix > 128)) {
+ radlog(L_ERR, "rlm_sql (%s): Invalid Prefix value '%s' for IP.",
+ inst->config->xlat_name, prefix_ptr + 1);
free(c);
continue;
}
-
- if (mask_length == 0) {
- c->netmask = 0;
- } else {
- c->netmask = ~0 << (32 - mask_length);
- }
-
- *netmask = '\0';
- c->netmask = htonl(c->netmask);
+ /* Replace '/' with '\0' */
+ *prefix_ptr = '\0';
}
- c->ipaddr = ip_getaddr(row[1]);
- if (c->ipaddr == INADDR_NONE) {
- radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s",
- inst->config->xlat_name, row[1]);
+ /*
+ * Always get the numeric representation of IP
+ */
+ if (ip_hton(row[1], AF_UNSPEC, &c->ipaddr) < 0) {
+ radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s: %s",
+ inst->config->xlat_name,
+ row[1], fr_strerror());
free(c);
continue;
+ } else {
+ char buffer[256];
+ ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
+ c->longname = strdup(buffer);
}
- /*
- * Update the client name again...
- */
- if (netmask) {
- *netmask = '/';
- c->ipaddr &= c->netmask;
- strcpy(c->longname, row[1]);
- } else {
- ip_hostname(c->longname, sizeof(c->longname),
- c->ipaddr);
+ if (c->prefix < 0) switch (c->ipaddr.af) {
+ case AF_INET:
+ c->prefix = 32;
+ break;
+ case AF_INET6:
+ c->prefix = 128;
+ break;
+ default:
+ break;
}
- strcpy((char *)c->secret, row[5]);
- strcpy(c->shortname, row[2]);
+ /*
+ * Other values (secret, shortname, nastype, virtual_server)
+ */
+ c->secret = strdup(row[4]);
+ c->shortname = strdup(row[2]);
if(row[3] != NULL)
- strcpy(c->nastype, row[3]);
+ c->nastype = strdup(row[3]);
- DEBUG("rlm_sql (%s): Adding client %s (%s) to clients list",inst->config->xlat_name,
- c->longname,c->shortname);
-
- c->next = mainconfig.clients;
- mainconfig.clients = c;
+ numf = (inst->module->sql_num_fields)(sqlsocket, inst->config);
+ if ((numf > 5) && (row[5] != NULL) && *row[5]) c->server = strdup(row[5]);
+ DEBUG("rlm_sql (%s): Adding client %s (%s, server=%s) to clients list",
+ inst->config->xlat_name,
+ c->longname,c->shortname, c->server ? c->server : "<none>");
+ if (!client_add(NULL, c)) {
+ sql_release_socket(inst, sqlsocket);
+ DEBUG("rlm_sql (%s): Failed to add client %s (%s) to clients list. Maybe there's a duplicate?",
+ inst->config->xlat_name,
+ c->longname,c->shortname);
+ client_free(c);
+ return -1;
+ }
}
(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
sql_release_socket(inst, sqlsocket);
/*
* Translate the SQL queries.
*/
-static int sql_escape_func(char *out, int outlen, const char *in)
+static size_t sql_escape_func(char *out, size_t outlen, const char *in)
{
- int len = 0;
+ size_t len = 0;
while (in[0]) {
/*
* escape it twice. (it will make things wrong if we have an
* escape candidate character in the username)
*/
-static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
+int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
{
VALUE_PAIR *vp=NULL;
char tmpuser[MAX_STRING_LEN];
sqlusername[0]= '\0';
/* Remove any user attr we added previously */
- pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
+ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
if (username != NULL) {
- strNcpy(tmpuser, username, MAX_STRING_LEN);
+ strlcpy(tmpuser, username, sizeof(tmpuser));
} else if (strlen(inst->config->query_user)) {
radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
} else {
return 0;
}
- if (*tmpuser) {
- strNcpy(sqlusername, tmpuser, MAX_STRING_LEN);
- DEBUG2("rlm_sql (%s): sql_set_user escaped user --> '%s'",
- inst->config->xlat_name, sqlusername);
- vp = pairmake("SQL-User-Name", sqlusername, 0);
- if (vp == NULL) {
- radlog(L_ERR, "%s", librad_errstr);
- return -1;
- }
+ strlcpy(sqlusername, tmpuser, MAX_STRING_LEN);
+ RDEBUG2("sql_set_user escaped user --> '%s'", sqlusername);
+ vp = radius_pairmake(request, &request->packet->vps,
+ "SQL-User-Name", NULL, 0);
+ if (!vp) {
+ radlog(L_ERR, "%s", fr_strerror());
+ return -1;
+ }
+
+ strlcpy(vp->vp_strvalue, tmpuser, sizeof(vp->vp_strvalue));
+ vp->length = strlen(vp->vp_strvalue);
+
+ return 0;
+
+}
+
+
+static void sql_grouplist_free (SQL_GROUPLIST **group_list)
+{
+ SQL_GROUPLIST *last;
+
+ while(*group_list) {
+ last = *group_list;
+ *group_list = (*group_list)->next;
+ free(last);
+ }
+}
+
- pairadd(&request->packet->vps, vp);
+static int sql_get_grouplist (SQL_INST *inst, SQLSOCK *sqlsocket, REQUEST *request, SQL_GROUPLIST **group_list)
+{
+ char querystr[MAX_QUERY_LEN];
+ int num_groups = 0;
+ SQL_ROW row;
+ SQL_GROUPLIST *group_list_tmp;
+
+ /* NOTE: sql_set_user should have been run before calling this function */
+
+ group_list_tmp = *group_list = NULL;
+
+ if (!inst->config->groupmemb_query ||
+ (inst->config->groupmemb_query[0] == 0))
return 0;
+
+ if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, request, sql_escape_func)) {
+ radlog_request(L_ERR, 0, request, "xlat \"%s\" failed.",
+ inst->config->groupmemb_query);
+ return -1;
+ }
+
+ if (rlm_sql_select_query(sqlsocket, inst, querystr) < 0) {
+ radlog_request(L_ERR, 0, request,
+ "database query error, %s: %s",
+ querystr,
+ (inst->module->sql_error)(sqlsocket,inst->config));
+ return -1;
+ }
+ while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
+ row = sqlsocket->row;
+ if (row == NULL)
+ break;
+ if (row[0] == NULL){
+ RDEBUG("row[0] returned NULL");
+ (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
+ sql_grouplist_free(group_list);
+ return -1;
+ }
+ if (*group_list == NULL) {
+ *group_list = rad_malloc(sizeof(SQL_GROUPLIST));
+ group_list_tmp = *group_list;
+ } else {
+ rad_assert(group_list_tmp != NULL);
+ group_list_tmp->next = rad_malloc(sizeof(SQL_GROUPLIST));
+ group_list_tmp = group_list_tmp->next;
+ }
+ group_list_tmp->next = NULL;
+ strlcpy(group_list_tmp->groupname, row[0], MAX_STRING_LEN);
}
- return -1;
+
+ (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
+
+ return num_groups;
}
+
/*
* sql groupcmp function. That way we can do group comparisons (in the users file for example)
* with the group memberships reciding in sql
* username will then be checked with the passed check string.
*/
-static int sql_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
+static int sql_groupcmp(void *instance, REQUEST *request, VALUE_PAIR *request_vp, VALUE_PAIR *check,
VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
{
SQLSOCK *sqlsocket;
- SQL_ROW row;
SQL_INST *inst = instance;
- char querystr[MAX_QUERY_LEN];
char sqlusername[MAX_STRING_LEN];
+ SQL_GROUPLIST *group_list, *group_list_tmp;
check_pairs = check_pairs;
reply_pairs = reply_pairs;
+ request_vp = request_vp;
- DEBUG("rlm_sql (%s): - sql_groupcmp", inst->config->xlat_name);
- if (!check || !check->strvalue || !check->length){
- DEBUG("rlm_sql (%s): sql_groupcmp: Illegal group name",
- inst->config->xlat_name);
+ RDEBUG("sql_groupcmp");
+ if (!check || !check->vp_strvalue || !check->length){
+ RDEBUG("sql_groupcmp: Illegal group name");
return 1;
}
- if (req == NULL){
- DEBUG("rlm_sql (%s): sql_groupcmp: NULL request",
- inst->config->xlat_name);
+ if (!request){
+ RDEBUG("sql_groupcmp: NULL request");
return 1;
}
- if (inst->config->groupmemb_query[0] == 0)
- return 1;
/*
* Set, escape, and check the user attr here
*/
- if (sql_set_user(inst, req, sqlusername, NULL) < 0)
+ if (sql_set_user(inst, request, sqlusername, NULL) < 0)
return 1;
- if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, req, sql_escape_func)){
- radlog(L_ERR, "rlm_sql (%s): xlat failed.",
- inst->config->xlat_name);
+
+ /*
+ * Get a socket for this lookup
+ */
+ sqlsocket = sql_get_socket(inst);
+ if (sqlsocket == NULL) {
/* Remove the username we (maybe) added above */
- pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
+ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
return 1;
}
- /* Remove the username we (maybe) added above */
- pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
- sqlsocket = sql_get_socket(inst);
- if (sqlsocket == NULL)
- return 1;
- if ((inst->module->sql_select_query)(sqlsocket,inst->config,querystr) <0){
- radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
- inst->config->xlat_name,querystr,
- (char *)(inst->module->sql_error)(sqlsocket,inst->config));
- sql_release_socket(inst,sqlsocket);
+ /*
+ * Get the list of groups this user is a member of
+ */
+ if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
+ radlog_request(L_ERR, 0, request,
+ "Error getting group membership");
+ /* Remove the username we (maybe) added above */
+ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
+ sql_release_socket(inst, sqlsocket);
return 1;
}
- while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
- row = sqlsocket->row;
- if (row == NULL)
- break;
- if (row[0] == NULL){
- DEBUG("rlm_sql (%s): row[0] returned NULL",
- inst->config->xlat_name);
- (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
+
+ for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) {
+ if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){
+ RDEBUG("sql_groupcmp finished: User is a member of group %s",
+ check->vp_strvalue);
+ /* Free the grouplist */
+ sql_grouplist_free(&group_list);
+ /* Remove the username we (maybe) added above */
+ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
sql_release_socket(inst, sqlsocket);
- return 1;
- }
- if (strcmp(row[0],check->strvalue) == 0){
- DEBUG("rlm_sql (%s): - sql_groupcmp finished: User belongs in group %s",
- inst->config->xlat_name,
- (char *)check->strvalue);
- (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst, sqlsocket);
return 0;
}
}
- (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
+ /* Free the grouplist */
+ sql_grouplist_free(&group_list);
+ /* Remove the username we (maybe) added above */
+ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
sql_release_socket(inst,sqlsocket);
- DEBUG("rlm_sql (%s): - sql_groupcmp finished: User does not belong in group %s",
- inst->config->xlat_name, (char *)check->strvalue);
+ RDEBUG("sql_groupcmp finished: User is NOT a member of group %s",
+ check->vp_strvalue);
return 1;
}
-static int rlm_sql_detach(void *instance)
+
+static int rlm_sql_process_groups(SQL_INST *inst, REQUEST *request, SQLSOCK *sqlsocket, int *dofallthrough)
{
- SQL_INST *inst = instance;
+ VALUE_PAIR *check_tmp = NULL;
+ VALUE_PAIR *reply_tmp = NULL;
+ SQL_GROUPLIST *group_list, *group_list_tmp;
+ VALUE_PAIR *sql_group = NULL;
+ char querystr[MAX_QUERY_LEN];
+ int found = 0;
+ int rows;
- if (inst->sqlpool) {
- sql_poolfree(inst);
+ /*
+ * Get the list of groups this user is a member of
+ */
+ if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
+ radlog_request(L_ERR, 0, request, "Error retrieving group list");
+ return -1;
}
- if (inst->config->xlat_name) {
- xlat_unregister(inst->config->xlat_name,sql_xlat);
- free(inst->config->xlat_name);
+ for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) {
+ /*
+ * Add the Sql-Group attribute to the request list so we know
+ * which group we're retrieving attributes for
+ */
+ sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ);
+ if (!sql_group) {
+ radlog_request(L_ERR, 0, request,
+ "Error creating Sql-Group attribute");
+ return -1;
+ }
+ pairadd(&request->packet->vps, sql_group);
+ if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func)) {
+ radlog_request(L_ERR, 0, request,
+ "Error generating query; rejecting user");
+ /* Remove the grouup we added above */
+ pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
+ return -1;
+ }
+ rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
+ if (rows < 0) {
+ radlog_request(L_ERR, 0, request, "Error retrieving check pairs for group %s",
+ group_list_tmp->groupname);
+ /* Remove the grouup we added above */
+ pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
+ pairfree(&check_tmp);
+ return -1;
+ } else if (rows > 0) {
+ /*
+ * Only do this if *some* check pairs were returned
+ */
+ if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
+ found = 1;
+ RDEBUG2("User found in group %s",
+ group_list_tmp->groupname);
+ /*
+ * Now get the reply pairs since the paircompare matched
+ */
+ if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
+ radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
+ /* Remove the grouup we added above */
+ pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
+ pairfree(&check_tmp);
+ return -1;
+ }
+ if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
+ radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
+ group_list_tmp->groupname);
+ /* Remove the grouup we added above */
+ pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
+ pairfree(&check_tmp);
+ pairfree(&reply_tmp);
+ return -1;
+ }
+ *dofallthrough = fallthrough(reply_tmp);
+ pairxlatmove(request, &request->reply->vps, &reply_tmp);
+ pairxlatmove(request, &request->config_items, &check_tmp);
+ }
+ } else {
+ /*
+ * rows == 0. This is like having the username on a line
+ * in the user's file with no check vp's. As such, we treat
+ * it as found and add the reply attributes, so that we
+ * match expected behavior
+ */
+ found = 1;
+ RDEBUG2("User found in group %s",
+ group_list_tmp->groupname);
+ /*
+ * Now get the reply pairs since the paircompare matched
+ */
+ if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
+ radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
+ /* Remove the grouup we added above */
+ pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
+ pairfree(&check_tmp);
+ return -1;
+ }
+ if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
+ radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
+ group_list_tmp->groupname);
+ /* Remove the grouup we added above */
+ pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
+ pairfree(&check_tmp);
+ pairfree(&reply_tmp);
+ return -1;
+ }
+ *dofallthrough = fallthrough(reply_tmp);
+ pairxlatmove(request, &request->reply->vps, &reply_tmp);
+ pairxlatmove(request, &request->config_items, &check_tmp);
+ }
+
+ /*
+ * Delete the Sql-Group we added above
+ * And clear out the pairlists
+ */
+ pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
+ pairfree(&check_tmp);
+ pairfree(&reply_tmp);
}
+ sql_grouplist_free(&group_list);
+ return found;
+}
+
+
+static int rlm_sql_detach(void *instance)
+{
+ SQL_INST *inst = instance;
+
paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
if (inst->config) {
int i;
+ if (inst->sqlpool) {
+ sql_poolfree(inst);
+ }
+
+ if (inst->config->xlat_name) {
+ xlat_unregister(inst->config->xlat_name,(RAD_XLAT_FUNC)sql_xlat);
+ free(inst->config->xlat_name);
+ }
+
/*
* Free up dynamically allocated string pointers.
*/
free(*p);
*p = NULL;
}
- allowed_chars = NULL;
+ /*
+ * Catch multiple instances of the module.
+ */
+ if (allowed_chars == inst->config->allowed_chars) {
+ allowed_chars = NULL;
+ }
free(inst->config);
inst->config = NULL;
}
static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
{
SQL_INST *inst;
- char *xlat_name;
+ const char *xlat_name;
inst = rad_malloc(sizeof(SQL_INST));
memset(inst, 0, sizeof(SQL_INST));
memset(inst->config, 0, sizeof(SQL_CONFIG));
/*
+ * Export these methods, too. This avoids RTDL_GLOBAL.
+ */
+ inst->sql_set_user = sql_set_user;
+ inst->sql_get_socket = sql_get_socket;
+ inst->sql_release_socket = sql_release_socket;
+ inst->sql_escape_func = sql_escape_func;
+ inst->sql_query = rlm_sql_query;
+ inst->sql_select_query = rlm_sql_select_query;
+ inst->sql_fetch_row = rlm_sql_fetch_row;
+
+ /*
* If the configuration parameters can't be parsed, then
* fail.
*/
}
xlat_name = cf_section_name2(conf);
- if (xlat_name == NULL)
+ if (xlat_name == NULL) {
xlat_name = cf_section_name1(conf);
+ } else {
+ char *group_name;
+ DICT_ATTR *dattr;
+ ATTR_FLAGS flags;
+
+ /*
+ * Allocate room for <instance>-SQL-Group
+ */
+ group_name = rad_malloc((strlen(xlat_name) + 1 + 11) * sizeof(char));
+ sprintf(group_name,"%s-SQL-Group",xlat_name);
+ DEBUG("rlm_sql Creating new attribute %s",group_name);
+
+ memset(&flags, 0, sizeof(flags));
+ dict_addattr(group_name, 0, PW_TYPE_STRING, -1, flags);
+ dattr = dict_attrbyname(group_name);
+ if (dattr == NULL){
+ radlog(L_ERR, "rlm_ldap: Failed to create attribute %s",group_name);
+ free(group_name);
+ free(inst); /* FIXME: detach */
+ return -1;
+ }
+
+ if (inst->config->groupmemb_query &&
+ inst->config->groupmemb_query[0]) {
+ DEBUG("rlm_sql: Registering sql_groupcmp for %s",group_name);
+ paircompare_register(dattr->attr, PW_USER_NAME, sql_groupcmp, inst);
+ }
+
+ free(group_name);
+ }
if (xlat_name){
inst->config->xlat_name = strdup(xlat_name);
- xlat_register(xlat_name, sql_xlat, inst);
+ xlat_register(xlat_name, (RAD_XLAT_FUNC)sql_xlat, inst);
}
if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
- radlog(L_ERR | L_CONS, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
+ radlog(L_ERR, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
inst->config->xlat_name, MAX_SQL_SOCKS);
rlm_sql_detach(inst);
return -1;
* Sanity check for crazy people.
*/
if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
- radlog(L_ERR, "rlm_sql (%s): \"%s\" is NOT an SQL driver!",
- inst->config->xlat_name, inst->config->sql_driver);
+ radlog(L_ERR, "\"%s\" is NOT an SQL driver!",
+ inst->config->sql_driver);
rlm_sql_detach(inst);
return -1;
}
inst->handle = lt_dlopenext(inst->config->sql_driver);
if (inst->handle == NULL) {
- radlog(L_ERR, "rlm_sql (%s): Could not link driver %s: %s",
- inst->config->xlat_name, inst->config->sql_driver,
+ radlog(L_ERR, "Could not link driver %s: %s",
+ inst->config->sql_driver,
lt_dlerror());
- radlog(L_ERR, "rlm_sql (%s): Make sure it (and all its dependent libraries!) are in the search path of your system's ld.",
- inst->config->xlat_name);
+ radlog(L_ERR, "Make sure it (and all its dependent libraries!) are in the search path of your system's ld.");
rlm_sql_detach(inst);
return -1;
}
inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
if (!inst->module) {
- radlog(L_ERR, "rlm_sql (%s): Could not link symbol %s: %s",
- inst->config->xlat_name, inst->config->sql_driver,
+ radlog(L_ERR, "Could not link symbol %s: %s",
+ inst->config->sql_driver,
lt_dlerror());
rlm_sql_detach(inst);
return -1;
rlm_sql_detach(inst);
return -1;
}
- paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
+
+ if (inst->config->groupmemb_query &&
+ inst->config->groupmemb_query[0]) {
+ paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
+ }
if (inst->config->do_clients){
if (generate_sql_clients(inst) == -1){
- radlog(L_ERR, "rlm_sql (%s): generate_sql_clients() returned error",inst->config->xlat_name);
+ radlog(L_ERR, "Failed to load clients from SQL.");
rlm_sql_detach(inst);
return -1;
}
return RLM_MODULE_OK;
}
-static int rlm_sql_destroy(void)
-{
- return 0;
-}
-
static int rlm_sql_authorize(void *instance, REQUEST * request)
{
VALUE_PAIR *reply_tmp = NULL;
VALUE_PAIR *user_profile = NULL;
int found = 0;
+ int dofallthrough = 1;
+ int rows;
SQLSOCK *sqlsocket;
SQL_INST *inst = instance;
char querystr[MAX_QUERY_LEN];
char sqlusername[MAX_STRING_LEN];
-
/*
- * They MUST have a user name to do SQL authorization.
+ * the profile username is used as the sqlusername during
+ * profile checking so that we don't overwrite the orignal
+ * sqlusername string
*/
- if ((request->username == NULL) ||
- (request->username->length == 0)) {
- radlog(L_ERR, "rlm_sql (%s): zero length username not permitted\n", inst->config->xlat_name);
- return RLM_MODULE_INVALID;
- }
+ char profileusername[MAX_STRING_LEN];
/*
- * Set, escape, and check the user attr here.
+ * Set, escape, and check the user attr here
*/
if (sql_set_user(inst, request, sqlusername, NULL) < 0)
return RLM_MODULE_FAIL;
- radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func);
+
+ /*
+ * reserve a socket
+ */
sqlsocket = sql_get_socket(inst);
if (sqlsocket == NULL) {
/* Remove the username we (maybe) added above */
- pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
+ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
return RLM_MODULE_FAIL;
}
+
/*
- * After this point, ALL 'return's MUST release the SQL socket!
+ * After this point, ALL 'return's MUST release the SQL socket!
*/
- found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_USERDATA);
/*
- * Find the entry for the user.
+ * Alright, start by getting the specific entry for the user
*/
- if (found > 0) {
- radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func);
- sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
- radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func);
- sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_USERDATA);
- radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func);
- sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
- } else if (found < 0) {
- radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
- inst->config->xlat_name);
+ if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func)) {
+ radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
sql_release_socket(inst, sqlsocket);
/* Remove the username we (maybe) added above */
- pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
+ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
+ return RLM_MODULE_FAIL;
+ }
+ rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
+ if (rows < 0) {
+ radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
+ sql_release_socket(inst, sqlsocket);
+ /* Remove the username we (maybe) added above */
+ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
pairfree(&check_tmp);
return RLM_MODULE_FAIL;
+ } else if (rows > 0) {
+ /*
+ * Only do this if *some* check pairs were returned
+ */
+ if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
+ found = 1;
+ RDEBUG2("User found in radcheck table");
- } else {
- radlog(L_DBG, "rlm_sql (%s): User %s not found in radcheck",
- inst->config->xlat_name, sqlusername);
+ if (inst->config->authorize_reply_query &&
+ *inst->config->authorize_reply_query) {
- /*
- * We didn't find the user in radcheck, so we try looking
- * for radgroupcheck entry
- */
- radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func);
- found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
- radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func);
- sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
- }
- if (!found)
- radlog(L_DBG, "rlm_sql (%s): User %s not found in radgroupcheck",
- inst->config->xlat_name, sqlusername);
- if (found || (!found && inst->config->query_on_not_found)){
- int def_found = 0;
+ /*
+ * Now get the reply pairs since the paircompare matched
+ */
+ if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func)) {
+ radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
+ sql_release_socket(inst, sqlsocket);
+ /* Remove the username we (maybe) added above */
+ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
+ pairfree(&check_tmp);
+ return RLM_MODULE_FAIL;
+ }
+ if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
+ radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
+ sql_release_socket(inst, sqlsocket);
+ /* Remove the username we (maybe) added above */
+ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
+ pairfree(&check_tmp);
+ pairfree(&reply_tmp);
+ return RLM_MODULE_FAIL;
+ }
+
+ if (!inst->config->read_groups)
+ dofallthrough = fallthrough(reply_tmp);
+ pairxlatmove(request, &request->reply->vps, &reply_tmp);
+ }
+ pairxlatmove(request, &request->config_items, &check_tmp);
+ }
+ }
+
+ /*
+ * Clear out the pairlists
+ */
+ pairfree(&check_tmp);
+ pairfree(&reply_tmp);
+
+ /*
+ * dofallthrough is set to 1 by default so that if the user information
+ * is not found, we will still process groups. If the user information,
+ * however, *is* found, Fall-Through must be set in order to process
+ * the groups as well
+ */
+ if (dofallthrough) {
+ rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
+ if (rows < 0) {
+ radlog_request(L_ERR, 0, request, "Error processing groups; rejecting user");
+ sql_release_socket(inst, sqlsocket);
+ /* Remove the username we (maybe) added above */
+ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
+ return RLM_MODULE_FAIL;
+ } else if (rows > 0) {
+ found = 1;
+ }
+ }
+ /*
+ * repeat the above process with the default profile or User-Profile
+ */
+ if (dofallthrough) {
+ int profile_found = 0;
/*
* Check for a default_profile or for a User-Profile.
*/
- user_profile = pairfind(request->config_items, PW_USER_PROFILE);
+ user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0);
if (inst->config->default_profile[0] != 0 || user_profile != NULL){
char *profile = inst->config->default_profile;
if (user_profile != NULL)
- profile = user_profile->strvalue;
+ profile = user_profile->vp_strvalue;
if (profile && strlen(profile)){
- radlog(L_DBG, "rlm_sql (%s): Checking profile %s",
- inst->config->xlat_name, profile);
- if (sql_set_user(inst, request, sqlusername, profile) < 0) {
+ RDEBUG("Checking profile %s", profile);
+ if (sql_set_user(inst, request, profileusername, profile) < 0) {
+ radlog_request(L_ERR, 0, request, "Error setting profile; rejecting user");
sql_release_socket(inst, sqlsocket);
- pairfree(&reply_tmp);
- pairfree(&check_tmp);
+ /* Remove the username we (maybe) added above */
+ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
return RLM_MODULE_FAIL;
+ } else {
+ profile_found = 1;
}
- radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query,
- request, sql_escape_func);
- def_found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
- if (def_found)
- found = 1;
- radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query,
- request, sql_escape_func);
- sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
+ }
+ }
+
+ if (profile_found) {
+ rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
+ if (rows < 0) {
+ radlog_request(L_ERR, 0, request, "Error processing profile groups; rejecting user");
+ sql_release_socket(inst, sqlsocket);
+ /* Remove the username we (maybe) added above */
+ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
+ return RLM_MODULE_FAIL;
+ } else if (rows > 0) {
+ found = 1;
}
}
}
- /*
- * We don't need the SQL socket anymore.
- */
+ /* Remove the username we (maybe) added above */
+ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
sql_release_socket(inst, sqlsocket);
if (!found) {
- radlog(L_DBG, "rlm_sql (%s): User not found",
- inst->config->xlat_name);
- /* Remove the username we (maybe) added above */
- pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
- pairfree(&reply_tmp);
- pairfree(&check_tmp);
- return RLM_MODULE_NOTFOUND;
- }
-
- /*
- * Uncomment these lines for debugging
- * Recompile, and run 'radiusd -X'
- */
-
- /*
- DEBUG2("rlm_sql: check items");
- vp_listdebug(check_tmp);
- DEBUG2("rlm_sql: reply items");
- vp_listdebug(reply_tmp);
- */
-
- if (paircmp(request, request->packet->vps, check_tmp, &reply_tmp) != 0) {
- radlog(L_INFO, "rlm_sql (%s): No matching entry in the database for request from user [%s]",
- inst->config->xlat_name, sqlusername);
- /* Remove the username we (maybe) added above */
- pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
- pairfree(&reply_tmp);
- pairfree(&check_tmp);
+ RDEBUG("User %s not found", sqlusername);
return RLM_MODULE_NOTFOUND;
+ } else {
+ return RLM_MODULE_OK;
}
-
- pairxlatmove(request, &request->reply->vps, &reply_tmp);
- pairxlatmove(request, &request->config_items, &check_tmp);
- pairfree(&reply_tmp);
- pairfree(&check_tmp);
-
- /* Remove the username we (maybe) added above */
- pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
-
- return RLM_MODULE_OK;
}
/*
/*
* Find the Acct Status Type
*/
- if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
- acctstatustype = pair->lvalue;
+ if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0)) != NULL) {
+ acctstatustype = pair->vp_integer;
} else {
radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
- radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s",
- inst->config->xlat_name, logstr);
+ radlog_request(L_ERR, 0, request, "%s", logstr);
return RLM_MODULE_INVALID;
}
*/
case PW_STATUS_ACCOUNTING_ON:
case PW_STATUS_ACCOUNTING_OFF:
- radlog(L_INFO, "rlm_sql (%s): received Acct On/Off packet", inst->config->xlat_name);
+ RDEBUG("Received Acct On/Off packet");
radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
query_log(request, inst, querystr);
return(RLM_MODULE_FAIL);
if (*querystr) { /* non-empty query */
if (rlm_sql_query(sqlsocket, inst, querystr)) {
- radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting for Acct On/Off packet - %s",
- inst->config->xlat_name,
- (char *)(inst->module->sql_error)(sqlsocket, inst->config));
+ radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting for Acct On/Off packet - %s",
+ (inst->module->sql_error)(sqlsocket, inst->config));
ret = RLM_MODULE_FAIL;
}
(inst->module->sql_finish_query)(sqlsocket, inst->config);
return(RLM_MODULE_FAIL);
if (*querystr) { /* non-empty query */
if (rlm_sql_query(sqlsocket, inst, querystr)) {
- radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting ALIVE record - %s",
- inst->config->xlat_name,
- (char *)(inst->module->sql_error)(sqlsocket, inst->config));
+ radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting ALIVE record - %s",
+ (inst->module->sql_error)(sqlsocket, inst->config));
ret = RLM_MODULE_FAIL;
}
else {
query_log(request, inst, querystr);
if (*querystr) { /* non-empty query */
if (rlm_sql_query(sqlsocket, inst, querystr)) {
- radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting ALIVE record - %s",
- inst->config->xlat_name,
- (char *)(inst->module->sql_error)(sqlsocket, inst->config));
+ radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting ALIVE record - %s",
+ (inst->module->sql_error)(sqlsocket, inst->config));
ret = RLM_MODULE_FAIL;
}
(inst->module->sql_finish_query)(sqlsocket, inst->config);
return(RLM_MODULE_FAIL);
if (*querystr) { /* non-empty query */
if (rlm_sql_query(sqlsocket, inst, querystr)) {
- radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting START record - %s",
- inst->config->xlat_name,
- (char *)(inst->module->sql_error)(sqlsocket, inst->config));
+ radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting START record - %s",
+ (inst->module->sql_error)(sqlsocket, inst->config));
/*
* We failed the insert above. It's probably because
if (*querystr) { /* non-empty query */
if (rlm_sql_query(sqlsocket, inst, querystr)) {
- radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting START record - %s",
- inst->config->xlat_name,
- (char *)(inst->module->sql_error)(sqlsocket, inst->config));
+ radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting START record - %s",
+ (inst->module->sql_error)(sqlsocket, inst->config));
ret = RLM_MODULE_FAIL;
}
(inst->module->sql_finish_query)(sqlsocket, inst->config);
return(RLM_MODULE_FAIL);
if (*querystr) { /* non-empty query */
if (rlm_sql_query(sqlsocket, inst, querystr)) {
- radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting STOP record - %s",
- inst->config->xlat_name,
- (char *)(inst->module->sql_error)(sqlsocket, inst->config));
+ radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting STOP record - %s",
+ (inst->module->sql_error)(sqlsocket, inst->config));
ret = RLM_MODULE_FAIL;
}
else {
* 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 ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME, 0)) != NULL)
+ acctsessiontime = pair->vp_integer;
if (acctsessiontime <= 0) {
radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
- radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s", inst->config->xlat_name, logstr);
+ radlog_request(L_DBG, 0, request, "%s", logstr);
sql_release_socket(inst, sqlsocket);
- ret = RLM_MODULE_NOOP;
+ return RLM_MODULE_NOOP;
}
#endif
if (*querystr) { /* non-empty query */
if (rlm_sql_query(sqlsocket, inst, querystr)) {
- radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting STOP record - %s",
- inst->config->xlat_name,
- (char *)(inst->module->sql_error)(sqlsocket, inst->config));
+ radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting STOP record - %s",
+
+ (inst->module->sql_error)(sqlsocket, inst->config));
ret = RLM_MODULE_FAIL;
}
(inst->module->sql_finish_query)(sqlsocket, inst->config);
* Anything else is ignored.
*/
default:
- radlog(L_INFO, "rlm_sql (%s): Unsupported Acct-Status-Type = %d", inst->config->xlat_name, acctstatustype);
+ RDEBUG("Unsupported Acct-Status-Type = %d",
+ acctstatustype);
return RLM_MODULE_NOOP;
break;
int nas_port = 0;
/* If simul_count_query is not defined, we don't do any checking */
- if (inst->config->simul_count_query[0] == 0) {
+ if (!inst->config->simul_count_query ||
+ (inst->config->simul_count_query[0] == 0)) {
return RLM_MODULE_NOOP;
}
if((request->username == NULL) || (request->username->length == 0)) {
- radlog(L_ERR, "rlm_sql (%s): Zero Length username not permitted\n", inst->config->xlat_name);
+ radlog_request(L_ERR, 0, request, "Zero Length username not permitted\n");
return RLM_MODULE_INVALID;
}
return RLM_MODULE_OK;
}
- /* Looks like too many sessions, so lets start verifying them */
-
- if (inst->config->simul_verify_query[0] == 0) {
- /* No verify query defined, so skip verify step and rely on count query only */
+ /*
+ * Looks like too many sessions, so let's start verifying
+ * them, unless told to rely on count query only.
+ */
+ if (!inst->config->simul_verify_query ||
+ (inst->config->simul_verify_query[0] == '\0')) {
sql_release_socket(inst, sqlsocket);
return RLM_MODULE_OK;
}
radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
- radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Database query error", inst->config->xlat_name);
+ radlog_request(L_ERR, 0, request, "Database query error");
sql_release_socket(inst, sqlsocket);
return RLM_MODULE_FAIL;
}
*/
request->simul_count = 0;
- if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
- ipno = vp->lvalue;
- if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
- call_num = vp->strvalue;
+ if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0)) != NULL)
+ ipno = vp->vp_ipaddr;
+ if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0)) != NULL)
+ call_num = vp->vp_strvalue;
while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
if (!row[2]){
(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
sql_release_socket(inst, sqlsocket);
- DEBUG("rlm_sql (%s): Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
+ RDEBUG("Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
return RLM_MODULE_FAIL;
}
if (!row[1]){
(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
sql_release_socket(inst, sqlsocket);
- DEBUG("rlm_sql (%s): Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
+ RDEBUG("Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
return RLM_MODULE_FAIL;
}
if (row[3])
check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
- /*
- * Failed to check the terminal server for
- * duplicate logins: Return an error.
- */
- if (check < 0) {
- (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
- sql_release_socket(inst, sqlsocket);
- DEBUG("rlm_sql (%s) rad_check_ts() failed.",
- inst->config->xlat_name);
- return RLM_MODULE_FAIL;
+ if (check == 0) {
+ /*
+ * Stale record - zap it.
+ */
+ if (inst->config->deletestalesessions == TRUE) {
+ uint32_t framed_addr = 0;
+ char proto = 0;
+ int sess_time = 0;
+
+ if (row[5])
+ framed_addr = inet_addr(row[5]);
+ if (row[7]){
+ if (strcmp(row[7], "PPP") == 0)
+ proto = 'P';
+ else if (strcmp(row[7], "SLIP") == 0)
+ proto = 'S';
+ }
+ if (row[8])
+ sess_time = atoi(row[8]);
+ session_zap(request, nas_addr, nas_port,
+ row[2], row[1], framed_addr,
+ proto, sess_time);
+ }
}
-
- if(check == 1) {
+ else if (check == 1) {
+ /*
+ * User is still logged in.
+ */
++request->simul_count;
/*
request->simul_mpp = 2;
}
else {
- /*
- * Stale record - zap it.
- */
- uint32_t framed_addr = 0;
- char proto = 'P';
-
- if (row[5])
- framed_addr = inet_addr(row[5]);
- if (row[7])
- if (strcmp(row[7],"SLIP") == 0)
- proto = 'S';
-
- session_zap(request,
- nas_addr,nas_port,row[2],row[1],
- framed_addr, proto);
+ /*
+ * Failed to check the terminal server for
+ * duplicate logins: return an error.
+ */
+ (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
+ sql_release_socket(inst, sqlsocket);
+ radlog_request(L_ERR, 0, request, "Failed to check the terminal server for user '%s'.", row[2]);
+ return RLM_MODULE_FAIL;
}
}
(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
sql_release_socket(inst, sqlsocket);
- /* The Auth module apparently looks at request->simul_count, not the return value
- of this module when deciding to deny a call for too many sessions */
+ /*
+ * The Auth module apparently looks at request->simul_count,
+ * not the return value of this module when deciding to deny
+ * a call for too many sessions.
+ */
return RLM_MODULE_OK;
-
}
/*
char querystr[MAX_QUERY_LEN];
char sqlusername[MAX_STRING_LEN];
- DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name);
-
if(sql_set_user(inst, request, sqlusername, NULL) < 0)
return RLM_MODULE_FAIL;
/* If postauth_query is not defined, we stop here */
- if (inst->config->postauth_query[0] == '\0')
+ if (!inst->config->postauth_query ||
+ (inst->config->postauth_query[0] == '\0'))
return RLM_MODULE_NOOP;
/* Expand variables in the query */
if (rlm_sql_query(sqlsocket, inst, querystr)) {
radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
inst->config->xlat_name,
- (char *)(inst->module->sql_error)(sqlsocket, inst->config));
+ (inst->module->sql_error)(sqlsocket, inst->config));
sql_release_socket(inst, sqlsocket);
return RLM_MODULE_FAIL;
}
/* globally exported name */
module_t rlm_sql = {
+ RLM_MODULE_INIT,
"SQL",
RLM_TYPE_THREAD_SAFE, /* type: reserved */
- rlm_sql_init, /* initialization */
rlm_sql_instantiate, /* instantiation */
+ rlm_sql_detach, /* detach */
{
NULL, /* authentication */
rlm_sql_authorize, /* authorization */
NULL, /* post-proxy */
rlm_sql_postauth /* post-auth */
},
- rlm_sql_detach, /* detach */
- rlm_sql_destroy, /* destroy */
};