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 {"sqltrace", PW_TYPE_BOOLEAN,
55 offsetof(SQL_CONFIG,sqltrace), NULL, "no"},
56 {"sqltracefile", PW_TYPE_STRING_PTR,
57 offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE},
58 {"readclients", PW_TYPE_BOOLEAN,
59 offsetof(SQL_CONFIG,do_clients), NULL, "no"},
60 {"deletestalesessions", PW_TYPE_BOOLEAN,
61 offsetof(SQL_CONFIG,deletestalesessions), NULL, "yes"},
62 {"num_sql_socks", PW_TYPE_INTEGER,
63 offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"},
64 {"sql_user_name", PW_TYPE_STRING_PTR,
65 offsetof(SQL_CONFIG,query_user), NULL, ""},
66 {"default_user_profile", PW_TYPE_STRING_PTR,
67 offsetof(SQL_CONFIG,default_profile), NULL, ""},
68 {"nas_query", PW_TYPE_STRING_PTR,
69 offsetof(SQL_CONFIG,nas_query), NULL, "SELECT id,nasname,shortname,type,secret FROM nas"},
70 {"authorize_check_query", PW_TYPE_STRING_PTR,
71 offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
72 {"authorize_reply_query", PW_TYPE_STRING_PTR,
73 offsetof(SQL_CONFIG,authorize_reply_query), NULL, ""},
74 {"authorize_group_check_query", PW_TYPE_STRING_PTR,
75 offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
76 {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
77 offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
78 {"accounting_onoff_query", PW_TYPE_STRING_PTR,
79 offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
80 {"accounting_update_query", PW_TYPE_STRING_PTR,
81 offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
82 {"accounting_update_query_alt", PW_TYPE_STRING_PTR,
83 offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""},
84 {"accounting_start_query", PW_TYPE_STRING_PTR,
85 offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
86 {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
87 offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
88 {"accounting_stop_query", PW_TYPE_STRING_PTR,
89 offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
90 {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
91 offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
92 {"group_membership_query", PW_TYPE_STRING_PTR,
93 offsetof(SQL_CONFIG,groupmemb_query), NULL, ""},
94 {"connect_failure_retry_delay", PW_TYPE_INTEGER,
95 offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
96 {"simul_count_query", PW_TYPE_STRING_PTR,
97 offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
98 {"simul_verify_query", PW_TYPE_STRING_PTR,
99 offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
100 {"postauth_query", PW_TYPE_STRING_PTR,
101 offsetof(SQL_CONFIG,postauth_query), NULL, ""},
102 {"safe-characters", PW_TYPE_STRING_PTR,
103 offsetof(SQL_CONFIG,allowed_chars), NULL,
104 "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
106 {NULL, -1, 0, NULL, NULL}
110 * Fall-Through checking function from rlm_files.c
112 static int fallthrough(VALUE_PAIR *vp)
115 tmp = pairfind(vp, PW_FALL_THROUGH);
117 return tmp ? tmp->lvalue : 0;
125 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username);
126 static int generate_sql_clients(SQL_INST *inst);
127 static int sql_escape_func(char *out, int outlen, const char *in);
130 * sql xlat function. Right now only SELECTs are supported. Only
131 * the first element of the SELECT result will be used.
133 static int sql_xlat(void *instance, REQUEST *request,
134 char *fmt, char *out, size_t freespace,
135 UNUSED RADIUS_ESCAPE_STRING func)
139 SQL_INST *inst = instance;
140 char querystr[MAX_QUERY_LEN];
141 char sqlusername[MAX_STRING_LEN];
144 DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name);
146 * Add SQL-User-Name attribute just in case it is needed
147 * We could search the string fmt for SQL-User-Name to see if this is
150 sql_set_user(inst, request, sqlusername, NULL);
152 * Do an xlat on the provided string (nice recursive operation).
154 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
155 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
156 inst->config->xlat_name);
160 query_log(request, inst,querystr);
161 sqlsocket = sql_get_socket(inst);
162 if (sqlsocket == NULL)
164 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
165 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
166 inst->config->xlat_name,querystr,
167 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
168 sql_release_socket(inst,sqlsocket);
172 ret = rlm_sql_fetch_row(sqlsocket, inst);
175 DEBUG("rlm_sql (%s): SQL query did not succeed",
176 inst->config->xlat_name);
177 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
178 sql_release_socket(inst,sqlsocket);
182 row = sqlsocket->row;
184 DEBUG("rlm_sql (%s): SQL query did not return any results",
185 inst->config->xlat_name);
186 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
187 sql_release_socket(inst,sqlsocket);
192 DEBUG("rlm_sql (%s): row[0] returned NULL",
193 inst->config->xlat_name);
194 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
195 sql_release_socket(inst,sqlsocket);
198 ret = strlen(row[0]);
199 if (ret > freespace){
200 DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space",
201 inst->config->xlat_name);
202 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
203 sql_release_socket(inst,sqlsocket);
207 strlcpy(out,row[0],ret);
209 DEBUG("rlm_sql (%s): - sql_xlat finished",
210 inst->config->xlat_name);
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;
226 DEBUG("rlm_sql (%s): Processing generate_sql_clients",
227 inst->config->xlat_name);
229 /* NAS query isn't xlat'ed */
230 strlcpy(querystr, inst->config->nas_query, sizeof(querystr));
231 DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s",
232 inst->config->xlat_name, querystr);
234 sqlsocket = sql_get_socket(inst);
235 if (sqlsocket == NULL)
237 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
238 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
239 inst->config->xlat_name,querystr,
240 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
241 sql_release_socket(inst,sqlsocket);
245 while(rlm_sql_fetch_row(sqlsocket, inst) == 0) {
247 row = sqlsocket->row;
251 * The return data for each row MUST be in the following order:
253 * 0. Row ID (currently unused)
254 * 1. Name (or IP address)
260 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
264 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
268 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
272 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
276 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
277 row[1],row[2],row[4]);
279 c = rad_malloc(sizeof(RADCLIENT));
280 memset(c, 0, sizeof(RADCLIENT));
286 prefix_ptr = strchr(row[1], '/');
288 c->prefix = atoi(prefix_ptr + 1);
289 if ((c->prefix < 0) || (c->prefix > 128)) {
290 radlog(L_ERR, "rlm_sql (%s): Invalid Prefix value '%s' for IP.",
291 inst->config->xlat_name, prefix_ptr + 1);
295 /* Replace '/' with '\0' */
300 * Always get the numeric representation of IP
302 if (ip_hton(row[1], AF_UNSPEC, &c->ipaddr) < 0) {
303 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s: %s",
304 inst->config->xlat_name,
305 row[1], librad_errstr);
310 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
311 c->longname = strdup(buffer);
314 if (c->prefix < 0) switch (c->ipaddr.af) {
326 * Other values (secret, shortname, nastype)
328 c->secret = (u_char *)strdup(row[4]);
329 c->shortname = strdup(row[2]);
331 c->nastype = strdup(row[3]);
333 DEBUG("rlm_sql (%s): Adding client %s (%s) to clients list",
334 inst->config->xlat_name,
335 c->longname,c->shortname);
336 if (!client_add(mainconfig.clients, c)) {
341 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
342 sql_release_socket(inst, sqlsocket);
349 * Translate the SQL queries.
351 static int sql_escape_func(char *out, int outlen, const char *in)
357 * Non-printable characters get replaced with their
358 * mime-encoded equivalents.
361 strchr(allowed_chars, *in) == NULL) {
363 * Only 3 or less bytes available.
369 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
378 * Only one byte left.
398 * Set the SQL user name.
400 * We don't call the escape function here. The resulting string
401 * will be escaped later in the queries xlat so we don't need to
402 * escape it twice. (it will make things wrong if we have an
403 * escape candidate character in the username)
405 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
408 char tmpuser[MAX_STRING_LEN];
411 sqlusername[0]= '\0';
413 /* Remove any user attr we added previously */
414 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
416 if (username != NULL) {
417 strlcpy(tmpuser, username, sizeof(tmpuser));
418 } else if (strlen(inst->config->query_user)) {
419 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
424 strlcpy(sqlusername, tmpuser, MAX_STRING_LEN);
425 DEBUG2("rlm_sql (%s): sql_set_user escaped user --> '%s'",
426 inst->config->xlat_name, sqlusername);
427 vp = pairmake("SQL-User-Name", sqlusername, 0);
429 radlog(L_ERR, "%s", librad_errstr);
433 pairadd(&request->packet->vps, vp);
439 static void sql_grouplist_free (SQL_GROUPLIST **group_list)
445 *group_list = (*group_list)->next;
451 static int sql_get_grouplist (SQL_INST *inst, SQLSOCK *sqlsocket, REQUEST *request, SQL_GROUPLIST **group_list)
453 char querystr[MAX_QUERY_LEN];
456 SQL_GROUPLIST *group_list_tmp;
458 /* NOTE: sql_set_user should have been run before calling this function */
460 group_list_tmp = *group_list = NULL;
462 if (inst->config->groupmemb_query[0] == 0)
465 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, request, sql_escape_func)) {
466 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
467 inst->config->xlat_name);
471 if (rlm_sql_select_query(sqlsocket, inst, querystr) < 0) {
472 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
473 inst->config->xlat_name,querystr,
474 (char *)(inst->module->sql_error)(sqlsocket,inst->config));
477 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
478 row = sqlsocket->row;
482 DEBUG("rlm_sql (%s): row[0] returned NULL",
483 inst->config->xlat_name);
484 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
485 sql_grouplist_free(group_list);
488 if (*group_list == NULL) {
489 *group_list = rad_malloc(sizeof(SQL_GROUPLIST));
490 group_list_tmp = *group_list;
492 group_list_tmp->next = rad_malloc(sizeof(SQL_GROUPLIST));
493 group_list_tmp = group_list_tmp->next;
495 group_list_tmp->next = NULL;
496 strlcpy(group_list_tmp->groupname, row[0], MAX_STRING_LEN);
499 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
506 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
507 * with the group memberships reciding in sql
508 * The group membership query should only return one element which is the username. The returned
509 * username will then be checked with the passed check string.
512 static int sql_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
513 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
516 SQL_INST *inst = instance;
517 char sqlusername[MAX_STRING_LEN];
518 SQL_GROUPLIST *group_list, *group_list_tmp;
520 check_pairs = check_pairs;
521 reply_pairs = reply_pairs;
524 DEBUG("rlm_sql (%s): - sql_groupcmp", inst->config->xlat_name);
525 if (!check || !check->vp_strvalue || !check->length){
526 DEBUG("rlm_sql (%s): sql_groupcmp: Illegal group name",
527 inst->config->xlat_name);
531 DEBUG("rlm_sql (%s): sql_groupcmp: NULL request",
532 inst->config->xlat_name);
536 * Set, escape, and check the user attr here
538 if (sql_set_user(inst, req, sqlusername, NULL) < 0)
542 * Get a socket for this lookup
544 sqlsocket = sql_get_socket(inst);
545 if (sqlsocket == NULL) {
546 /* Remove the username we (maybe) added above */
547 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
552 * Get the list of groups this user is a member of
554 if (sql_get_grouplist(inst, sqlsocket, req, &group_list)) {
555 radlog(L_ERR, "rlm_sql (%s): Error getting group membership",
556 inst->config->xlat_name);
557 /* Remove the username we (maybe) added above */
558 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
559 sql_release_socket(inst, sqlsocket);
563 for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) {
564 if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){
565 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User is a member of group %s",
566 inst->config->xlat_name,
567 (char *)check->vp_strvalue);
568 /* Free the grouplist */
569 sql_grouplist_free(&group_list);
570 /* Remove the username we (maybe) added above */
571 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
572 sql_release_socket(inst, sqlsocket);
577 /* Free the grouplist */
578 sql_grouplist_free(&group_list);
579 /* Remove the username we (maybe) added above */
580 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
581 sql_release_socket(inst,sqlsocket);
583 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User is NOT a member of group %s",
584 inst->config->xlat_name, (char *)check->vp_strvalue);
591 static int rlm_sql_process_groups(SQL_INST *inst, REQUEST *request, SQLSOCK *sqlsocket, int *dofallthrough)
593 VALUE_PAIR *check_tmp = NULL;
594 VALUE_PAIR *reply_tmp = NULL;
595 SQL_GROUPLIST *group_list, *group_list_tmp;
596 VALUE_PAIR *sql_group = NULL;
597 char querystr[MAX_QUERY_LEN];
602 * Get the list of groups this user is a member of
604 if (sql_get_grouplist(inst, sqlsocket, request, &group_list)) {
605 radlog(L_ERR, "rlm_sql (%s): Error retrieving group list",
606 inst->config->xlat_name);
610 for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) {
612 * Add the Sql-Group attribute to the request list so we know
613 * which group we're retrieving attributes for
615 sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ);
617 radlog(L_ERR, "rlm_sql (%s): Error creating Sql-Group attribute",
618 inst->config->xlat_name);
621 pairadd(&request->packet->vps, sql_group);
622 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func)) {
623 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
624 inst->config->xlat_name);
625 /* Remove the grouup we added above */
626 pairdelete(&request->packet->vps, PW_SQL_GROUP);
629 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
631 radlog(L_ERR, "rlm_sql (%s): Error retrieving check pairs for group %s",
632 inst->config->xlat_name, group_list_tmp->groupname);
633 /* Remove the grouup we added above */
634 pairdelete(&request->packet->vps, PW_SQL_GROUP);
635 pairfree(&check_tmp);
637 } else if (rows > 0) {
639 * Only do this if *some* check pairs were returned
641 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
643 DEBUG2("rlm_sql (%s): User found in group %s",
644 inst->config->xlat_name, group_list_tmp->groupname);
646 * Now get the reply pairs since the paircompare matched
648 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
649 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
650 inst->config->xlat_name);
651 /* Remove the grouup we added above */
652 pairdelete(&request->packet->vps, PW_SQL_GROUP);
653 pairfree(&check_tmp);
656 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
657 radlog(L_ERR, "rlm_sql (%s): Error retrieving reply pairs for group %s",
658 inst->config->xlat_name, group_list_tmp->groupname);
659 /* Remove the grouup we added above */
660 pairdelete(&request->packet->vps, PW_SQL_GROUP);
661 pairfree(&check_tmp);
662 pairfree(&reply_tmp);
665 *dofallthrough = fallthrough(reply_tmp);
666 pairxlatmove(request, &request->reply->vps, &reply_tmp);
667 pairxlatmove(request, &request->config_items, &check_tmp);
671 * rows == 0. This is like having the username on a line
672 * in the user's file with no check vp's. As such, we treat
673 * it as found and add the reply attributes, so that we
674 * match expected behavior
677 DEBUG2("rlm_sql (%s): User found in group %s",
678 inst->config->xlat_name, group_list_tmp->groupname);
680 * Now get the reply pairs since the paircompare matched
682 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
683 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
684 inst->config->xlat_name);
685 /* Remove the grouup we added above */
686 pairdelete(&request->packet->vps, PW_SQL_GROUP);
687 pairfree(&check_tmp);
690 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
691 radlog(L_ERR, "rlm_sql (%s): Error retrieving reply pairs for group %s",
692 inst->config->xlat_name, group_list_tmp->groupname);
693 /* Remove the grouup we added above */
694 pairdelete(&request->packet->vps, PW_SQL_GROUP);
695 pairfree(&check_tmp);
696 pairfree(&reply_tmp);
699 *dofallthrough = fallthrough(reply_tmp);
700 pairxlatmove(request, &request->reply->vps, &reply_tmp);
701 pairxlatmove(request, &request->config_items, &check_tmp);
705 * Delete the Sql-Group we added above
706 * And clear out the pairlists
708 pairdelete(&request->packet->vps, PW_SQL_GROUP);
709 pairfree(&check_tmp);
710 pairfree(&reply_tmp);
713 sql_grouplist_free(&group_list);
718 static int rlm_sql_detach(void *instance)
720 SQL_INST *inst = instance;
726 if (inst->config->xlat_name) {
727 xlat_unregister(inst->config->xlat_name,(RAD_XLAT_FUNC)sql_xlat);
728 free(inst->config->xlat_name);
731 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
737 * Free up dynamically allocated string pointers.
739 for (i = 0; module_config[i].name != NULL; i++) {
741 if (module_config[i].type != PW_TYPE_STRING_PTR) {
746 * Treat 'config' as an opaque array of bytes,
747 * and take the offset into it. There's a
748 * (char*) pointer at that offset, and we want
751 p = (char **) (((char *)inst->config) + module_config[i].offset);
752 if (!*p) { /* nothing allocated */
758 allowed_chars = NULL;
766 * FIXME: Call the modules 'destroy' function?
768 lt_dlclose(inst->handle); /* ignore any errors */
775 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
778 const char *xlat_name;
780 inst = rad_malloc(sizeof(SQL_INST));
781 memset(inst, 0, sizeof(SQL_INST));
783 inst->config = rad_malloc(sizeof(SQL_CONFIG));
784 memset(inst->config, 0, sizeof(SQL_CONFIG));
787 * If the configuration parameters can't be parsed, then
790 if (cf_section_parse(conf, inst->config, module_config) < 0) {
791 rlm_sql_detach(inst);
795 xlat_name = cf_section_name2(conf);
796 if (xlat_name == NULL)
797 xlat_name = cf_section_name1(conf);
799 inst->config->xlat_name = strdup(xlat_name);
800 xlat_register(xlat_name, (RAD_XLAT_FUNC)sql_xlat, inst);
803 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
804 radlog(L_ERR | L_CONS, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
805 inst->config->xlat_name, MAX_SQL_SOCKS);
806 rlm_sql_detach(inst);
811 * Sanity check for crazy people.
813 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
814 radlog(L_ERR, "rlm_sql (%s): \"%s\" is NOT an SQL driver!",
815 inst->config->xlat_name, inst->config->sql_driver);
816 rlm_sql_detach(inst);
820 inst->handle = lt_dlopenext(inst->config->sql_driver);
821 if (inst->handle == NULL) {
822 radlog(L_ERR, "rlm_sql (%s): Could not link driver %s: %s",
823 inst->config->xlat_name, inst->config->sql_driver,
825 radlog(L_ERR, "rlm_sql (%s): Make sure it (and all its dependent libraries!) are in the search path of your system's ld.",
826 inst->config->xlat_name);
827 rlm_sql_detach(inst);
831 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
833 radlog(L_ERR, "rlm_sql (%s): Could not link symbol %s: %s",
834 inst->config->xlat_name, inst->config->sql_driver,
836 rlm_sql_detach(inst);
840 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
841 inst->config->xlat_name, inst->config->sql_driver,
843 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
844 inst->config->xlat_name, inst->config->sql_login,
845 inst->config->sql_server, inst->config->sql_port,
846 inst->config->sql_db);
848 if (sql_init_socketpool(inst) < 0) {
849 rlm_sql_detach(inst);
852 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
854 if (inst->config->do_clients){
855 if (generate_sql_clients(inst) == -1){
856 radlog(L_ERR, "rlm_sql (%s): generate_sql_clients() returned error",inst->config->xlat_name);
857 rlm_sql_detach(inst);
861 allowed_chars = inst->config->allowed_chars;
865 return RLM_MODULE_OK;
869 static int rlm_sql_authorize(void *instance, REQUEST * request)
871 VALUE_PAIR *check_tmp = NULL;
872 VALUE_PAIR *reply_tmp = NULL;
873 VALUE_PAIR *user_profile = NULL;
875 int dofallthrough = 1;
878 SQL_INST *inst = instance;
879 char querystr[MAX_QUERY_LEN];
880 char sqlusername[MAX_STRING_LEN];
882 * the profile username is used as the sqlusername during
883 * profile checking so that we don't overwrite the orignal
886 char profileusername[MAX_STRING_LEN];
889 * Set, escape, and check the user attr here
891 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
892 return RLM_MODULE_FAIL;
898 sqlsocket = sql_get_socket(inst);
899 if (sqlsocket == NULL) {
900 /* Remove the username we (maybe) added above */
901 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
902 return RLM_MODULE_FAIL;
907 * After this point, ALL 'return's MUST release the SQL socket!
911 * Alright, start by getting the specific entry for the user
913 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func)) {
914 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
915 inst->config->xlat_name);
916 sql_release_socket(inst, sqlsocket);
917 /* Remove the username we (maybe) added above */
918 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
919 return RLM_MODULE_FAIL;
921 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
923 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
924 inst->config->xlat_name);
925 sql_release_socket(inst, sqlsocket);
926 /* Remove the username we (maybe) added above */
927 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
928 pairfree(&check_tmp);
929 return RLM_MODULE_FAIL;
930 } else if (rows > 0) {
932 * Only do this if *some* check pairs were returned
934 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
936 DEBUG2("rlm_sql (%s): User found in radcheck table", inst->config->xlat_name);
938 * Now get the reply pairs since the paircompare matched
940 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func)) {
941 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
942 inst->config->xlat_name);
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;
949 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
950 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
951 inst->config->xlat_name);
952 sql_release_socket(inst, sqlsocket);
953 /* Remove the username we (maybe) added above */
954 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
955 pairfree(&check_tmp);
956 pairfree(&reply_tmp);
957 return RLM_MODULE_FAIL;
959 if (!inst->config->read_groups)
960 dofallthrough = fallthrough(reply_tmp);
961 pairxlatmove(request, &request->reply->vps, &reply_tmp);
962 pairxlatmove(request, &request->config_items, &check_tmp);
967 * Clear out the pairlists
969 pairfree(&check_tmp);
970 pairfree(&reply_tmp);
973 * dofallthrough is set to 1 by default so that if the user information
974 * is not found, we will still process groups. If the user information,
975 * however, *is* found, Fall-Through must be set in order to process
979 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
981 radlog(L_ERR, "rlm_sql (%s): Error processing groups; rejecting user",
982 inst->config->xlat_name);
983 sql_release_socket(inst, sqlsocket);
984 /* Remove the username we (maybe) added above */
985 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
986 return RLM_MODULE_FAIL;
987 } else if (rows > 0) {
993 * repeat the above process with the default profile or User-Profile
996 int profile_found = 0;
998 * Check for a default_profile or for a User-Profile.
1000 user_profile = pairfind(request->config_items, PW_USER_PROFILE);
1001 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
1002 char *profile = inst->config->default_profile;
1004 if (user_profile != NULL)
1005 profile = user_profile->vp_strvalue;
1006 if (profile && strlen(profile)){
1007 radlog(L_DBG, "rlm_sql (%s): Checking profile %s",
1008 inst->config->xlat_name, profile);
1009 if (sql_set_user(inst, request, profileusername, profile) < 0) {
1010 radlog(L_ERR, "rlm_sql (%s): Error setting profile; rejecting user",
1011 inst->config->xlat_name);
1012 sql_release_socket(inst, sqlsocket);
1013 /* Remove the username we (maybe) added above */
1014 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1015 return RLM_MODULE_FAIL;
1022 if (profile_found) {
1023 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1025 radlog(L_ERR, "rlm_sql (%s): Error processing profile groups; rejecting user",
1026 inst->config->xlat_name);
1027 sql_release_socket(inst, sqlsocket);
1028 /* Remove the username we (maybe) added above */
1029 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1030 return RLM_MODULE_FAIL;
1031 } else if (rows > 0) {
1037 /* Remove the username we (maybe) added above */
1038 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1039 sql_release_socket(inst, sqlsocket);
1042 radlog(L_DBG, "rlm_sql (%s): User %s not found",
1043 inst->config->xlat_name, sqlusername);
1044 return RLM_MODULE_NOTFOUND;
1046 return RLM_MODULE_OK;
1051 * Accounting: save the account data to our sql table
1053 static int rlm_sql_accounting(void *instance, REQUEST * request) {
1055 SQLSOCK *sqlsocket = NULL;
1057 SQL_INST *inst = instance;
1058 int ret = RLM_MODULE_OK;
1059 int numaffected = 0;
1060 int acctstatustype = 0;
1061 char querystr[MAX_QUERY_LEN];
1062 char logstr[MAX_QUERY_LEN];
1063 char sqlusername[MAX_STRING_LEN];
1065 #ifdef CISCO_ACCOUNTING_HACK
1066 int acctsessiontime = 0;
1069 memset(querystr, 0, MAX_QUERY_LEN);
1072 * Find the Acct Status Type
1074 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
1075 acctstatustype = pair->lvalue;
1077 radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1078 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s",
1079 inst->config->xlat_name, logstr);
1080 return RLM_MODULE_INVALID;
1083 switch (acctstatustype) {
1085 * The Terminal server informed us that it was rebooted
1086 * STOP all records from this NAS
1088 case PW_STATUS_ACCOUNTING_ON:
1089 case PW_STATUS_ACCOUNTING_OFF:
1090 radlog(L_INFO, "rlm_sql (%s): received Acct On/Off packet", inst->config->xlat_name);
1091 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
1092 query_log(request, inst, querystr);
1094 sqlsocket = sql_get_socket(inst);
1095 if (sqlsocket == NULL)
1096 return(RLM_MODULE_FAIL);
1097 if (*querystr) { /* non-empty query */
1098 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1099 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting for Acct On/Off packet - %s",
1100 inst->config->xlat_name,
1101 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1102 ret = RLM_MODULE_FAIL;
1104 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1110 * Got an update accounting packet
1112 case PW_STATUS_ALIVE:
1115 * Set, escape, and check the user attr here
1117 sql_set_user(inst, request, sqlusername, NULL);
1119 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
1120 query_log(request, inst, querystr);
1122 sqlsocket = sql_get_socket(inst);
1123 if (sqlsocket == NULL)
1124 return(RLM_MODULE_FAIL);
1125 if (*querystr) { /* non-empty query */
1126 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1127 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting ALIVE record - %s",
1128 inst->config->xlat_name,
1129 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1130 ret = RLM_MODULE_FAIL;
1133 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1134 if (numaffected < 1) {
1137 * If our update above didn't match anything
1138 * we assume it's because we haven't seen a
1139 * matching Start record. So we have to
1140 * insert this update rather than do an update
1142 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
1143 query_log(request, inst, querystr);
1144 if (*querystr) { /* non-empty query */
1145 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1146 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting ALIVE record - %s",
1147 inst->config->xlat_name,
1148 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1149 ret = RLM_MODULE_FAIL;
1151 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1155 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1160 * Got accounting start packet
1162 case PW_STATUS_START:
1165 * Set, escape, and check the user attr here
1167 sql_set_user(inst, request, sqlusername, NULL);
1169 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
1170 query_log(request, inst, querystr);
1172 sqlsocket = sql_get_socket(inst);
1173 if (sqlsocket == NULL)
1174 return(RLM_MODULE_FAIL);
1175 if (*querystr) { /* non-empty query */
1176 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1177 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting START record - %s",
1178 inst->config->xlat_name,
1179 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1182 * We failed the insert above. It's probably because
1183 * the stop record came before the start. We try
1184 * our alternate query now (typically an UPDATE)
1186 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1187 query_log(request, inst, querystr);
1189 if (*querystr) { /* non-empty query */
1190 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1191 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting START record - %s",
1192 inst->config->xlat_name,
1193 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1194 ret = RLM_MODULE_FAIL;
1196 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1199 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1204 * Got accounting stop packet
1206 case PW_STATUS_STOP:
1209 * Set, escape, and check the user attr here
1211 sql_set_user(inst, request, sqlusername, NULL);
1213 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1214 query_log(request, inst, querystr);
1216 sqlsocket = sql_get_socket(inst);
1217 if (sqlsocket == NULL)
1218 return(RLM_MODULE_FAIL);
1219 if (*querystr) { /* non-empty query */
1220 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1221 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting STOP record - %s",
1222 inst->config->xlat_name,
1223 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1224 ret = RLM_MODULE_FAIL;
1227 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1228 if (numaffected < 1) {
1230 * If our update above didn't match anything
1231 * we assume it's because we haven't seen a
1232 * matching Start record. So we have to
1233 * insert this stop rather than do an update
1235 #ifdef CISCO_ACCOUNTING_HACK
1237 * If stop but zero session length AND no previous
1238 * session found, drop it as in invalid packet
1239 * This is to fix CISCO's aaa from filling our
1240 * table with bogus crap
1242 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
1243 acctsessiontime = pair->lvalue;
1245 if (acctsessiontime <= 0) {
1246 radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1247 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s", inst->config->xlat_name, logstr);
1248 sql_release_socket(inst, sqlsocket);
1249 ret = RLM_MODULE_NOOP;
1253 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1254 query_log(request, inst, querystr);
1256 if (*querystr) { /* non-empty query */
1257 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1258 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting STOP record - %s",
1259 inst->config->xlat_name,
1260 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1261 ret = RLM_MODULE_FAIL;
1263 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1267 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1272 * Anything else is ignored.
1275 radlog(L_INFO, "rlm_sql (%s): Unsupported Acct-Status-Type = %d", inst->config->xlat_name, acctstatustype);
1276 return RLM_MODULE_NOOP;
1281 sql_release_socket(inst, sqlsocket);
1288 * See if a user is already logged in. Sets request->simul_count to the
1289 * current session count for this user.
1291 * Check twice. If on the first pass the user exceeds his
1292 * max. number of logins, do a second pass and validate all
1293 * logins by querying the terminal server (using eg. SNMP).
1296 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1298 SQL_INST *inst = instance;
1300 char querystr[MAX_QUERY_LEN];
1301 char sqlusername[MAX_STRING_LEN];
1304 char *call_num = NULL;
1307 uint32_t nas_addr = 0;
1310 /* If simul_count_query is not defined, we don't do any checking */
1311 if (inst->config->simul_count_query[0] == 0) {
1312 return RLM_MODULE_NOOP;
1315 if((request->username == NULL) || (request->username->length == 0)) {
1316 radlog(L_ERR, "rlm_sql (%s): Zero Length username not permitted\n", inst->config->xlat_name);
1317 return RLM_MODULE_INVALID;
1321 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1322 return RLM_MODULE_FAIL;
1324 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1326 /* initialize the sql socket */
1327 sqlsocket = sql_get_socket(inst);
1328 if(sqlsocket == NULL)
1329 return RLM_MODULE_FAIL;
1331 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1332 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1333 sql_release_socket(inst, sqlsocket);
1334 return RLM_MODULE_FAIL;
1337 ret = rlm_sql_fetch_row(sqlsocket, inst);
1340 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1341 sql_release_socket(inst, sqlsocket);
1342 return RLM_MODULE_FAIL;
1345 row = sqlsocket->row;
1347 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1348 sql_release_socket(inst, sqlsocket);
1349 return RLM_MODULE_FAIL;
1352 request->simul_count = atoi(row[0]);
1353 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1355 if(request->simul_count < request->simul_max) {
1356 sql_release_socket(inst, sqlsocket);
1357 return RLM_MODULE_OK;
1361 * Looks like too many sessions, so let's start verifying
1362 * them, unless told to rely on count query only.
1364 if (inst->config->simul_verify_query[0] == '\0') {
1365 sql_release_socket(inst, sqlsocket);
1366 return RLM_MODULE_OK;
1369 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1370 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1371 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Database query error", inst->config->xlat_name);
1372 sql_release_socket(inst, sqlsocket);
1373 return RLM_MODULE_FAIL;
1377 * Setup some stuff, like for MPP detection.
1379 request->simul_count = 0;
1381 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1383 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1384 call_num = vp->vp_strvalue;
1387 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1388 row = sqlsocket->row;
1392 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1393 sql_release_socket(inst, sqlsocket);
1394 DEBUG("rlm_sql (%s): Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1395 return RLM_MODULE_FAIL;
1398 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1399 sql_release_socket(inst, sqlsocket);
1400 DEBUG("rlm_sql (%s): Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1401 return RLM_MODULE_FAIL;
1404 nas_addr = inet_addr(row[3]);
1406 nas_port = atoi(row[4]);
1408 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1412 * Stale record - zap it.
1414 if (inst->config->deletestalesessions == TRUE) {
1415 uint32_t framed_addr = 0;
1420 framed_addr = inet_addr(row[5]);
1422 if (strcmp(row[7], "PPP") == 0)
1424 else if (strcmp(row[7], "SLIP") == 0)
1428 sess_time = atoi(row[8]);
1429 session_zap(request, nas_addr, nas_port,
1430 row[2], row[1], framed_addr,
1434 else if (check == 1) {
1436 * User is still logged in.
1438 ++request->simul_count;
1441 * Does it look like a MPP attempt?
1443 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1444 request->simul_mpp = 2;
1445 else if (row[6] && call_num &&
1446 !strncmp(row[6],call_num,16))
1447 request->simul_mpp = 2;
1451 * Failed to check the terminal server for
1452 * duplicate logins: return an error.
1454 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1455 sql_release_socket(inst, sqlsocket);
1456 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Failed to check the terminal server for user '%s'.", inst->config->xlat_name, row[2]);
1457 return RLM_MODULE_FAIL;
1461 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1462 sql_release_socket(inst, sqlsocket);
1465 * The Auth module apparently looks at request->simul_count,
1466 * not the return value of this module when deciding to deny
1467 * a call for too many sessions.
1469 return RLM_MODULE_OK;
1473 * Execute postauth_query after authentication
1475 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1476 SQLSOCK *sqlsocket = NULL;
1477 SQL_INST *inst = instance;
1478 char querystr[MAX_QUERY_LEN];
1479 char sqlusername[MAX_STRING_LEN];
1481 DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name);
1483 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1484 return RLM_MODULE_FAIL;
1486 /* If postauth_query is not defined, we stop here */
1487 if (inst->config->postauth_query[0] == '\0')
1488 return RLM_MODULE_NOOP;
1490 /* Expand variables in the query */
1491 memset(querystr, 0, MAX_QUERY_LEN);
1492 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1493 request, sql_escape_func);
1494 query_log(request, inst, querystr);
1495 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1496 inst->config->xlat_name, querystr);
1498 /* Initialize the sql socket */
1499 sqlsocket = sql_get_socket(inst);
1500 if (sqlsocket == NULL)
1501 return RLM_MODULE_FAIL;
1503 /* Process the query */
1504 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1505 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1506 inst->config->xlat_name,
1507 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1508 sql_release_socket(inst, sqlsocket);
1509 return RLM_MODULE_FAIL;
1511 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1513 sql_release_socket(inst, sqlsocket);
1514 return RLM_MODULE_OK;
1517 /* globally exported name */
1518 module_t rlm_sql = {
1521 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1522 rlm_sql_instantiate, /* instantiation */
1523 rlm_sql_detach, /* detach */
1525 NULL, /* authentication */
1526 rlm_sql_authorize, /* authorization */
1527 NULL, /* preaccounting */
1528 rlm_sql_accounting, /* accounting */
1529 rlm_sql_checksimul, /* checksimul */
1530 NULL, /* pre-proxy */
1531 NULL, /* post-proxy */
1532 rlm_sql_postauth /* post-auth */