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