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