4 * Main SQL module file. Most ICRADIUS code is located in sql.c
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * Copyright 2000,2006 The FreeRADIUS server project
23 * Copyright 2000 Mike Machado <mike@innercite.com>
24 * Copyright 2000 Alan DeKok <aland@ox.org>
27 #include <freeradius-devel/ident.h>
30 #include <freeradius-devel/radiusd.h>
31 #include <freeradius-devel/modules.h>
32 #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 {"filename", PW_TYPE_FILENAME, /* for sqlite */
54 offsetof(SQL_CONFIG,sql_file), NULL, NULL},
55 {"read_groups", PW_TYPE_BOOLEAN,
56 offsetof(SQL_CONFIG,read_groups), NULL, "yes"},
57 {"sqltrace", PW_TYPE_BOOLEAN,
58 offsetof(SQL_CONFIG,sqltrace), NULL, "no"},
59 {"sqltracefile", PW_TYPE_STRING_PTR,
60 offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE},
61 {"readclients", PW_TYPE_BOOLEAN,
62 offsetof(SQL_CONFIG,do_clients), NULL, "no"},
63 {"deletestalesessions", PW_TYPE_BOOLEAN,
64 offsetof(SQL_CONFIG,deletestalesessions), NULL, "yes"},
65 {"sql_user_name", PW_TYPE_STRING_PTR,
66 offsetof(SQL_CONFIG,query_user), NULL, ""},
67 {"default_user_profile", PW_TYPE_STRING_PTR,
68 offsetof(SQL_CONFIG,default_profile), NULL, ""},
69 {"nas_query", PW_TYPE_STRING_PTR,
70 offsetof(SQL_CONFIG,nas_query), NULL, "SELECT id,nasname,shortname,type,secret FROM nas"},
71 {"authorize_check_query", PW_TYPE_STRING_PTR,
72 offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
73 {"authorize_reply_query", PW_TYPE_STRING_PTR,
74 offsetof(SQL_CONFIG,authorize_reply_query), NULL, NULL},
75 {"authorize_group_check_query", PW_TYPE_STRING_PTR,
76 offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
77 {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
78 offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
79 #ifdef WITH_ACCOUNTING
80 {"accounting_onoff_query", PW_TYPE_STRING_PTR,
81 offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
82 {"accounting_update_query", PW_TYPE_STRING_PTR,
83 offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
84 {"accounting_update_query_alt", PW_TYPE_STRING_PTR,
85 offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""},
86 {"accounting_start_query", PW_TYPE_STRING_PTR,
87 offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
88 {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
89 offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
90 {"accounting_stop_query", PW_TYPE_STRING_PTR,
91 offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
92 {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
93 offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
95 {"group_membership_query", PW_TYPE_STRING_PTR,
96 offsetof(SQL_CONFIG,groupmemb_query), NULL, NULL},
97 #ifdef WITH_SESSION_MGMT
98 {"simul_count_query", PW_TYPE_STRING_PTR,
99 offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
100 {"simul_verify_query", PW_TYPE_STRING_PTR,
101 offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
103 {"postauth_query", PW_TYPE_STRING_PTR,
104 offsetof(SQL_CONFIG,postauth_query), NULL, ""},
105 {"safe-characters", PW_TYPE_STRING_PTR,
106 offsetof(SQL_CONFIG,allowed_chars), NULL,
107 "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
110 * This only works for a few drivers.
112 {"query_timeout", PW_TYPE_INTEGER,
113 offsetof(SQL_CONFIG,query_timeout), NULL, NULL},
115 {NULL, -1, 0, NULL, NULL}
119 * Fall-Through checking function from rlm_files.c
121 static int fallthrough(VALUE_PAIR *vp)
124 tmp = pairfind(vp, PW_FALL_THROUGH, 0);
126 return tmp ? tmp->vp_integer : 0;
134 static int generate_sql_clients(SQL_INST *inst);
135 static size_t sql_escape_func(char *out, size_t outlen, const char *in);
138 * sql xlat function. Right now only SELECTs are supported. Only
139 * the first element of the SELECT result will be used.
141 * For other statements (insert, update, delete, etc.), the
142 * number of affected rows will be returned.
144 static int sql_xlat(void *instance, REQUEST *request,
145 char *fmt, char *out, size_t freespace,
146 UNUSED RADIUS_ESCAPE_STRING func)
150 SQL_INST *inst = instance;
151 char querystr[MAX_QUERY_LEN];
152 char sqlusername[MAX_STRING_LEN];
158 * Add SQL-User-Name attribute just in case it is needed
159 * We could search the string fmt for SQL-User-Name to see if this is
162 sql_set_user(inst, request, sqlusername, NULL);
164 * Do an xlat on the provided string (nice recursive operation).
166 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
167 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
168 inst->config->xlat_name);
172 query_log(request, inst,querystr);
173 sqlsocket = sql_get_socket(inst);
174 if (sqlsocket == NULL)
178 * If the query starts with any of the following prefixes,
179 * then return the number of rows affected
181 if ((strncasecmp(querystr, "insert", 6) == 0) ||
182 (strncasecmp(querystr, "update", 6) == 0) ||
183 (strncasecmp(querystr, "delete", 6) == 0)) {
185 char buffer[21]; /* 64bit max is 20 decimal chars + null byte */
187 if (rlm_sql_query(&sqlsocket,inst,querystr)) {
188 sql_release_socket(inst,sqlsocket);
193 numaffected = (inst->module->sql_affected_rows)(sqlsocket,
195 if (numaffected < 1) {
196 RDEBUG("rlm_sql (%s): SQL query affected no rows",
197 inst->config->xlat_name);
201 * Don't chop the returned number if freespace is
202 * too small. This hack is necessary because
203 * some implementations of snprintf return the
204 * size of the written data, and others return
205 * the size of the data they *would* have written
206 * if the output buffer was large enough.
208 snprintf(buffer, sizeof(buffer), "%d", numaffected);
209 ret = strlen(buffer);
210 if (ret >= freespace){
211 RDEBUG("rlm_sql (%s): Can't write result, insufficient string space",
212 inst->config->xlat_name);
213 (inst->module->sql_finish_query)(sqlsocket,
215 sql_release_socket(inst,sqlsocket);
219 memcpy(out, buffer, ret + 1); /* we did bounds checking above */
221 (inst->module->sql_finish_query)(sqlsocket, inst->config);
222 sql_release_socket(inst,sqlsocket);
224 } /* else it's a SELECT statement */
226 if (rlm_sql_select_query(&sqlsocket,inst,querystr)){
227 sql_release_socket(inst,sqlsocket);
231 ret = rlm_sql_fetch_row(&sqlsocket, inst);
233 RDEBUG("SQL query did not succeed");
234 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
235 sql_release_socket(inst,sqlsocket);
239 row = sqlsocket->row;
241 RDEBUG("SQL query did not return any results");
242 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
243 sql_release_socket(inst,sqlsocket);
248 RDEBUG("row[0] returned NULL");
249 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
250 sql_release_socket(inst,sqlsocket);
253 ret = strlen(row[0]);
254 if (ret >= freespace){
255 RDEBUG("Insufficient string space");
256 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
257 sql_release_socket(inst,sqlsocket);
261 strlcpy(out,row[0],freespace);
263 RDEBUG("sql_xlat finished");
265 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
266 sql_release_socket(inst,sqlsocket);
270 static int generate_sql_clients(SQL_INST *inst)
274 char querystr[MAX_QUERY_LEN];
276 char *prefix_ptr = NULL;
280 DEBUG("rlm_sql (%s): Processing generate_sql_clients",
281 inst->config->xlat_name);
283 /* NAS query isn't xlat'ed */
284 strlcpy(querystr, inst->config->nas_query, sizeof(querystr));
285 DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s",
286 inst->config->xlat_name, querystr);
288 sqlsocket = sql_get_socket(inst);
289 if (sqlsocket == NULL)
291 if (rlm_sql_select_query(&sqlsocket,inst,querystr)){
295 while(rlm_sql_fetch_row(&sqlsocket, inst) == 0) {
297 row = sqlsocket->row;
301 * The return data for each row MUST be in the following order:
303 * 0. Row ID (currently unused)
304 * 1. Name (or IP address)
308 * 5. Virtual Server (optional)
311 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
315 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
319 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
323 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
327 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
328 row[1],row[2],row[4]);
330 c = rad_malloc(sizeof(*c));
331 memset(c, 0, sizeof(*c));
333 #ifdef WITH_DYNAMIC_CLIENTS
341 prefix_ptr = strchr(row[1], '/');
343 c->prefix = atoi(prefix_ptr + 1);
344 if ((c->prefix < 0) || (c->prefix > 128)) {
345 radlog(L_ERR, "rlm_sql (%s): Invalid Prefix value '%s' for IP.",
346 inst->config->xlat_name, prefix_ptr + 1);
350 /* Replace '/' with '\0' */
355 * Always get the numeric representation of IP
357 if (ip_hton(row[1], AF_UNSPEC, &c->ipaddr) < 0) {
358 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s: %s",
359 inst->config->xlat_name,
360 row[1], fr_strerror());
365 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
366 c->longname = strdup(buffer);
369 if (c->prefix < 0) switch (c->ipaddr.af) {
381 * Other values (secret, shortname, nastype, virtual_server)
383 c->secret = strdup(row[4]);
384 c->shortname = strdup(row[2]);
386 c->nastype = strdup(row[3]);
388 numf = (inst->module->sql_num_fields)(sqlsocket, inst->config);
389 if ((numf > 5) && (row[5] != NULL) && *row[5]) c->server = strdup(row[5]);
391 DEBUG("rlm_sql (%s): Adding client %s (%s, server=%s) to clients list",
392 inst->config->xlat_name,
393 c->longname,c->shortname, c->server ? c->server : "<none>");
394 if (!client_add(NULL, c)) {
395 sql_release_socket(inst, sqlsocket);
396 DEBUG("rlm_sql (%s): Failed to add client %s (%s) to clients list. Maybe there's a duplicate?",
397 inst->config->xlat_name,
398 c->longname,c->shortname);
403 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
404 sql_release_socket(inst, sqlsocket);
411 * Translate the SQL queries.
413 static size_t sql_escape_func(char *out, size_t outlen, const char *in)
419 * Non-printable characters get replaced with their
420 * mime-encoded equivalents.
423 strchr(allowed_chars, *in) == NULL) {
425 * Only 3 or less bytes available.
431 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
440 * Only one byte left.
460 * Set the SQL user name.
462 * We don't call the escape function here. The resulting string
463 * will be escaped later in the queries xlat so we don't need to
464 * escape it twice. (it will make things wrong if we have an
465 * escape candidate character in the username)
467 int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
470 char tmpuser[MAX_STRING_LEN];
473 sqlusername[0]= '\0';
475 /* Remove any user attr we added previously */
476 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
478 if (username != NULL) {
479 strlcpy(tmpuser, username, sizeof(tmpuser));
480 } else if (strlen(inst->config->query_user)) {
481 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
486 strlcpy(sqlusername, tmpuser, MAX_STRING_LEN);
487 RDEBUG2("sql_set_user escaped user --> '%s'", sqlusername);
488 vp = radius_pairmake(request, &request->packet->vps,
489 "SQL-User-Name", NULL, 0);
491 radlog(L_ERR, "%s", fr_strerror());
495 strlcpy(vp->vp_strvalue, tmpuser, sizeof(vp->vp_strvalue));
496 vp->length = strlen(vp->vp_strvalue);
503 static void sql_grouplist_free (SQL_GROUPLIST **group_list)
509 *group_list = (*group_list)->next;
515 static int sql_get_grouplist (SQL_INST *inst, SQLSOCK *sqlsocket, REQUEST *request, SQL_GROUPLIST **group_list)
517 char querystr[MAX_QUERY_LEN];
520 SQL_GROUPLIST *group_list_tmp;
522 /* NOTE: sql_set_user should have been run before calling this function */
524 group_list_tmp = *group_list = NULL;
526 if (!inst->config->groupmemb_query ||
527 (inst->config->groupmemb_query[0] == 0))
530 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, request, sql_escape_func)) {
531 radlog_request(L_ERR, 0, request, "xlat \"%s\" failed.",
532 inst->config->groupmemb_query);
536 if (rlm_sql_select_query(&sqlsocket, inst, querystr) < 0) {
539 while (rlm_sql_fetch_row(&sqlsocket, inst) == 0) {
540 row = sqlsocket->row;
544 RDEBUG("row[0] returned NULL");
545 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
546 sql_grouplist_free(group_list);
549 if (*group_list == NULL) {
550 *group_list = rad_malloc(sizeof(SQL_GROUPLIST));
551 group_list_tmp = *group_list;
553 rad_assert(group_list_tmp != NULL);
554 group_list_tmp->next = rad_malloc(sizeof(SQL_GROUPLIST));
555 group_list_tmp = group_list_tmp->next;
557 group_list_tmp->next = NULL;
558 strlcpy(group_list_tmp->groupname, row[0], MAX_STRING_LEN);
561 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
568 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
569 * with the group memberships reciding in sql
570 * The group membership query should only return one element which is the username. The returned
571 * username will then be checked with the passed check string.
574 static int sql_groupcmp(void *instance, REQUEST *request, VALUE_PAIR *request_vp, VALUE_PAIR *check,
575 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
578 SQL_INST *inst = instance;
579 char sqlusername[MAX_STRING_LEN];
580 SQL_GROUPLIST *group_list, *group_list_tmp;
582 check_pairs = check_pairs;
583 reply_pairs = reply_pairs;
584 request_vp = request_vp;
586 RDEBUG("sql_groupcmp");
587 if (!check || !check->vp_strvalue || !check->length){
588 RDEBUG("sql_groupcmp: Illegal group name");
592 RDEBUG("sql_groupcmp: NULL request");
596 * Set, escape, and check the user attr here
598 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
602 * Get a socket for this lookup
604 sqlsocket = sql_get_socket(inst);
605 if (sqlsocket == NULL) {
606 /* Remove the username we (maybe) added above */
607 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
612 * Get the list of groups this user is a member of
614 if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
615 radlog_request(L_ERR, 0, request,
616 "Error getting group membership");
617 /* Remove the username we (maybe) added above */
618 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
619 sql_release_socket(inst, sqlsocket);
623 for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) {
624 if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){
625 RDEBUG("sql_groupcmp finished: User is a member of group %s",
627 /* Free the grouplist */
628 sql_grouplist_free(&group_list);
629 /* Remove the username we (maybe) added above */
630 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
631 sql_release_socket(inst, sqlsocket);
636 /* Free the grouplist */
637 sql_grouplist_free(&group_list);
638 /* Remove the username we (maybe) added above */
639 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
640 sql_release_socket(inst,sqlsocket);
642 RDEBUG("sql_groupcmp finished: User is NOT a member of group %s",
650 static int rlm_sql_process_groups(SQL_INST *inst, REQUEST *request, SQLSOCK *sqlsocket, int *dofallthrough)
652 VALUE_PAIR *check_tmp = NULL;
653 VALUE_PAIR *reply_tmp = NULL;
654 SQL_GROUPLIST *group_list, *group_list_tmp;
655 VALUE_PAIR *sql_group = NULL;
656 char querystr[MAX_QUERY_LEN];
661 * Get the list of groups this user is a member of
663 if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
664 radlog_request(L_ERR, 0, request, "Error retrieving group list");
668 for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) {
670 * Add the Sql-Group attribute to the request list so we know
671 * which group we're retrieving attributes for
673 sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ);
675 radlog_request(L_ERR, 0, request,
676 "Error creating Sql-Group attribute");
677 sql_grouplist_free(&group_list);
680 pairadd(&request->packet->vps, sql_group);
681 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func)) {
682 radlog_request(L_ERR, 0, request,
683 "Error generating query; rejecting user");
684 /* Remove the grouup we added above */
685 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
686 sql_grouplist_free(&group_list);
689 rows = sql_getvpdata(inst, &sqlsocket, &check_tmp, querystr);
691 radlog_request(L_ERR, 0, request, "Error retrieving check pairs for group %s",
692 group_list_tmp->groupname);
693 /* Remove the grouup we added above */
694 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
695 pairfree(&check_tmp);
696 sql_grouplist_free(&group_list);
698 } else if (rows > 0) {
700 * Only do this if *some* check pairs were returned
702 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
704 RDEBUG2("User found in group %s",
705 group_list_tmp->groupname);
707 * Now get the reply pairs since the paircompare matched
709 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
710 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
711 /* Remove the grouup we added above */
712 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
713 pairfree(&check_tmp);
714 sql_grouplist_free(&group_list);
717 if (sql_getvpdata(inst, &sqlsocket, &reply_tmp, querystr) < 0) {
718 radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
719 group_list_tmp->groupname);
720 /* Remove the grouup we added above */
721 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
722 pairfree(&check_tmp);
723 pairfree(&reply_tmp);
724 sql_grouplist_free(&group_list);
727 *dofallthrough = fallthrough(reply_tmp);
728 pairxlatmove(request, &request->reply->vps, &reply_tmp);
729 pairxlatmove(request, &request->config_items, &check_tmp);
733 * rows == 0. This is like having the username on a line
734 * in the user's file with no check vp's. As such, we treat
735 * it as found and add the reply attributes, so that we
736 * match expected behavior
739 RDEBUG2("User found in group %s",
740 group_list_tmp->groupname);
742 * Now get the reply pairs since the paircompare matched
744 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
745 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
746 /* Remove the grouup we added above */
747 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
748 pairfree(&check_tmp);
749 sql_grouplist_free(&group_list);
752 if (sql_getvpdata(inst, &sqlsocket, &reply_tmp, querystr) < 0) {
753 radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
754 group_list_tmp->groupname);
755 /* Remove the grouup we added above */
756 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
757 pairfree(&check_tmp);
758 pairfree(&reply_tmp);
759 sql_grouplist_free(&group_list);
762 *dofallthrough = fallthrough(reply_tmp);
763 pairxlatmove(request, &request->reply->vps, &reply_tmp);
764 pairxlatmove(request, &request->config_items, &check_tmp);
768 * Delete the Sql-Group we added above
769 * And clear out the pairlists
771 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
772 pairfree(&check_tmp);
773 pairfree(&reply_tmp);
776 sql_grouplist_free(&group_list);
781 static int rlm_sql_detach(void *instance)
783 SQL_INST *inst = instance;
785 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
790 if (inst->pool) sql_poolfree(inst);
792 if (inst->config->xlat_name) {
793 xlat_unregister(inst->config->xlat_name,(RAD_XLAT_FUNC)sql_xlat, instance);
794 free(inst->config->xlat_name);
798 * Free up dynamically allocated string pointers.
800 for (i = 0; module_config[i].name != NULL; i++) {
802 if (module_config[i].type != PW_TYPE_STRING_PTR) {
807 * Treat 'config' as an opaque array of bytes,
808 * and take the offset into it. There's a
809 * (char*) pointer at that offset, and we want
812 p = (char **) (((char *)inst->config) + module_config[i].offset);
813 if (!*p) { /* nothing allocated */
820 * Catch multiple instances of the module.
822 if (allowed_chars == inst->config->allowed_chars) {
823 allowed_chars = NULL;
832 * FIXME: Call the modules 'destroy' function?
834 lt_dlclose(inst->handle); /* ignore any errors */
841 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
844 const char *xlat_name;
846 inst = rad_malloc(sizeof(SQL_INST));
847 memset(inst, 0, sizeof(SQL_INST));
849 inst->config = rad_malloc(sizeof(SQL_CONFIG));
850 memset(inst->config, 0, sizeof(SQL_CONFIG));
854 * Export these methods, too. This avoids RTDL_GLOBAL.
856 inst->sql_set_user = sql_set_user;
857 inst->sql_get_socket = sql_get_socket;
858 inst->sql_release_socket = sql_release_socket;
859 inst->sql_escape_func = sql_escape_func;
860 inst->sql_query = rlm_sql_query;
861 inst->sql_select_query = rlm_sql_select_query;
862 inst->sql_fetch_row = rlm_sql_fetch_row;
865 * If the configuration parameters can't be parsed, then
868 if (cf_section_parse(conf, inst->config, module_config) < 0) {
869 rlm_sql_detach(inst);
873 xlat_name = cf_section_name2(conf);
874 if (xlat_name == NULL) {
875 xlat_name = cf_section_name1(conf);
882 * Allocate room for <instance>-SQL-Group
884 group_name = rad_malloc((strlen(xlat_name) + 1 + 11) * sizeof(char));
885 sprintf(group_name,"%s-SQL-Group",xlat_name);
886 DEBUG("rlm_sql Creating new attribute %s",group_name);
888 memset(&flags, 0, sizeof(flags));
889 dict_addattr(group_name, 0, PW_TYPE_STRING, -1, flags);
890 dattr = dict_attrbyname(group_name);
892 radlog(L_ERR, "rlm_sql: Failed to create attribute %s",group_name);
894 free(inst); /* FIXME: detach */
898 if (inst->config->groupmemb_query &&
899 inst->config->groupmemb_query[0]) {
900 DEBUG("rlm_sql: Registering sql_groupcmp for %s",group_name);
901 paircompare_register(dattr->attr, PW_USER_NAME, sql_groupcmp, inst);
907 inst->config->xlat_name = strdup(xlat_name);
908 xlat_register(xlat_name, (RAD_XLAT_FUNC)sql_xlat, inst);
912 * Sanity check for crazy people.
914 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
915 radlog(L_ERR, "\"%s\" is NOT an SQL driver!",
916 inst->config->sql_driver);
917 rlm_sql_detach(inst);
921 inst->handle = lt_dlopenext(inst->config->sql_driver);
922 if (inst->handle == NULL) {
923 radlog(L_ERR, "Could not link driver %s: %s",
924 inst->config->sql_driver,
926 radlog(L_ERR, "Make sure it (and all its dependent libraries!) are in the search path of your system's ld.");
927 rlm_sql_detach(inst);
931 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
933 radlog(L_ERR, "Could not link symbol %s: %s",
934 inst->config->sql_driver,
936 rlm_sql_detach(inst);
940 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
941 inst->config->xlat_name, inst->config->sql_driver,
943 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
944 inst->config->xlat_name, inst->config->sql_login,
945 inst->config->sql_server, inst->config->sql_port,
946 inst->config->sql_db);
948 if (sql_init_socketpool(inst) < 0) {
949 rlm_sql_detach(inst);
953 if (inst->config->groupmemb_query &&
954 inst->config->groupmemb_query[0]) {
955 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
958 if (inst->config->do_clients){
959 if (generate_sql_clients(inst) == -1){
960 radlog(L_ERR, "Failed to load clients from SQL.");
961 rlm_sql_detach(inst);
965 allowed_chars = inst->config->allowed_chars;
969 return RLM_MODULE_OK;
973 static int rlm_sql_authorize(void *instance, REQUEST * request)
975 VALUE_PAIR *check_tmp = NULL;
976 VALUE_PAIR *reply_tmp = NULL;
977 VALUE_PAIR *user_profile = NULL;
979 int dofallthrough = 1;
982 SQL_INST *inst = instance;
983 char querystr[MAX_QUERY_LEN];
984 char sqlusername[MAX_STRING_LEN];
986 * the profile username is used as the sqlusername during
987 * profile checking so that we don't overwrite the orignal
990 char profileusername[MAX_STRING_LEN];
993 * Set, escape, and check the user attr here
995 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
996 return RLM_MODULE_FAIL;
1002 sqlsocket = sql_get_socket(inst);
1003 if (sqlsocket == NULL) {
1004 /* Remove the username we (maybe) added above */
1005 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1006 return RLM_MODULE_FAIL;
1011 * After this point, ALL 'return's MUST release the SQL socket!
1015 * Alright, start by getting the specific entry for the user
1017 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func)) {
1018 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
1019 sql_release_socket(inst, sqlsocket);
1020 /* Remove the username we (maybe) added above */
1021 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1022 return RLM_MODULE_FAIL;
1024 rows = sql_getvpdata(inst, &sqlsocket, &check_tmp, querystr);
1026 radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
1027 sql_release_socket(inst, sqlsocket);
1028 /* Remove the username we (maybe) added above */
1029 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1030 pairfree(&check_tmp);
1031 return RLM_MODULE_FAIL;
1032 } else if (rows > 0) {
1034 * Only do this if *some* check pairs were returned
1036 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
1038 RDEBUG2("User found in radcheck table");
1040 if (inst->config->authorize_reply_query &&
1041 *inst->config->authorize_reply_query) {
1044 * Now get the reply pairs since the paircompare matched
1046 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func)) {
1047 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
1048 sql_release_socket(inst, sqlsocket);
1049 /* Remove the username we (maybe) added above */
1050 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1051 pairfree(&check_tmp);
1052 return RLM_MODULE_FAIL;
1054 if (sql_getvpdata(inst, &sqlsocket, &reply_tmp, querystr) < 0) {
1055 radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
1056 sql_release_socket(inst, sqlsocket);
1057 /* Remove the username we (maybe) added above */
1058 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1059 pairfree(&check_tmp);
1060 pairfree(&reply_tmp);
1061 return RLM_MODULE_FAIL;
1064 if (!inst->config->read_groups)
1065 dofallthrough = fallthrough(reply_tmp);
1066 pairxlatmove(request, &request->reply->vps, &reply_tmp);
1068 pairxlatmove(request, &request->config_items, &check_tmp);
1073 * Clear out the pairlists
1075 pairfree(&check_tmp);
1076 pairfree(&reply_tmp);
1079 * dofallthrough is set to 1 by default so that if the user information
1080 * is not found, we will still process groups. If the user information,
1081 * however, *is* found, Fall-Through must be set in order to process
1082 * the groups as well
1084 if (dofallthrough) {
1085 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1087 radlog_request(L_ERR, 0, request, "Error processing groups; rejecting user");
1088 sql_release_socket(inst, sqlsocket);
1089 /* Remove the username we (maybe) added above */
1090 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1091 return RLM_MODULE_FAIL;
1092 } else if (rows > 0) {
1098 * repeat the above process with the default profile or User-Profile
1100 if (dofallthrough) {
1101 int profile_found = 0;
1103 * Check for a default_profile or for a User-Profile.
1105 user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0);
1106 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
1107 char *profile = inst->config->default_profile;
1109 if (user_profile != NULL)
1110 profile = user_profile->vp_strvalue;
1111 if (profile && strlen(profile)){
1112 RDEBUG("Checking profile %s", profile);
1113 if (sql_set_user(inst, request, profileusername, profile) < 0) {
1114 radlog_request(L_ERR, 0, request, "Error setting profile; rejecting user");
1115 sql_release_socket(inst, sqlsocket);
1116 /* Remove the username we (maybe) added above */
1117 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1118 return RLM_MODULE_FAIL;
1125 if (profile_found) {
1126 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1128 radlog_request(L_ERR, 0, request, "Error processing profile groups; rejecting user");
1129 sql_release_socket(inst, sqlsocket);
1130 /* Remove the username we (maybe) added above */
1131 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1132 return RLM_MODULE_FAIL;
1133 } else if (rows > 0) {
1139 /* Remove the username we (maybe) added above */
1140 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1141 sql_release_socket(inst, sqlsocket);
1144 RDEBUG("User %s not found", sqlusername);
1145 return RLM_MODULE_NOTFOUND;
1147 return RLM_MODULE_OK;
1151 #ifdef WITH_ACCOUNTING
1153 * Accounting: save the account data to our sql table
1155 static int rlm_sql_accounting(void *instance, REQUEST * request) {
1157 SQLSOCK *sqlsocket = NULL;
1159 SQL_INST *inst = instance;
1161 int ret = RLM_MODULE_OK;
1162 int numaffected = 0;
1163 int acctstatustype = 0;
1164 char querystr[MAX_QUERY_LEN];
1165 char logstr[MAX_QUERY_LEN];
1166 char sqlusername[MAX_STRING_LEN];
1168 #ifdef CISCO_ACCOUNTING_HACK
1169 int acctsessiontime = 0;
1172 memset(querystr, 0, MAX_QUERY_LEN);
1175 * Find the Acct Status Type
1177 pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0);
1179 radius_xlat(logstr, sizeof(logstr), "Packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1180 radlog_request(L_ERR, 0, request, "%s", logstr);
1182 return RLM_MODULE_INVALID;
1185 acctstatustype = pair->vp_integer;
1188 * Bulk update queries
1190 if (acctstatustype == PW_STATUS_ACCOUNTING_ON ||
1191 acctstatustype == PW_STATUS_ACCOUNTING_OFF) {
1193 * The NAS informed us that it was rebooted, close all sessions
1194 * associated with it.
1196 radius_xlat(logstr, sizeof(logstr), "Bulk closing sessions using 'accounting_onoff_query' - [nas '%{NAS-IP-Address}']", request, NULL);
1197 radlog_request(L_DBG, 0, request, "%s", logstr);
1199 radius_xlat(querystr, sizeof(querystr),
1200 inst->config->accounting_onoff_query, request,
1205 query_log(request, inst, querystr);
1207 sqlsocket = sql_get_socket(inst);
1208 if (sqlsocket == NULL)
1209 return RLM_MODULE_FAIL;
1211 sql_ret = rlm_sql_query(&sqlsocket, inst, querystr);
1213 return RLM_MODULE_FAIL;
1215 numaffected = (inst->module->sql_affected_rows)(sqlsocket,
1217 radlog_request(L_DBG, 0, request, "Closed %i sessions", numaffected);
1218 if (numaffected < 1)
1219 ret = RLM_MODULE_NOOP;
1225 * Session specific queries
1227 sql_set_user(inst, request, sqlusername, NULL);
1230 * Setup the primary query for a given packet type
1232 switch (acctstatustype) {
1233 case PW_STATUS_ALIVE:
1234 radlog_request(L_DBG, 0, request, "Updating session using 'accounting_update_query'");
1236 radius_xlat(querystr, sizeof(querystr),
1237 inst->config->accounting_update_query, request,
1244 case PW_STATUS_START:
1245 radlog_request(L_DBG, 0, request, "Creating session using 'accounting_start_query'");
1247 radius_xlat(querystr, sizeof(querystr),
1248 inst->config->accounting_start_query, request,
1255 case PW_STATUS_STOP:
1256 radlog_request(L_DBG, 0, request, "Closing session using 'accounting_stop_query'");
1258 radius_xlat(querystr, sizeof(querystr),
1259 inst->config->accounting_stop_query, request,
1267 RDEBUG("Unsupported Acct-Status-Type value (%d)", acctstatustype);
1269 return RLM_MODULE_NOOP;
1273 * Log the query. Maybe we should do this only if were not using the
1276 query_log(request, inst, querystr);
1278 sqlsocket = sql_get_socket(inst);
1279 if (sqlsocket == NULL)
1280 return RLM_MODULE_FAIL;
1282 sql_ret = rlm_sql_query(&sqlsocket, inst, querystr);
1283 if (sql_ret == SQL_DOWN)
1284 return RLM_MODULE_FAIL;
1286 rad_assert(sqlsocket);
1289 * Assume all other errors are incidental, and just meant our operation
1290 * failed and its not a client or SQL syntax error.
1293 numaffected = (inst->module->sql_affected_rows)(sqlsocket,
1295 if (numaffected > 0)
1300 * The primary query failed this may be because:
1301 * update - The session hasn't be created with a start record -
1302 * so create the session.
1303 * start - The session was created from a stop or interim update
1304 * packet so the insert failed (presumably on unique index
1305 * constraint) - so update the session.
1306 * stop - The session does not already exist. We never received
1307 * a Start or Interim-Update packet for this session -
1308 * so create the session.
1310 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1313 * Setup the alternate query for a given packet type
1315 switch (acctstatustype) {
1316 case PW_STATUS_ALIVE:
1317 radlog_request(L_DBG, 0, request, "Failed updating session, creating session using 'accounting_update_query_alt'");
1319 radius_xlat(querystr, sizeof(querystr),
1320 inst->config->accounting_update_query_alt,
1321 request, sql_escape_func);
1327 case PW_STATUS_START:
1328 radlog_request(L_DBG, 0, request, "Failed creating session, updating session using 'accounting_start_query_alt'");
1330 radius_xlat(querystr, sizeof(querystr),
1331 inst->config->accounting_start_query_alt,
1332 request, sql_escape_func);
1338 case PW_STATUS_STOP:
1339 #ifdef CISCO_ACCOUNTING_HACK
1341 * If stop but zero session length AND no previous session found,
1342 * drop it as in invalid packet This is to fix CISCO's aaa from
1343 * filling our table with bogus crap
1345 pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME, 0);
1347 acctsessiontime = pair->vp_integer;
1349 if (acctsessiontime <= 0) {
1350 radius_xlat(logstr, sizeof(logstr), "Stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1351 radlog_request(L_DBG, 0, request, "%s", logstr);
1357 radlog_request(L_DBG, 0, request, "Failed closing session, creating closed session using 'accounting_stop_query_alt'");
1359 radius_xlat(querystr, sizeof(querystr),
1360 inst->config->accounting_stop_query_alt,
1361 request, sql_escape_func);
1370 query_log(request, inst, querystr);
1372 if (rlm_sql_query(&sqlsocket, inst, querystr) == SQL_DOWN)
1373 return RLM_MODULE_FAIL;
1375 rad_assert(sqlsocket);
1378 * This time we *know* this was some sort of error...
1381 ret = RLM_MODULE_FAIL;
1385 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1386 if (numaffected < 1)
1387 ret = RLM_MODULE_NOOP;
1390 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1393 sql_release_socket(inst, sqlsocket);
1398 radlog_request(L_DBG, 0, request, "Ignoring null query");
1399 return RLM_MODULE_NOOP;
1404 #ifdef WITH_SESSION_MGMT
1406 * See if a user is already logged in. Sets request->simul_count to the
1407 * current session count for this user.
1409 * Check twice. If on the first pass the user exceeds his
1410 * max. number of logins, do a second pass and validate all
1411 * logins by querying the terminal server (using eg. SNMP).
1414 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1416 SQL_INST *inst = instance;
1418 char querystr[MAX_QUERY_LEN];
1419 char sqlusername[MAX_STRING_LEN];
1422 char *call_num = NULL;
1425 uint32_t nas_addr = 0;
1428 /* If simul_count_query is not defined, we don't do any checking */
1429 if (!inst->config->simul_count_query ||
1430 (inst->config->simul_count_query[0] == 0)) {
1431 return RLM_MODULE_NOOP;
1434 if((request->username == NULL) || (request->username->length == 0)) {
1435 radlog_request(L_ERR, 0, request,
1436 "Zero Length username not permitted\n");
1437 return RLM_MODULE_INVALID;
1441 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1442 return RLM_MODULE_FAIL;
1444 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1446 /* initialize the sql socket */
1447 sqlsocket = sql_get_socket(inst);
1448 if(sqlsocket == NULL)
1449 return RLM_MODULE_FAIL;
1451 if(rlm_sql_select_query(&sqlsocket, inst, querystr)) {
1452 sql_release_socket(inst, sqlsocket);
1453 return RLM_MODULE_FAIL;
1456 ret = rlm_sql_fetch_row(&sqlsocket, inst);
1458 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1459 sql_release_socket(inst, sqlsocket);
1460 return RLM_MODULE_FAIL;
1463 row = sqlsocket->row;
1465 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1466 sql_release_socket(inst, sqlsocket);
1467 return RLM_MODULE_FAIL;
1470 request->simul_count = atoi(row[0]);
1471 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1473 if(request->simul_count < request->simul_max) {
1474 sql_release_socket(inst, sqlsocket);
1475 return RLM_MODULE_OK;
1479 * Looks like too many sessions, so let's start verifying
1480 * them, unless told to rely on count query only.
1482 if (!inst->config->simul_verify_query ||
1483 (inst->config->simul_verify_query[0] == '\0')) {
1484 sql_release_socket(inst, sqlsocket);
1485 return RLM_MODULE_OK;
1488 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1489 if(rlm_sql_select_query(&sqlsocket, inst, querystr)) {
1490 sql_release_socket(inst, sqlsocket);
1491 return RLM_MODULE_FAIL;
1495 * Setup some stuff, like for MPP detection.
1497 request->simul_count = 0;
1499 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0)) != NULL)
1500 ipno = vp->vp_ipaddr;
1501 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0)) != NULL)
1502 call_num = vp->vp_strvalue;
1505 while (rlm_sql_fetch_row(&sqlsocket, inst) == 0) {
1506 row = sqlsocket->row;
1510 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1511 sql_release_socket(inst, sqlsocket);
1512 RDEBUG("Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1513 return RLM_MODULE_FAIL;
1516 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1517 sql_release_socket(inst, sqlsocket);
1518 RDEBUG("Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1519 return RLM_MODULE_FAIL;
1522 nas_addr = inet_addr(row[3]);
1524 nas_port = atoi(row[4]);
1526 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1530 * Stale record - zap it.
1532 if (inst->config->deletestalesessions == TRUE) {
1533 uint32_t framed_addr = 0;
1538 framed_addr = inet_addr(row[5]);
1540 if (strcmp(row[7], "PPP") == 0)
1542 else if (strcmp(row[7], "SLIP") == 0)
1546 sess_time = atoi(row[8]);
1547 session_zap(request, nas_addr, nas_port,
1548 row[2], row[1], framed_addr,
1552 else if (check == 1) {
1554 * User is still logged in.
1556 ++request->simul_count;
1559 * Does it look like a MPP attempt?
1561 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1562 request->simul_mpp = 2;
1563 else if (row[6] && call_num &&
1564 !strncmp(row[6],call_num,16))
1565 request->simul_mpp = 2;
1569 * Failed to check the terminal server for
1570 * duplicate logins: return an error.
1572 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1573 sql_release_socket(inst, sqlsocket);
1574 radlog_request(L_ERR, 0, request, "Failed to check the terminal server for user '%s'.", row[2]);
1575 return RLM_MODULE_FAIL;
1579 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1580 sql_release_socket(inst, sqlsocket);
1583 * The Auth module apparently looks at request->simul_count,
1584 * not the return value of this module when deciding to deny
1585 * a call for too many sessions.
1587 return RLM_MODULE_OK;
1592 * Execute postauth_query after authentication
1594 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1595 SQLSOCK *sqlsocket = NULL;
1596 SQL_INST *inst = instance;
1597 char querystr[MAX_QUERY_LEN];
1598 char sqlusername[MAX_STRING_LEN];
1600 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1601 return RLM_MODULE_FAIL;
1603 /* If postauth_query is not defined, we stop here */
1604 if (!inst->config->postauth_query ||
1605 (inst->config->postauth_query[0] == '\0'))
1606 return RLM_MODULE_NOOP;
1608 /* Expand variables in the query */
1609 memset(querystr, 0, MAX_QUERY_LEN);
1610 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1611 request, sql_escape_func);
1612 query_log(request, inst, querystr);
1613 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1614 inst->config->xlat_name, querystr);
1616 /* Initialize the sql socket */
1617 sqlsocket = sql_get_socket(inst);
1618 if (sqlsocket == NULL)
1619 return RLM_MODULE_FAIL;
1621 /* Process the query */
1622 if (rlm_sql_query(&sqlsocket, inst, querystr)) {
1623 sql_release_socket(inst, sqlsocket);
1625 return RLM_MODULE_FAIL;
1627 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1629 sql_release_socket(inst, sqlsocket);
1630 return RLM_MODULE_OK;
1633 /* globally exported name */
1634 module_t rlm_sql = {
1637 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1638 rlm_sql_instantiate, /* instantiation */
1639 rlm_sql_detach, /* detach */
1641 NULL, /* authentication */
1642 rlm_sql_authorize, /* authorization */
1643 NULL, /* preaccounting */
1644 #ifdef WITH_ACCOUNTING
1645 rlm_sql_accounting, /* accounting */
1649 #ifdef WITH_SESSION_MGMT
1650 rlm_sql_checksimul, /* checksimul */
1654 NULL, /* pre-proxy */
1655 NULL, /* post-proxy */
1656 rlm_sql_postauth /* post-auth */