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