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