3 * Main SQL module file. Most ICRADIUS code is located in sql.c
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.
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.
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
21 * Copyright 2000,2006 The FreeRADIUS server project
22 * Copyright 2000 Mike Machado <mike@innercite.com>
23 * Copyright 2000 Alan DeKok <aland@ox.org>
26 #include <freeradius-devel/ident.h>
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/modules.h>
31 #include <freeradius-devel/rad_assert.h>
38 static char *allowed_chars = NULL;
40 static const CONF_PARSER module_config[] = {
41 {"driver",PW_TYPE_STRING_PTR,
42 offsetof(SQL_CONFIG,sql_driver), NULL, "mysql"},
43 {"server",PW_TYPE_STRING_PTR,
44 offsetof(SQL_CONFIG,sql_server), NULL, "localhost"},
45 {"port",PW_TYPE_STRING_PTR,
46 offsetof(SQL_CONFIG,sql_port), NULL, ""},
47 {"login", PW_TYPE_STRING_PTR,
48 offsetof(SQL_CONFIG,sql_login), NULL, ""},
49 {"password", PW_TYPE_STRING_PTR,
50 offsetof(SQL_CONFIG,sql_password), NULL, ""},
51 {"radius_db", PW_TYPE_STRING_PTR,
52 offsetof(SQL_CONFIG,sql_db), NULL, "radius"},
53 {"read_groups", PW_TYPE_BOOLEAN,
54 offsetof(SQL_CONFIG,read_groups), NULL, "yes"},
55 {"sqltrace", PW_TYPE_BOOLEAN,
56 offsetof(SQL_CONFIG,sqltrace), NULL, "no"},
57 {"sqltracefile", PW_TYPE_STRING_PTR,
58 offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE},
59 {"readclients", PW_TYPE_BOOLEAN,
60 offsetof(SQL_CONFIG,do_clients), NULL, "no"},
61 {"deletestalesessions", PW_TYPE_BOOLEAN,
62 offsetof(SQL_CONFIG,deletestalesessions), NULL, "yes"},
63 {"num_sql_socks", PW_TYPE_INTEGER,
64 offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"},
65 {"lifetime", PW_TYPE_INTEGER,
66 offsetof(SQL_CONFIG,lifetime), NULL, "0"},
67 {"max_queries", PW_TYPE_INTEGER,
68 offsetof(SQL_CONFIG,max_queries), NULL, "0"},
69 {"sql_user_name", PW_TYPE_STRING_PTR,
70 offsetof(SQL_CONFIG,query_user), NULL, ""},
71 {"default_user_profile", PW_TYPE_STRING_PTR,
72 offsetof(SQL_CONFIG,default_profile), NULL, ""},
73 {"nas_query", PW_TYPE_STRING_PTR,
74 offsetof(SQL_CONFIG,nas_query), NULL, "SELECT id,nasname,shortname,type,secret FROM nas"},
75 {"authorize_check_query", PW_TYPE_STRING_PTR,
76 offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
77 {"authorize_reply_query", PW_TYPE_STRING_PTR,
78 offsetof(SQL_CONFIG,authorize_reply_query), NULL, NULL},
79 {"authorize_group_check_query", PW_TYPE_STRING_PTR,
80 offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
81 {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
82 offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
83 {"accounting_onoff_query", PW_TYPE_STRING_PTR,
84 offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
85 {"accounting_update_query", PW_TYPE_STRING_PTR,
86 offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
87 {"accounting_update_query_alt", PW_TYPE_STRING_PTR,
88 offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""},
89 {"accounting_start_query", PW_TYPE_STRING_PTR,
90 offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
91 {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
92 offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
93 {"accounting_stop_query", PW_TYPE_STRING_PTR,
94 offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
95 {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
96 offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
97 {"group_membership_query", PW_TYPE_STRING_PTR,
98 offsetof(SQL_CONFIG,groupmemb_query), NULL, NULL},
99 {"connect_failure_retry_delay", PW_TYPE_INTEGER,
100 offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
101 {"simul_count_query", PW_TYPE_STRING_PTR,
102 offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
103 {"simul_verify_query", PW_TYPE_STRING_PTR,
104 offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
105 {"postauth_query", PW_TYPE_STRING_PTR,
106 offsetof(SQL_CONFIG,postauth_query), NULL, ""},
107 {"safe-characters", PW_TYPE_STRING_PTR,
108 offsetof(SQL_CONFIG,allowed_chars), NULL,
109 "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
111 {NULL, -1, 0, NULL, NULL}
115 * Fall-Through checking function from rlm_files.c
117 static int fallthrough(VALUE_PAIR *vp)
120 tmp = pairfind(vp, PW_FALL_THROUGH);
122 return tmp ? tmp->vp_integer : 0;
130 static int generate_sql_clients(SQL_INST *inst);
131 static size_t sql_escape_func(char *out, size_t outlen, const char *in);
134 * sql xlat function. Right now only SELECTs are supported. Only
135 * the first element of the SELECT result will be used.
137 static int sql_xlat(void *instance, REQUEST *request,
138 char *fmt, char *out, size_t freespace,
139 UNUSED RADIUS_ESCAPE_STRING func)
143 SQL_INST *inst = instance;
144 char querystr[MAX_QUERY_LEN];
145 char sqlusername[MAX_STRING_LEN];
151 * Add SQL-User-Name attribute just in case it is needed
152 * We could search the string fmt for SQL-User-Name to see if this is
155 sql_set_user(inst, request, sqlusername, NULL);
157 * Do an xlat on the provided string (nice recursive operation).
159 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
160 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
161 inst->config->xlat_name);
165 query_log(request, inst,querystr);
166 sqlsocket = sql_get_socket(inst);
167 if (sqlsocket == NULL)
169 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
170 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
171 inst->config->xlat_name,querystr,
172 (inst->module->sql_error)(sqlsocket, inst->config));
173 sql_release_socket(inst,sqlsocket);
177 ret = rlm_sql_fetch_row(sqlsocket, inst);
180 RDEBUG("SQL query did not succeed");
181 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
182 sql_release_socket(inst,sqlsocket);
186 row = sqlsocket->row;
188 RDEBUG("SQL query did not return any results");
189 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
190 sql_release_socket(inst,sqlsocket);
195 RDEBUG("row[0] returned NULL");
196 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
197 sql_release_socket(inst,sqlsocket);
200 ret = strlen(row[0]);
201 if (ret >= freespace){
202 RDEBUG("Insufficient string space");
203 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
204 sql_release_socket(inst,sqlsocket);
208 strlcpy(out,row[0],freespace);
210 RDEBUG("sql_xlat finished");
212 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
213 sql_release_socket(inst,sqlsocket);
217 static int generate_sql_clients(SQL_INST *inst)
221 char querystr[MAX_QUERY_LEN];
223 char *prefix_ptr = NULL;
227 DEBUG("rlm_sql (%s): Processing generate_sql_clients",
228 inst->config->xlat_name);
230 /* NAS query isn't xlat'ed */
231 strlcpy(querystr, inst->config->nas_query, sizeof(querystr));
232 DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s",
233 inst->config->xlat_name, querystr);
235 sqlsocket = sql_get_socket(inst);
236 if (sqlsocket == NULL)
238 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
239 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
240 inst->config->xlat_name,querystr,
241 (inst->module->sql_error)(sqlsocket, inst->config));
242 sql_release_socket(inst,sqlsocket);
246 while(rlm_sql_fetch_row(sqlsocket, inst) == 0) {
248 row = sqlsocket->row;
252 * The return data for each row MUST be in the following order:
254 * 0. Row ID (currently unused)
255 * 1. Name (or IP address)
259 * 5. Virtual Server (optional)
262 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
266 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
270 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
274 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
278 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
279 row[1],row[2],row[4]);
281 c = rad_malloc(sizeof(*c));
282 memset(c, 0, sizeof(*c));
284 #ifdef WITH_DYNAMIC_CLIENTS
292 prefix_ptr = strchr(row[1], '/');
294 c->prefix = atoi(prefix_ptr + 1);
295 if ((c->prefix < 0) || (c->prefix > 128)) {
296 radlog(L_ERR, "rlm_sql (%s): Invalid Prefix value '%s' for IP.",
297 inst->config->xlat_name, prefix_ptr + 1);
301 /* Replace '/' with '\0' */
306 * Always get the numeric representation of IP
308 if (ip_hton(row[1], AF_UNSPEC, &c->ipaddr) < 0) {
309 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s: %s",
310 inst->config->xlat_name,
311 row[1], fr_strerror());
316 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
317 c->longname = strdup(buffer);
320 if (c->prefix < 0) switch (c->ipaddr.af) {
332 * Other values (secret, shortname, nastype, virtual_server)
334 c->secret = strdup(row[4]);
335 c->shortname = strdup(row[2]);
337 c->nastype = strdup(row[3]);
339 numf = (inst->module->sql_num_fields)(sqlsocket, inst->config);
340 if ((numf > 5) && (row[5] != NULL)) c->server = strdup(row[5]);
342 DEBUG("rlm_sql (%s): Adding client %s (%s, server=%s) to clients list",
343 inst->config->xlat_name,
344 c->longname,c->shortname, c->server ? c->server : "<none>");
345 if (!client_add(NULL, c)) {
346 DEBUG("rlm_sql (%s): Failed to add client %s (%s) to clients list. Maybe there's a duplicate?",
347 inst->config->xlat_name,
348 c->longname,c->shortname);
353 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
354 sql_release_socket(inst, sqlsocket);
361 * Translate the SQL queries.
363 static size_t sql_escape_func(char *out, size_t outlen, const char *in)
369 * Non-printable characters get replaced with their
370 * mime-encoded equivalents.
373 strchr(allowed_chars, *in) == NULL) {
375 * Only 3 or less bytes available.
381 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
390 * Only one byte left.
410 * Set the SQL user name.
412 * We don't call the escape function here. The resulting string
413 * will be escaped later in the queries xlat so we don't need to
414 * escape it twice. (it will make things wrong if we have an
415 * escape candidate character in the username)
417 int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
420 char tmpuser[MAX_STRING_LEN];
423 sqlusername[0]= '\0';
425 /* Remove any user attr we added previously */
426 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
428 if (username != NULL) {
429 strlcpy(tmpuser, username, sizeof(tmpuser));
430 } else if (strlen(inst->config->query_user)) {
431 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
436 strlcpy(sqlusername, tmpuser, MAX_STRING_LEN);
437 RDEBUG2("sql_set_user escaped user --> '%s'", sqlusername);
438 vp = radius_pairmake(request, &request->packet->vps,
439 "SQL-User-Name", NULL, 0);
441 radlog(L_ERR, "%s", fr_strerror());
445 strlcpy(vp->vp_strvalue, tmpuser, sizeof(vp->vp_strvalue));
446 vp->length = strlen(vp->vp_strvalue);
453 static void sql_grouplist_free (SQL_GROUPLIST **group_list)
459 *group_list = (*group_list)->next;
465 static int sql_get_grouplist (SQL_INST *inst, SQLSOCK *sqlsocket, REQUEST *request, SQL_GROUPLIST **group_list)
467 char querystr[MAX_QUERY_LEN];
470 SQL_GROUPLIST *group_list_tmp;
472 /* NOTE: sql_set_user should have been run before calling this function */
474 group_list_tmp = *group_list = NULL;
476 if (!inst->config->groupmemb_query ||
477 (inst->config->groupmemb_query[0] == 0))
480 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, request, sql_escape_func)) {
481 radlog_request(L_ERR, 0, request, "xlat \"%s\" failed.",
482 inst->config->groupmemb_query);
486 if (rlm_sql_select_query(sqlsocket, inst, querystr) < 0) {
487 radlog_request(L_ERR, 0, request,
488 "database query error, %s: %s",
490 (inst->module->sql_error)(sqlsocket,inst->config));
493 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
494 row = sqlsocket->row;
498 RDEBUG("row[0] returned NULL");
499 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
500 sql_grouplist_free(group_list);
503 if (*group_list == NULL) {
504 *group_list = rad_malloc(sizeof(SQL_GROUPLIST));
505 group_list_tmp = *group_list;
507 group_list_tmp->next = rad_malloc(sizeof(SQL_GROUPLIST));
508 group_list_tmp = group_list_tmp->next;
510 group_list_tmp->next = NULL;
511 strlcpy(group_list_tmp->groupname, row[0], MAX_STRING_LEN);
514 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
521 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
522 * with the group memberships reciding in sql
523 * The group membership query should only return one element which is the username. The returned
524 * username will then be checked with the passed check string.
527 static int sql_groupcmp(void *instance, REQUEST *request, VALUE_PAIR *request_vp, VALUE_PAIR *check,
528 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
531 SQL_INST *inst = instance;
532 char sqlusername[MAX_STRING_LEN];
533 SQL_GROUPLIST *group_list, *group_list_tmp;
535 check_pairs = check_pairs;
536 reply_pairs = reply_pairs;
537 request_vp = request_vp;
539 RDEBUG("sql_groupcmp");
540 if (!check || !check->vp_strvalue || !check->length){
541 RDEBUG("sql_groupcmp: Illegal group name");
545 RDEBUG("sql_groupcmp: NULL request");
549 * Set, escape, and check the user attr here
551 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
555 * Get a socket for this lookup
557 sqlsocket = sql_get_socket(inst);
558 if (sqlsocket == NULL) {
559 /* Remove the username we (maybe) added above */
560 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
565 * Get the list of groups this user is a member of
567 if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
568 radlog_request(L_ERR, 0, request,
569 "Error getting group membership");
570 /* Remove the username we (maybe) added above */
571 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
572 sql_release_socket(inst, sqlsocket);
576 for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) {
577 if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){
578 RDEBUG("sql_groupcmp finished: User is a member of group %s",
580 /* Free the grouplist */
581 sql_grouplist_free(&group_list);
582 /* Remove the username we (maybe) added above */
583 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
584 sql_release_socket(inst, sqlsocket);
589 /* Free the grouplist */
590 sql_grouplist_free(&group_list);
591 /* Remove the username we (maybe) added above */
592 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
593 sql_release_socket(inst,sqlsocket);
595 RDEBUG("sql_groupcmp finished: User is NOT a member of group %s",
603 static int rlm_sql_process_groups(SQL_INST *inst, REQUEST *request, SQLSOCK *sqlsocket, int *dofallthrough)
605 VALUE_PAIR *check_tmp = NULL;
606 VALUE_PAIR *reply_tmp = NULL;
607 SQL_GROUPLIST *group_list, *group_list_tmp;
608 VALUE_PAIR *sql_group = NULL;
609 char querystr[MAX_QUERY_LEN];
614 * Get the list of groups this user is a member of
616 if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
617 radlog_request(L_ERR, 0, request, "Error retrieving group list");
621 for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) {
623 * Add the Sql-Group attribute to the request list so we know
624 * which group we're retrieving attributes for
626 sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ);
628 radlog_request(L_ERR, 0, request,
629 "Error creating Sql-Group attribute");
632 pairadd(&request->packet->vps, sql_group);
633 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func)) {
634 radlog_request(L_ERR, 0, request,
635 "Error generating query; rejecting user");
636 /* Remove the grouup we added above */
637 pairdelete(&request->packet->vps, PW_SQL_GROUP);
640 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
642 radlog_request(L_ERR, 0, request, "Error retrieving check pairs for group %s",
643 group_list_tmp->groupname);
644 /* Remove the grouup we added above */
645 pairdelete(&request->packet->vps, PW_SQL_GROUP);
646 pairfree(&check_tmp);
648 } else if (rows > 0) {
650 * Only do this if *some* check pairs were returned
652 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
654 RDEBUG2("User found in group %s",
655 group_list_tmp->groupname);
657 * Now get the reply pairs since the paircompare matched
659 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
660 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
661 /* Remove the grouup we added above */
662 pairdelete(&request->packet->vps, PW_SQL_GROUP);
663 pairfree(&check_tmp);
666 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
667 radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
668 group_list_tmp->groupname);
669 /* Remove the grouup we added above */
670 pairdelete(&request->packet->vps, PW_SQL_GROUP);
671 pairfree(&check_tmp);
672 pairfree(&reply_tmp);
675 *dofallthrough = fallthrough(reply_tmp);
676 pairxlatmove(request, &request->reply->vps, &reply_tmp);
677 pairxlatmove(request, &request->config_items, &check_tmp);
681 * rows == 0. This is like having the username on a line
682 * in the user's file with no check vp's. As such, we treat
683 * it as found and add the reply attributes, so that we
684 * match expected behavior
687 RDEBUG2("User found in group %s",
688 group_list_tmp->groupname);
690 * Now get the reply pairs since the paircompare matched
692 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
693 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
694 /* Remove the grouup we added above */
695 pairdelete(&request->packet->vps, PW_SQL_GROUP);
696 pairfree(&check_tmp);
699 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
700 radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
701 group_list_tmp->groupname);
702 /* Remove the grouup we added above */
703 pairdelete(&request->packet->vps, PW_SQL_GROUP);
704 pairfree(&check_tmp);
705 pairfree(&reply_tmp);
708 *dofallthrough = fallthrough(reply_tmp);
709 pairxlatmove(request, &request->reply->vps, &reply_tmp);
710 pairxlatmove(request, &request->config_items, &check_tmp);
714 * Delete the Sql-Group we added above
715 * And clear out the pairlists
717 pairdelete(&request->packet->vps, PW_SQL_GROUP);
718 pairfree(&check_tmp);
719 pairfree(&reply_tmp);
722 sql_grouplist_free(&group_list);
727 static int rlm_sql_detach(void *instance)
729 SQL_INST *inst = instance;
731 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
740 if (inst->config->xlat_name) {
741 xlat_unregister(inst->config->xlat_name,(RAD_XLAT_FUNC)sql_xlat);
742 free(inst->config->xlat_name);
746 * Free up dynamically allocated string pointers.
748 for (i = 0; module_config[i].name != NULL; i++) {
750 if (module_config[i].type != PW_TYPE_STRING_PTR) {
755 * Treat 'config' as an opaque array of bytes,
756 * and take the offset into it. There's a
757 * (char*) pointer at that offset, and we want
760 p = (char **) (((char *)inst->config) + module_config[i].offset);
761 if (!*p) { /* nothing allocated */
767 allowed_chars = NULL;
775 * FIXME: Call the modules 'destroy' function?
777 lt_dlclose(inst->handle); /* ignore any errors */
784 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
787 const char *xlat_name;
789 inst = rad_malloc(sizeof(SQL_INST));
790 memset(inst, 0, sizeof(SQL_INST));
792 inst->config = rad_malloc(sizeof(SQL_CONFIG));
793 memset(inst->config, 0, sizeof(SQL_CONFIG));
796 * Export these methods, too. This avoids RTDL_GLOBAL.
798 inst->sql_set_user = sql_set_user;
799 inst->sql_get_socket = sql_get_socket;
800 inst->sql_release_socket = sql_release_socket;
801 inst->sql_escape_func = sql_escape_func;
802 inst->sql_query = rlm_sql_query;
803 inst->sql_select_query = rlm_sql_select_query;
804 inst->sql_fetch_row = rlm_sql_fetch_row;
807 * If the configuration parameters can't be parsed, then
810 if (cf_section_parse(conf, inst->config, module_config) < 0) {
811 rlm_sql_detach(inst);
815 xlat_name = cf_section_name2(conf);
816 if (xlat_name == NULL)
817 xlat_name = cf_section_name1(conf);
819 inst->config->xlat_name = strdup(xlat_name);
820 xlat_register(xlat_name, (RAD_XLAT_FUNC)sql_xlat, inst);
823 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
824 radlog(L_ERR, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
825 inst->config->xlat_name, MAX_SQL_SOCKS);
826 rlm_sql_detach(inst);
831 * Sanity check for crazy people.
833 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
834 radlog(L_ERR, "\"%s\" is NOT an SQL driver!",
835 inst->config->sql_driver);
836 rlm_sql_detach(inst);
840 inst->handle = lt_dlopenext(inst->config->sql_driver);
841 if (inst->handle == NULL) {
842 radlog(L_ERR, "Could not link driver %s: %s",
843 inst->config->sql_driver,
845 radlog(L_ERR, "Make sure it (and all its dependent libraries!) are in the search path of your system's ld.");
846 rlm_sql_detach(inst);
850 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
852 radlog(L_ERR, "Could not link symbol %s: %s",
853 inst->config->sql_driver,
855 rlm_sql_detach(inst);
859 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
860 inst->config->xlat_name, inst->config->sql_driver,
862 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
863 inst->config->xlat_name, inst->config->sql_login,
864 inst->config->sql_server, inst->config->sql_port,
865 inst->config->sql_db);
867 if (sql_init_socketpool(inst) < 0) {
868 rlm_sql_detach(inst);
872 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
874 if (inst->config->do_clients){
875 if (generate_sql_clients(inst) == -1){
876 radlog(L_ERR, "Failed to load clients from SQL.");
877 rlm_sql_detach(inst);
881 allowed_chars = inst->config->allowed_chars;
885 return RLM_MODULE_OK;
889 static int rlm_sql_authorize(void *instance, REQUEST * request)
891 VALUE_PAIR *check_tmp = NULL;
892 VALUE_PAIR *reply_tmp = NULL;
893 VALUE_PAIR *user_profile = NULL;
895 int dofallthrough = 1;
898 SQL_INST *inst = instance;
899 char querystr[MAX_QUERY_LEN];
900 char sqlusername[MAX_STRING_LEN];
902 * the profile username is used as the sqlusername during
903 * profile checking so that we don't overwrite the orignal
906 char profileusername[MAX_STRING_LEN];
909 * Set, escape, and check the user attr here
911 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
912 return RLM_MODULE_FAIL;
918 sqlsocket = sql_get_socket(inst);
919 if (sqlsocket == NULL) {
920 /* Remove the username we (maybe) added above */
921 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
922 return RLM_MODULE_FAIL;
927 * After this point, ALL 'return's MUST release the SQL socket!
931 * Alright, start by getting the specific entry for the user
933 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func)) {
934 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
935 sql_release_socket(inst, sqlsocket);
936 /* Remove the username we (maybe) added above */
937 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
938 return RLM_MODULE_FAIL;
940 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
942 radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
943 sql_release_socket(inst, sqlsocket);
944 /* Remove the username we (maybe) added above */
945 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
946 pairfree(&check_tmp);
947 return RLM_MODULE_FAIL;
948 } else if (rows > 0) {
950 * Only do this if *some* check pairs were returned
952 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
954 RDEBUG2("User found in radcheck table");
956 if (inst->config->authorize_reply_query &&
957 *inst->config->authorize_reply_query) {
960 * Now get the reply pairs since the paircompare matched
962 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func)) {
963 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
964 sql_release_socket(inst, sqlsocket);
965 /* Remove the username we (maybe) added above */
966 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
967 pairfree(&check_tmp);
968 return RLM_MODULE_FAIL;
970 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
971 radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
972 sql_release_socket(inst, sqlsocket);
973 /* Remove the username we (maybe) added above */
974 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
975 pairfree(&check_tmp);
976 pairfree(&reply_tmp);
977 return RLM_MODULE_FAIL;
980 if (!inst->config->read_groups)
981 dofallthrough = fallthrough(reply_tmp);
982 pairxlatmove(request, &request->reply->vps, &reply_tmp);
984 pairxlatmove(request, &request->config_items, &check_tmp);
989 * Clear out the pairlists
991 pairfree(&check_tmp);
992 pairfree(&reply_tmp);
995 * dofallthrough is set to 1 by default so that if the user information
996 * is not found, we will still process groups. If the user information,
997 * however, *is* found, Fall-Through must be set in order to process
1000 if (dofallthrough) {
1001 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1003 radlog_request(L_ERR, 0, request, "Error processing groups; rejecting user");
1004 sql_release_socket(inst, sqlsocket);
1005 /* Remove the username we (maybe) added above */
1006 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1007 return RLM_MODULE_FAIL;
1008 } else if (rows > 0) {
1014 * repeat the above process with the default profile or User-Profile
1016 if (dofallthrough) {
1017 int profile_found = 0;
1019 * Check for a default_profile or for a User-Profile.
1021 user_profile = pairfind(request->config_items, PW_USER_PROFILE);
1022 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
1023 char *profile = inst->config->default_profile;
1025 if (user_profile != NULL)
1026 profile = user_profile->vp_strvalue;
1027 if (profile && strlen(profile)){
1028 RDEBUG("Checking profile %s", profile);
1029 if (sql_set_user(inst, request, profileusername, profile) < 0) {
1030 radlog_request(L_ERR, 0, request, "Error setting profile; rejecting user");
1031 sql_release_socket(inst, sqlsocket);
1032 /* Remove the username we (maybe) added above */
1033 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1034 return RLM_MODULE_FAIL;
1041 if (profile_found) {
1042 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1044 radlog_request(L_ERR, 0, request, "Error processing profile groups; rejecting user");
1045 sql_release_socket(inst, sqlsocket);
1046 /* Remove the username we (maybe) added above */
1047 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1048 return RLM_MODULE_FAIL;
1049 } else if (rows > 0) {
1055 /* Remove the username we (maybe) added above */
1056 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1057 sql_release_socket(inst, sqlsocket);
1060 RDEBUG("User %s not found", sqlusername);
1061 return RLM_MODULE_NOTFOUND;
1063 return RLM_MODULE_OK;
1068 * Accounting: save the account data to our sql table
1070 static int rlm_sql_accounting(void *instance, REQUEST * request) {
1072 SQLSOCK *sqlsocket = NULL;
1074 SQL_INST *inst = instance;
1075 int ret = RLM_MODULE_OK;
1076 int numaffected = 0;
1077 int acctstatustype = 0;
1078 char querystr[MAX_QUERY_LEN];
1079 char logstr[MAX_QUERY_LEN];
1080 char sqlusername[MAX_STRING_LEN];
1082 #ifdef CISCO_ACCOUNTING_HACK
1083 int acctsessiontime = 0;
1086 memset(querystr, 0, MAX_QUERY_LEN);
1089 * Find the Acct Status Type
1091 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
1092 acctstatustype = pair->vp_integer;
1094 radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1095 radlog_request(L_ERR, 0, request, "%s", logstr);
1096 return RLM_MODULE_INVALID;
1099 switch (acctstatustype) {
1101 * The Terminal server informed us that it was rebooted
1102 * STOP all records from this NAS
1104 case PW_STATUS_ACCOUNTING_ON:
1105 case PW_STATUS_ACCOUNTING_OFF:
1106 RDEBUG("Received Acct On/Off packet");
1107 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
1108 query_log(request, inst, querystr);
1110 sqlsocket = sql_get_socket(inst);
1111 if (sqlsocket == NULL)
1112 return(RLM_MODULE_FAIL);
1113 if (*querystr) { /* non-empty query */
1114 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1115 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting for Acct On/Off packet - %s",
1116 (inst->module->sql_error)(sqlsocket, inst->config));
1117 ret = RLM_MODULE_FAIL;
1119 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1125 * Got an update accounting packet
1127 case PW_STATUS_ALIVE:
1130 * Set, escape, and check the user attr here
1132 sql_set_user(inst, request, sqlusername, NULL);
1134 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
1135 query_log(request, inst, querystr);
1137 sqlsocket = sql_get_socket(inst);
1138 if (sqlsocket == NULL)
1139 return(RLM_MODULE_FAIL);
1140 if (*querystr) { /* non-empty query */
1141 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1142 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting ALIVE record - %s",
1143 (inst->module->sql_error)(sqlsocket, inst->config));
1144 ret = RLM_MODULE_FAIL;
1147 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1148 if (numaffected < 1) {
1151 * If our update above didn't match anything
1152 * we assume it's because we haven't seen a
1153 * matching Start record. So we have to
1154 * insert this update rather than do an update
1156 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
1157 query_log(request, inst, querystr);
1158 if (*querystr) { /* non-empty query */
1159 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1160 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting ALIVE record - %s",
1161 (inst->module->sql_error)(sqlsocket, inst->config));
1162 ret = RLM_MODULE_FAIL;
1164 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1168 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1173 * Got accounting start packet
1175 case PW_STATUS_START:
1178 * Set, escape, and check the user attr here
1180 sql_set_user(inst, request, sqlusername, NULL);
1182 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
1183 query_log(request, inst, querystr);
1185 sqlsocket = sql_get_socket(inst);
1186 if (sqlsocket == NULL)
1187 return(RLM_MODULE_FAIL);
1188 if (*querystr) { /* non-empty query */
1189 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1190 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting START record - %s",
1191 (inst->module->sql_error)(sqlsocket, inst->config));
1194 * We failed the insert above. It's probably because
1195 * the stop record came before the start. We try
1196 * our alternate query now (typically an UPDATE)
1198 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1199 query_log(request, inst, querystr);
1201 if (*querystr) { /* non-empty query */
1202 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1203 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting START record - %s",
1204 (inst->module->sql_error)(sqlsocket, inst->config));
1205 ret = RLM_MODULE_FAIL;
1207 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1210 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1215 * Got accounting stop packet
1217 case PW_STATUS_STOP:
1220 * Set, escape, and check the user attr here
1222 sql_set_user(inst, request, sqlusername, NULL);
1224 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1225 query_log(request, inst, querystr);
1227 sqlsocket = sql_get_socket(inst);
1228 if (sqlsocket == NULL)
1229 return(RLM_MODULE_FAIL);
1230 if (*querystr) { /* non-empty query */
1231 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1232 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting STOP record - %s",
1233 (inst->module->sql_error)(sqlsocket, inst->config));
1234 ret = RLM_MODULE_FAIL;
1237 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1238 if (numaffected < 1) {
1240 * If our update above didn't match anything
1241 * we assume it's because we haven't seen a
1242 * matching Start record. So we have to
1243 * insert this stop rather than do an update
1245 #ifdef CISCO_ACCOUNTING_HACK
1247 * If stop but zero session length AND no previous
1248 * session found, drop it as in invalid packet
1249 * This is to fix CISCO's aaa from filling our
1250 * table with bogus crap
1252 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
1253 acctsessiontime = pair->vp_integer;
1255 if (acctsessiontime <= 0) {
1256 radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1257 radlog_request(L_ERR, 0, request, "%s", logstr);
1258 sql_release_socket(inst, sqlsocket);
1259 ret = RLM_MODULE_NOOP;
1263 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1264 query_log(request, inst, querystr);
1266 if (*querystr) { /* non-empty query */
1267 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1268 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting STOP record - %s",
1270 (inst->module->sql_error)(sqlsocket, inst->config));
1271 ret = RLM_MODULE_FAIL;
1273 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1277 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1282 * Anything else is ignored.
1285 RDEBUG("Unsupported Acct-Status-Type = %d",
1287 return RLM_MODULE_NOOP;
1292 sql_release_socket(inst, sqlsocket);
1299 * See if a user is already logged in. Sets request->simul_count to the
1300 * current session count for this user.
1302 * Check twice. If on the first pass the user exceeds his
1303 * max. number of logins, do a second pass and validate all
1304 * logins by querying the terminal server (using eg. SNMP).
1307 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1309 SQL_INST *inst = instance;
1311 char querystr[MAX_QUERY_LEN];
1312 char sqlusername[MAX_STRING_LEN];
1315 char *call_num = NULL;
1318 uint32_t nas_addr = 0;
1321 /* If simul_count_query is not defined, we don't do any checking */
1322 if (!inst->config->simul_count_query ||
1323 (inst->config->simul_count_query[0] == 0)) {
1324 return RLM_MODULE_NOOP;
1327 if((request->username == NULL) || (request->username->length == 0)) {
1328 radlog_request(L_ERR, 0, request, "Zero Length username not permitted\n");
1329 return RLM_MODULE_INVALID;
1333 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1334 return RLM_MODULE_FAIL;
1336 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1338 /* initialize the sql socket */
1339 sqlsocket = sql_get_socket(inst);
1340 if(sqlsocket == NULL)
1341 return RLM_MODULE_FAIL;
1343 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1344 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1345 sql_release_socket(inst, sqlsocket);
1346 return RLM_MODULE_FAIL;
1349 ret = rlm_sql_fetch_row(sqlsocket, inst);
1352 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1353 sql_release_socket(inst, sqlsocket);
1354 return RLM_MODULE_FAIL;
1357 row = sqlsocket->row;
1359 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1360 sql_release_socket(inst, sqlsocket);
1361 return RLM_MODULE_FAIL;
1364 request->simul_count = atoi(row[0]);
1365 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1367 if(request->simul_count < request->simul_max) {
1368 sql_release_socket(inst, sqlsocket);
1369 return RLM_MODULE_OK;
1373 * Looks like too many sessions, so let's start verifying
1374 * them, unless told to rely on count query only.
1376 if (!inst->config->simul_verify_query ||
1377 (inst->config->simul_verify_query[0] == '\0')) {
1378 sql_release_socket(inst, sqlsocket);
1379 return RLM_MODULE_OK;
1382 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1383 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1384 radlog_request(L_ERR, 0, request, "Database query error");
1385 sql_release_socket(inst, sqlsocket);
1386 return RLM_MODULE_FAIL;
1390 * Setup some stuff, like for MPP detection.
1392 request->simul_count = 0;
1394 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1395 ipno = vp->vp_ipaddr;
1396 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1397 call_num = vp->vp_strvalue;
1400 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1401 row = sqlsocket->row;
1405 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1406 sql_release_socket(inst, sqlsocket);
1407 RDEBUG("Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1408 return RLM_MODULE_FAIL;
1411 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1412 sql_release_socket(inst, sqlsocket);
1413 RDEBUG("Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1414 return RLM_MODULE_FAIL;
1417 nas_addr = inet_addr(row[3]);
1419 nas_port = atoi(row[4]);
1421 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1425 * Stale record - zap it.
1427 if (inst->config->deletestalesessions == TRUE) {
1428 uint32_t framed_addr = 0;
1433 framed_addr = inet_addr(row[5]);
1435 if (strcmp(row[7], "PPP") == 0)
1437 else if (strcmp(row[7], "SLIP") == 0)
1441 sess_time = atoi(row[8]);
1442 session_zap(request, nas_addr, nas_port,
1443 row[2], row[1], framed_addr,
1447 else if (check == 1) {
1449 * User is still logged in.
1451 ++request->simul_count;
1454 * Does it look like a MPP attempt?
1456 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1457 request->simul_mpp = 2;
1458 else if (row[6] && call_num &&
1459 !strncmp(row[6],call_num,16))
1460 request->simul_mpp = 2;
1464 * Failed to check the terminal server for
1465 * duplicate logins: return an error.
1467 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1468 sql_release_socket(inst, sqlsocket);
1469 radlog_request(L_ERR, 0, request, "Failed to check the terminal server for user '%s'.", row[2]);
1470 return RLM_MODULE_FAIL;
1474 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1475 sql_release_socket(inst, sqlsocket);
1478 * The Auth module apparently looks at request->simul_count,
1479 * not the return value of this module when deciding to deny
1480 * a call for too many sessions.
1482 return RLM_MODULE_OK;
1486 * Execute postauth_query after authentication
1488 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1489 SQLSOCK *sqlsocket = NULL;
1490 SQL_INST *inst = instance;
1491 char querystr[MAX_QUERY_LEN];
1492 char sqlusername[MAX_STRING_LEN];
1494 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1495 return RLM_MODULE_FAIL;
1497 /* If postauth_query is not defined, we stop here */
1498 if (!inst->config->postauth_query ||
1499 (inst->config->postauth_query[0] == '\0'))
1500 return RLM_MODULE_NOOP;
1502 /* Expand variables in the query */
1503 memset(querystr, 0, MAX_QUERY_LEN);
1504 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1505 request, sql_escape_func);
1506 query_log(request, inst, querystr);
1507 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1508 inst->config->xlat_name, querystr);
1510 /* Initialize the sql socket */
1511 sqlsocket = sql_get_socket(inst);
1512 if (sqlsocket == NULL)
1513 return RLM_MODULE_FAIL;
1515 /* Process the query */
1516 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1517 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1518 inst->config->xlat_name,
1519 (inst->module->sql_error)(sqlsocket, inst->config));
1520 sql_release_socket(inst, sqlsocket);
1521 return RLM_MODULE_FAIL;
1523 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1525 sql_release_socket(inst, sqlsocket);
1526 return RLM_MODULE_OK;
1529 /* globally exported name */
1530 module_t rlm_sql = {
1533 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1534 rlm_sql_instantiate, /* instantiation */
1535 rlm_sql_detach, /* detach */
1537 NULL, /* authentication */
1538 rlm_sql_authorize, /* authorization */
1539 NULL, /* preaccounting */
1540 rlm_sql_accounting, /* accounting */
1541 rlm_sql_checksimul, /* checksimul */
1542 NULL, /* pre-proxy */
1543 NULL, /* post-proxy */
1544 rlm_sql_postauth /* post-auth */