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