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>
39 static char *allowed_chars = NULL;
41 static const CONF_PARSER module_config[] = {
42 {"driver",PW_TYPE_STRING_PTR,
43 offsetof(SQL_CONFIG,sql_driver), NULL, "mysql"},
44 {"server",PW_TYPE_STRING_PTR,
45 offsetof(SQL_CONFIG,sql_server), NULL, "localhost"},
46 {"port",PW_TYPE_STRING_PTR,
47 offsetof(SQL_CONFIG,sql_port), NULL, ""},
48 {"login", PW_TYPE_STRING_PTR,
49 offsetof(SQL_CONFIG,sql_login), NULL, ""},
50 {"password", PW_TYPE_STRING_PTR,
51 offsetof(SQL_CONFIG,sql_password), NULL, ""},
52 {"radius_db", PW_TYPE_STRING_PTR,
53 offsetof(SQL_CONFIG,sql_db), NULL, "radius"},
54 {"read_groups", PW_TYPE_BOOLEAN,
55 offsetof(SQL_CONFIG,read_groups), NULL, "yes"},
56 {"sqltrace", PW_TYPE_BOOLEAN,
57 offsetof(SQL_CONFIG,sqltrace), NULL, "no"},
58 {"sqltracefile", PW_TYPE_STRING_PTR,
59 offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE},
60 {"readclients", PW_TYPE_BOOLEAN,
61 offsetof(SQL_CONFIG,do_clients), NULL, "no"},
62 {"deletestalesessions", PW_TYPE_BOOLEAN,
63 offsetof(SQL_CONFIG,deletestalesessions), NULL, "yes"},
64 {"num_sql_socks", PW_TYPE_INTEGER,
65 offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"},
66 {"sql_user_name", PW_TYPE_STRING_PTR,
67 offsetof(SQL_CONFIG,query_user), NULL, ""},
68 {"default_user_profile", PW_TYPE_STRING_PTR,
69 offsetof(SQL_CONFIG,default_profile), NULL, ""},
70 {"nas_query", PW_TYPE_STRING_PTR,
71 offsetof(SQL_CONFIG,nas_query), NULL, "SELECT id,nasname,shortname,type,secret FROM nas"},
72 {"authorize_check_query", PW_TYPE_STRING_PTR,
73 offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
74 {"authorize_reply_query", PW_TYPE_STRING_PTR,
75 offsetof(SQL_CONFIG,authorize_reply_query), NULL, ""},
76 {"authorize_group_check_query", PW_TYPE_STRING_PTR,
77 offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
78 {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
79 offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
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, ""},
94 {"group_membership_query", PW_TYPE_STRING_PTR,
95 offsetof(SQL_CONFIG,groupmemb_query), NULL, ""},
96 {"connect_failure_retry_delay", PW_TYPE_INTEGER,
97 offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
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, ""},
102 {"postauth_query", PW_TYPE_STRING_PTR,
103 offsetof(SQL_CONFIG,postauth_query), NULL, ""},
104 {"safe-characters", PW_TYPE_STRING_PTR,
105 offsetof(SQL_CONFIG,allowed_chars), NULL,
106 "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
108 {NULL, -1, 0, NULL, NULL}
112 * Fall-Through checking function from rlm_files.c
114 static int fallthrough(VALUE_PAIR *vp)
117 tmp = pairfind(vp, PW_FALL_THROUGH);
119 return tmp ? tmp->vp_integer : 0;
127 static int generate_sql_clients(SQL_INST *inst);
128 static int sql_escape_func(char *out, int outlen, const char *in);
131 * sql xlat function. Right now only SELECTs are supported. Only
132 * the first element of the SELECT result will be used.
134 static int sql_xlat(void *instance, REQUEST *request,
135 char *fmt, char *out, size_t freespace,
136 UNUSED RADIUS_ESCAPE_STRING func)
140 SQL_INST *inst = instance;
141 char querystr[MAX_QUERY_LEN];
142 char sqlusername[MAX_STRING_LEN];
145 DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name);
147 * Add SQL-User-Name attribute just in case it is needed
148 * We could search the string fmt for SQL-User-Name to see if this is
151 sql_set_user(inst, request, sqlusername, NULL);
153 * Do an xlat on the provided string (nice recursive operation).
155 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
156 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
157 inst->config->xlat_name);
161 query_log(request, inst,querystr);
162 sqlsocket = sql_get_socket(inst);
163 if (sqlsocket == NULL)
165 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
166 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
167 inst->config->xlat_name,querystr,
168 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
169 sql_release_socket(inst,sqlsocket);
173 ret = rlm_sql_fetch_row(sqlsocket, inst);
176 DEBUG("rlm_sql (%s): SQL query did not succeed",
177 inst->config->xlat_name);
178 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
179 sql_release_socket(inst,sqlsocket);
183 row = sqlsocket->row;
185 DEBUG("rlm_sql (%s): SQL query did not return any results",
186 inst->config->xlat_name);
187 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
188 sql_release_socket(inst,sqlsocket);
193 DEBUG("rlm_sql (%s): row[0] returned NULL",
194 inst->config->xlat_name);
195 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
196 sql_release_socket(inst,sqlsocket);
199 ret = strlen(row[0]);
200 if (ret >= freespace){
201 DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space",
202 inst->config->xlat_name);
203 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
204 sql_release_socket(inst,sqlsocket);
208 strlcpy(out,row[0],freespace);
210 DEBUG("rlm_sql (%s): - sql_xlat finished",
211 inst->config->xlat_name);
213 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
214 sql_release_socket(inst,sqlsocket);
218 static int generate_sql_clients(SQL_INST *inst)
222 char querystr[MAX_QUERY_LEN];
224 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 (char *)(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)
261 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
265 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
269 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
273 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
277 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
278 row[1],row[2],row[4]);
280 c = rad_malloc(sizeof(*c));
281 memset(c, 0, sizeof(*c));
287 prefix_ptr = strchr(row[1], '/');
289 c->prefix = atoi(prefix_ptr + 1);
290 if ((c->prefix < 0) || (c->prefix > 128)) {
291 radlog(L_ERR, "rlm_sql (%s): Invalid Prefix value '%s' for IP.",
292 inst->config->xlat_name, prefix_ptr + 1);
296 /* Replace '/' with '\0' */
301 * Always get the numeric representation of IP
303 if (ip_hton(row[1], AF_UNSPEC, &c->ipaddr) < 0) {
304 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s: %s",
305 inst->config->xlat_name,
306 row[1], librad_errstr);
311 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
312 c->longname = strdup(buffer);
315 if (c->prefix < 0) switch (c->ipaddr.af) {
327 * Other values (secret, shortname, nastype)
329 c->secret = (u_char *)strdup(row[4]);
330 c->shortname = strdup(row[2]);
332 c->nastype = strdup(row[3]);
334 DEBUG("rlm_sql (%s): Adding client %s (%s) to clients list",
335 inst->config->xlat_name,
336 c->longname,c->shortname);
337 if (!client_add(mainconfig.clients, c)) {
342 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
343 sql_release_socket(inst, sqlsocket);
350 * Translate the SQL queries.
352 static int sql_escape_func(char *out, int outlen, const char *in)
358 * Non-printable characters get replaced with their
359 * mime-encoded equivalents.
362 strchr(allowed_chars, *in) == NULL) {
364 * Only 3 or less bytes available.
370 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
379 * Only one byte left.
399 * Set the SQL user name.
401 * We don't call the escape function here. The resulting string
402 * will be escaped later in the queries xlat so we don't need to
403 * escape it twice. (it will make things wrong if we have an
404 * escape candidate character in the username)
406 int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
409 char tmpuser[MAX_STRING_LEN];
412 sqlusername[0]= '\0';
414 /* Remove any user attr we added previously */
415 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
417 if (username != NULL) {
418 strlcpy(tmpuser, username, sizeof(tmpuser));
419 } else if (strlen(inst->config->query_user)) {
420 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
425 strlcpy(sqlusername, tmpuser, MAX_STRING_LEN);
426 DEBUG2("rlm_sql (%s): sql_set_user escaped user --> '%s'",
427 inst->config->xlat_name, sqlusername);
428 vp = pairmake("SQL-User-Name", sqlusername, 0);
430 radlog(L_ERR, "%s", librad_errstr);
434 pairadd(&request->packet->vps, vp);
440 static void sql_grouplist_free (SQL_GROUPLIST **group_list)
446 *group_list = (*group_list)->next;
452 static int sql_get_grouplist (SQL_INST *inst, SQLSOCK *sqlsocket, REQUEST *request, SQL_GROUPLIST **group_list)
454 char querystr[MAX_QUERY_LEN];
457 SQL_GROUPLIST *group_list_tmp;
459 /* NOTE: sql_set_user should have been run before calling this function */
461 group_list_tmp = *group_list = NULL;
463 if (inst->config->groupmemb_query[0] == 0)
466 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, request, sql_escape_func)) {
467 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
468 inst->config->xlat_name);
472 if (rlm_sql_select_query(sqlsocket, inst, querystr) < 0) {
473 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
474 inst->config->xlat_name,querystr,
475 (char *)(inst->module->sql_error)(sqlsocket,inst->config));
478 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
479 row = sqlsocket->row;
483 DEBUG("rlm_sql (%s): row[0] returned NULL",
484 inst->config->xlat_name);
485 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
486 sql_grouplist_free(group_list);
489 if (*group_list == NULL) {
490 *group_list = rad_malloc(sizeof(SQL_GROUPLIST));
491 group_list_tmp = *group_list;
493 group_list_tmp->next = rad_malloc(sizeof(SQL_GROUPLIST));
494 group_list_tmp = group_list_tmp->next;
496 group_list_tmp->next = NULL;
497 strlcpy(group_list_tmp->groupname, row[0], MAX_STRING_LEN);
500 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
507 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
508 * with the group memberships reciding in sql
509 * The group membership query should only return one element which is the username. The returned
510 * username will then be checked with the passed check string.
513 static int sql_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
514 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
517 SQL_INST *inst = instance;
518 char sqlusername[MAX_STRING_LEN];
519 SQL_GROUPLIST *group_list, *group_list_tmp;
521 check_pairs = check_pairs;
522 reply_pairs = reply_pairs;
525 DEBUG("rlm_sql (%s): - sql_groupcmp", inst->config->xlat_name);
526 if (!check || !check->vp_strvalue || !check->length){
527 DEBUG("rlm_sql (%s): sql_groupcmp: Illegal group name",
528 inst->config->xlat_name);
532 DEBUG("rlm_sql (%s): sql_groupcmp: NULL request",
533 inst->config->xlat_name);
537 * Set, escape, and check the user attr here
539 if (sql_set_user(inst, req, sqlusername, NULL) < 0)
543 * Get a socket for this lookup
545 sqlsocket = sql_get_socket(inst);
546 if (sqlsocket == NULL) {
547 /* Remove the username we (maybe) added above */
548 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
553 * Get the list of groups this user is a member of
555 if (sql_get_grouplist(inst, sqlsocket, req, &group_list)) {
556 radlog(L_ERR, "rlm_sql (%s): Error getting group membership",
557 inst->config->xlat_name);
558 /* Remove the username we (maybe) added above */
559 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
560 sql_release_socket(inst, sqlsocket);
564 for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) {
565 if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){
566 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User is a member of group %s",
567 inst->config->xlat_name,
568 (char *)check->vp_strvalue);
569 /* Free the grouplist */
570 sql_grouplist_free(&group_list);
571 /* Remove the username we (maybe) added above */
572 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
573 sql_release_socket(inst, sqlsocket);
578 /* Free the grouplist */
579 sql_grouplist_free(&group_list);
580 /* Remove the username we (maybe) added above */
581 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
582 sql_release_socket(inst,sqlsocket);
584 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User is NOT a member of group %s",
585 inst->config->xlat_name, (char *)check->vp_strvalue);
592 static int rlm_sql_process_groups(SQL_INST *inst, REQUEST *request, SQLSOCK *sqlsocket, int *dofallthrough)
594 VALUE_PAIR *check_tmp = NULL;
595 VALUE_PAIR *reply_tmp = NULL;
596 SQL_GROUPLIST *group_list, *group_list_tmp;
597 VALUE_PAIR *sql_group = NULL;
598 char querystr[MAX_QUERY_LEN];
603 * Get the list of groups this user is a member of
605 if (sql_get_grouplist(inst, sqlsocket, request, &group_list)) {
606 radlog(L_ERR, "rlm_sql (%s): Error retrieving group list",
607 inst->config->xlat_name);
611 for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) {
613 * Add the Sql-Group attribute to the request list so we know
614 * which group we're retrieving attributes for
616 sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ);
618 radlog(L_ERR, "rlm_sql (%s): Error creating Sql-Group attribute",
619 inst->config->xlat_name);
622 pairadd(&request->packet->vps, sql_group);
623 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func)) {
624 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
625 inst->config->xlat_name);
626 /* Remove the grouup we added above */
627 pairdelete(&request->packet->vps, PW_SQL_GROUP);
630 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
632 radlog(L_ERR, "rlm_sql (%s): Error retrieving check pairs for group %s",
633 inst->config->xlat_name, group_list_tmp->groupname);
634 /* Remove the grouup we added above */
635 pairdelete(&request->packet->vps, PW_SQL_GROUP);
636 pairfree(&check_tmp);
638 } else if (rows > 0) {
640 * Only do this if *some* check pairs were returned
642 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
644 DEBUG2("rlm_sql (%s): User found in group %s",
645 inst->config->xlat_name, group_list_tmp->groupname);
647 * Now get the reply pairs since the paircompare matched
649 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
650 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
651 inst->config->xlat_name);
652 /* Remove the grouup we added above */
653 pairdelete(&request->packet->vps, PW_SQL_GROUP);
654 pairfree(&check_tmp);
657 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
658 radlog(L_ERR, "rlm_sql (%s): Error retrieving reply pairs for group %s",
659 inst->config->xlat_name, group_list_tmp->groupname);
660 /* Remove the grouup we added above */
661 pairdelete(&request->packet->vps, PW_SQL_GROUP);
662 pairfree(&check_tmp);
663 pairfree(&reply_tmp);
666 *dofallthrough = fallthrough(reply_tmp);
667 pairxlatmove(request, &request->reply->vps, &reply_tmp);
668 pairxlatmove(request, &request->config_items, &check_tmp);
672 * rows == 0. This is like having the username on a line
673 * in the user's file with no check vp's. As such, we treat
674 * it as found and add the reply attributes, so that we
675 * match expected behavior
678 DEBUG2("rlm_sql (%s): User found in group %s",
679 inst->config->xlat_name, group_list_tmp->groupname);
681 * Now get the reply pairs since the paircompare matched
683 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
684 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
685 inst->config->xlat_name);
686 /* Remove the grouup we added above */
687 pairdelete(&request->packet->vps, PW_SQL_GROUP);
688 pairfree(&check_tmp);
691 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
692 radlog(L_ERR, "rlm_sql (%s): Error retrieving reply pairs for group %s",
693 inst->config->xlat_name, group_list_tmp->groupname);
694 /* Remove the grouup we added above */
695 pairdelete(&request->packet->vps, PW_SQL_GROUP);
696 pairfree(&check_tmp);
697 pairfree(&reply_tmp);
700 *dofallthrough = fallthrough(reply_tmp);
701 pairxlatmove(request, &request->reply->vps, &reply_tmp);
702 pairxlatmove(request, &request->config_items, &check_tmp);
706 * Delete the Sql-Group we added above
707 * And clear out the pairlists
709 pairdelete(&request->packet->vps, PW_SQL_GROUP);
710 pairfree(&check_tmp);
711 pairfree(&reply_tmp);
714 sql_grouplist_free(&group_list);
719 static int rlm_sql_detach(void *instance)
721 SQL_INST *inst = instance;
723 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
732 if (inst->config->xlat_name) {
733 xlat_unregister(inst->config->xlat_name,(RAD_XLAT_FUNC)sql_xlat);
734 free(inst->config->xlat_name);
738 * Free up dynamically allocated string pointers.
740 for (i = 0; module_config[i].name != NULL; i++) {
742 if (module_config[i].type != PW_TYPE_STRING_PTR) {
747 * Treat 'config' as an opaque array of bytes,
748 * and take the offset into it. There's a
749 * (char*) pointer at that offset, and we want
752 p = (char **) (((char *)inst->config) + module_config[i].offset);
753 if (!*p) { /* nothing allocated */
759 allowed_chars = NULL;
767 * FIXME: Call the modules 'destroy' function?
769 lt_dlclose(inst->handle); /* ignore any errors */
776 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
779 const char *xlat_name;
781 inst = rad_malloc(sizeof(SQL_INST));
782 memset(inst, 0, sizeof(SQL_INST));
784 inst->config = rad_malloc(sizeof(SQL_CONFIG));
785 memset(inst->config, 0, sizeof(SQL_CONFIG));
788 * If the configuration parameters can't be parsed, then
791 if (cf_section_parse(conf, inst->config, module_config) < 0) {
792 rlm_sql_detach(inst);
796 xlat_name = cf_section_name2(conf);
797 if (xlat_name == NULL)
798 xlat_name = cf_section_name1(conf);
800 inst->config->xlat_name = strdup(xlat_name);
801 xlat_register(xlat_name, (RAD_XLAT_FUNC)sql_xlat, inst);
804 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
805 radlog(L_ERR | L_CONS, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
806 inst->config->xlat_name, MAX_SQL_SOCKS);
807 rlm_sql_detach(inst);
812 * Sanity check for crazy people.
814 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
815 radlog(L_ERR, "rlm_sql (%s): \"%s\" is NOT an SQL driver!",
816 inst->config->xlat_name, inst->config->sql_driver);
817 rlm_sql_detach(inst);
821 inst->handle = lt_dlopenext(inst->config->sql_driver);
822 if (inst->handle == NULL) {
823 radlog(L_ERR, "rlm_sql (%s): Could not link driver %s: %s",
824 inst->config->xlat_name, inst->config->sql_driver,
826 radlog(L_ERR, "rlm_sql (%s): Make sure it (and all its dependent libraries!) are in the search path of your system's ld.",
827 inst->config->xlat_name);
828 rlm_sql_detach(inst);
832 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
834 radlog(L_ERR, "rlm_sql (%s): Could not link symbol %s: %s",
835 inst->config->xlat_name, inst->config->sql_driver,
837 rlm_sql_detach(inst);
841 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
842 inst->config->xlat_name, inst->config->sql_driver,
844 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
845 inst->config->xlat_name, inst->config->sql_login,
846 inst->config->sql_server, inst->config->sql_port,
847 inst->config->sql_db);
849 if (sql_init_socketpool(inst) < 0) {
850 rlm_sql_detach(inst);
853 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
855 if (inst->config->do_clients){
856 if (generate_sql_clients(inst) == -1){
857 radlog(L_ERR, "rlm_sql (%s): generate_sql_clients() returned error",inst->config->xlat_name);
858 rlm_sql_detach(inst);
862 allowed_chars = inst->config->allowed_chars;
866 return RLM_MODULE_OK;
870 static int rlm_sql_authorize(void *instance, REQUEST * request)
872 VALUE_PAIR *check_tmp = NULL;
873 VALUE_PAIR *reply_tmp = NULL;
874 VALUE_PAIR *user_profile = NULL;
876 int dofallthrough = 1;
879 SQL_INST *inst = instance;
880 char querystr[MAX_QUERY_LEN];
881 char sqlusername[MAX_STRING_LEN];
883 * the profile username is used as the sqlusername during
884 * profile checking so that we don't overwrite the orignal
887 char profileusername[MAX_STRING_LEN];
890 * Set, escape, and check the user attr here
892 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
893 return RLM_MODULE_FAIL;
899 sqlsocket = sql_get_socket(inst);
900 if (sqlsocket == NULL) {
901 /* Remove the username we (maybe) added above */
902 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
903 return RLM_MODULE_FAIL;
908 * After this point, ALL 'return's MUST release the SQL socket!
912 * Alright, start by getting the specific entry for the user
914 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func)) {
915 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
916 inst->config->xlat_name);
917 sql_release_socket(inst, sqlsocket);
918 /* Remove the username we (maybe) added above */
919 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
920 return RLM_MODULE_FAIL;
922 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
924 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
925 inst->config->xlat_name);
926 sql_release_socket(inst, sqlsocket);
927 /* Remove the username we (maybe) added above */
928 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
929 pairfree(&check_tmp);
930 return RLM_MODULE_FAIL;
931 } else if (rows > 0) {
933 * Only do this if *some* check pairs were returned
935 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
937 DEBUG2("rlm_sql (%s): User found in radcheck table", inst->config->xlat_name);
939 * Now get the reply pairs since the paircompare matched
941 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func)) {
942 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
943 inst->config->xlat_name);
944 sql_release_socket(inst, sqlsocket);
945 /* Remove the username we (maybe) added above */
946 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
947 pairfree(&check_tmp);
948 return RLM_MODULE_FAIL;
950 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
951 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
952 inst->config->xlat_name);
953 sql_release_socket(inst, sqlsocket);
954 /* Remove the username we (maybe) added above */
955 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
956 pairfree(&check_tmp);
957 pairfree(&reply_tmp);
958 return RLM_MODULE_FAIL;
960 if (!inst->config->read_groups)
961 dofallthrough = fallthrough(reply_tmp);
962 pairxlatmove(request, &request->reply->vps, &reply_tmp);
963 pairxlatmove(request, &request->config_items, &check_tmp);
968 * Clear out the pairlists
970 pairfree(&check_tmp);
971 pairfree(&reply_tmp);
974 * dofallthrough is set to 1 by default so that if the user information
975 * is not found, we will still process groups. If the user information,
976 * however, *is* found, Fall-Through must be set in order to process
980 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
982 radlog(L_ERR, "rlm_sql (%s): Error processing groups; rejecting user",
983 inst->config->xlat_name);
984 sql_release_socket(inst, sqlsocket);
985 /* Remove the username we (maybe) added above */
986 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
987 return RLM_MODULE_FAIL;
988 } else if (rows > 0) {
994 * repeat the above process with the default profile or User-Profile
997 int profile_found = 0;
999 * Check for a default_profile or for a User-Profile.
1001 user_profile = pairfind(request->config_items, PW_USER_PROFILE);
1002 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
1003 char *profile = inst->config->default_profile;
1005 if (user_profile != NULL)
1006 profile = user_profile->vp_strvalue;
1007 if (profile && strlen(profile)){
1008 radlog(L_DBG, "rlm_sql (%s): Checking profile %s",
1009 inst->config->xlat_name, profile);
1010 if (sql_set_user(inst, request, profileusername, profile) < 0) {
1011 radlog(L_ERR, "rlm_sql (%s): Error setting profile; rejecting user",
1012 inst->config->xlat_name);
1013 sql_release_socket(inst, sqlsocket);
1014 /* Remove the username we (maybe) added above */
1015 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1016 return RLM_MODULE_FAIL;
1023 if (profile_found) {
1024 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1026 radlog(L_ERR, "rlm_sql (%s): Error processing profile groups; rejecting user",
1027 inst->config->xlat_name);
1028 sql_release_socket(inst, sqlsocket);
1029 /* Remove the username we (maybe) added above */
1030 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1031 return RLM_MODULE_FAIL;
1032 } else if (rows > 0) {
1038 /* Remove the username we (maybe) added above */
1039 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1040 sql_release_socket(inst, sqlsocket);
1043 radlog(L_DBG, "rlm_sql (%s): User %s not found",
1044 inst->config->xlat_name, sqlusername);
1045 return RLM_MODULE_NOTFOUND;
1047 return RLM_MODULE_OK;
1052 * Accounting: save the account data to our sql table
1054 static int rlm_sql_accounting(void *instance, REQUEST * request) {
1056 SQLSOCK *sqlsocket = NULL;
1058 SQL_INST *inst = instance;
1059 int ret = RLM_MODULE_OK;
1060 int numaffected = 0;
1061 int acctstatustype = 0;
1062 char querystr[MAX_QUERY_LEN];
1063 char logstr[MAX_QUERY_LEN];
1064 char sqlusername[MAX_STRING_LEN];
1066 #ifdef CISCO_ACCOUNTING_HACK
1067 int acctsessiontime = 0;
1070 memset(querystr, 0, MAX_QUERY_LEN);
1073 * Find the Acct Status Type
1075 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
1076 acctstatustype = pair->vp_integer;
1078 radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1079 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s",
1080 inst->config->xlat_name, logstr);
1081 return RLM_MODULE_INVALID;
1084 switch (acctstatustype) {
1086 * The Terminal server informed us that it was rebooted
1087 * STOP all records from this NAS
1089 case PW_STATUS_ACCOUNTING_ON:
1090 case PW_STATUS_ACCOUNTING_OFF:
1091 radlog(L_INFO, "rlm_sql (%s): received Acct On/Off packet", inst->config->xlat_name);
1092 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
1093 query_log(request, inst, querystr);
1095 sqlsocket = sql_get_socket(inst);
1096 if (sqlsocket == NULL)
1097 return(RLM_MODULE_FAIL);
1098 if (*querystr) { /* non-empty query */
1099 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1100 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting for Acct On/Off packet - %s",
1101 inst->config->xlat_name,
1102 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1103 ret = RLM_MODULE_FAIL;
1105 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1111 * Got an update accounting packet
1113 case PW_STATUS_ALIVE:
1116 * Set, escape, and check the user attr here
1118 sql_set_user(inst, request, sqlusername, NULL);
1120 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
1121 query_log(request, inst, querystr);
1123 sqlsocket = sql_get_socket(inst);
1124 if (sqlsocket == NULL)
1125 return(RLM_MODULE_FAIL);
1126 if (*querystr) { /* non-empty query */
1127 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1128 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting ALIVE record - %s",
1129 inst->config->xlat_name,
1130 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1131 ret = RLM_MODULE_FAIL;
1134 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1135 if (numaffected < 1) {
1138 * If our update above didn't match anything
1139 * we assume it's because we haven't seen a
1140 * matching Start record. So we have to
1141 * insert this update rather than do an update
1143 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
1144 query_log(request, inst, querystr);
1145 if (*querystr) { /* non-empty query */
1146 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1147 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting ALIVE record - %s",
1148 inst->config->xlat_name,
1149 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1150 ret = RLM_MODULE_FAIL;
1152 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1156 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1161 * Got accounting start packet
1163 case PW_STATUS_START:
1166 * Set, escape, and check the user attr here
1168 sql_set_user(inst, request, sqlusername, NULL);
1170 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
1171 query_log(request, inst, querystr);
1173 sqlsocket = sql_get_socket(inst);
1174 if (sqlsocket == NULL)
1175 return(RLM_MODULE_FAIL);
1176 if (*querystr) { /* non-empty query */
1177 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1178 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting START record - %s",
1179 inst->config->xlat_name,
1180 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1183 * We failed the insert above. It's probably because
1184 * the stop record came before the start. We try
1185 * our alternate query now (typically an UPDATE)
1187 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1188 query_log(request, inst, querystr);
1190 if (*querystr) { /* non-empty query */
1191 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1192 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting START record - %s",
1193 inst->config->xlat_name,
1194 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1195 ret = RLM_MODULE_FAIL;
1197 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1200 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1205 * Got accounting stop packet
1207 case PW_STATUS_STOP:
1210 * Set, escape, and check the user attr here
1212 sql_set_user(inst, request, sqlusername, NULL);
1214 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1215 query_log(request, inst, querystr);
1217 sqlsocket = sql_get_socket(inst);
1218 if (sqlsocket == NULL)
1219 return(RLM_MODULE_FAIL);
1220 if (*querystr) { /* non-empty query */
1221 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1222 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting STOP record - %s",
1223 inst->config->xlat_name,
1224 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1225 ret = RLM_MODULE_FAIL;
1228 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1229 if (numaffected < 1) {
1231 * If our update above didn't match anything
1232 * we assume it's because we haven't seen a
1233 * matching Start record. So we have to
1234 * insert this stop rather than do an update
1236 #ifdef CISCO_ACCOUNTING_HACK
1238 * If stop but zero session length AND no previous
1239 * session found, drop it as in invalid packet
1240 * This is to fix CISCO's aaa from filling our
1241 * table with bogus crap
1243 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
1244 acctsessiontime = pair->vp_integer;
1246 if (acctsessiontime <= 0) {
1247 radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1248 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s", inst->config->xlat_name, logstr);
1249 sql_release_socket(inst, sqlsocket);
1250 ret = RLM_MODULE_NOOP;
1254 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1255 query_log(request, inst, querystr);
1257 if (*querystr) { /* non-empty query */
1258 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1259 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting STOP record - %s",
1260 inst->config->xlat_name,
1261 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1262 ret = RLM_MODULE_FAIL;
1264 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1268 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1273 * Anything else is ignored.
1276 radlog(L_INFO, "rlm_sql (%s): Unsupported Acct-Status-Type = %d", inst->config->xlat_name, acctstatustype);
1277 return RLM_MODULE_NOOP;
1282 sql_release_socket(inst, sqlsocket);
1289 * See if a user is already logged in. Sets request->simul_count to the
1290 * current session count for this user.
1292 * Check twice. If on the first pass the user exceeds his
1293 * max. number of logins, do a second pass and validate all
1294 * logins by querying the terminal server (using eg. SNMP).
1297 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1299 SQL_INST *inst = instance;
1301 char querystr[MAX_QUERY_LEN];
1302 char sqlusername[MAX_STRING_LEN];
1305 char *call_num = NULL;
1308 uint32_t nas_addr = 0;
1311 /* If simul_count_query is not defined, we don't do any checking */
1312 if (inst->config->simul_count_query[0] == 0) {
1313 return RLM_MODULE_NOOP;
1316 if((request->username == NULL) || (request->username->length == 0)) {
1317 radlog(L_ERR, "rlm_sql (%s): Zero Length username not permitted\n", inst->config->xlat_name);
1318 return RLM_MODULE_INVALID;
1322 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1323 return RLM_MODULE_FAIL;
1325 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1327 /* initialize the sql socket */
1328 sqlsocket = sql_get_socket(inst);
1329 if(sqlsocket == NULL)
1330 return RLM_MODULE_FAIL;
1332 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1333 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1334 sql_release_socket(inst, sqlsocket);
1335 return RLM_MODULE_FAIL;
1338 ret = rlm_sql_fetch_row(sqlsocket, inst);
1341 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1342 sql_release_socket(inst, sqlsocket);
1343 return RLM_MODULE_FAIL;
1346 row = sqlsocket->row;
1348 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1349 sql_release_socket(inst, sqlsocket);
1350 return RLM_MODULE_FAIL;
1353 request->simul_count = atoi(row[0]);
1354 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1356 if(request->simul_count < request->simul_max) {
1357 sql_release_socket(inst, sqlsocket);
1358 return RLM_MODULE_OK;
1362 * Looks like too many sessions, so let's start verifying
1363 * them, unless told to rely on count query only.
1365 if (inst->config->simul_verify_query[0] == '\0') {
1366 sql_release_socket(inst, sqlsocket);
1367 return RLM_MODULE_OK;
1370 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1371 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1372 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Database query error", inst->config->xlat_name);
1373 sql_release_socket(inst, sqlsocket);
1374 return RLM_MODULE_FAIL;
1378 * Setup some stuff, like for MPP detection.
1380 request->simul_count = 0;
1382 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1383 ipno = vp->vp_ipaddr;
1384 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1385 call_num = vp->vp_strvalue;
1388 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1389 row = sqlsocket->row;
1393 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1394 sql_release_socket(inst, sqlsocket);
1395 DEBUG("rlm_sql (%s): Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1396 return RLM_MODULE_FAIL;
1399 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1400 sql_release_socket(inst, sqlsocket);
1401 DEBUG("rlm_sql (%s): Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1402 return RLM_MODULE_FAIL;
1405 nas_addr = inet_addr(row[3]);
1407 nas_port = atoi(row[4]);
1409 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1413 * Stale record - zap it.
1415 if (inst->config->deletestalesessions == TRUE) {
1416 uint32_t framed_addr = 0;
1421 framed_addr = inet_addr(row[5]);
1423 if (strcmp(row[7], "PPP") == 0)
1425 else if (strcmp(row[7], "SLIP") == 0)
1429 sess_time = atoi(row[8]);
1430 session_zap(request, nas_addr, nas_port,
1431 row[2], row[1], framed_addr,
1435 else if (check == 1) {
1437 * User is still logged in.
1439 ++request->simul_count;
1442 * Does it look like a MPP attempt?
1444 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1445 request->simul_mpp = 2;
1446 else if (row[6] && call_num &&
1447 !strncmp(row[6],call_num,16))
1448 request->simul_mpp = 2;
1452 * Failed to check the terminal server for
1453 * duplicate logins: return an error.
1455 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1456 sql_release_socket(inst, sqlsocket);
1457 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Failed to check the terminal server for user '%s'.", inst->config->xlat_name, row[2]);
1458 return RLM_MODULE_FAIL;
1462 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1463 sql_release_socket(inst, sqlsocket);
1466 * The Auth module apparently looks at request->simul_count,
1467 * not the return value of this module when deciding to deny
1468 * a call for too many sessions.
1470 return RLM_MODULE_OK;
1474 * Execute postauth_query after authentication
1476 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1477 SQLSOCK *sqlsocket = NULL;
1478 SQL_INST *inst = instance;
1479 char querystr[MAX_QUERY_LEN];
1480 char sqlusername[MAX_STRING_LEN];
1482 DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name);
1484 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1485 return RLM_MODULE_FAIL;
1487 /* If postauth_query is not defined, we stop here */
1488 if (inst->config->postauth_query[0] == '\0')
1489 return RLM_MODULE_NOOP;
1491 /* Expand variables in the query */
1492 memset(querystr, 0, MAX_QUERY_LEN);
1493 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1494 request, sql_escape_func);
1495 query_log(request, inst, querystr);
1496 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1497 inst->config->xlat_name, querystr);
1499 /* Initialize the sql socket */
1500 sqlsocket = sql_get_socket(inst);
1501 if (sqlsocket == NULL)
1502 return RLM_MODULE_FAIL;
1504 /* Process the query */
1505 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1506 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1507 inst->config->xlat_name,
1508 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1509 sql_release_socket(inst, sqlsocket);
1510 return RLM_MODULE_FAIL;
1512 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1514 sql_release_socket(inst, sqlsocket);
1515 return RLM_MODULE_OK;
1518 /* globally exported name */
1519 module_t rlm_sql = {
1522 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1523 rlm_sql_instantiate, /* instantiation */
1524 rlm_sql_detach, /* detach */
1526 NULL, /* authentication */
1527 rlm_sql_authorize, /* authorization */
1528 NULL, /* preaccounting */
1529 rlm_sql_accounting, /* accounting */
1530 rlm_sql_checksimul, /* checksimul */
1531 NULL, /* pre-proxy */
1532 NULL, /* post-proxy */
1533 rlm_sql_postauth /* post-auth */