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