Fixed typo in rlm_sql.c
[freeradius.git] / src / modules / rlm_sql / rlm_sql.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License, version 2 if the
4  *   License as published by the Free Software Foundation.
5  *
6  *   This program is distributed in the hope that it will be useful,
7  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *   GNU General Public License for more details.
10  *
11  *   You should have received a copy of the GNU General Public License
12  *   along with this program; if not, write to the Free Software
13  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14  */
15
16 /**
17  * $Id$
18  * @file rlm_sql.c
19  * @brief Implements SQL 'users' file, and SQL accounting.
20  *
21  * @copyright 2012  Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22  * @copyright 2000,2006  The FreeRADIUS server project
23  * @copyright 2000  Mike Machado <mike@innercite.com>
24  * @copyright 2000  Alan DeKok <aland@ox.org>
25  */
26 RCSID("$Id$")
27
28 #include <ctype.h>
29
30 #include <freeradius-devel/radiusd.h>
31 #include <freeradius-devel/modules.h>
32 #include <freeradius-devel/token.h>
33 #include <freeradius-devel/rad_assert.h>
34
35 #include <sys/stat.h>
36
37 #include "rlm_sql.h"
38
39 static const CONF_PARSER acct_section_config[] = {
40         { "reference", FR_CONF_OFFSET(PW_TYPE_STRING, sql_acct_section_t, reference), ".query" },
41         { "logfile", FR_CONF_OFFSET(PW_TYPE_STRING, sql_acct_section_t, logfile), NULL },
42
43         {NULL, -1, 0, NULL, NULL}
44 };
45
46 static const CONF_PARSER module_config[] = {
47         { "driver", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, sql_driver_name), "rlm_sql_null" },
48         { "server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, sql_server), "localhost" },
49         { "port", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, sql_port), "" },
50         { "login", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, sql_login), "" },
51         { "password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, rlm_sql_config_t, sql_password), "" },
52         { "radius_db", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, sql_db), "radius" },
53         { "read_groups", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sql_config_t, read_groups), "yes" },
54         { "read_profiles", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sql_config_t, read_profiles), "yes" },
55         { "readclients", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, rlm_sql_config_t, do_clients), NULL },
56         { "read_clients", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sql_config_t, do_clients), "no" },
57         { "deletestalesessions", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, rlm_sql_config_t, deletestalesessions), NULL },
58         { "delete_stale_sessions", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sql_config_t, deletestalesessions), "yes" },
59         { "sql_user_name", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, query_user), "" },
60         { "logfile", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, logfile), NULL },
61         { "default_user_profile", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, default_profile), "" },
62         { "nas_query", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sql_config_t, client_query), NULL },
63         { "client_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, client_query), "SELECT id,nasname,shortname,type,secret FROM nas" },
64         { "authorize_check_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, authorize_check_query), "" },
65         { "open_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, open_query), NULL },
66         { "authorize_reply_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, authorize_reply_query), NULL },
67         { "authorize_group_check_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, authorize_group_check_query), "" },
68         { "authorize_group_reply_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, authorize_group_reply_query), "" },
69         { "group_membership_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, groupmemb_query), NULL },
70 #ifdef WITH_SESSION_MGMT
71         { "simul_count_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, simul_count_query), "" },
72         { "simul_verify_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, simul_verify_query), "" },
73 #endif
74         { "safe-characters", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sql_config_t, allowed_chars), NULL },
75         { "safe_characters", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, allowed_chars), "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /" },
76
77         /*
78          *      This only works for a few drivers.
79          */
80         { "query_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_sql_config_t, query_timeout), NULL },
81
82         {NULL, -1, 0, NULL, NULL}
83 };
84
85 /*
86  *      Fall-Through checking function from rlm_files.c
87  */
88 static sql_fall_through_t fall_through(VALUE_PAIR *vp)
89 {
90         VALUE_PAIR *tmp;
91         tmp = pairfind(vp, PW_FALL_THROUGH, 0, TAG_ANY);
92
93         return tmp ? tmp->vp_integer : FALL_THROUGH_DEFAULT;
94 }
95
96 /*
97  *      Yucky prototype.
98  */
99 static int generate_sql_clients(rlm_sql_t *inst);
100 static size_t sql_escape_func(REQUEST *, char *out, size_t outlen, char const *in, void *arg);
101
102 /*
103  *                      SQL xlat function
104  *
105  *  For selects the first value of the first column will be returned,
106  *  for inserts, updates and deletes the number of rows affected will be
107  *  returned instead.
108  */
109 static ssize_t sql_xlat(void *instance, REQUEST *request, char const *query, char *out, size_t freespace)
110 {
111         rlm_sql_handle_t *handle = NULL;
112         rlm_sql_row_t row;
113         rlm_sql_t *inst = instance;
114         ssize_t ret = 0;
115         size_t len = 0;
116
117         /*
118          *      Add SQL-User-Name attribute just in case it is needed
119          *      We could search the string fmt for SQL-User-Name to see if this is
120          *      needed or not
121          */
122         sql_set_user(inst, request, NULL);
123
124         handle = sql_get_socket(inst);
125         if (!handle) {
126                 return 0;
127         }
128
129         rlm_sql_query_log(inst, request, NULL, query);
130
131         /*
132          *      If the query starts with any of the following prefixes,
133          *      then return the number of rows affected
134          */
135         if ((strncasecmp(query, "insert", 6) == 0) ||
136             (strncasecmp(query, "update", 6) == 0) ||
137             (strncasecmp(query, "delete", 6) == 0)) {
138                 int numaffected;
139                 char buffer[21]; /* 64bit max is 20 decimal chars + null byte */
140
141                 if (rlm_sql_query(&handle, inst, query) != RLM_SQL_OK) {
142                         char const *error = (inst->module->sql_error)(handle, inst->config);
143                         REDEBUG("SQL query failed: %s", error);
144
145                         ret = -1;
146                         goto finish;
147                 }
148
149                 numaffected = (inst->module->sql_affected_rows)(handle, inst->config);
150                 if (numaffected < 1) {
151                         RDEBUG("SQL query affected no rows");
152
153                         goto finish;
154                 }
155
156                 /*
157                  *      Don't chop the returned number if freespace is
158                  *      too small.  This hack is necessary because
159                  *      some implementations of snprintf return the
160                  *      size of the written data, and others return
161                  *      the size of the data they *would* have written
162                  *      if the output buffer was large enough.
163                  */
164                 snprintf(buffer, sizeof(buffer), "%d", numaffected);
165
166                 len = strlen(buffer);
167                 if (len >= freespace){
168                         RDEBUG("rlm_sql (%s): Can't write result, insufficient string space", inst->config->xlat_name);
169
170                         (inst->module->sql_finish_query)(handle, inst->config);
171
172                         ret = -1;
173                         goto finish;
174                 }
175
176                 memcpy(out, buffer, len + 1); /* we did bounds checking above */
177                 ret = len;
178
179                 (inst->module->sql_finish_query)(handle, inst->config);
180
181                 goto finish;
182         } /* else it's a SELECT statement */
183
184         if (rlm_sql_select_query(&handle, inst, query) != RLM_SQL_OK){
185                 char const *error = (inst->module->sql_error)(handle, inst->config);
186                 REDEBUG("SQL query failed: %s", error);
187                 ret = -1;
188
189                 goto finish;
190         }
191
192         ret = rlm_sql_fetch_row(&handle, inst);
193         if (ret) {
194                 REDEBUG("SQL query failed");
195                 (inst->module->sql_finish_select_query)(handle, inst->config);
196                 ret = -1;
197
198                 goto finish;
199         }
200
201         row = handle->row;
202         if (!row) {
203                 RDEBUG("SQL query returned no results");
204                 (inst->module->sql_finish_select_query)(handle, inst->config);
205                 ret = -1;
206
207                 goto finish;
208         }
209
210         if (!row[0]){
211                 RDEBUG("NULL value in first column of result");
212                 (inst->module->sql_finish_select_query)(handle, inst->config);
213                 ret = -1;
214
215                 goto finish;
216         }
217
218         len = strlen(row[0]);
219         if (len >= freespace){
220                 RDEBUG("Insufficient string space");
221                 (inst->module->sql_finish_select_query)(handle, inst->config);
222
223                 ret = -1;
224                 goto finish;
225         }
226
227         strlcpy(out, row[0], freespace);
228         ret = len;
229
230         (inst->module->sql_finish_select_query)(handle, inst->config);
231
232 finish:
233         sql_release_socket(inst, handle);
234
235         return ret;
236 }
237
238 static int generate_sql_clients(rlm_sql_t *inst)
239 {
240         rlm_sql_handle_t *handle;
241         rlm_sql_row_t row;
242         unsigned int i = 0;
243         RADCLIENT *c;
244
245         DEBUG("rlm_sql (%s): Processing generate_sql_clients",
246               inst->config->xlat_name);
247
248         DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s",
249               inst->config->xlat_name, inst->config->client_query);
250
251         handle = sql_get_socket(inst);
252         if (!handle) {
253                 return -1;
254         }
255
256         if (rlm_sql_select_query(&handle, inst, inst->config->client_query) != RLM_SQL_OK){
257                 return -1;
258         }
259
260         while ((rlm_sql_fetch_row(&handle, inst) == 0) && (row = handle->row)) {
261                 char *server = NULL;
262                 i++;
263
264                 /*
265                  *  The return data for each row MUST be in the following order:
266                  *
267                  *  0. Row ID (currently unused)
268                  *  1. Name (or IP address)
269                  *  2. Shortname
270                  *  3. Type
271                  *  4. Secret
272                  *  5. Virtual Server (optional)
273                  */
274                 if (!row[0]){
275                         ERROR("rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
276                         continue;
277                 }
278                 if (!row[1]){
279                         ERROR("rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
280                         continue;
281                 }
282                 if (!row[2]){
283                         ERROR("rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
284                         continue;
285                 }
286                 if (!row[4]){
287                         ERROR("rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
288                         continue;
289                 }
290
291                 if (((inst->module->sql_num_fields)(handle, inst->config) > 5) && (row[5] != NULL) && *row[5]) {
292                         server = row[5];
293                 }
294
295                 DEBUG("rlm_sql (%s): Adding client %s (%s) to %s clients list",
296                       inst->config->xlat_name,
297                       row[1], row[2], server ? server : "global");
298
299                 /* FIXME: We should really pass a proper ctx */
300                 c = client_from_query(NULL,
301                                       row[1],   /* identifier */
302                                       row[4],   /* secret */
303                                       row[2],   /* shortname */
304                                       row[3],   /* type */
305                                       server,   /* server */
306                                       false);   /* require message authenticator */
307                 if (!c) {
308                         continue;
309                 }
310
311                 if (!client_add(NULL, c)) {
312                         WARN("Failed to add client, possible duplicate?");
313
314                         client_free(c);
315                         continue;
316                 }
317
318                 DEBUG("rlm_sql (%s): Client \"%s\" (%s) added", c->longname, c->shortname,
319                       inst->config->xlat_name);
320         }
321
322         (inst->module->sql_finish_select_query)(handle, inst->config);
323         sql_release_socket(inst, handle);
324
325         return 0;
326 }
327
328
329 /*
330  *      Translate the SQL queries.
331  */
332 static size_t sql_escape_func(UNUSED REQUEST *request, char *out, size_t outlen,
333                               char const *in, void *arg)
334 {
335         rlm_sql_t *inst = arg;
336         size_t len = 0;
337
338         while (in[0]) {
339                 /*
340                  *      Non-printable characters get replaced with their
341                  *      mime-encoded equivalents.
342                  */
343                 if ((in[0] < 32) ||
344                     strchr(inst->config->allowed_chars, *in) == NULL) {
345                         /*
346                          *      Only 3 or less bytes available.
347                          */
348                         if (outlen <= 3) {
349                                 break;
350                         }
351
352                         snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
353                         in++;
354                         out += 3;
355                         outlen -= 3;
356                         len += 3;
357                         continue;
358                 }
359
360                 /*
361                  *      Only one byte left.
362                  */
363                 if (outlen <= 1) {
364                         break;
365                 }
366
367                 /*
368                  *      Allowed character.
369                  */
370                 *out = *in;
371                 out++;
372                 in++;
373                 outlen--;
374                 len++;
375         }
376         *out = '\0';
377         return len;
378 }
379
380 /*
381  *      Set the SQL user name.
382  *
383  *      We don't call the escape function here. The resulting string
384  *      will be escaped later in the queries xlat so we don't need to
385  *      escape it twice. (it will make things wrong if we have an
386  *      escape candidate character in the username)
387  */
388 int sql_set_user(rlm_sql_t *inst, REQUEST *request, char const *username)
389 {
390         char *expanded = NULL;
391         VALUE_PAIR *vp = NULL;
392         char const *sqluser;
393         ssize_t len;
394
395         rad_assert(request->packet != NULL);
396
397         if (username != NULL) {
398                 sqluser = username;
399         } else if (inst->config->query_user[0] != '\0') {
400                 sqluser = inst->config->query_user;
401         } else {
402                 return 0;
403         }
404
405         len = radius_axlat(&expanded, request, sqluser, NULL, NULL);
406         if (len < 0) {
407                 return -1;
408         }
409
410         vp = pairalloc(request->packet, inst->sql_user);
411         if (!vp) {
412                 talloc_free(expanded);
413                 return -1;
414         }
415
416         pairstrsteal(vp, expanded);
417         RDEBUG2("SQL-User-Name set to '%s'", vp->vp_strvalue);
418         vp->op = T_OP_SET;
419         radius_pairmove(request, &request->packet->vps, vp, false);     /* needs to be pair move else op is not respected */
420
421         return 0;
422 }
423
424 /*
425  *      Do a set/unset user, so it's a bit clearer what's going on.
426  */
427 #define sql_unset_user(_i, _r) pairdelete(&_r->packet->vps, _i->sql_user->attr, _i->sql_user->vendor, TAG_ANY)
428
429 static int sql_get_grouplist(rlm_sql_t *inst, rlm_sql_handle_t **handle, REQUEST *request,
430                              rlm_sql_grouplist_t **phead)
431 {
432         char    *expanded = NULL;
433         int     num_groups = 0;
434         rlm_sql_row_t row;
435         rlm_sql_grouplist_t *entry;
436         int ret;
437
438         /* NOTE: sql_set_user should have been run before calling this function */
439
440         entry = *phead = NULL;
441
442         if (!inst->config->groupmemb_query || (inst->config->groupmemb_query[0] == 0)) {
443                 return 0;
444         }
445
446         if (radius_axlat(&expanded, request, inst->config->groupmemb_query, sql_escape_func, inst) < 0) {
447                 return -1;
448         }
449
450         ret = rlm_sql_select_query(handle, inst, expanded);
451         talloc_free(expanded);
452         if (ret != RLM_SQL_OK) {
453                 return -1;
454         }
455
456         while (rlm_sql_fetch_row(handle, inst) == 0) {
457                 row = (*handle)->row;
458                 if (!row)
459                         break;
460
461                 if (!row[0]){
462                         RDEBUG("row[0] returned NULL");
463                         (inst->module->sql_finish_select_query)(*handle, inst->config);
464                         talloc_free(entry);
465                         return -1;
466                 }
467
468                 if (!*phead) {
469                         *phead = talloc_zero(*handle, rlm_sql_grouplist_t);
470                         entry = *phead;
471                 } else {
472                         entry->next = talloc_zero(*phead, rlm_sql_grouplist_t);
473                         entry = entry->next;
474                 }
475                 entry->next = NULL;
476                 entry->name = talloc_typed_strdup(entry, row[0]);
477
478                 num_groups++;
479         }
480
481         (inst->module->sql_finish_select_query)(*handle, inst->config);
482
483         return num_groups;
484 }
485
486
487 /*
488  * sql groupcmp function. That way we can do group comparisons (in the users file for example)
489  * with the group memberships reciding in sql
490  * The group membership query should only return one element which is the username. The returned
491  * username will then be checked with the passed check string.
492  */
493
494 static int CC_HINT(nonnull (1 ,2, 4)) sql_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *request_vp,
495                                                    VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs,
496                                                    UNUSED VALUE_PAIR **reply_pairs)
497 {
498         rlm_sql_handle_t *handle;
499         rlm_sql_t *inst = instance;
500         rlm_sql_grouplist_t *head, *entry;
501
502         RDEBUG("sql_groupcmp");
503
504         if (check->length == 0){
505                 RDEBUG("sql_groupcmp: Illegal group name");
506                 return 1;
507         }
508
509         /*
510          *      Set, escape, and check the user attr here
511          */
512         if (sql_set_user(inst, request, NULL) < 0)
513                 return 1;
514
515         /*
516          *      Get a socket for this lookup
517          */
518         handle = sql_get_socket(inst);
519         if (!handle) {
520                 return 1;
521         }
522
523         /*
524          *      Get the list of groups this user is a member of
525          */
526         if (sql_get_grouplist(inst, &handle, request, &head) < 0) {
527                 REDEBUG("Error getting group membership");
528                 sql_release_socket(inst, handle);
529                 return 1;
530         }
531
532         for (entry = head; entry != NULL; entry = entry->next) {
533                 if (strcmp(entry->name, check->vp_strvalue) == 0){
534                         RDEBUG("sql_groupcmp finished: User is a member of group %s",
535                                check->vp_strvalue);
536                         talloc_free(head);
537                         sql_release_socket(inst, handle);
538                         return 0;
539                 }
540         }
541
542         /* Free the grouplist */
543         talloc_free(head);
544         sql_release_socket(inst, handle);
545
546         RDEBUG("sql_groupcmp finished: User is NOT a member of group %s", check->vp_strvalue);
547
548         return 1;
549 }
550
551 static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **handle,
552                                           sql_fall_through_t *do_fall_through)
553 {
554         rlm_rcode_t             rcode = RLM_MODULE_NOOP;
555         VALUE_PAIR              *check_tmp = NULL, *reply_tmp = NULL, *sql_group = NULL;
556         rlm_sql_grouplist_t     *head = NULL, *entry = NULL;
557
558         char                    *expanded = NULL;
559         int                     rows;
560
561         rad_assert(request->packet != NULL);
562
563         /*
564          *      Get the list of groups this user is a member of
565          */
566         rows = sql_get_grouplist(inst, handle, request, &head);
567         if (rows < 0) {
568                 REDEBUG("Error retrieving group list");
569
570                 return RLM_MODULE_FAIL;
571         }
572         if (rows == 0) {
573                 RDEBUG2("User not found in any groups");
574                 rcode = RLM_MODULE_NOTFOUND;
575                 goto finish;
576         }
577
578         RDEBUG2("User found in the group table");
579
580         for (entry = head; entry != NULL && (*do_fall_through == FALL_THROUGH_YES); entry = entry->next) {
581                 /*
582                  *      Add the Sql-Group attribute to the request list so we know
583                  *      which group we're retrieving attributes for
584                  */
585                 sql_group = pairmake_packet("Sql-Group", entry->name, T_OP_EQ);
586                 if (!sql_group) {
587                         REDEBUG("Error creating Sql-Group attribute");
588                         rcode = RLM_MODULE_FAIL;
589                         goto finish;
590                 }
591
592                 if (inst->config->authorize_group_check_query && (inst->config->authorize_group_check_query != '\0')) {
593                         /*
594                          *      Expand the group query
595                          */
596                         if (radius_axlat(&expanded, request, inst->config->authorize_group_check_query,
597                                          sql_escape_func, inst) < 0) {
598                                 REDEBUG("Error generating query");
599                                 rcode = RLM_MODULE_FAIL;
600                                 goto finish;
601                         }
602
603                         rows = sql_getvpdata(inst, handle, request, &check_tmp, expanded);
604                         TALLOC_FREE(expanded);
605                         if (rows < 0) {
606                                 REDEBUG("Error retrieving check pairs for group %s", entry->name);
607                                 rcode = RLM_MODULE_FAIL;
608                                 goto finish;
609                         }
610
611                         /*
612                          *      If we got check rows we need to process them before we decide to process the reply rows
613                          */
614                         if ((rows > 0) &&
615                             (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0)) {
616                                 pairfree(&check_tmp);
617                                 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY);
618
619                                 continue;
620                         }
621
622                         RDEBUG2("Group \"%s\" check items matched", entry->name);
623                         rcode = RLM_MODULE_OK;
624
625                         radius_pairmove(request, &request->config_items, check_tmp, true);
626                         check_tmp = NULL;
627                 }
628
629                 if (inst->config->authorize_group_reply_query && (inst->config->authorize_group_reply_query != '\0')) {
630                         /*
631                          *      Now get the reply pairs since the paircompare matched
632                          */
633                         if (radius_axlat(&expanded, request, inst->config->authorize_group_reply_query,
634                                          sql_escape_func, inst) < 0) {
635                                 REDEBUG("Error generating query");
636                                 rcode = RLM_MODULE_FAIL;
637                                 goto finish;
638                         }
639
640                         rows = sql_getvpdata(inst, handle, request->reply, &reply_tmp, expanded);
641                         TALLOC_FREE(expanded);
642                         if (rows < 0) {
643                                 REDEBUG("Error retrieving reply pairs for group %s", entry->name);
644                                 rcode = RLM_MODULE_FAIL;
645                                 goto finish;
646                         }
647
648                         *do_fall_through = fall_through(reply_tmp);
649
650                         RDEBUG2("Group \"%s\" reply items processed", entry->name);
651                         rcode = RLM_MODULE_OK;
652
653                         radius_pairmove(request, &request->reply->vps, reply_tmp, true);
654                         reply_tmp = NULL;
655                 }
656
657                 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY);
658         }
659
660         finish:
661
662         talloc_free(head);
663         pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY);
664
665         return rcode;
666 }
667
668
669 static int mod_detach(void *instance)
670 {
671         rlm_sql_t *inst = instance;
672
673         if (inst->config) {
674                 if (inst->pool) sql_poolfree(inst);
675         }
676
677         if (inst->handle) {
678 #if 0
679                 /*
680                  *      FIXME: Call the modules 'destroy' function?
681                  */
682                 dlclose(inst->handle);  /* ignore any errors */
683 #endif
684         }
685
686         return 0;
687 }
688
689 static int parse_sub_section(CONF_SECTION *parent,
690                              rlm_sql_t *inst,
691                              sql_acct_section_t **config,
692                              rlm_components_t comp)
693 {
694         CONF_SECTION *cs;
695
696         char const *name = section_type_value[comp].section;
697
698         cs = cf_section_sub_find(parent, name);
699         if (!cs) {
700                 INFO("rlm_sql (%s): Couldn't find configuration for "
701                        "%s, will return NOOP for calls from this section",
702                        inst->config->xlat_name, name);
703
704                 return 0;
705         }
706
707         *config = talloc_zero(parent, sql_acct_section_t);
708         if (cf_section_parse(cs, *config, acct_section_config) < 0) {
709                 ERROR("rlm_sql (%s): Couldn't find configuration for "
710                        "%s, will return NOOP for calls from this section",
711                        inst->config->xlat_name, name);
712                 return -1;
713         }
714
715         (*config)->cs = cs;
716
717         return 0;
718 }
719
720 static int mod_instantiate(CONF_SECTION *conf, void *instance)
721 {
722         rlm_sql_t *inst = instance;
723
724         /*
725          *      Hack...
726          */
727         inst->config = &inst->myconfig;
728         inst->cs = conf;
729
730         inst->config->xlat_name = cf_section_name2(conf);
731         if (!inst->config->xlat_name) {
732                 inst->config->xlat_name = cf_section_name1(conf);
733         } else {
734                 char *group_name;
735                 DICT_ATTR const *da;
736                 ATTR_FLAGS flags;
737
738                 /*
739                  *      Allocate room for <instance>-SQL-Group
740                  */
741                 group_name = talloc_typed_asprintf(inst, "%s-SQL-Group", inst->config->xlat_name);
742                 DEBUG("rlm_sql (%s): Creating new attribute %s",
743                       inst->config->xlat_name, group_name);
744
745                 memset(&flags, 0, sizeof(flags));
746                 if (dict_addattr(group_name, -1, 0, PW_TYPE_STRING, flags) < 0) {
747                         ERROR("rlm_sql (%s): Failed to create "
748                                "attribute %s: %s", inst->config->xlat_name, group_name,
749                                fr_strerror());
750                         return -1;
751                 }
752
753                 da = dict_attrbyname(group_name);
754                 if (!da) {
755                         ERROR("rlm_sql (%s): Failed to create "
756                                "attribute %s", inst->config->xlat_name, group_name);
757                         return -1;
758                 }
759
760                 if (inst->config->groupmemb_query &&
761                     inst->config->groupmemb_query[0]) {
762                         DEBUG("rlm_sql (%s): Registering sql_groupcmp for %s",
763                               inst->config->xlat_name, group_name);
764                         paircompare_register(da, dict_attrbyvalue(PW_USER_NAME, 0),
765                                              false, sql_groupcmp, inst);
766                 }
767         }
768
769         rad_assert(inst->config->xlat_name);
770
771         /*
772          *      If the configuration parameters can't be parsed, then fail.
773          */
774         if ((parse_sub_section(conf, inst, &inst->config->accounting, RLM_COMPONENT_ACCT) < 0) ||
775             (parse_sub_section(conf, inst, &inst->config->postauth, RLM_COMPONENT_POST_AUTH) < 0)) {
776                 cf_log_err_cs(conf, "Invalid configuration");
777                 return -1;
778         }
779
780         /*
781          *      Cache the SQL-User-Name DICT_ATTR, so we can be slightly
782          *      more efficient about creating SQL-User-Name attributes.
783          */
784         inst->sql_user = dict_attrbyname("SQL-User-Name");
785         if (!inst->sql_user) {
786                 return -1;
787         }
788
789         /*
790          *      Export these methods, too.  This avoids RTDL_GLOBAL.
791          */
792         inst->sql_set_user              = sql_set_user;
793         inst->sql_get_socket            = sql_get_socket;
794         inst->sql_release_socket        = sql_release_socket;
795         inst->sql_escape_func           = sql_escape_func;
796         inst->sql_query                 = rlm_sql_query;
797         inst->sql_select_query          = rlm_sql_select_query;
798         inst->sql_fetch_row             = rlm_sql_fetch_row;
799
800         /*
801          *      Register the SQL xlat function
802          */
803         xlat_register(inst->config->xlat_name, sql_xlat, sql_escape_func, inst);
804
805         /*
806          *      Sanity check for crazy people.
807          */
808         if (strncmp(inst->config->sql_driver_name, "rlm_sql_", 8) != 0) {
809                 ERROR("rlm_sql (%s): \"%s\" is NOT an SQL driver!",
810                        inst->config->xlat_name, inst->config->sql_driver_name);
811                 return -1;
812         }
813
814         /*
815          *      Load the appropriate driver for our database
816          */
817         inst->handle = lt_dlopenext(inst->config->sql_driver_name);
818         if (!inst->handle) {
819                 ERROR("Could not link driver %s: %s",
820                        inst->config->sql_driver_name,
821                        dlerror());
822                 ERROR("Make sure it (and all its dependent libraries!)"
823                        "are in the search path of your system's ld");
824                 return -1;
825         }
826
827         inst->module = (rlm_sql_module_t *) dlsym(inst->handle,
828                                                   inst->config->sql_driver_name);
829         if (!inst->module) {
830                 ERROR("Could not link symbol %s: %s",
831                        inst->config->sql_driver_name,
832                        dlerror());
833                 return -1;
834         }
835
836         if (inst->module->mod_instantiate) {
837                 CONF_SECTION *cs;
838                 char const *name;
839
840                 name = strrchr(inst->config->sql_driver_name, '_');
841                 if (!name) {
842                         name = inst->config->sql_driver_name;
843                 } else {
844                         name++;
845                 }
846
847                 cs = cf_section_sub_find(conf, name);
848                 if (!cs) {
849                         cs = cf_section_alloc(conf, name, NULL);
850                         if (!cs) {
851                                 return -1;
852                         }
853                 }
854
855                 /*
856                  *      It's up to the driver to register a destructor
857                  */
858                 if (inst->module->mod_instantiate(cs, inst->config) < 0) {
859                         return -1;
860                 }
861         }
862
863         inst->lf = fr_logfile_init(inst);
864         if (!inst->lf) {
865                 cf_log_err_cs(conf, "Failed creating log file context");
866                 return -1;
867         }
868
869         INFO("rlm_sql (%s): Driver %s (module %s) loaded and linked",
870                inst->config->xlat_name, inst->config->sql_driver_name,
871                inst->module->name);
872
873         /*
874          *      Initialise the connection pool for this instance
875          */
876         INFO("rlm_sql (%s): Attempting to connect to database \"%s\"",
877                inst->config->xlat_name, inst->config->sql_db);
878
879         if (sql_socket_pool_init(inst) < 0) return -1;
880
881         if (inst->config->groupmemb_query &&
882             inst->config->groupmemb_query[0]) {
883                 paircompare_register(dict_attrbyvalue(PW_SQL_GROUP, 0),
884                                 dict_attrbyvalue(PW_USER_NAME, 0), false, sql_groupcmp, inst);
885         }
886
887         if (inst->config->do_clients) {
888                 if (generate_sql_clients(inst) == -1){
889                         ERROR("Failed to load clients from SQL");
890                         return -1;
891                 }
892         }
893
894         return RLM_MODULE_OK;
895 }
896
897
898 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
899 {
900         rlm_rcode_t rcode = RLM_MODULE_NOOP;
901
902         rlm_sql_t *inst = instance;
903         rlm_sql_handle_t  *handle;
904
905         VALUE_PAIR *check_tmp = NULL;
906         VALUE_PAIR *reply_tmp = NULL;
907         VALUE_PAIR *user_profile = NULL;
908
909         bool    user_found = false;
910
911         sql_fall_through_t do_fall_through = FALL_THROUGH_DEFAULT;
912
913         int     rows;
914
915         char    *expanded = NULL;
916
917         rad_assert(request->packet != NULL);
918         rad_assert(request->reply != NULL);
919
920         /*
921          *      Set, escape, and check the user attr here
922          */
923         if (sql_set_user(inst, request, NULL) < 0) {
924                 return RLM_MODULE_FAIL;
925         }
926
927         /*
928          *      Reserve a socket
929          *
930          *      After this point use goto error or goto release to cleanup socket temporary pairlists and
931          *      temporary attributes.
932          */
933         handle = sql_get_socket(inst);
934         if (!handle) {
935                 rcode = RLM_MODULE_FAIL;
936                 goto error;
937         }
938
939         /*
940          *      Query the check table to find any conditions associated with this user/realm/whatever...
941          */
942         if (inst->config->authorize_check_query && (inst->config->authorize_check_query[0] != '\0')) {
943                 if (radius_axlat(&expanded, request, inst->config->authorize_check_query,
944                                  sql_escape_func, inst) < 0) {
945                         REDEBUG("Error generating query");
946                         rcode = RLM_MODULE_FAIL;
947                         goto error;
948                 }
949
950                 rows = sql_getvpdata(inst, &handle, request, &check_tmp, expanded);
951                 TALLOC_FREE(expanded);
952                 if (rows < 0) {
953                         REDEBUG("SQL query error");
954                         rcode = RLM_MODULE_FAIL;
955                         goto error;
956                 }
957
958                 if (rows == 0) goto skipreply;  /* Don't need to free VPs we don't have */
959
960                 /*
961                  *      Only do this if *some* check pairs were returned
962                  */
963                 RDEBUG2("User found in radcheck table");
964                 user_found = true;
965                 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0) {
966                         pairfree(&check_tmp);
967                         check_tmp = NULL;
968                         goto skipreply;
969                 }
970
971                 RDEBUG2("Check items matched");
972                 radius_pairmove(request, &request->config_items, check_tmp, true);
973                 rcode = RLM_MODULE_OK;
974                 check_tmp = NULL;
975         }
976
977         if (inst->config->authorize_reply_query && (inst->config->authorize_reply_query[0] != '\0')) {
978                 /*
979                  *      Now get the reply pairs since the paircompare matched
980                  */
981                 if (radius_axlat(&expanded, request, inst->config->authorize_reply_query,
982                                  sql_escape_func, inst) < 0) {
983                         REDEBUG("Error generating query");
984                         rcode = RLM_MODULE_FAIL;
985                         goto error;
986                 }
987
988                 rows = sql_getvpdata(inst, &handle, request->reply, &reply_tmp, expanded);
989                 TALLOC_FREE(expanded);
990                 if (rows < 0) {
991                         REDEBUG("SQL query error");
992                         rcode = RLM_MODULE_FAIL;
993                         goto error;
994                 }
995
996                 if (rows == 0) goto skipreply;
997
998                 do_fall_through = fall_through(reply_tmp);
999
1000                 RDEBUG2("User found in radreply table");
1001                 user_found = true;
1002                 radius_pairmove(request, &request->reply->vps, reply_tmp, true);
1003                 rcode = RLM_MODULE_OK;
1004                 reply_tmp = NULL;
1005         }
1006
1007 skipreply:
1008         if ((do_fall_through == FALL_THROUGH_YES) ||
1009             (inst->config->read_groups && (do_fall_through == FALL_THROUGH_DEFAULT))) {
1010                 rlm_rcode_t ret;
1011
1012                 RDEBUG3("... falling-through to group processing");
1013                 ret = rlm_sql_process_groups(inst, request, &handle, &do_fall_through);
1014                 switch (ret) {
1015                         /*
1016                          *      Nothing bad happened, continue...
1017                          */
1018                         case RLM_MODULE_UPDATED:
1019                                 rcode = RLM_MODULE_UPDATED;
1020                                 /* FALL-THROUGH */
1021                         case RLM_MODULE_OK:
1022                                 if (rcode != RLM_MODULE_UPDATED) {
1023                                         rcode = RLM_MODULE_OK;
1024                                 }
1025                                 /* FALL-THROUGH */
1026                         case RLM_MODULE_NOOP:
1027                                 user_found = true;
1028                                 break;
1029
1030                         case RLM_MODULE_NOTFOUND:
1031                                 break;
1032
1033                         default:
1034                                 rcode = ret;
1035                                 goto release;
1036                 }
1037         }
1038
1039         /*
1040          *      Repeat the above process with the default profile or User-Profile
1041          */
1042         if ((do_fall_through == FALL_THROUGH_YES) ||
1043             (inst->config->read_profiles && (do_fall_through == FALL_THROUGH_DEFAULT))) {
1044                 rlm_rcode_t ret;
1045
1046                 /*
1047                  *  Check for a default_profile or for a User-Profile.
1048                  */
1049                 RDEBUG3("... falling-through to profile processing");
1050                 user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY);
1051
1052                 char const *profile = user_profile ?
1053                                       user_profile->vp_strvalue :
1054                                       inst->config->default_profile;
1055
1056                 if (!profile || !*profile) {
1057                         goto release;
1058                 }
1059
1060                 RDEBUG2("Checking profile %s", profile);
1061
1062                 if (sql_set_user(inst, request, profile) < 0) {
1063                         REDEBUG("Error setting profile");
1064                         rcode = RLM_MODULE_FAIL;
1065                         goto error;
1066                 }
1067
1068                 ret = rlm_sql_process_groups(inst, request, &handle, &do_fall_through);
1069                 switch (ret) {
1070                         /*
1071                          *      Nothing bad happened, continue...
1072                          */
1073                         case RLM_MODULE_UPDATED:
1074                                 rcode = RLM_MODULE_UPDATED;
1075                                 /* FALL-THROUGH */
1076                         case RLM_MODULE_OK:
1077                                 if (rcode != RLM_MODULE_UPDATED) {
1078                                         rcode = RLM_MODULE_OK;
1079                                 }
1080                                 /* FALL-THROUGH */
1081                         case RLM_MODULE_NOOP:
1082                                 user_found = true;
1083                                 break;
1084
1085                         case RLM_MODULE_NOTFOUND:
1086                                 break;
1087
1088                         default:
1089                                 rcode = ret;
1090                                 goto release;
1091                 }
1092         }
1093
1094         /*
1095          *      At this point the key (user) hasn't be found in the check table, the reply table
1096          *      or the group mapping table, and there was no matching profile.
1097          */
1098 release:
1099         if (!user_found) {
1100                 rcode = RLM_MODULE_NOTFOUND;
1101         }
1102
1103         sql_release_socket(inst, handle);
1104         sql_unset_user(inst, request);
1105
1106         return rcode;
1107
1108 error:
1109         pairfree(&check_tmp);
1110         pairfree(&reply_tmp);
1111         sql_unset_user(inst, request);
1112
1113         sql_release_socket(inst, handle);
1114
1115         return rcode;
1116 }
1117
1118 /*
1119  *      Generic function for failing between a bunch of queries.
1120  *
1121  *      Uses the same principle as rlm_linelog, expanding the 'reference' config
1122  *      item using xlat to figure out what query it should execute.
1123  *
1124  *      If the reference matches multiple config items, and a query fails or
1125  *      doesn't update any rows, the next matching config item is used.
1126  *
1127  */
1128 static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t *section)
1129 {
1130         rlm_rcode_t             rcode = RLM_MODULE_OK;
1131
1132         rlm_sql_handle_t        *handle = NULL;
1133         int                     sql_ret;
1134         int                     numaffected = 0;
1135
1136         CONF_ITEM               *item;
1137         CONF_PAIR               *pair;
1138         char const              *attr = NULL;
1139         char const              *value;
1140
1141         char                    path[MAX_STRING_LEN];
1142         char                    *p = path;
1143         char                    *expanded = NULL;
1144
1145         rad_assert(section);
1146
1147         if (section->reference[0] != '.') {
1148                 *p++ = '.';
1149         }
1150
1151         if (radius_xlat(p, sizeof(path) - (p - path), request, section->reference, NULL, NULL) < 0) {
1152                 rcode = RLM_MODULE_FAIL;
1153
1154                 goto finish;
1155         }
1156
1157         item = cf_reference_item(NULL, section->cs, path);
1158         if (!item) {
1159                 rcode = RLM_MODULE_FAIL;
1160
1161                 goto finish;
1162         }
1163
1164         if (cf_item_is_section(item)){
1165                 REDEBUG("Sections are not supported as references");
1166                 rcode = RLM_MODULE_FAIL;
1167
1168                 goto finish;
1169         }
1170
1171         pair = cf_itemtopair(item);
1172         attr = cf_pair_attr(pair);
1173
1174         RDEBUG2("Using query template '%s'", attr);
1175
1176         handle = sql_get_socket(inst);
1177         if (!handle) {
1178                 rcode = RLM_MODULE_FAIL;
1179
1180                 goto finish;
1181         }
1182
1183         sql_set_user(inst, request, NULL);
1184
1185         while (true) {
1186                 value = cf_pair_value(pair);
1187                 if (!value) {
1188                         RDEBUG("Ignoring null query");
1189                         rcode = RLM_MODULE_NOOP;
1190
1191                         goto finish;
1192                 }
1193
1194                 if (radius_axlat(&expanded, request, value, sql_escape_func, inst) < 0) {
1195                         rcode = RLM_MODULE_FAIL;
1196
1197                         goto finish;
1198                 }
1199
1200                 if (!*expanded) {
1201                         RDEBUG("Ignoring null query");
1202                         rcode = RLM_MODULE_NOOP;
1203                         talloc_free(expanded);
1204
1205                         goto finish;
1206                 }
1207
1208                 rlm_sql_query_log(inst, request, section, expanded);
1209
1210                 /*
1211                  *  If rlm_sql_query cannot use the socket it'll try and
1212                  *  reconnect. Reconnecting will automatically release
1213                  *  the current socket, and try to select a new one.
1214                  *
1215                  *  If we get RLM_SQL_RECONNECT it means all connections in the pool
1216                  *  were exhausted, and we couldn't create a new connection,
1217                  *  so we do not need to call sql_release_socket.
1218                  */
1219                 sql_ret = rlm_sql_query(&handle, inst, expanded);
1220                 TALLOC_FREE(expanded);
1221                 if (sql_ret == RLM_SQL_RECONNECT) {
1222                         rcode = RLM_MODULE_FAIL;
1223                         goto finish;
1224                 }
1225                 rad_assert(handle);
1226
1227                 /*
1228                  *  Assume all other errors are incidental, and just meant our
1229                  *  operation failed and its not a client or SQL syntax error.
1230                  *
1231                  *  @fixme We should actually be able to distinguish between key
1232                  *  constraint violations (which we expect) and other errors.
1233                  */
1234                 if (sql_ret == RLM_SQL_OK) {
1235                         numaffected = (inst->module->sql_affected_rows)(handle, inst->config);
1236                         if (numaffected > 0) {
1237                                 break;  /* A query succeeded, were done! */
1238                         }
1239
1240                         RDEBUG("No records updated");
1241                 }
1242
1243                 (inst->module->sql_finish_query)(handle, inst->config);
1244
1245                 /*
1246                  *  We assume all entries with the same name form a redundant
1247                  *  set of queries.
1248                  */
1249                 pair = cf_pair_find_next(section->cs, pair, attr);
1250
1251                 if (!pair) {
1252                         RDEBUG("No additional queries configured");
1253                         rcode = RLM_MODULE_NOOP;
1254
1255                         goto finish;
1256                 }
1257
1258                 RDEBUG("Trying next query...");
1259         }
1260
1261         (inst->module->sql_finish_query)(handle, inst->config);
1262
1263 finish:
1264         talloc_free(expanded);
1265         sql_release_socket(inst, handle);
1266         sql_unset_user(inst, request);
1267
1268         return rcode;
1269 }
1270
1271 #ifdef WITH_ACCOUNTING
1272
1273 /*
1274  *      Accounting: Insert or update session data in our sql table
1275  */
1276 static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST * request) {
1277         rlm_sql_t *inst = instance;
1278
1279         if (inst->config->accounting) {
1280                 return acct_redundant(inst, request, inst->config->accounting);
1281         }
1282
1283         return RLM_MODULE_NOOP;
1284 }
1285
1286 #endif
1287
1288 #ifdef WITH_SESSION_MGMT
1289 /*
1290  *      See if a user is already logged in. Sets request->simul_count to the
1291  *      current session count for this user.
1292  *
1293  *      Check twice. If on the first pass the user exceeds his
1294  *      max. number of logins, do a second pass and validate all
1295  *      logins by querying the terminal server (using eg. SNMP).
1296  */
1297
1298 static rlm_rcode_t CC_HINT(nonnull) mod_checksimul(void *instance, REQUEST * request) {
1299         rlm_rcode_t             rcode = RLM_MODULE_OK;
1300         rlm_sql_handle_t        *handle = NULL;
1301         rlm_sql_t               *inst = instance;
1302         rlm_sql_row_t           row;
1303         int                     check = 0;
1304         uint32_t                ipno = 0;
1305         char const              *call_num = NULL;
1306         VALUE_PAIR              *vp;
1307         int                     ret;
1308         uint32_t                nas_addr = 0;
1309         uint32_t                nas_port = 0;
1310
1311         char                    *expanded = NULL;
1312
1313         /* If simul_count_query is not defined, we don't do any checking */
1314         if (!inst->config->simul_count_query || (inst->config->simul_count_query[0] == '\0')) {
1315                 return RLM_MODULE_NOOP;
1316         }
1317
1318         if((!request->username) || (request->username->length == '\0')) {
1319                 REDEBUG("Zero Length username not permitted");
1320
1321                 return RLM_MODULE_INVALID;
1322         }
1323
1324
1325         if(sql_set_user(inst, request, NULL) < 0) {
1326                 return RLM_MODULE_FAIL;
1327         }
1328
1329         if (radius_axlat(&expanded, request, inst->config->simul_count_query, sql_escape_func, inst) < 0) {
1330                 sql_unset_user(inst, request);
1331                 return RLM_MODULE_FAIL;
1332         }
1333
1334         /* initialize the sql socket */
1335         handle = sql_get_socket(inst);
1336         if (!handle) {
1337                 talloc_free(expanded);
1338                 sql_unset_user(inst, request);
1339                 return RLM_MODULE_FAIL;
1340         }
1341
1342         if (rlm_sql_select_query(&handle, inst, expanded) != RLM_SQL_OK) {
1343                 rcode = RLM_MODULE_FAIL;
1344                 goto finish;
1345         }
1346
1347         ret = rlm_sql_fetch_row(&handle, inst);
1348         if (ret != 0) {
1349                 rcode = RLM_MODULE_FAIL;
1350                 goto finish;
1351         }
1352
1353         row = handle->row;
1354         if (!row) {
1355                 rcode = RLM_MODULE_FAIL;
1356                 goto finish;
1357         }
1358
1359         request->simul_count = atoi(row[0]);
1360
1361         (inst->module->sql_finish_select_query)(handle, inst->config);
1362         TALLOC_FREE(expanded);
1363
1364         if (request->simul_count < request->simul_max) {
1365                 rcode = RLM_MODULE_OK;
1366                 goto finish;
1367         }
1368
1369         /*
1370          *      Looks like too many sessions, so let's start verifying
1371          *      them, unless told to rely on count query only.
1372          */
1373         if (!inst->config->simul_verify_query || (inst->config->simul_verify_query[0] == '\0')) {
1374                 rcode = RLM_MODULE_OK;
1375
1376                 goto finish;
1377         }
1378
1379         if (radius_axlat(&expanded, request, inst->config->simul_verify_query, sql_escape_func, inst) < 0) {
1380                 rcode = RLM_MODULE_FAIL;
1381
1382                 goto finish;
1383         }
1384
1385         if (rlm_sql_select_query(&handle, inst, expanded) != RLM_SQL_OK) {
1386                 goto finish;
1387         }
1388
1389         /*
1390          *      Setup some stuff, like for MPP detection.
1391          */
1392         request->simul_count = 0;
1393
1394         if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL) {
1395                 ipno = vp->vp_ipaddr;
1396         }
1397
1398         if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL) {
1399                 call_num = vp->vp_strvalue;
1400         }
1401
1402         while (rlm_sql_fetch_row(&handle, inst) == 0) {
1403                 row = handle->row;
1404                 if (!row) {
1405                         break;
1406                 }
1407
1408                 if (!row[2]){
1409                         RDEBUG("Cannot zap stale entry. No username present in entry");
1410                         rcode = RLM_MODULE_FAIL;
1411
1412                         goto finish;
1413                 }
1414
1415                 if (!row[1]){
1416                         RDEBUG("Cannot zap stale entry. No session id in entry");
1417                         rcode = RLM_MODULE_FAIL;
1418
1419                         goto finish;
1420                 }
1421
1422                 if (row[3]) {
1423                         nas_addr = inet_addr(row[3]);
1424                 }
1425
1426                 if (row[4]) {
1427                         nas_port = atoi(row[4]);
1428                 }
1429
1430                 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1431                 if (check == 0) {
1432                         /*
1433                          *      Stale record - zap it.
1434                          */
1435                         if (inst->config->deletestalesessions == true) {
1436                                 uint32_t framed_addr = 0;
1437                                 char proto = 0;
1438                                 int sess_time = 0;
1439
1440                                 if (row[5])
1441                                         framed_addr = inet_addr(row[5]);
1442                                 if (row[7]){
1443                                         if (strcmp(row[7], "PPP") == 0)
1444                                                 proto = 'P';
1445                                         else if (strcmp(row[7], "SLIP") == 0)
1446                                                 proto = 'S';
1447                                 }
1448                                 if (row[8])
1449                                         sess_time = atoi(row[8]);
1450                                 session_zap(request, nas_addr, nas_port,
1451                                             row[2], row[1], framed_addr,
1452                                             proto, sess_time);
1453                         }
1454                 }
1455                 else if (check == 1) {
1456                         /*
1457                          *      User is still logged in.
1458                          */
1459                         ++request->simul_count;
1460
1461                         /*
1462                          *      Does it look like a MPP attempt?
1463                          */
1464                         if (row[5] && ipno && inet_addr(row[5]) == ipno) {
1465                                 request->simul_mpp = 2;
1466                         } else if (row[6] && call_num && !strncmp(row[6],call_num,16)) {
1467                                 request->simul_mpp = 2;
1468                         }
1469                 } else {
1470                         /*
1471                          *      Failed to check the terminal server for
1472                          *      duplicate logins: return an error.
1473                          */
1474                         REDEBUG("Failed to check the terminal server for user '%s'.", row[2]);
1475
1476                         rcode = RLM_MODULE_FAIL;
1477                         goto finish;
1478                 }
1479         }
1480
1481         finish:
1482
1483         (inst->module->sql_finish_select_query)(handle, inst->config);
1484         sql_release_socket(inst, handle);
1485         talloc_free(expanded);
1486         sql_unset_user(inst, request);
1487
1488         /*
1489          *      The Auth module apparently looks at request->simul_count,
1490          *      not the return value of this module when deciding to deny
1491          *      a call for too many sessions.
1492          */
1493         return rcode;
1494 }
1495 #endif
1496
1497 /*
1498  *      Postauth: Write a record of the authentication attempt
1499  */
1500 static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST * request) {
1501         rlm_sql_t *inst = instance;
1502
1503         if (inst->config->postauth) {
1504                 return acct_redundant(inst, request, inst->config->postauth);
1505         }
1506
1507         return RLM_MODULE_NOOP;
1508 }
1509
1510 /*
1511  *      Execute postauth_query after authentication
1512  */
1513
1514
1515 /* globally exported name */
1516 module_t rlm_sql = {
1517         RLM_MODULE_INIT,
1518         "SQL",
1519         RLM_TYPE_THREAD_SAFE,   /* type: reserved */
1520         sizeof(rlm_sql_t),
1521         module_config,
1522         mod_instantiate,        /* instantiation */
1523         mod_detach,             /* detach */
1524         {
1525                 NULL,                   /* authentication */
1526                 mod_authorize,  /* authorization */
1527                 NULL,                   /* preaccounting */
1528 #ifdef WITH_ACCOUNTING
1529                 mod_accounting, /* accounting */
1530 #else
1531                 NULL,
1532 #endif
1533 #ifdef WITH_SESSION_MGMT
1534                 mod_checksimul, /* checksimul */
1535 #else
1536                 NULL,
1537 #endif
1538                 NULL,                   /* pre-proxy */
1539                 NULL,                   /* post-proxy */
1540                 mod_post_auth   /* post-auth */
1541         },
1542 };