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