import from HEAD
[freeradius.git] / src / modules / rlm_sql / rlm_sql.c
1 /*
2  * rlm_sql.c            SQL Module
3  *              Main SQL module file. Most ICRADIUS code is located in sql.c
4  *
5  * Version:     $Id$
6  *
7  *   This program is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU General Public License as published by
9  *   the Free Software Foundation; either version 2 of the License, or
10  *   (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program; if not, write to the Free Software
19  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * Copyright 2000  The FreeRADIUS server project
22  * Copyright 2000  Mike Machado <mike@innercite.com>
23  * Copyright 2000  Alan DeKok <aland@ox.org>
24  */
25
26 static const char rcsid[] =
27         "$Id$";
28
29 #include "autoconf.h"
30
31 #include <stdio.h>
32 #include <sys/stat.h>
33 #include <stdlib.h>
34
35 #include <time.h>
36 #include <unistd.h>
37 #include <sys/types.h>
38 #include <sys/wait.h>
39 #include <string.h>
40
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
44
45 #include "radiusd.h"
46 #include "modules.h"
47 #include "conffile.h"
48 #include "rlm_sql.h"
49 #include "rad_assert.h"
50
51 static char *allowed_chars = NULL;
52
53 static CONF_PARSER module_config[] = {
54         {"driver",PW_TYPE_STRING_PTR,
55          offsetof(SQL_CONFIG,sql_driver), NULL, "mysql"},
56         {"server",PW_TYPE_STRING_PTR,
57          offsetof(SQL_CONFIG,sql_server), NULL, "localhost"},
58         {"port",PW_TYPE_STRING_PTR,
59          offsetof(SQL_CONFIG,sql_port), NULL, ""},
60         {"login", PW_TYPE_STRING_PTR,
61          offsetof(SQL_CONFIG,sql_login), NULL, ""},
62         {"password", PW_TYPE_STRING_PTR,
63          offsetof(SQL_CONFIG,sql_password), NULL, ""},
64         {"radius_db", PW_TYPE_STRING_PTR,
65          offsetof(SQL_CONFIG,sql_db), NULL, "radius"},
66         {"nas_table", PW_TYPE_STRING_PTR,
67          offsetof(SQL_CONFIG,sql_nas_table), NULL, "nas"},
68         {"sqltrace", PW_TYPE_BOOLEAN,
69          offsetof(SQL_CONFIG,sqltrace), NULL, "no"},
70         {"sqltracefile", PW_TYPE_STRING_PTR,
71          offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE},
72         {"readclients", PW_TYPE_BOOLEAN,
73          offsetof(SQL_CONFIG,do_clients), NULL, "no"},
74         {"deletestalesessions", PW_TYPE_BOOLEAN,
75          offsetof(SQL_CONFIG,deletestalesessions), NULL, "no"},
76         {"num_sql_socks", PW_TYPE_INTEGER,
77          offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"},
78         {"sql_user_name", PW_TYPE_STRING_PTR,
79          offsetof(SQL_CONFIG,query_user), NULL, ""},
80         {"default_user_profile", PW_TYPE_STRING_PTR,
81          offsetof(SQL_CONFIG,default_profile), NULL, ""},
82         {"query_on_not_found", PW_TYPE_BOOLEAN,
83          offsetof(SQL_CONFIG,query_on_not_found), NULL, "no"},
84         {"authorize_check_query", PW_TYPE_STRING_PTR,
85          offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
86         {"authorize_reply_query", PW_TYPE_STRING_PTR,
87          offsetof(SQL_CONFIG,authorize_reply_query), NULL, ""},
88         {"authorize_group_check_query", PW_TYPE_STRING_PTR,
89          offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
90         {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
91          offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
92         {"accounting_onoff_query", PW_TYPE_STRING_PTR,
93          offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
94         {"accounting_update_query", PW_TYPE_STRING_PTR,
95          offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
96         {"accounting_update_query_alt", PW_TYPE_STRING_PTR,
97          offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""},
98         {"accounting_start_query", PW_TYPE_STRING_PTR,
99          offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
100         {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
101          offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
102         {"accounting_stop_query", PW_TYPE_STRING_PTR,
103          offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
104         {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
105          offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
106         {"group_membership_query", PW_TYPE_STRING_PTR,
107          offsetof(SQL_CONFIG,groupmemb_query), NULL, ""},
108         {"connect_failure_retry_delay", PW_TYPE_INTEGER,
109          offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
110         {"simul_count_query", PW_TYPE_STRING_PTR,
111          offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
112         {"simul_verify_query", PW_TYPE_STRING_PTR,
113          offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
114         {"postauth_query", PW_TYPE_STRING_PTR,
115          offsetof(SQL_CONFIG,postauth_query), NULL, ""},
116         {"safe-characters", PW_TYPE_STRING_PTR,
117          offsetof(SQL_CONFIG,allowed_chars), NULL, 
118         "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
119
120         {NULL, -1, 0, NULL, NULL}
121 };
122
123 /***********************************************************************
124  * start of main routines
125  ***********************************************************************/
126 static int rlm_sql_init(void) {
127
128         /*
129          * FIXME:
130          * We should put the sqlsocket array here once
131          * the module code is reworked to not unload
132          * modules on HUP.  This way we can have
133          * persistant connections.  -jcarneal
134          */
135         return 0;
136 }
137
138 /*
139  *      Yucky prototype.
140  */
141 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username);
142 static int generate_sql_clients(SQL_INST *inst);
143 static int sql_escape_func(char *out, int outlen, const char *in);
144
145 /*
146  *      sql xlat function. Right now only SELECTs are supported. Only
147  *      the first element of the SELECT result will be used.
148  */
149 static int sql_xlat(void *instance, REQUEST *request,
150                     char *fmt, char *out, size_t freespace,
151                     RADIUS_ESCAPE_STRING func)
152 {
153         SQLSOCK *sqlsocket;
154         SQL_ROW row;
155         SQL_INST *inst = instance;
156         char querystr[MAX_QUERY_LEN];
157         char sqlusername[MAX_STRING_LEN];
158         int ret = 0;
159
160         DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name);
161         /*
162          * Add SQL-User-Name attribute just in case it is needed
163          *  We could search the string fmt for SQL-User-Name to see if this is
164          *  needed or not
165          */
166         sql_set_user(inst, request, sqlusername, NULL);
167         /*
168          * Do an xlat on the provided string (nice recursive operation).
169          */
170         if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
171                 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
172                        inst->config->xlat_name);
173                 return 0;
174         }
175
176         query_log(request, inst,querystr);
177         sqlsocket = sql_get_socket(inst);
178         if (sqlsocket == NULL)
179                 return 0;
180         if (rlm_sql_select_query(sqlsocket,inst,querystr)){
181                 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
182                        inst->config->xlat_name,querystr,
183                        (char *)(inst->module->sql_error)(sqlsocket, inst->config));
184                 sql_release_socket(inst,sqlsocket);
185                 return 0;
186         }
187
188         ret = rlm_sql_fetch_row(sqlsocket, inst);
189
190         if (ret) {
191                 DEBUG("rlm_sql (%s): SQL query did not succeed",
192                       inst->config->xlat_name);
193                 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
194                 sql_release_socket(inst,sqlsocket);
195                 return 0;
196         }
197
198         row = sqlsocket->row;
199         if (row == NULL) {
200                 DEBUG("rlm_sql (%s): SQL query did not return any results",
201                       inst->config->xlat_name);
202                 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
203                 sql_release_socket(inst,sqlsocket);
204                 return 0;
205         }
206
207         if (row[0] == NULL){
208                 DEBUG("rlm_sql (%s): row[0] returned NULL",
209                       inst->config->xlat_name);
210                 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
211                 sql_release_socket(inst,sqlsocket);
212                 return 0;
213         }
214         ret = strlen(row[0]);
215         if (ret > freespace){
216                 DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space",
217                       inst->config->xlat_name);
218                 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
219                 sql_release_socket(inst,sqlsocket);
220                 return 0;
221         }
222
223         strncpy(out,row[0],ret);
224
225         DEBUG("rlm_sql (%s): - sql_xlat finished",
226               inst->config->xlat_name);
227
228         (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
229         sql_release_socket(inst,sqlsocket);
230         return ret;
231 }
232
233 static int generate_sql_clients(SQL_INST *inst)
234 {
235         SQLSOCK *sqlsocket;
236         SQL_ROW row;
237         char querystr[MAX_QUERY_LEN];
238         RADCLIENT *c;
239         char *netmask;
240         unsigned int i = 0;
241         
242         DEBUG("rlm_sql (%s): - generate_sql_clients",inst->config->xlat_name);
243
244         if (inst->config->sql_nas_table == NULL){
245                 radlog(L_ERR, "rlm_sql (%s): sql_nas_table is NULL.",inst->config->xlat_name);
246                 return -1;
247         }
248         snprintf(querystr,MAX_QUERY_LEN - 1,"SELECT * FROM %s",inst->config->sql_nas_table);
249
250         DEBUG("rlm_sql (%s): Query: %s",inst->config->xlat_name,querystr);
251         sqlsocket = sql_get_socket(inst);
252         if (sqlsocket == NULL)
253                 return -1;
254         if (rlm_sql_select_query(sqlsocket,inst,querystr)){
255                 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
256                         inst->config->xlat_name,querystr,
257                         (char *)(inst->module->sql_error)(sqlsocket, inst->config));
258                 sql_release_socket(inst,sqlsocket);
259                 return -1;
260         }
261
262         while(rlm_sql_fetch_row(sqlsocket, inst) == 0) {
263                 i++;
264                 row = sqlsocket->row;
265                 if (row == NULL)
266                         break;
267 /*
268  * Format:
269  * Row1 Row2    Row3            Row4    Row5    Row6    Row7            Row8
270  *
271  * id   nasname shortname       type    ports   secret  community       description
272  *
273  */
274
275                 if (!row[0]){
276                         radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
277                         continue;
278                 }
279                 if (!row[1]){
280                         radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
281                         continue;
282                 }
283                 if (strlen(row[1]) >= sizeof(c->longname)){
284                         radlog(L_ERR, "rlm_sql (%s): nasname of length %d is greater than the allowed maximum of %d",
285                                 inst->config->xlat_name,strlen(row[1]),sizeof(c->longname) - 1);
286                         continue;
287                 }       
288                 
289                 if (!row[2]){
290                         radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
291                         continue;
292                 }
293                 if (strlen(row[2]) >= sizeof(c->shortname)){
294                         radlog(L_ERR, "rlm_sql (%s): shortname of length %d is greater than the allowed maximum of %d",
295                                 inst->config->xlat_name,strlen(row[2]),sizeof(c->shortname) - 1);
296                         continue;
297                 }
298                 if (row[3] && strlen(row[3]) >= sizeof(c->nastype)){
299                         radlog(L_ERR, "rlm_sql (%s): nastype of length %d is greater than the allowed maximum of %d",
300                                 inst->config->xlat_name,strlen(row[3]),sizeof(c->nastype) - 1);
301                         continue;
302                 }
303                 if (!row[5]){
304                         radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
305                         continue;
306                 }
307                 if (strlen(row[5]) >= sizeof(c->secret)){
308                         radlog(L_ERR, "rlm_sql (%s): secret of length %d is greater than the allowed maximum of %d",
309                                 inst->config->xlat_name,strlen(row[5]),sizeof(c->secret) - 1);
310                         continue;
311                 }
312
313                 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
314                         row[1],row[2],row[5]);
315
316                 c = rad_malloc(sizeof(RADCLIENT));
317                 memset(c, 0, sizeof(RADCLIENT));
318
319                 c->netmask = ~0;
320                 netmask = strchr(row[1], '/');
321                 
322                 /*
323                  *      Look for netmasks.
324                  */
325                 c->netmask = ~0;
326                 if (netmask) {
327                         int mask_length;
328
329                         mask_length = atoi(netmask + 1);
330                         if ((mask_length < 0) || (mask_length > 32)) {
331                                 radlog(L_ERR, "rlm_sql (%s): Invalid value '%s' for IP network mask for nasname %s.",
332                                                 inst->config->xlat_name, netmask + 1,row[1]);
333                                 free(c);
334                                 continue;
335                         }
336
337                         if (mask_length == 0) {
338                                 c->netmask = 0;
339                         } else {
340                                 c->netmask = ~0 << (32 - mask_length);
341                         }
342
343                         *netmask = '\0';
344                         c->netmask = htonl(c->netmask);
345                 }
346
347                 c->ipaddr = ip_getaddr(row[1]);
348                 if (c->ipaddr == INADDR_NONE) {
349                         radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s",
350                                         inst->config->xlat_name, row[1]);
351                         free(c);
352                         continue;
353                 }
354
355                 /*
356                  *      Update the client name again...
357                  */
358                 if (netmask) {
359                         *netmask = '/';
360                         c->ipaddr &= c->netmask;
361                         strcpy(c->longname, row[1]);
362                 } else {
363                         ip_hostname(c->longname, sizeof(c->longname),
364                                         c->ipaddr);
365                 }
366
367                 strcpy((char *)c->secret, row[5]);
368                 strcpy(c->shortname, row[2]);
369                 if(row[3] != NULL)
370                         strcpy(c->nastype, row[3]);
371
372                 DEBUG("rlm_sql (%s): Adding client %s (%s) to clients list",inst->config->xlat_name,
373                         c->longname,c->shortname);
374
375                 c->next = mainconfig.clients;
376                 mainconfig.clients = c;
377
378         }
379         (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
380         sql_release_socket(inst, sqlsocket);
381
382         return 0;
383 }
384
385
386 /*
387  *      Translate the SQL queries.
388  */
389 static int sql_escape_func(char *out, int outlen, const char *in)
390 {
391         int len = 0;
392
393         while (in[0]) {
394                 /*
395                  *      Non-printable characters get replaced with their
396                  *      mime-encoded equivalents.
397                  */
398                 if ((in[0] < 32) ||
399                     strchr(allowed_chars, *in) == NULL) {
400                         /*
401                          *      Only 3 or less bytes available.
402                          */
403                         if (outlen <= 3) {
404                                 break;
405                         }
406
407                         snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
408                         in++;
409                         out += 3;
410                         outlen -= 3;
411                         len += 3;
412                         continue;
413                 }
414
415                 /*
416                  *      Only one byte left.
417                  */
418                 if (outlen <= 1) {
419                         break;
420                 }
421
422                 /*
423                  *      Allowed character.
424                  */
425                 *out = *in;
426                 out++;
427                 in++;
428                 outlen--;
429                 len++;
430         }
431         *out = '\0';
432         return len;
433 }
434
435 /*
436  *      Set the SQL user name.
437  *
438  *      We don't call the escape function here. The resulting string
439  *      will be escaped later in the queries xlat so we don't need to
440  *      escape it twice. (it will make things wrong if we have an
441  *      escape candidate character in the username)
442  */
443 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
444 {
445         VALUE_PAIR *vp=NULL;
446         char tmpuser[MAX_STRING_LEN];
447
448         tmpuser[0] = '\0';
449         sqlusername[0]= '\0';
450
451         /* Remove any user attr we added previously */
452         pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
453
454         if (username != NULL) {
455                 strNcpy(tmpuser, username, MAX_STRING_LEN);
456         } else if (strlen(inst->config->query_user)) {
457                 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
458         } else {
459                 return 0;
460         }
461
462         if (*tmpuser) {
463                 strNcpy(sqlusername, tmpuser, MAX_STRING_LEN);
464                 DEBUG2("rlm_sql (%s): sql_set_user escaped user --> '%s'",
465                        inst->config->xlat_name, sqlusername);
466                 vp = pairmake("SQL-User-Name", sqlusername, 0);
467                 if (vp == NULL) {
468                         radlog(L_ERR, "%s", librad_errstr);
469                         return -1;
470                 }
471
472                 pairadd(&request->packet->vps, vp);
473                 return 0;
474         }
475         return -1;
476 }
477
478 /*
479  * sql groupcmp function. That way we can do group comparisons (in the users file for example)
480  * with the group memberships reciding in sql
481  * The group membership query should only return one element which is the username. The returned
482  * username will then be checked with the passed check string.
483  */
484
485 static int sql_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
486                         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
487 {
488         SQLSOCK *sqlsocket;
489         SQL_ROW row;
490         SQL_INST *inst = instance;
491         char querystr[MAX_QUERY_LEN];
492         char sqlusername[MAX_STRING_LEN];
493
494         check_pairs = check_pairs;
495         reply_pairs = reply_pairs;
496
497         DEBUG("rlm_sql (%s): - sql_groupcmp", inst->config->xlat_name);
498         if (!check || !check->strvalue || !check->length){
499                 DEBUG("rlm_sql (%s): sql_groupcmp: Illegal group name",
500                       inst->config->xlat_name);
501                 return 1;
502         }
503         if (req == NULL){
504                 DEBUG("rlm_sql (%s): sql_groupcmp: NULL request",
505                       inst->config->xlat_name);
506                 return 1;
507         }
508         if (inst->config->groupmemb_query[0] == 0)
509                 return 1;
510         /*
511          * Set, escape, and check the user attr here
512          */
513         if (sql_set_user(inst, req, sqlusername, NULL) < 0)
514                 return 1;
515         if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, req, sql_escape_func)){
516                 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
517                        inst->config->xlat_name);
518                 /* Remove the username we (maybe) added above */
519                 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
520                 return 1;
521         }
522         /* Remove the username we (maybe) added above */
523         pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
524
525         sqlsocket = sql_get_socket(inst);
526         if (sqlsocket == NULL)
527                 return 1;
528         if ((inst->module->sql_select_query)(sqlsocket,inst->config,querystr) <0){
529                 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
530                        inst->config->xlat_name,querystr,
531                        (char *)(inst->module->sql_error)(sqlsocket,inst->config));
532                 sql_release_socket(inst,sqlsocket);
533                 return 1;
534         }
535         while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
536                 row = sqlsocket->row;
537                 if (row == NULL)
538                         break;
539                 if (row[0] == NULL){
540                         DEBUG("rlm_sql (%s): row[0] returned NULL",
541                               inst->config->xlat_name);
542                         (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
543                         sql_release_socket(inst, sqlsocket);
544                         return 1;
545                 }
546                 if (strcmp(row[0],check->strvalue) == 0){
547                         DEBUG("rlm_sql (%s): - sql_groupcmp finished: User belongs in group %s",
548                               inst->config->xlat_name,
549                               (char *)check->strvalue);
550                         (inst->module->sql_finish_select_query)(sqlsocket, inst->config);                       sql_release_socket(inst, sqlsocket);
551                         return 0;
552                 }
553         }
554
555         (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
556         sql_release_socket(inst,sqlsocket);
557
558         DEBUG("rlm_sql (%s): - sql_groupcmp finished: User does not belong in group %s",
559               inst->config->xlat_name, (char *)check->strvalue);
560
561         return 1;
562 }
563
564
565 static int rlm_sql_detach(void *instance)
566 {
567         SQL_INST *inst = instance;
568
569         if (inst->sqlpool) {
570                 sql_poolfree(inst);
571         }
572
573         if (inst->config->xlat_name) {
574                 xlat_unregister(inst->config->xlat_name,sql_xlat);
575                 free(inst->config->xlat_name);
576         }
577
578         paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
579
580         if (inst->config) {
581                 int i;
582
583                 /*
584                  *      Free up dynamically allocated string pointers.
585                  */
586                 for (i = 0; module_config[i].name != NULL; i++) {
587                         char **p;
588                         if (module_config[i].type != PW_TYPE_STRING_PTR) {
589                                 continue;
590                         }
591
592                         /*
593                          *      Treat 'config' as an opaque array of bytes,
594                          *      and take the offset into it.  There's a
595                          *      (char*) pointer at that offset, and we want
596                          *      to point to it.
597                          */
598                         p = (char **) (((char *)inst->config) + module_config[i].offset);
599                         if (!*p) { /* nothing allocated */
600                                 continue;
601                         }
602                         free(*p);
603                         *p = NULL;
604                 }
605                 allowed_chars = NULL;
606                 free(inst->config);
607                 inst->config = NULL;
608         }
609
610         if (inst->handle) {
611 #if 0
612                 /*
613                  *      FIXME: Call the modules 'destroy' function?
614                  */
615                 lt_dlclose(inst->handle);       /* ignore any errors */
616 #endif
617         }
618         free(inst);
619
620         return 0;
621 }
622 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
623 {
624         SQL_INST *inst;
625         char *xlat_name;
626
627         inst = rad_malloc(sizeof(SQL_INST));
628         memset(inst, 0, sizeof(SQL_INST));
629
630         inst->config = rad_malloc(sizeof(SQL_CONFIG));
631         memset(inst->config, 0, sizeof(SQL_CONFIG));
632
633         /*
634          * If the configuration parameters can't be parsed, then
635          * fail.
636          */
637         if (cf_section_parse(conf, inst->config, module_config) < 0) {
638                 rlm_sql_detach(inst);
639                 return -1;
640         }
641
642         xlat_name = cf_section_name2(conf);
643         if (xlat_name == NULL)
644                 xlat_name = cf_section_name1(conf);
645         if (xlat_name){
646                 inst->config->xlat_name = strdup(xlat_name);
647                 xlat_register(xlat_name, sql_xlat, inst);
648         }
649
650         if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
651                 radlog(L_ERR | L_CONS, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
652                        inst->config->xlat_name, MAX_SQL_SOCKS);
653                 rlm_sql_detach(inst);
654                 return -1;
655         }
656
657         /*
658          *      Sanity check for crazy people.
659          */
660         if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
661                 radlog(L_ERR, "rlm_sql (%s): \"%s\" is NOT an SQL driver!",
662                        inst->config->xlat_name, inst->config->sql_driver);
663                 rlm_sql_detach(inst);
664                 return -1;
665         }
666
667         inst->handle = lt_dlopenext(inst->config->sql_driver);
668         if (inst->handle == NULL) {
669                 radlog(L_ERR, "rlm_sql (%s): Could not link driver %s: %s",
670                        inst->config->xlat_name, inst->config->sql_driver,
671                        lt_dlerror());
672                 radlog(L_ERR, "rlm_sql (%s): Make sure it (and all its dependent libraries!) are in the search path of your system's ld.",
673                        inst->config->xlat_name);
674                 rlm_sql_detach(inst);
675                 return -1;
676         }
677
678         inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
679         if (!inst->module) {
680                 radlog(L_ERR, "rlm_sql (%s): Could not link symbol %s: %s",
681                        inst->config->xlat_name, inst->config->sql_driver,
682                        lt_dlerror());
683                 rlm_sql_detach(inst);
684                 return -1;
685         }
686
687         radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
688                inst->config->xlat_name, inst->config->sql_driver,
689                inst->module->name);
690         radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
691                inst->config->xlat_name, inst->config->sql_login,
692                inst->config->sql_server, inst->config->sql_port,
693                inst->config->sql_db);
694
695         if (sql_init_socketpool(inst) < 0) {
696                 rlm_sql_detach(inst);
697                 return -1;
698         }
699         paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
700
701         if (inst->config->do_clients){
702                 if (generate_sql_clients(inst) == -1){
703                         radlog(L_ERR, "rlm_sql (%s): generate_sql_clients() returned error",inst->config->xlat_name);
704                         rlm_sql_detach(inst);
705                         return -1;
706                 }
707         }
708         allowed_chars = inst->config->allowed_chars;
709
710         *instance = inst;
711
712         return RLM_MODULE_OK;
713 }
714
715 static int rlm_sql_destroy(void)
716 {
717         return 0;
718 }
719
720
721 static int rlm_sql_authorize(void *instance, REQUEST * request)
722 {
723         VALUE_PAIR *check_tmp = NULL;
724         VALUE_PAIR *reply_tmp = NULL;
725         VALUE_PAIR *user_profile = NULL;
726         int     found = 0;
727         SQLSOCK *sqlsocket;
728         SQL_INST *inst = instance;
729         char    querystr[MAX_QUERY_LEN];
730         char    sqlusername[MAX_STRING_LEN];
731
732         /*
733          *      They MUST have a user name to do SQL authorization.
734          */
735         if ((request->username == NULL) ||
736             (request->username->length == 0)) {
737                 radlog(L_ERR, "rlm_sql (%s): zero length username not permitted\n", inst->config->xlat_name);
738                 return RLM_MODULE_INVALID;
739         }
740
741         /*
742          *      Set, escape, and check the user attr here.
743          */
744         if (sql_set_user(inst, request, sqlusername, NULL) < 0)
745                 return RLM_MODULE_FAIL;
746         radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func);
747
748         sqlsocket = sql_get_socket(inst);
749         if (sqlsocket == NULL) {
750                 /* Remove the username we (maybe) added above */
751                 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
752                 return RLM_MODULE_FAIL;
753         }
754
755         /*
756          *      After this point, ALL 'return's MUST release the SQL socket!
757          */
758
759         found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_USERDATA);
760         /*
761          *      Find the entry for the user.
762          */
763         if (found > 0) {
764                 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func);
765                 sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
766                 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func);
767                 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_USERDATA);
768                 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func);
769                 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
770         } else if (found < 0) {
771                 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
772                        inst->config->xlat_name);
773                 sql_release_socket(inst, sqlsocket);
774                 /* Remove the username we (maybe) added above */
775                 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
776                 pairfree(&check_tmp);
777                 return RLM_MODULE_FAIL;
778
779         } else {
780                 radlog(L_DBG, "rlm_sql (%s): User %s not found in radcheck",
781                        inst->config->xlat_name, sqlusername);
782
783                 /*
784                  * We didn't find the user in radcheck, so we try looking
785                  * for radgroupcheck entry
786                  */
787                 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func);
788                 found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
789                 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func);
790                 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
791         }
792         if (!found)
793                 radlog(L_DBG, "rlm_sql (%s): User %s not found in radgroupcheck",
794                        inst->config->xlat_name, sqlusername);
795         if (found || (!found && inst->config->query_on_not_found)){
796                 int def_found = 0;
797
798                 /*
799                 * Check for a default_profile or for a User-Profile.
800                 */
801                 user_profile = pairfind(request->config_items, PW_USER_PROFILE);
802                 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
803                         char *profile = inst->config->default_profile;
804
805                         if (user_profile != NULL)
806                                 profile = user_profile->strvalue;
807                         if (profile && strlen(profile)){
808                                 radlog(L_DBG, "rlm_sql (%s): Checking profile %s",
809                                        inst->config->xlat_name, profile);
810                                 if (sql_set_user(inst, request, sqlusername, profile) < 0) {
811                                         sql_release_socket(inst, sqlsocket);
812                                         pairfree(&reply_tmp);
813                                         pairfree(&check_tmp);
814                                         return RLM_MODULE_FAIL;
815                                 }
816                                 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query,
817                                                                         request, sql_escape_func);
818                                 def_found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
819                                 if (def_found)
820                                         found = 1;
821                                 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query,
822                                                                         request, sql_escape_func);
823                                 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
824                         }
825                 }
826         }
827
828         /*
829          *      We don't need the SQL socket anymore.
830          */
831         sql_release_socket(inst, sqlsocket);
832
833         if (!found) {
834                 radlog(L_DBG, "rlm_sql (%s): User not found",
835                        inst->config->xlat_name);
836                 /* Remove the username we (maybe) added above */
837                 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
838                 pairfree(&reply_tmp);
839                 pairfree(&check_tmp);
840                 return RLM_MODULE_NOTFOUND;
841         }
842
843         /*
844          * Uncomment these lines for debugging
845          * Recompile, and run 'radiusd -X'
846          */
847
848         /*
849         DEBUG2("rlm_sql:  check items");
850         vp_listdebug(check_tmp);
851         DEBUG2("rlm_sql:  reply items");
852         vp_listdebug(reply_tmp);
853         */
854
855         if (paircmp(request, request->packet->vps, check_tmp, &reply_tmp) != 0) {
856                 radlog(L_INFO, "rlm_sql (%s): No matching entry in the database for request from user [%s]",
857                        inst->config->xlat_name, sqlusername);
858                 /* Remove the username we (maybe) added above */
859                 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
860                 pairfree(&reply_tmp);
861                 pairfree(&check_tmp);
862                 return RLM_MODULE_NOTFOUND;
863         }
864
865         pairxlatmove(request, &request->reply->vps, &reply_tmp);
866         pairxlatmove(request, &request->config_items, &check_tmp);
867         pairfree(&reply_tmp);
868         pairfree(&check_tmp);
869
870         /* Remove the username we (maybe) added above */
871         pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
872
873         return RLM_MODULE_OK;
874 }
875
876 /*
877  *      Accounting: save the account data to our sql table
878  */
879 static int rlm_sql_accounting(void *instance, REQUEST * request) {
880
881         SQLSOCK *sqlsocket = NULL;
882         VALUE_PAIR *pair;
883         SQL_INST *inst = instance;
884         int     ret = RLM_MODULE_OK;
885         int     numaffected = 0;
886         int     acctstatustype = 0;
887         char    querystr[MAX_QUERY_LEN];
888         char    logstr[MAX_QUERY_LEN];
889         char    sqlusername[MAX_STRING_LEN];
890
891 #ifdef CISCO_ACCOUNTING_HACK
892         int     acctsessiontime = 0;
893 #endif
894
895         memset(querystr, 0, MAX_QUERY_LEN);
896
897         /*
898          * Find the Acct Status Type
899          */
900         if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
901                 acctstatustype = pair->lvalue;
902         } else {
903                 radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
904                 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s",
905                        inst->config->xlat_name, logstr);
906                 return RLM_MODULE_INVALID;
907         }
908
909         switch (acctstatustype) {
910                         /*
911                          * The Terminal server informed us that it was rebooted
912                          * STOP all records from this NAS
913                          */
914                 case PW_STATUS_ACCOUNTING_ON:
915                 case PW_STATUS_ACCOUNTING_OFF:
916                         radlog(L_INFO, "rlm_sql (%s): received Acct On/Off packet", inst->config->xlat_name);
917                         radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
918                         query_log(request, inst, querystr);
919
920                         sqlsocket = sql_get_socket(inst);
921                         if (sqlsocket == NULL)
922                                 return(RLM_MODULE_FAIL);
923                         if (*querystr) { /* non-empty query */
924                                 if (rlm_sql_query(sqlsocket, inst, querystr)) {
925                                         radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting for Acct On/Off packet - %s",
926                                                inst->config->xlat_name,
927                                                (char *)(inst->module->sql_error)(sqlsocket, inst->config));
928                                         ret = RLM_MODULE_FAIL;
929                                 }
930                                 (inst->module->sql_finish_query)(sqlsocket, inst->config);
931                         }
932
933                         break;
934
935                         /*
936                          * Got an update accounting packet
937                          */
938                 case PW_STATUS_ALIVE:
939
940                         /*
941                          * Set, escape, and check the user attr here
942                          */
943                         sql_set_user(inst, request, sqlusername, NULL);
944
945                         radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
946                         query_log(request, inst, querystr);
947
948                         sqlsocket = sql_get_socket(inst);
949                         if (sqlsocket == NULL)
950                                 return(RLM_MODULE_FAIL);
951                         if (*querystr) { /* non-empty query */
952                                 if (rlm_sql_query(sqlsocket, inst, querystr)) {
953                                         radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting ALIVE record - %s",
954                                                inst->config->xlat_name,
955                                                (char *)(inst->module->sql_error)(sqlsocket, inst->config));
956                                         ret = RLM_MODULE_FAIL;
957                                 }
958                                 else {
959                                         numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
960                                         if (numaffected < 1) {
961
962                                                 /*
963                                                  * If our update above didn't match anything
964                                                  * we assume it's because we haven't seen a
965                                                  * matching Start record.  So we have to
966                                                  * insert this update rather than do an update
967                                                  */
968                                                 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
969                                                 query_log(request, inst, querystr);
970                                                 if (*querystr) { /* non-empty query */
971                                                         if (rlm_sql_query(sqlsocket, inst, querystr)) {
972                                                                 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting ALIVE record - %s",
973                                                                            inst->config->xlat_name,
974                                                                            (char *)(inst->module->sql_error)(sqlsocket, inst->config));
975                                                                 ret = RLM_MODULE_FAIL;
976                                                         }
977                                                         (inst->module->sql_finish_query)(sqlsocket, inst->config);
978                                                 }
979                                         }
980                                 }
981                                 (inst->module->sql_finish_query)(sqlsocket, inst->config);
982                         }
983                         break;
984
985                         /*
986                          * Got accounting start packet
987                          */
988                 case PW_STATUS_START:
989
990                         /*
991                          * Set, escape, and check the user attr here
992                          */
993                         sql_set_user(inst, request, sqlusername, NULL);
994
995                         radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
996                         query_log(request, inst, querystr);
997
998                         sqlsocket = sql_get_socket(inst);
999                         if (sqlsocket == NULL)
1000                                 return(RLM_MODULE_FAIL);
1001                         if (*querystr) { /* non-empty query */
1002                                 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1003                                         radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting START record - %s",
1004                                                inst->config->xlat_name,
1005                                                (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1006
1007                                         /*
1008                                          * We failed the insert above.  It's probably because
1009                                          * the stop record came before the start.  We try
1010                                          * our alternate query now (typically an UPDATE)
1011                                          */
1012                                         radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1013                                         query_log(request, inst, querystr);
1014
1015                                         if (*querystr) { /* non-empty query */
1016                                                 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1017                                                         radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting START record - %s",
1018                                                                inst->config->xlat_name,
1019                                                                (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1020                                                         ret = RLM_MODULE_FAIL;
1021                                                 }
1022                                                 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1023                                         }
1024                                 }
1025                                 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1026                         }
1027                         break;
1028
1029                         /*
1030                          * Got accounting stop packet
1031                          */
1032                 case PW_STATUS_STOP:
1033
1034                         /*
1035                          * Set, escape, and check the user attr here
1036                          */
1037                         sql_set_user(inst, request, sqlusername, NULL);
1038
1039                         radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1040                         query_log(request, inst, querystr);
1041
1042                         sqlsocket = sql_get_socket(inst);
1043                         if (sqlsocket == NULL)
1044                                 return(RLM_MODULE_FAIL);
1045                         if (*querystr) { /* non-empty query */
1046                                 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1047                                         radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting STOP record - %s",
1048                                                inst->config->xlat_name,
1049                                                (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1050                                         ret = RLM_MODULE_FAIL;
1051                                 }
1052                                 else {
1053                                         numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1054                                         if (numaffected < 1) {
1055                                                 /*
1056                                                  * If our update above didn't match anything
1057                                                  * we assume it's because we haven't seen a
1058                                                  * matching Start record.  So we have to
1059                                                  * insert this stop rather than do an update
1060                                                  */
1061 #ifdef CISCO_ACCOUNTING_HACK
1062                                                 /*
1063                                                  * If stop but zero session length AND no previous
1064                                                  * session found, drop it as in invalid packet
1065                                                  * This is to fix CISCO's aaa from filling our
1066                                                  * table with bogus crap
1067                                                  */
1068                                                 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
1069                                                         acctsessiontime = pair->lvalue;
1070
1071                                                 if (acctsessiontime <= 0) {
1072                                                         radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1073                                                         radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s", inst->config->xlat_name, logstr);
1074                                                         sql_release_socket(inst, sqlsocket);
1075                                                         ret = RLM_MODULE_NOOP;
1076                                                 }
1077 #endif
1078
1079                                                 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1080                                                 query_log(request, inst, querystr);
1081
1082                                                 if (*querystr) { /* non-empty query */
1083                                                         if (rlm_sql_query(sqlsocket, inst, querystr)) {
1084                                                                 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting STOP record - %s",
1085                                                                                 inst->config->xlat_name,
1086                                                                                 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1087                                                                 ret = RLM_MODULE_FAIL;
1088                                                         }
1089                                                         (inst->module->sql_finish_query)(sqlsocket, inst->config);
1090                                                 }
1091                                         }
1092                                 }
1093                                 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1094                         }
1095                         break;
1096
1097                         /*
1098                          *      Anything else is ignored.
1099                          */
1100                 default:
1101                         radlog(L_INFO, "rlm_sql (%s): Unsupported Acct-Status-Type = %d", inst->config->xlat_name, acctstatustype);
1102                         return RLM_MODULE_NOOP;
1103                         break;
1104
1105         }
1106
1107         sql_release_socket(inst, sqlsocket);
1108
1109         return ret;
1110 }
1111
1112
1113 /*
1114  *        See if a user is already logged in. Sets request->simul_count to the
1115  *        current session count for this user.
1116  *
1117  *        Check twice. If on the first pass the user exceeds his
1118  *        max. number of logins, do a second pass and validate all
1119  *        logins by querying the terminal server (using eg. SNMP).
1120  */
1121
1122 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1123         SQLSOCK         *sqlsocket;
1124         SQL_INST        *inst = instance;
1125         SQL_ROW         row;
1126         char            querystr[MAX_QUERY_LEN];
1127         char            sqlusername[MAX_STRING_LEN];
1128         int             check = 0;
1129         uint32_t        ipno = 0;
1130         char            *call_num = NULL;
1131         VALUE_PAIR      *vp;
1132         int             ret;
1133         uint32_t        nas_addr = 0;
1134         int             nas_port = 0;
1135
1136         /* If simul_count_query is not defined, we don't do any checking */
1137         if (inst->config->simul_count_query[0] == 0) {
1138                 return RLM_MODULE_NOOP;
1139         }
1140
1141         if((request->username == NULL) || (request->username->length == 0)) {
1142                 radlog(L_ERR, "rlm_sql (%s): Zero Length username not permitted\n", inst->config->xlat_name);
1143                 return RLM_MODULE_INVALID;
1144         }
1145
1146
1147         if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1148                 return RLM_MODULE_FAIL;
1149
1150         radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1151
1152         /* initialize the sql socket */
1153         sqlsocket = sql_get_socket(inst);
1154         if(sqlsocket == NULL)
1155                 return RLM_MODULE_FAIL;
1156
1157         if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1158                 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1159                 sql_release_socket(inst, sqlsocket);
1160                 return RLM_MODULE_FAIL;
1161         }
1162
1163         ret = rlm_sql_fetch_row(sqlsocket, inst);
1164
1165         if (ret != 0) {
1166                 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1167                 sql_release_socket(inst, sqlsocket);
1168                 return RLM_MODULE_FAIL;
1169         }
1170
1171         row = sqlsocket->row;
1172         if (row == NULL) {
1173                 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1174                 sql_release_socket(inst, sqlsocket);
1175                 return RLM_MODULE_FAIL;
1176         }
1177
1178         request->simul_count = atoi(row[0]);
1179         (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1180
1181         if(request->simul_count < request->simul_max) {
1182                 sql_release_socket(inst, sqlsocket);
1183                 return RLM_MODULE_OK;
1184         }
1185
1186         /* Looks like too many sessions, so lets start verifying them */
1187
1188         if (inst->config->simul_verify_query[0] == 0) {
1189                 /* No verify query defined, so skip verify step and rely on count query only */
1190                 sql_release_socket(inst, sqlsocket);
1191                 return RLM_MODULE_OK;
1192         }
1193
1194         radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1195         if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1196                 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Database query error", inst->config->xlat_name);
1197                 sql_release_socket(inst, sqlsocket);
1198                 return RLM_MODULE_FAIL;
1199         }
1200
1201         /*
1202          *      Setup some stuff, like for MPP detection.
1203          */
1204         request->simul_count = 0;
1205
1206         if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1207                 ipno = vp->lvalue;
1208         if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1209                 call_num = vp->strvalue;
1210
1211
1212         while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1213                 row = sqlsocket->row;
1214                 if (row == NULL)
1215                         break;
1216                 if (!row[2]){
1217                         (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1218                         sql_release_socket(inst, sqlsocket);
1219                         DEBUG("rlm_sql (%s): Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1220                         return RLM_MODULE_FAIL;
1221                 }
1222                 if (!row[1]){
1223                         (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1224                         sql_release_socket(inst, sqlsocket);
1225                         DEBUG("rlm_sql (%s): Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1226                         return RLM_MODULE_FAIL;
1227                 }
1228                 if (row[3])
1229                         nas_addr = inet_addr(row[3]);
1230                 if (row[4])
1231                         nas_port = atoi(row[4]);
1232
1233                 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1234
1235                 /*
1236                  *      Failed to check the terminal server for
1237                  *      duplicate logins: Return an error.
1238                  */
1239                 if (check < 0) {
1240                         (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1241                         sql_release_socket(inst, sqlsocket);
1242                         DEBUG("rlm_sql (%s) rad_check_ts() failed.",
1243                               inst->config->xlat_name);
1244                         return RLM_MODULE_FAIL;
1245                 }
1246
1247                 if(check == 1) {
1248                         ++request->simul_count;
1249
1250                         /*
1251                          *      Does it look like a MPP attempt?
1252                          */
1253                         if (row[5] && ipno && inet_addr(row[5]) == ipno)
1254                                 request->simul_mpp = 2;
1255                         else if (row[6] && call_num &&
1256                                 !strncmp(row[6],call_num,16))
1257                                 request->simul_mpp = 2;
1258                 }
1259                 else {
1260                         /*
1261                          *      Stale record - zap it.
1262                          */
1263                         uint32_t framed_addr = 0;
1264                         char proto = 'P';
1265
1266                         if (row[5])
1267                                 framed_addr = inet_addr(row[5]);
1268                         if (row[7])
1269                                 if (strcmp(row[7],"SLIP") == 0)
1270                                         proto = 'S';
1271
1272                         session_zap(request,
1273                                     nas_addr,nas_port,row[2],row[1],
1274                                     framed_addr, proto);
1275                 }
1276         }
1277
1278         (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1279         sql_release_socket(inst, sqlsocket);
1280
1281         /* The Auth module apparently looks at request->simul_count, not the return value
1282            of this module when deciding to deny a call for too many sessions */
1283         return RLM_MODULE_OK;
1284
1285 }
1286
1287 /*
1288  *      Execute postauth_query after authentication
1289  */
1290 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1291         SQLSOCK         *sqlsocket = NULL;
1292         SQL_INST        *inst = instance;
1293         char            querystr[MAX_QUERY_LEN];
1294         char            sqlusername[MAX_STRING_LEN];
1295
1296         DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name);
1297
1298         if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1299                 return RLM_MODULE_FAIL;
1300
1301         /* If postauth_query is not defined, we stop here */
1302         if (inst->config->postauth_query[0] == '\0')
1303                 return RLM_MODULE_NOOP;
1304
1305         /* Expand variables in the query */
1306         memset(querystr, 0, MAX_QUERY_LEN);
1307         radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1308                     request, sql_escape_func);
1309         query_log(request, inst, querystr);
1310         DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1311                inst->config->xlat_name, querystr);
1312
1313         /* Initialize the sql socket */
1314         sqlsocket = sql_get_socket(inst);
1315         if (sqlsocket == NULL)
1316                 return RLM_MODULE_FAIL;
1317
1318         /* Process the query */
1319         if (rlm_sql_query(sqlsocket, inst, querystr)) {
1320                 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1321                        inst->config->xlat_name,
1322                        (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1323                 sql_release_socket(inst, sqlsocket);
1324                 return RLM_MODULE_FAIL;
1325         }
1326         (inst->module->sql_finish_query)(sqlsocket, inst->config);
1327
1328         sql_release_socket(inst, sqlsocket);
1329         return RLM_MODULE_OK;
1330 }
1331
1332 /* globally exported name */
1333 module_t rlm_sql = {
1334         "SQL",
1335         RLM_TYPE_THREAD_SAFE,   /* type: reserved */
1336         rlm_sql_init,           /* initialization */
1337         rlm_sql_instantiate,    /* instantiation */
1338         {
1339                 NULL,                   /* authentication */
1340                 rlm_sql_authorize,      /* authorization */
1341                 NULL,                   /* preaccounting */
1342                 rlm_sql_accounting,     /* accounting */
1343                 rlm_sql_checksimul,     /* checksimul */
1344                 NULL,                   /* pre-proxy */
1345                 NULL,                   /* post-proxy */
1346                 rlm_sql_postauth        /* post-auth */
1347         },
1348         rlm_sql_detach,         /* detach */
1349         rlm_sql_destroy,        /* destroy */
1350 };