Allow sqlite filename to be specified in the configuration file.
[freeradius.git] / src / modules / rlm_sql / rlm_sql.c
index fabe4cb..15da1c6 100644 (file)
  *
  *   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,
@@ -63,24 +49,10 @@ static CONF_PARSER module_config[] = {
         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,
@@ -88,19 +60,23 @@ static CONF_PARSER module_config[] = {
        {"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,
@@ -120,62 +96,67 @@ static CONF_PARSER module_config[] = {
        {"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
@@ -195,10 +176,63 @@ static int sql_xlat(void *instance, REQUEST *request,
        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;
        }
@@ -206,8 +240,7 @@ static int sql_xlat(void *instance, REQUEST *request,
        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;
@@ -215,33 +248,29 @@ static int sql_xlat(void *instance, REQUEST *request,
 
        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);
@@ -254,25 +283,25 @@ static int generate_sql_clients(SQL_INST *inst)
        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;
        }
@@ -282,14 +311,16 @@ static int generate_sql_clients(SQL_INST *inst)
                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;
@@ -298,101 +329,90 @@ static int generate_sql_clients(SQL_INST *inst)
                        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);
@@ -404,9 +424,9 @@ static int generate_sql_clients(SQL_INST *inst)
 /*
  *     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]) {
                /*
@@ -458,7 +478,7 @@ static int sql_escape_func(char *out, int outlen, const char *in)
  *     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];
@@ -467,32 +487,101 @@ static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, con
        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
@@ -500,104 +589,224 @@ static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, con
  * 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.
                 */
@@ -620,7 +829,12 @@ static int rlm_sql_detach(void *instance)
                        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;
        }
@@ -640,7 +854,7 @@ static int rlm_sql_detach(void *instance)
 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));
@@ -649,6 +863,17 @@ static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
        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.
         */
@@ -658,15 +883,45 @@ static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
        }
 
        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;
@@ -676,27 +931,26 @@ static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
         *      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;
@@ -714,11 +968,15 @@ static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
                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;
                }
@@ -730,11 +988,6 @@ static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
        return RLM_MODULE_OK;
 }
 
-static int rlm_sql_destroy(void)
-{
-       return 0;
-}
-
 
 static int rlm_sql_authorize(void *instance, REQUEST * request)
 {
@@ -742,153 +995,176 @@ 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;
 }
 
 /*
@@ -915,12 +1191,11 @@ static int rlm_sql_accounting(void *instance, REQUEST * request) {
        /*
         * 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;
        }
 
@@ -931,7 +1206,7 @@ static int rlm_sql_accounting(void *instance, REQUEST * request) {
                         */
                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);
 
@@ -940,9 +1215,8 @@ static int rlm_sql_accounting(void *instance, REQUEST * request) {
                                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);
@@ -968,9 +1242,8 @@ static int rlm_sql_accounting(void *instance, REQUEST * request) {
                                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 {
@@ -987,9 +1260,8 @@ static int rlm_sql_accounting(void *instance, REQUEST * request) {
                                                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);
@@ -1018,9 +1290,8 @@ static int rlm_sql_accounting(void *instance, REQUEST * request) {
                                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
@@ -1032,9 +1303,8 @@ static int rlm_sql_accounting(void *instance, REQUEST * request) {
 
                                        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);
@@ -1062,9 +1332,8 @@ static int rlm_sql_accounting(void *instance, REQUEST * request) {
                                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 {
@@ -1083,14 +1352,14 @@ static int rlm_sql_accounting(void *instance, REQUEST * request) {
                                                 * 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
 
@@ -1099,9 +1368,9 @@ static int rlm_sql_accounting(void *instance, REQUEST * request) {
 
                                                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);
@@ -1116,7 +1385,8 @@ static int rlm_sql_accounting(void *instance, REQUEST * request) {
                         *      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;
 
@@ -1152,12 +1422,13 @@ static int rlm_sql_checksimul(void *instance, REQUEST * request) {
        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;
        }
 
@@ -1201,17 +1472,19 @@ static int rlm_sql_checksimul(void *instance, REQUEST * request) {
                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;
        }
@@ -1221,10 +1494,10 @@ static int rlm_sql_checksimul(void *instance, REQUEST * request) {
          */
        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) {
@@ -1234,13 +1507,13 @@ static int rlm_sql_checksimul(void *instance, REQUEST * request) {
                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])
@@ -1250,19 +1523,34 @@ static int rlm_sql_checksimul(void *instance, REQUEST * request) {
 
                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;
 
                         /*
@@ -1275,31 +1563,26 @@ static int rlm_sql_checksimul(void *instance, REQUEST * request) {
                                 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;
-
 }
 
 /*
@@ -1311,13 +1594,12 @@ static int rlm_sql_postauth(void *instance, REQUEST *request) {
        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 */
@@ -1337,7 +1619,7 @@ static int rlm_sql_postauth(void *instance, REQUEST *request) {
        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;
        }
@@ -1349,10 +1631,11 @@ static int rlm_sql_postauth(void *instance, REQUEST *request) {
 
 /* 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 */
@@ -1363,6 +1646,4 @@ module_t rlm_sql = {
                NULL,                   /* post-proxy */
                rlm_sql_postauth        /* post-auth */
        },
-       rlm_sql_detach,         /* detach */
-       rlm_sql_destroy,        /* destroy */
 };