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