Rename SQL data types so they don't conflict with drivers
[freeradius.git] / src / modules / rlm_sql / rlm_sql.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License, version 2 if the
4  *   License as published by the Free Software Foundation.
5  *
6  *   This program is distributed in the hope that it will be useful,
7  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *   GNU General Public License for more details.
10  *
11  *   You should have received a copy of the GNU General Public License
12  *   along with this program; if not, write to the Free Software
13  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14  */
15  
16 /**
17  * $Id$
18  * @file rlm_sql.c
19  * @brief Implements SQL 'users' file, and SQL accounting.
20  *
21  * @copyright 2012  Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22  * @copyright 2000,2006  The FreeRADIUS server project
23  * @copyright 2000  Mike Machado <mike@innercite.com>
24  * @copyright 2000  Alan DeKok <aland@ox.org>
25  */
26 #include <freeradius-devel/ident.h>
27 RCSID("$Id$")
28
29 #include <ctype.h>
30
31 #include <freeradius-devel/radiusd.h>
32 #include <freeradius-devel/modules.h>
33 #include <freeradius-devel/token.h>
34 #include <freeradius-devel/rad_assert.h>
35
36 #include <sys/stat.h>
37
38 #include "rlm_sql.h"
39
40 static const CONF_PARSER acct_section_config[] = {
41         {"reference", PW_TYPE_STRING_PTR,
42           offsetof(sql_acct_section_t, reference), NULL, ".query"},
43           
44         {"logfile", PW_TYPE_STRING_PTR,
45          offsetof(sql_acct_section_t, logfile), NULL, NULL},
46         {NULL, -1, 0, NULL, NULL}
47 };
48
49 static const CONF_PARSER module_config[] = {
50         {"driver", PW_TYPE_STRING_PTR,
51          offsetof(rlm_sql_config_t,sql_driver), NULL, "mysql"},
52         {"server", PW_TYPE_STRING_PTR,
53          offsetof(rlm_sql_config_t,sql_server), NULL, "localhost"},
54         {"port", PW_TYPE_STRING_PTR,
55          offsetof(rlm_sql_config_t,sql_port), NULL, ""},
56         {"login", PW_TYPE_STRING_PTR,
57          offsetof(rlm_sql_config_t,sql_login), NULL, ""},
58         {"password", PW_TYPE_STRING_PTR,
59          offsetof(rlm_sql_config_t,sql_password), NULL, ""},
60         {"radius_db", PW_TYPE_STRING_PTR,
61          offsetof(rlm_sql_config_t,sql_db), NULL, "radius"},
62         {"filename", PW_TYPE_FILENAME, /* for sqlite */
63          offsetof(rlm_sql_config_t,sql_file), NULL, NULL},
64         {"read_groups", PW_TYPE_BOOLEAN,
65          offsetof(rlm_sql_config_t,read_groups), NULL, "yes"},
66         {"readclients", PW_TYPE_BOOLEAN,
67          offsetof(rlm_sql_config_t,do_clients), NULL, "no"},
68         {"deletestalesessions", PW_TYPE_BOOLEAN,
69          offsetof(rlm_sql_config_t,deletestalesessions), NULL, "yes"},
70         {"sql_user_name", PW_TYPE_STRING_PTR,
71          offsetof(rlm_sql_config_t,query_user), NULL, ""},
72         {"logfile", PW_TYPE_STRING_PTR,
73          offsetof(rlm_sql_config_t,logfile), NULL, NULL},
74         {"default_user_profile", PW_TYPE_STRING_PTR,
75          offsetof(rlm_sql_config_t,default_profile), NULL, ""},
76         {"nas_query", PW_TYPE_STRING_PTR,
77          offsetof(rlm_sql_config_t,nas_query), NULL,
78          "SELECT id,nasname,shortname,type,secret FROM nas"},
79         {"authorize_check_query", PW_TYPE_STRING_PTR,
80          offsetof(rlm_sql_config_t,authorize_check_query), NULL, ""},
81         {"authorize_reply_query", PW_TYPE_STRING_PTR,
82          offsetof(rlm_sql_config_t,authorize_reply_query), NULL, NULL},
83         {"authorize_group_check_query", PW_TYPE_STRING_PTR,
84          offsetof(rlm_sql_config_t,authorize_group_check_query), NULL, ""},
85         {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
86          offsetof(rlm_sql_config_t,authorize_group_reply_query), NULL, ""},
87         {"group_membership_query", PW_TYPE_STRING_PTR,
88          offsetof(rlm_sql_config_t,groupmemb_query), NULL, NULL},
89 #ifdef WITH_SESSION_MGMT
90         {"simul_count_query", PW_TYPE_STRING_PTR,
91          offsetof(rlm_sql_config_t,simul_count_query), NULL, ""},
92         {"simul_verify_query", PW_TYPE_STRING_PTR,
93          offsetof(rlm_sql_config_t,simul_verify_query), NULL, ""},
94 #endif
95         {"safe-characters", PW_TYPE_STRING_PTR,
96          offsetof(rlm_sql_config_t,allowed_chars), NULL,
97         "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
98
99         /*
100          *      This only works for a few drivers.
101          */
102         {"query_timeout", PW_TYPE_INTEGER,
103          offsetof(rlm_sql_config_t,query_timeout), NULL, NULL},
104          
105         {NULL, -1, 0, NULL, NULL}
106 };
107
108 /*
109  *      Fall-Through checking function from rlm_files.c
110  */
111 static int fallthrough(VALUE_PAIR *vp)
112 {
113         VALUE_PAIR *tmp;
114         tmp = pairfind(vp, PW_FALL_THROUGH, 0, TAG_ANY);
115
116         return tmp ? tmp->vp_integer : 0;
117 }
118
119 /*
120  *      Yucky prototype.
121  */
122 static int generate_sql_clients(rlm_sql_t *inst);
123 static size_t sql_escape_func(REQUEST *, char *out, size_t outlen, const char *in, void *arg);
124
125 /*
126  *                      SQL xlat function
127  *
128  *  For selects the first value of the first column will be returned,
129  *  for inserts, updates and deletes the number of rows afftected will be
130  *  returned instead.
131  */
132 static size_t sql_xlat(void *instance, REQUEST *request,
133                     const char *fmt, char *out, size_t freespace)
134 {
135         rlm_sql_handle_t *handle;
136         rlm_sql_row_t row;
137         rlm_sql_t *inst = instance;
138         char querystr[MAX_QUERY_LEN];
139         size_t ret = 0;
140
141         RDEBUG("sql_xlat");
142
143         /*
144          * Add SQL-User-Name attribute just in case it is needed
145          *  We could search the string fmt for SQL-User-Name to see if this is
146          *  needed or not
147          */
148         sql_set_user(inst, request, NULL);
149         /*
150          * Do an xlat on the provided string (nice recursive operation).
151          */
152         if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func, inst)) {
153                 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
154                        inst->config->xlat_name);
155                 return 0;
156         }
157
158         handle = sql_get_socket(inst);
159         if (handle == NULL)
160                 return 0;
161
162         rlm_sql_query_log(inst, request, NULL, querystr);
163
164         /*
165          *      If the query starts with any of the following prefixes,
166          *      then return the number of rows affected
167          */
168         if ((strncasecmp(querystr, "insert", 6) == 0) ||
169             (strncasecmp(querystr, "update", 6) == 0) ||
170             (strncasecmp(querystr, "delete", 6) == 0)) {
171                 int numaffected;
172                 char buffer[21]; /* 64bit max is 20 decimal chars + null byte */
173
174                 if (rlm_sql_query(&handle,inst,querystr)) {
175                         sql_release_socket(inst,handle);
176                         
177                         return 0;
178                 }
179                
180                 numaffected = (inst->module->sql_affected_rows)(handle,
181                                                                 inst->config);
182                 if (numaffected < 1) {
183                         RDEBUG("rlm_sql (%s): SQL query affected no rows",
184                                 inst->config->xlat_name);
185                 }
186
187                 /*
188                  *      Don't chop the returned number if freespace is
189                  *      too small.  This hack is necessary because
190                  *      some implementations of snprintf return the
191                  *      size of the written data, and others return
192                  *      the size of the data they *would* have written
193                  *      if the output buffer was large enough.
194                  */
195                 snprintf(buffer, sizeof(buffer), "%d", numaffected);
196                 ret = strlen(buffer);
197                 if (ret >= freespace){
198                         RDEBUG("rlm_sql (%s): Can't write result, insufficient string space",
199                                inst->config->xlat_name);
200                         (inst->module->sql_finish_query)(handle,
201                                                          inst->config);
202                         sql_release_socket(inst,handle);
203                         return 0;
204                 }
205                 
206                 memcpy(out, buffer, ret + 1); /* we did bounds checking above */
207
208                 (inst->module->sql_finish_query)(handle, inst->config);
209                 sql_release_socket(inst,handle);
210                 return ret;
211         } /* else it's a SELECT statement */
212
213         if (rlm_sql_select_query(&handle,inst,querystr)){
214                 sql_release_socket(inst,handle);
215                 return 0;
216         }
217
218         ret = rlm_sql_fetch_row(&handle, inst);
219         if (ret) {
220                 RDEBUG("SQL query did not succeed");
221                 (inst->module->sql_finish_select_query)(handle, inst->config);
222                 sql_release_socket(inst,handle);
223                 return 0;
224         }
225
226         row = handle->row;
227         if (row == NULL) {
228                 RDEBUG("SQL query did not return any results");
229                 (inst->module->sql_finish_select_query)(handle, inst->config);
230                 sql_release_socket(inst,handle);
231                 return 0;
232         }
233
234         if (row[0] == NULL){
235                 RDEBUG("Null value in first column");
236                 (inst->module->sql_finish_select_query)(handle, inst->config);
237                 sql_release_socket(inst,handle);
238                 return 0;
239         }
240         ret = strlen(row[0]);
241         if (ret >= freespace){
242                 RDEBUG("Insufficient string space");
243                 (inst->module->sql_finish_select_query)(handle, inst->config);
244                 sql_release_socket(inst,handle);
245                 return 0;
246         }
247
248         strlcpy(out,row[0],freespace);
249
250         RDEBUG("sql_xlat finished");
251
252         (inst->module->sql_finish_select_query)(handle, inst->config);
253         sql_release_socket(inst,handle);
254         return ret;
255 }
256
257 static int generate_sql_clients(rlm_sql_t *inst)
258 {
259         rlm_sql_handle_t *handle;
260         rlm_sql_row_t row;
261         char querystr[MAX_QUERY_LEN];
262         RADCLIENT *c;
263         char *prefix_ptr = NULL;
264         unsigned int i = 0;
265         int numf = 0;
266
267         DEBUG("rlm_sql (%s): Processing generate_sql_clients",
268               inst->config->xlat_name);
269
270         /* NAS query isn't xlat'ed */
271         strlcpy(querystr, inst->config->nas_query, sizeof(querystr));
272         DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s",
273               inst->config->xlat_name, querystr);
274
275         handle = sql_get_socket(inst);
276         if (handle == NULL)
277                 return -1;
278         if (rlm_sql_select_query(&handle,inst,querystr)){
279                 return -1;
280         }
281
282         while(rlm_sql_fetch_row(&handle, inst) == 0) {
283                 i++;
284                 row = handle->row;
285                 if (row == NULL)
286                         break;
287                 /*
288                  *  The return data for each row MUST be in the following order:
289                  *
290                  *  0. Row ID (currently unused)
291                  *  1. Name (or IP address)
292                  *  2. Shortname
293                  *  3. Type
294                  *  4. Secret
295                  *  5. Virtual Server (optional)
296                  */
297                 if (!row[0]){
298                         radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
299                         continue;
300                 }
301                 if (!row[1]){
302                         radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
303                         continue;
304                 }
305                 if (!row[2]){
306                         radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
307                         continue;
308                 }
309                 if (!row[4]){
310                         radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
311                         continue;
312                 }
313
314                 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
315                         row[1],row[2],row[4]);
316
317                 c = talloc_zero(inst, RADCLIENT);
318
319 #ifdef WITH_DYNAMIC_CLIENTS
320                 c->dynamic = 1;
321 #endif
322
323                 /*
324                  *      Look for prefixes
325                  */
326                 c->prefix = -1;
327                 prefix_ptr = strchr(row[1], '/');
328                 if (prefix_ptr) {
329                         c->prefix = atoi(prefix_ptr + 1);
330                         if ((c->prefix < 0) || (c->prefix > 128)) {
331                                 radlog(L_ERR, "rlm_sql (%s): Invalid Prefix value '%s' for IP.",
332                                        inst->config->xlat_name, prefix_ptr + 1);
333                                 talloc_free(c);
334                                 continue;
335                         }
336                         /* Replace '/' with '\0' */
337                         *prefix_ptr = '\0';
338                 }
339
340                 /*
341                  *      Always get the numeric representation of IP
342                  */
343                 if (ip_hton(row[1], AF_UNSPEC, &c->ipaddr) < 0) {
344                         radlog(L_ERR, "rlm_sql (%s): Failed to look up hostname %s: %s",
345                                inst->config->xlat_name,
346                                row[1], fr_strerror());
347                         talloc_free(c);
348                         continue;
349                 } else {
350                         char buffer[256];
351                         ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
352                         c->longname = talloc_strdup(c, buffer);
353                 }
354
355                 if (c->prefix < 0) switch (c->ipaddr.af) {
356                 case AF_INET:
357                         c->prefix = 32;
358                         break;
359                 case AF_INET6:
360                         c->prefix = 128;
361                         break;
362                 default:
363                         break;
364                 }
365
366                 /*
367                  *      Other values (secret, shortname, nastype, virtual_server)
368                  */
369                 c->secret = talloc_strdup(c, row[4]);
370                 c->shortname = talloc_strdup(c, row[2]);
371                 if(row[3] != NULL)
372                         c->nastype = strdup(row[3]);
373
374                 numf = (inst->module->sql_num_fields)(handle, inst->config);
375                 if ((numf > 5) && (row[5] != NULL) && *row[5]) c->server = strdup(row[5]);
376
377                 DEBUG("rlm_sql (%s): Adding client %s (%s, server=%s) to clients list",
378                       inst->config->xlat_name,
379                       c->longname,c->shortname, c->server ? c->server : "<none>");
380                 if (!client_add(NULL, c)) {
381                         sql_release_socket(inst, handle);
382                         DEBUG("rlm_sql (%s): Failed to add client %s (%s) to clients list.  Maybe there's a duplicate?",
383                               inst->config->xlat_name,
384                               c->longname,c->shortname);
385                         client_free(c);
386                         return -1;
387                 }
388         }
389         (inst->module->sql_finish_select_query)(handle, inst->config);
390         sql_release_socket(inst, handle);
391
392         return 0;
393 }
394
395
396 /*
397  *      Translate the SQL queries.
398  */
399 static size_t sql_escape_func(UNUSED REQUEST *request, char *out, size_t outlen,
400                               const char *in, void *arg)
401 {
402         rlm_sql_t *inst = arg;
403         size_t len = 0;
404
405         while (in[0]) {
406                 /*
407                  *      Non-printable characters get replaced with their
408                  *      mime-encoded equivalents.
409                  */
410                 if ((in[0] < 32) ||
411                     strchr(inst->config->allowed_chars, *in) == NULL) {
412                         /*
413                          *      Only 3 or less bytes available.
414                          */
415                         if (outlen <= 3) {
416                                 break;
417                         }
418
419                         snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
420                         in++;
421                         out += 3;
422                         outlen -= 3;
423                         len += 3;
424                         continue;
425                 }
426
427                 /*
428                  *      Only one byte left.
429                  */
430                 if (outlen <= 1) {
431                         break;
432                 }
433
434                 /*
435                  *      Allowed character.
436                  */
437                 *out = *in;
438                 out++;
439                 in++;
440                 outlen--;
441                 len++;
442         }
443         *out = '\0';
444         return len;
445 }
446
447 /*
448  *      Set the SQL user name.
449  *
450  *      We don't call the escape function here. The resulting string
451  *      will be escaped later in the queries xlat so we don't need to
452  *      escape it twice. (it will make things wrong if we have an
453  *      escape candidate character in the username)
454  */
455 int sql_set_user(rlm_sql_t *inst, REQUEST *request, const char *username)
456 {
457         char buffer[254];
458         VALUE_PAIR *vp = NULL;
459         const char *sqluser;
460         size_t len;
461
462         if (username != NULL) {
463                 sqluser = username;
464         } else if (*inst->config->query_user) {
465                 sqluser = inst->config->query_user;
466         } else {
467                 return 0;
468         }
469         
470         len = radius_xlat(buffer, sizeof(buffer), sqluser, request, NULL, NULL);
471         if (!len) {
472                 return -1;
473         }
474         
475         vp = pairalloc(NULL, inst->sql_user);
476         vp->op = T_OP_SET;
477         
478         strlcpy(vp->vp_strvalue, buffer, sizeof(vp->vp_strvalue));
479         vp->length = strlen(vp->vp_strvalue);
480         pairadd(&request->packet->vps, vp);
481
482         RDEBUG2("SQL-User-Name updated");
483
484         return 0;
485 }
486
487
488 static void sql_grouplist_free (rlm_sql_grouplist_t **group_list)
489 {
490         rlm_sql_grouplist_t *last;
491
492         while(*group_list) {
493                 last = *group_list;
494                 *group_list = (*group_list)->next;
495                 free(last);
496         }
497 }
498
499
500 static int sql_get_grouplist (rlm_sql_t *inst, rlm_sql_handle_t *handle, REQUEST *request, rlm_sql_grouplist_t **group_list)
501 {
502         char    querystr[MAX_QUERY_LEN];
503         int     num_groups = 0;
504         rlm_sql_row_t row;
505         rlm_sql_grouplist_t   *group_list_tmp;
506
507         /* NOTE: sql_set_user should have been run before calling this function */
508
509         group_list_tmp = *group_list = NULL;
510
511         if (!inst->config->groupmemb_query ||
512             (inst->config->groupmemb_query[0] == 0))
513                 return 0;
514
515         if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, request, sql_escape_func, inst)) {
516                 radlog_request(L_ERR, 0, request, "xlat \"%s\" failed.",
517                                inst->config->groupmemb_query);
518                 return -1;
519         }
520
521         if (rlm_sql_select_query(&handle, inst, querystr) < 0) {
522                 return -1;
523         }
524         while (rlm_sql_fetch_row(&handle, inst) == 0) {
525                 row = handle->row;
526                 if (row == NULL)
527                         break;
528                 if (row[0] == NULL){
529                         RDEBUG("row[0] returned NULL");
530                         (inst->module->sql_finish_select_query)(handle, inst->config);
531                         sql_grouplist_free(group_list);
532                         return -1;
533                 }
534                 if (*group_list == NULL) {
535                         *group_list = rad_malloc(sizeof(rlm_sql_grouplist_t));
536                         group_list_tmp = *group_list;
537                 } else {
538                         rad_assert(group_list_tmp != NULL);
539                         group_list_tmp->next = rad_malloc(sizeof(rlm_sql_grouplist_t));
540                         group_list_tmp = group_list_tmp->next;
541                 }
542                 group_list_tmp->next = NULL;
543                 strlcpy(group_list_tmp->groupname, row[0], MAX_STRING_LEN);
544         }
545
546         (inst->module->sql_finish_select_query)(handle, inst->config);
547
548         return num_groups;
549 }
550
551
552 /*
553  * sql groupcmp function. That way we can do group comparisons (in the users file for example)
554  * with the group memberships reciding in sql
555  * The group membership query should only return one element which is the username. The returned
556  * username will then be checked with the passed check string.
557  */
558
559 static int sql_groupcmp(void *instance, REQUEST *request, VALUE_PAIR *request_vp, VALUE_PAIR *check,
560                         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
561 {
562         rlm_sql_handle_t *handle;
563         rlm_sql_t *inst = instance;
564         rlm_sql_grouplist_t *group_list, *group_list_tmp;
565
566         check_pairs = check_pairs;
567         reply_pairs = reply_pairs;
568         request_vp = request_vp;
569
570         RDEBUG("sql_groupcmp");
571         if (!check || !check->length){
572                 RDEBUG("sql_groupcmp: Illegal group name");
573                 return 1;
574         }
575         if (!request){
576                 RDEBUG("sql_groupcmp: NULL request");
577                 return 1;
578         }
579         /*
580          *      Set, escape, and check the user attr here
581          */
582         if (sql_set_user(inst, request, NULL) < 0)
583                 return 1;
584
585         /*
586          *      Get a socket for this lookup
587          */
588         handle = sql_get_socket(inst);
589         if (handle == NULL) {
590                 return 1;
591         }
592
593         /*
594          *      Get the list of groups this user is a member of
595          */
596         if (sql_get_grouplist(inst, handle, request, &group_list) < 0) {
597                 radlog_request(L_ERR, 0, request,
598                                "Error getting group membership");
599                 sql_release_socket(inst, handle);
600                 return 1;
601         }
602
603         for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) {
604                 if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){
605                         RDEBUG("sql_groupcmp finished: User is a member of group %s",
606                                check->vp_strvalue);
607                         /* Free the grouplist */
608                         sql_grouplist_free(&group_list);
609                         sql_release_socket(inst, handle);
610                         return 0;
611                 }
612         }
613
614         /* Free the grouplist */
615         sql_grouplist_free(&group_list);
616         sql_release_socket(inst,handle);
617
618         RDEBUG("sql_groupcmp finished: User is NOT a member of group %s",
619                check->vp_strvalue);
620
621         return 1;
622 }
623
624
625
626 static int rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t *handle, int *dofallthrough)
627 {
628         VALUE_PAIR *check_tmp = NULL;
629         VALUE_PAIR *reply_tmp = NULL;
630         rlm_sql_grouplist_t *group_list, *group_list_tmp;
631         VALUE_PAIR *sql_group = NULL;
632         char    querystr[MAX_QUERY_LEN];
633         int found = 0;
634         int rows;
635
636         /*
637          *      Get the list of groups this user is a member of
638          */
639         if (sql_get_grouplist(inst, handle, request, &group_list) < 0) {
640                 radlog_request(L_ERR, 0, request, "Error retrieving group list");
641                 return -1;
642         }
643
644         for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) {
645                 /*
646                  *      Add the Sql-Group attribute to the request list so we know
647                  *      which group we're retrieving attributes for
648                  */
649                 sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ);
650                 if (!sql_group) {
651                         radlog_request(L_ERR, 0, request,
652                                        "Error creating Sql-Group attribute");
653                         sql_grouplist_free(&group_list);
654                         return -1;
655                 }
656                 pairadd(&request->packet->vps, sql_group);
657                 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func, inst)) {
658                         radlog_request(L_ERR, 0, request,
659                                        "Error generating query; rejecting user");
660                         /* Remove the grouup we added above */
661                         pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY);
662                         sql_grouplist_free(&group_list);
663                         return -1;
664                 }
665                 rows = sql_getvpdata(inst, &handle, &check_tmp, querystr);
666                 if (rows < 0) {
667                         radlog_request(L_ERR, 0, request, "Error retrieving check pairs for group %s",
668                                group_list_tmp->groupname);
669                         /* Remove the grouup we added above */
670                         pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY);
671                         pairfree(&check_tmp);
672                         sql_grouplist_free(&group_list);
673                         return -1;
674                 } else if (rows > 0) {
675                         /*
676                          *      Only do this if *some* check pairs were returned
677                          */
678                         if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
679                                 found = 1;
680                                 RDEBUG2("User found in group %s",
681                                         group_list_tmp->groupname);
682                                 /*
683                                  *      Now get the reply pairs since the paircompare matched
684                                  */
685                                 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func, inst)) {
686                                         radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
687                                         /* Remove the grouup we added above */
688                                         pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY);
689                                         pairfree(&check_tmp);
690                                         sql_grouplist_free(&group_list);
691                                         return -1;
692                                 }
693                                 if (sql_getvpdata(inst, &handle, &reply_tmp, querystr) < 0) {
694                                         radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
695                                                group_list_tmp->groupname);
696                                         /* Remove the grouup we added above */
697                                         pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY);
698                                         pairfree(&check_tmp);
699                                         pairfree(&reply_tmp);
700                                         sql_grouplist_free(&group_list);
701                                         return -1;
702                                 }
703                                 *dofallthrough = fallthrough(reply_tmp);
704                                 radius_xlat_move(request, &request->reply->vps, &reply_tmp);
705                                 radius_xlat_move(request, &request->config_items, &check_tmp);
706                         }
707                 } else {
708                         /*
709                          *      rows == 0.  This is like having the username on a line
710                          *      in the user's file with no check vp's.  As such, we treat
711                          *      it as found and add the reply attributes, so that we
712                          *      match expected behavior
713                          */
714                         found = 1;
715                         RDEBUG2("User found in group %s",
716                                 group_list_tmp->groupname);
717                         /*
718                          *      Now get the reply pairs since the paircompare matched
719                          */
720                         if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func, inst)) {
721                                 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
722                                 /* Remove the grouup we added above */
723                                 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY);
724                                 pairfree(&check_tmp);
725                                 sql_grouplist_free(&group_list);
726                                 return -1;
727                         }
728                         if (sql_getvpdata(inst, &handle, &reply_tmp, querystr) < 0) {
729                                 radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
730                                        group_list_tmp->groupname);
731                                 /* Remove the grouup we added above */
732                                 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY);
733                                 pairfree(&check_tmp);
734                                 pairfree(&reply_tmp);
735                                 sql_grouplist_free(&group_list);
736                                 return -1;
737                         }
738                         *dofallthrough = fallthrough(reply_tmp);
739                         radius_xlat_move(request, &request->reply->vps, &reply_tmp);
740                         radius_xlat_move(request, &request->config_items, &check_tmp);
741                 }
742
743                 /*
744                  * Delete the Sql-Group we added above
745                  * And clear out the pairlists
746                  */
747                 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY);
748                 pairfree(&check_tmp);
749                 pairfree(&reply_tmp);
750         }
751
752         sql_grouplist_free(&group_list);
753         return found;
754 }
755
756
757 static int rlm_sql_detach(void *instance)
758 {
759         rlm_sql_t *inst = instance;
760
761         paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
762         
763         if (inst->config) {
764                 if (inst->pool) sql_poolfree(inst);
765
766                 if (inst->config->xlat_name) {
767                         xlat_unregister(inst->config->xlat_name, sql_xlat, instance);
768                 }
769         }
770
771         if (inst->handle) {
772 #if 0
773                 /*
774                  *      FIXME: Call the modules 'destroy' function?
775                  */
776                 lt_dlclose(inst->handle);       /* ignore any errors */
777 #endif
778         }
779
780         return 0;
781 }
782
783 static int parse_sub_section(CONF_SECTION *parent, 
784                              rlm_sql_t *inst,
785                              sql_acct_section_t **config,
786                              rlm_components_t comp)
787 {
788         CONF_SECTION *cs;
789
790         const char *name = section_type_value[comp].section;
791         
792         cs = cf_section_sub_find(parent, name);
793         if (!cs) {
794                 radlog(L_INFO, "rlm_sql (%s): Couldn't find configuration for "
795                        "%s, will return NOOP for calls from this section",
796                        inst->config->xlat_name, name);
797                 
798                 return 0;
799         }
800         
801         *config = talloc_zero(parent, sql_acct_section_t);
802         if (cf_section_parse(cs, *config, acct_section_config) < 0) {
803                 radlog(L_ERR, "rlm_sql (%s): Couldn't find configuration for "
804                        "%s, will return NOOP for calls from this section",
805                        inst->config->xlat_name, name);
806                 return -1;
807         }
808                 
809         (*config)->cs = cs;
810
811         return 0;
812 }
813
814 static int rlm_sql_instantiate(CONF_SECTION *conf, void **instance)
815 {
816         rlm_sql_t *inst;
817         const char *xlat_name;
818
819         *instance = inst = talloc_zero(conf, rlm_sql_t);
820         if (!inst) return -1;
821         
822         /*
823          *      Cache the SQL-User-Name DICT_ATTR, so we can be slightly
824          *      more efficient about creating SQL-User-Name attributes.
825          */
826         inst->sql_user = dict_attrbyname("SQL-User-Name");
827         if (!inst->sql_user) return -1;
828
829         /*
830          *      Export these methods, too.  This avoids RTDL_GLOBAL.
831          */
832         inst->sql_set_user              = sql_set_user;
833         inst->sql_get_socket            = sql_get_socket;
834         inst->sql_release_socket        = sql_release_socket;
835         inst->sql_escape_func           = sql_escape_func;
836         inst->sql_query                 = rlm_sql_query;
837         inst->sql_select_query          = rlm_sql_select_query;
838         inst->sql_fetch_row             = rlm_sql_fetch_row;
839         
840         inst->config = talloc_zero(inst, rlm_sql_config_t);
841         inst->cs = conf;
842
843         xlat_name = cf_section_name2(conf);
844         if (xlat_name == NULL) {
845                 xlat_name = cf_section_name1(conf);
846         } else {
847                 char *group_name;
848                 const DICT_ATTR *dattr;
849                 ATTR_FLAGS flags;
850
851                 /*
852                  *      Allocate room for <instance>-SQL-Group
853                  */
854                 group_name = talloc_asprintf(inst, "%s-SQL-Group", xlat_name);
855                 DEBUG("rlm_sql (%s): Creating new attribute %s",
856                       xlat_name, group_name);
857
858                 memset(&flags, 0, sizeof(flags));
859                 if (dict_addattr(group_name, -1, 0, PW_TYPE_STRING, flags) < 0) {
860                         radlog(L_ERR, "rlm_sql (%s): Failed to create "
861                                "attribute %s: %s", xlat_name, group_name,
862                                fr_strerror());
863                         return -1;
864                 }
865
866                 dattr = dict_attrbyname(group_name);
867                 if (!dattr) {
868                         radlog(L_ERR, "rlm_sql (%s): Failed to create "
869                                "attribute %s", xlat_name, group_name);
870                         return -1;
871                 }
872
873                 if (inst->config->groupmemb_query && 
874                     inst->config->groupmemb_query[0]) {
875                         DEBUG("rlm_sql (%s): Registering sql_groupcmp for %s",
876                               xlat_name, group_name);
877                         paircompare_register(dattr->attr, PW_USER_NAME,
878                                              sql_groupcmp, inst);
879                 }
880         }
881         
882         rad_assert(xlat_name);
883
884         /*
885          *      Register the SQL xlat function
886          */
887         inst->config->xlat_name = talloc_strdup(inst->config, xlat_name);
888         xlat_register(xlat_name, sql_xlat, inst);
889
890         /*
891          *      If the configuration parameters can't be parsed, then fail.
892          */
893         if ((cf_section_parse(conf, inst->config, module_config) < 0) ||
894             (parse_sub_section(conf, inst,
895                                &inst->config->accounting,
896                                RLM_COMPONENT_ACCT) < 0) ||
897             (parse_sub_section(conf, inst,
898                                &inst->config->postauth,
899                                RLM_COMPONENT_POST_AUTH) < 0)) {
900                 radlog(L_ERR, "rlm_sql (%s): Failed parsing configuration",
901                        inst->config->xlat_name);
902                 return -1;
903         }
904                 
905         /*
906          *      Sanity check for crazy people.
907          */
908         if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
909                 radlog(L_ERR, "rlm_sql (%s): \"%s\" is NOT an SQL driver!",
910                        inst->config->xlat_name, inst->config->sql_driver);
911                 return -1;
912         }
913
914         /*
915          *      Load the appropriate driver for our database
916          */
917         inst->handle = lt_dlopenext(inst->config->sql_driver);
918         if (inst->handle == NULL) {
919                 radlog(L_ERR, "Could not link driver %s: %s",
920                        inst->config->sql_driver,
921                        lt_dlerror());
922                 radlog(L_ERR, "Make sure it (and all its dependent libraries!)"
923                        "are in the search path of your system's ld.");
924                 return -1;
925         }
926
927         inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle,
928                                                      inst->config->sql_driver);
929         if (!inst->module) {
930                 radlog(L_ERR, "Could not link symbol %s: %s",
931                        inst->config->sql_driver,
932                        lt_dlerror());
933                 return -1;
934         }
935
936         radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
937                inst->config->xlat_name, inst->config->sql_driver,
938                inst->module->name);
939
940         /*
941          *      Initialise the connection pool for this instance
942          */
943         radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
944                inst->config->xlat_name, inst->config->sql_login,
945                inst->config->sql_server, inst->config->sql_port,
946                inst->config->sql_db);
947                
948         if (sql_init_socketpool(inst) < 0) return -1;
949
950         if (inst->config->groupmemb_query && 
951             inst->config->groupmemb_query[0]) {
952                 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
953         }
954
955         if (inst->config->do_clients) {
956                 if (generate_sql_clients(inst) == -1){
957                         radlog(L_ERR, "Failed to load clients from SQL.");
958                         return -1;
959                 }
960         }
961
962         return RLM_MODULE_OK;
963 }
964
965
966 static rlm_rcode_t rlm_sql_authorize(void *instance, REQUEST * request)
967 {
968         int ret = RLM_MODULE_NOTFOUND;
969         
970         rlm_sql_t *inst = instance;
971         rlm_sql_handle_t  *handle;
972         
973         VALUE_PAIR *check_tmp = NULL;
974         VALUE_PAIR *reply_tmp = NULL;
975         VALUE_PAIR *user_profile = NULL;
976
977         int     dofallthrough = 1;
978         int     rows;
979
980         char    querystr[MAX_QUERY_LEN];
981
982         /*
983          *  Set, escape, and check the user attr here
984          */
985         if (sql_set_user(inst, request, NULL) < 0)
986                 return RLM_MODULE_FAIL;
987
988         /*
989          *  Reserve a socket 
990          *
991          *  After this point use goto error or goto release to cleanup sockets
992          *  temporary pairlists and temporary attributes.
993          */
994         handle = sql_get_socket(inst);
995         if (handle == NULL)
996                 goto error;
997
998         /*
999          *  Query the check table to find any conditions associated with 
1000          *  this user/realm/whatever...
1001          */
1002         if (inst->config->authorize_check_query &&
1003             *inst->config->authorize_check_query) {
1004                 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func, inst)) {
1005                         radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
1006         
1007                         goto error;
1008                 }
1009                 
1010                 rows = sql_getvpdata(inst, &handle, &check_tmp, querystr);
1011                 if (rows < 0) {
1012                         radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
1013         
1014                         goto error;
1015                 }
1016                 
1017                 /*
1018                  *  Only do this if *some* check pairs were returned
1019                  */
1020                 if ((rows > 0) && 
1021                     (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0)) {       
1022                         RDEBUG2("User found in radcheck table");
1023                         
1024                         radius_xlat_move(request, &request->config_items, &check_tmp);
1025                         
1026                         ret = RLM_MODULE_OK;
1027                 }
1028                 
1029                 /*
1030                  *  We only process reply table items if check conditions
1031                  *  were verified
1032                  */
1033                 else
1034                         goto skipreply;
1035         }
1036         
1037         if (inst->config->authorize_reply_query &&
1038             *inst->config->authorize_reply_query) {
1039                 /*
1040                  *  Now get the reply pairs since the paircompare matched
1041                  */
1042                 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func, inst)) {
1043                         radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
1044                         
1045                         goto error;
1046                 }
1047                 
1048                 rows = sql_getvpdata(inst, &handle, &reply_tmp, querystr);
1049                 if (rows < 0) {
1050                         radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
1051
1052                         goto error;
1053                 }
1054                 
1055                 if (rows > 0) {
1056                         if (!inst->config->read_groups)
1057                                 dofallthrough = fallthrough(reply_tmp);
1058                                 
1059                         radius_xlat_move(request, &request->reply->vps, &reply_tmp);
1060                         
1061                         ret = RLM_MODULE_OK;
1062                 }
1063         }
1064         
1065         skipreply:
1066
1067         /*
1068          *  Clear out the pairlists
1069          */
1070         pairfree(&check_tmp);
1071         pairfree(&reply_tmp);
1072
1073         /*
1074          *  dofallthrough is set to 1 by default so that if the user information
1075          *  is not found, we will still process groups.  If the user information,
1076          *  however, *is* found, Fall-Through must be set in order to process
1077          *  the groups as well.
1078          */
1079         if (dofallthrough) {
1080                 rows = rlm_sql_process_groups(inst, request, handle, &dofallthrough);
1081                 if (rows < 0) {
1082                         radlog_request(L_ERR, 0, request, "Error processing groups; rejecting user");
1083
1084                         goto error;
1085                 }
1086                 
1087                 if (rows > 0)
1088                         ret = RLM_MODULE_OK;
1089         }
1090
1091         /*
1092          *  Repeat the above process with the default profile or User-Profile
1093          */
1094         if (dofallthrough) {
1095                 /*
1096                  *  Check for a default_profile or for a User-Profile.
1097                  */
1098                 user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY);
1099                 
1100                 const char *profile = user_profile ?
1101                                       user_profile->vp_strvalue :
1102                                       inst->config->default_profile;
1103                         
1104                 if (!profile || !*profile)
1105                         goto release;
1106                         
1107                 RDEBUG("Checking profile %s", profile);
1108                 
1109                 if (sql_set_user(inst, request, profile) < 0) {
1110                         radlog_request(L_ERR, 0, request, "Error setting profile; rejecting user");
1111
1112                         goto error;
1113                 }
1114                 
1115                 rows = rlm_sql_process_groups(inst, request, handle, &dofallthrough);
1116                 if (rows < 0) {
1117                         radlog_request(L_ERR, 0, request, "Error processing profile groups; rejecting user");
1118
1119                         goto error;
1120                 }
1121                 
1122                 if (rows > 0)
1123                         ret = RLM_MODULE_OK;
1124         }
1125         
1126         goto release;
1127         
1128         error:
1129         ret = RLM_MODULE_FAIL;
1130         
1131         release:
1132         sql_release_socket(inst, handle);
1133                 
1134         pairfree(&check_tmp);
1135         pairfree(&reply_tmp);
1136         
1137         return ret;
1138 }
1139
1140 /*
1141  *      Generic function for failing between a bunch of queries.
1142  *
1143  *      Uses the same principle as rlm_linelog, expanding the 'reference' config
1144  *      item using xlat to figure out what query it should execute.
1145  *
1146  *      If the reference matches multiple config items, and a query fails or
1147  *      doesn't update any rows, the next matching config item is used.
1148  *  
1149  */
1150 static int acct_redundant(rlm_sql_t *inst, REQUEST *request, 
1151                           sql_acct_section_t *section)
1152 {
1153         int     ret = RLM_MODULE_OK;
1154
1155         rlm_sql_handle_t        *handle = NULL;
1156         int     sql_ret;
1157         int     numaffected = 0;
1158
1159         CONF_ITEM  *item;
1160         CONF_PAIR  *pair;
1161         const char *attr = NULL;
1162         const char *value;
1163
1164         char    path[MAX_STRING_LEN];
1165         char    querystr[MAX_QUERY_LEN];
1166         
1167         char    *p = path;
1168
1169         rad_assert(section);
1170         
1171         if (section->reference[0] != '.')
1172                 *p++ = '.';
1173         
1174         if (!radius_xlat(p, (sizeof(path) - (p - path)) - 1,
1175                         section->reference, request, NULL, NULL))
1176                 return RLM_MODULE_FAIL;
1177
1178         item = cf_reference_item(NULL, section->cs, path);
1179         if (!item)
1180                 return RLM_MODULE_FAIL;
1181
1182         if (cf_item_is_section(item)){
1183                 radlog(L_ERR, "Sections are not supported as references");
1184                 
1185                 return RLM_MODULE_FAIL;
1186         }
1187         
1188         pair = cf_itemtopair(item);
1189         attr = cf_pair_attr(pair);
1190         
1191         RDEBUG2("Using query template '%s'", attr);
1192         
1193         handle = sql_get_socket(inst);
1194         if (handle == NULL)
1195                 return RLM_MODULE_FAIL;
1196                 
1197         sql_set_user(inst, request, NULL);
1198
1199         while (TRUE) {
1200                 value = cf_pair_value(pair);
1201                 if (!value) {
1202                         RDEBUG("Ignoring null query");
1203                         ret = RLM_MODULE_NOOP;
1204                         
1205                         goto release;
1206                 }
1207                 
1208                 radius_xlat(querystr, sizeof(querystr), value, request,
1209                             sql_escape_func, inst);
1210                 if (!*querystr) {
1211                         RDEBUG("Ignoring null query");
1212                         ret = RLM_MODULE_NOOP;
1213                         
1214                         goto release;
1215                 }
1216                 
1217                 rlm_sql_query_log(inst, request, section, querystr);
1218                 
1219                 /*
1220                  *  If rlm_sql_query cannot use the socket it'll try and
1221                  *  reconnect. Reconnecting will automatically release 
1222                  *  the current socket, and try to select a new one.
1223                  *
1224                  *  If we get SQL_DOWN it means all connections in the pool
1225                  *  were exhausted, and we couldn't create a new connection,
1226                  *  so we do not need to call sql_release_socket.
1227                  */
1228                 sql_ret = rlm_sql_query(&handle, inst, querystr);       
1229                 if (sql_ret == SQL_DOWN)
1230                         return RLM_MODULE_FAIL;
1231                 
1232                 rad_assert(handle);
1233         
1234                 /* 
1235                  *  Assume all other errors are incidental, and just meant our
1236                  *  operation failed and its not a client or SQL syntax error.
1237                  */
1238                 if (sql_ret == 0) {
1239                         numaffected = (inst->module->sql_affected_rows)
1240                                         (handle, inst->config);
1241                         if (numaffected > 0)
1242                                 break;
1243                                 
1244                         RDEBUG("No records updated");
1245                 }
1246
1247                 (inst->module->sql_finish_query)(handle, inst->config);
1248                 
1249                 /*
1250                  *  We assume all entries with the same name form a redundant
1251                  *  set of queries.
1252                  */
1253                 pair = cf_pair_find_next(section->cs, pair, attr);
1254                 
1255                 if (!pair) {
1256                         RDEBUG("No additional queries configured");
1257                         
1258                         ret = RLM_MODULE_NOOP;
1259                         
1260                         goto release;
1261                 }
1262
1263                 RDEBUG("Trying next query...");
1264         }
1265         
1266         (inst->module->sql_finish_query)(handle, inst->config);
1267
1268         release:
1269         sql_release_socket(inst, handle);
1270
1271         return ret;
1272 }
1273
1274 #ifdef WITH_ACCOUNTING
1275
1276 /*
1277  *      Accounting: Insert or update session data in our sql table
1278  */
1279 static rlm_rcode_t rlm_sql_accounting(void *instance, REQUEST * request) {
1280         rlm_sql_t *inst = instance;             
1281
1282         if (inst->config->accounting) {
1283                 return acct_redundant(inst, request, inst->config->accounting); 
1284         }
1285         
1286         return RLM_MODULE_NOOP;
1287 }
1288
1289 #endif
1290
1291 #ifdef WITH_SESSION_MGMT
1292 /*
1293  *        See if a user is already logged in. Sets request->simul_count to the
1294  *        current session count for this user.
1295  *
1296  *        Check twice. If on the first pass the user exceeds his
1297  *        max. number of logins, do a second pass and validate all
1298  *        logins by querying the terminal server (using eg. SNMP).
1299  */
1300
1301 static rlm_rcode_t rlm_sql_checksimul(void *instance, REQUEST * request) {
1302         rlm_sql_handle_t        *handle;
1303         rlm_sql_t       *inst = instance;
1304         rlm_sql_row_t           row;
1305         char            querystr[MAX_QUERY_LEN];
1306         int             check = 0;
1307         uint32_t        ipno = 0;
1308         char            *call_num = NULL;
1309         VALUE_PAIR      *vp;
1310         int             ret;
1311         uint32_t        nas_addr = 0;
1312         int             nas_port = 0;
1313
1314         /* If simul_count_query is not defined, we don't do any checking */
1315         if (!inst->config->simul_count_query ||
1316             (inst->config->simul_count_query[0] == 0)) {
1317                 return RLM_MODULE_NOOP;
1318         }
1319
1320         if((request->username == NULL) || (request->username->length == 0)) {
1321                 radlog_request(L_ERR, 0, request,
1322                                            "Zero Length username not permitted\n");
1323                 return RLM_MODULE_INVALID;
1324         }
1325
1326
1327         if(sql_set_user(inst, request, NULL) < 0)
1328                 return RLM_MODULE_FAIL;
1329
1330         radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func, inst);
1331
1332         /* initialize the sql socket */
1333         handle = sql_get_socket(inst);
1334         if(handle == NULL)
1335                 return RLM_MODULE_FAIL;
1336
1337         if(rlm_sql_select_query(&handle, inst, querystr)) {
1338                 sql_release_socket(inst, handle);
1339                 return RLM_MODULE_FAIL;
1340         }
1341
1342         ret = rlm_sql_fetch_row(&handle, inst);
1343         if (ret != 0) {
1344                 (inst->module->sql_finish_select_query)(handle, inst->config);
1345                 sql_release_socket(inst, handle);
1346                 return RLM_MODULE_FAIL;
1347         }
1348
1349         row = handle->row;
1350         if (row == NULL) {
1351                 (inst->module->sql_finish_select_query)(handle, inst->config);
1352                 sql_release_socket(inst, handle);
1353                 return RLM_MODULE_FAIL;
1354         }
1355
1356         request->simul_count = atoi(row[0]);
1357         (inst->module->sql_finish_select_query)(handle, inst->config);
1358
1359         if(request->simul_count < request->simul_max) {
1360                 sql_release_socket(inst, handle);
1361                 return RLM_MODULE_OK;
1362         }
1363
1364         /*
1365          *      Looks like too many sessions, so let's start verifying
1366          *      them, unless told to rely on count query only.
1367          */
1368         if (!inst->config->simul_verify_query ||
1369             (inst->config->simul_verify_query[0] == '\0')) {
1370                 sql_release_socket(inst, handle);
1371                 return RLM_MODULE_OK;
1372         }
1373
1374         radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func, inst);
1375         if(rlm_sql_select_query(&handle, inst, querystr)) {
1376                 sql_release_socket(inst, handle);
1377                 return RLM_MODULE_FAIL;
1378         }
1379
1380         /*
1381          *      Setup some stuff, like for MPP detection.
1382          */
1383         request->simul_count = 0;
1384
1385         if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL)
1386                 ipno = vp->vp_ipaddr;
1387         if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL)
1388                 call_num = vp->vp_strvalue;
1389
1390
1391         while (rlm_sql_fetch_row(&handle, inst) == 0) {
1392                 row = handle->row;
1393                 if (row == NULL)
1394                         break;
1395                 if (!row[2]){
1396                         (inst->module->sql_finish_select_query)(handle, inst->config);
1397                         sql_release_socket(inst, handle);
1398                         RDEBUG("Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1399                         return RLM_MODULE_FAIL;
1400                 }
1401                 if (!row[1]){
1402                         (inst->module->sql_finish_select_query)(handle, inst->config);
1403                         sql_release_socket(inst, handle);
1404                         RDEBUG("Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1405                         return RLM_MODULE_FAIL;
1406                 }
1407                 if (row[3])
1408                         nas_addr = inet_addr(row[3]);
1409                 if (row[4])
1410                         nas_port = atoi(row[4]);
1411
1412                 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1413
1414                 if (check == 0) {
1415                         /*
1416                          *      Stale record - zap it.
1417                          */
1418                         if (inst->config->deletestalesessions == TRUE) {
1419                                 uint32_t framed_addr = 0;
1420                                 char proto = 0;
1421                                 int sess_time = 0;
1422
1423                                 if (row[5])
1424                                         framed_addr = inet_addr(row[5]);
1425                                 if (row[7]){
1426                                         if (strcmp(row[7], "PPP") == 0)
1427                                                 proto = 'P';
1428                                         else if (strcmp(row[7], "SLIP") == 0)
1429                                                 proto = 'S';
1430                                 }
1431                                 if (row[8])
1432                                         sess_time = atoi(row[8]);
1433                                 session_zap(request, nas_addr, nas_port,
1434                                             row[2], row[1], framed_addr,
1435                                             proto, sess_time);
1436                         }
1437                 }
1438                 else if (check == 1) {
1439                         /*
1440                          *      User is still logged in.
1441                          */
1442                         ++request->simul_count;
1443
1444                         /*
1445                          *      Does it look like a MPP attempt?
1446                          */
1447                         if (row[5] && ipno && inet_addr(row[5]) == ipno)
1448                                 request->simul_mpp = 2;
1449                         else if (row[6] && call_num &&
1450                                 !strncmp(row[6],call_num,16))
1451                                 request->simul_mpp = 2;
1452                 }
1453                 else {
1454                         /*
1455                          *      Failed to check the terminal server for
1456                          *      duplicate logins: return an error.
1457                          */
1458                         (inst->module->sql_finish_select_query)(handle, inst->config);
1459                         sql_release_socket(inst, handle);
1460                         radlog_request(L_ERR, 0, request, "Failed to check the terminal server for user '%s'.", row[2]);
1461                         return RLM_MODULE_FAIL;
1462                 }
1463         }
1464
1465         (inst->module->sql_finish_select_query)(handle, inst->config);
1466         sql_release_socket(inst, handle);
1467
1468         /*
1469          *      The Auth module apparently looks at request->simul_count,
1470          *      not the return value of this module when deciding to deny
1471          *      a call for too many sessions.
1472          */
1473         return RLM_MODULE_OK;
1474 }
1475 #endif
1476
1477 /*
1478  *      Postauth: Write a record of the authentication attempt
1479  */
1480 static rlm_rcode_t rlm_sql_postauth(void *instance, REQUEST * request) {
1481         rlm_sql_t *inst = instance;
1482         
1483         if (inst->config->postauth) {
1484                 return acct_redundant(inst, request, inst->config->postauth);
1485         }
1486         
1487         return RLM_MODULE_NOOP; 
1488 }
1489
1490 /*
1491  *      Execute postauth_query after authentication
1492  */
1493
1494
1495 /* globally exported name */
1496 module_t rlm_sql = {
1497         RLM_MODULE_INIT,
1498         "SQL",
1499         RLM_TYPE_THREAD_SAFE,   /* type: reserved */
1500         rlm_sql_instantiate,    /* instantiation */
1501         rlm_sql_detach,         /* detach */
1502         {
1503                 NULL,                   /* authentication */
1504                 rlm_sql_authorize,      /* authorization */
1505                 NULL,                   /* preaccounting */
1506 #ifdef WITH_ACCOUNTING
1507                 rlm_sql_accounting,     /* accounting */
1508 #else
1509                 NULL,
1510 #endif
1511 #ifdef WITH_SESSION_MGMT
1512                 rlm_sql_checksimul,     /* checksimul */
1513 #else
1514                 NULL,
1515 #endif
1516                 NULL,                   /* pre-proxy */
1517                 NULL,                   /* post-proxy */
1518                 rlm_sql_postauth        /* post-auth */
1519         },
1520 };