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