3 * Main SQL module file. Most ICRADIUS code is located in sql.c
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * Copyright 2000,2006 The FreeRADIUS server project
22 * Copyright 2000 Mike Machado <mike@innercite.com>
23 * Copyright 2000 Alan DeKok <aland@ox.org>
26 #include <freeradius-devel/ident.h>
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/modules.h>
31 #include <freeradius-devel/rad_assert.h>
38 static char *allowed_chars = NULL;
40 static const CONF_PARSER module_config[] = {
41 {"driver",PW_TYPE_STRING_PTR,
42 offsetof(SQL_CONFIG,sql_driver), NULL, "mysql"},
43 {"server",PW_TYPE_STRING_PTR,
44 offsetof(SQL_CONFIG,sql_server), NULL, "localhost"},
45 {"port",PW_TYPE_STRING_PTR,
46 offsetof(SQL_CONFIG,sql_port), NULL, ""},
47 {"login", PW_TYPE_STRING_PTR,
48 offsetof(SQL_CONFIG,sql_login), NULL, ""},
49 {"password", PW_TYPE_STRING_PTR,
50 offsetof(SQL_CONFIG,sql_password), NULL, ""},
51 {"radius_db", PW_TYPE_STRING_PTR,
52 offsetof(SQL_CONFIG,sql_db), NULL, "radius"},
53 {"read_groups", PW_TYPE_BOOLEAN,
54 offsetof(SQL_CONFIG,read_groups), NULL, "yes"},
55 {"sqltrace", PW_TYPE_BOOLEAN,
56 offsetof(SQL_CONFIG,sqltrace), NULL, "no"},
57 {"sqltracefile", PW_TYPE_STRING_PTR,
58 offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE},
59 {"readclients", PW_TYPE_BOOLEAN,
60 offsetof(SQL_CONFIG,do_clients), NULL, "no"},
61 {"deletestalesessions", PW_TYPE_BOOLEAN,
62 offsetof(SQL_CONFIG,deletestalesessions), NULL, "yes"},
63 {"num_sql_socks", PW_TYPE_INTEGER,
64 offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"},
65 {"lifetime", PW_TYPE_INTEGER,
66 offsetof(SQL_CONFIG,lifetime), NULL, "0"},
67 {"max_queries", PW_TYPE_INTEGER,
68 offsetof(SQL_CONFIG,max_queries), NULL, "0"},
69 {"sql_user_name", PW_TYPE_STRING_PTR,
70 offsetof(SQL_CONFIG,query_user), NULL, ""},
71 {"default_user_profile", PW_TYPE_STRING_PTR,
72 offsetof(SQL_CONFIG,default_profile), NULL, ""},
73 {"nas_query", PW_TYPE_STRING_PTR,
74 offsetof(SQL_CONFIG,nas_query), NULL, "SELECT id,nasname,shortname,type,secret FROM nas"},
75 {"authorize_check_query", PW_TYPE_STRING_PTR,
76 offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
77 {"authorize_reply_query", PW_TYPE_STRING_PTR,
78 offsetof(SQL_CONFIG,authorize_reply_query), NULL, NULL},
79 {"authorize_group_check_query", PW_TYPE_STRING_PTR,
80 offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
81 {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
82 offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
83 {"accounting_onoff_query", PW_TYPE_STRING_PTR,
84 offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
85 {"accounting_update_query", PW_TYPE_STRING_PTR,
86 offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
87 {"accounting_update_query_alt", PW_TYPE_STRING_PTR,
88 offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""},
89 {"accounting_start_query", PW_TYPE_STRING_PTR,
90 offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
91 {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
92 offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
93 {"accounting_stop_query", PW_TYPE_STRING_PTR,
94 offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
95 {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
96 offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
97 {"group_membership_query", PW_TYPE_STRING_PTR,
98 offsetof(SQL_CONFIG,groupmemb_query), NULL, NULL},
99 {"connect_failure_retry_delay", PW_TYPE_INTEGER,
100 offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
101 {"simul_count_query", PW_TYPE_STRING_PTR,
102 offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
103 {"simul_verify_query", PW_TYPE_STRING_PTR,
104 offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
105 {"postauth_query", PW_TYPE_STRING_PTR,
106 offsetof(SQL_CONFIG,postauth_query), NULL, ""},
107 {"safe-characters", PW_TYPE_STRING_PTR,
108 offsetof(SQL_CONFIG,allowed_chars), NULL,
109 "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
111 {NULL, -1, 0, NULL, NULL}
115 * Fall-Through checking function from rlm_files.c
117 static int fallthrough(VALUE_PAIR *vp)
120 tmp = pairfind(vp, PW_FALL_THROUGH);
122 return tmp ? tmp->vp_integer : 0;
130 static int generate_sql_clients(SQL_INST *inst);
131 static size_t sql_escape_func(char *out, size_t outlen, const char *in);
134 * sql xlat function. Right now only SELECTs are supported. Only
135 * the first element of the SELECT result will be used.
137 * For other statements (insert, update, delete, etc.), the
138 * number of affected rows will be returned.
140 static int sql_xlat(void *instance, REQUEST *request,
141 char *fmt, char *out, size_t freespace,
142 UNUSED RADIUS_ESCAPE_STRING func)
146 SQL_INST *inst = instance;
147 char querystr[MAX_QUERY_LEN];
148 char sqlusername[MAX_STRING_LEN];
154 * Add SQL-User-Name attribute just in case it is needed
155 * We could search the string fmt for SQL-User-Name to see if this is
158 sql_set_user(inst, request, sqlusername, NULL);
160 * Do an xlat on the provided string (nice recursive operation).
162 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
163 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
164 inst->config->xlat_name);
168 query_log(request, inst,querystr);
169 sqlsocket = sql_get_socket(inst);
170 if (sqlsocket == NULL)
174 * If the query starts with any of the following prefixes,
175 * then return the number of rows affected
177 if ((strncasecmp(querystr, "insert", 6) == 0) ||
178 (strncasecmp(querystr, "update", 6) == 0) ||
179 (strncasecmp(querystr, "delete", 6) == 0)) {
181 char buffer[21]; /* 64bit max is 20 decimal chars + null byte */
183 if (rlm_sql_query(sqlsocket,inst,querystr)) {
184 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
185 inst->config->xlat_name, querystr,
186 (inst->module->sql_error)(sqlsocket,
188 sql_release_socket(inst,sqlsocket);
192 numaffected = (inst->module->sql_affected_rows)(sqlsocket,
194 if (numaffected < 1) {
195 RDEBUG("rlm_sql (%s): SQL query affected no rows",
196 inst->config->xlat_name);
200 * Don't chop the returned number if freespace is
201 * too small. This hack is necessary because
202 * some implementations of snprintf return the
203 * size of the written data, and others return
204 * the size of the data they *would* have written
205 * if the output buffer was large enough.
207 snprintf(buffer, sizeof(buffer), "%d", numaffected);
208 ret = strlen(buffer);
209 if (ret >= freespace){
210 RDEBUG("rlm_sql (%s): Can't write result, insufficient string space",
211 inst->config->xlat_name);
212 (inst->module->sql_finish_query)(sqlsocket,
214 sql_release_socket(inst,sqlsocket);
218 memcpy(out, buffer, ret + 1); /* we did bounds checking above */
220 (inst->module->sql_finish_query)(sqlsocket, inst->config);
221 sql_release_socket(inst,sqlsocket);
223 } /* else it's a SELECT statement */
225 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
226 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
227 inst->config->xlat_name,querystr,
228 (inst->module->sql_error)(sqlsocket, inst->config));
229 sql_release_socket(inst,sqlsocket);
233 ret = rlm_sql_fetch_row(sqlsocket, inst);
236 RDEBUG("SQL query did not succeed");
237 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
238 sql_release_socket(inst,sqlsocket);
242 row = sqlsocket->row;
244 RDEBUG("SQL query did not return any results");
245 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
246 sql_release_socket(inst,sqlsocket);
251 RDEBUG("row[0] returned NULL");
252 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
253 sql_release_socket(inst,sqlsocket);
256 ret = strlen(row[0]);
257 if (ret >= freespace){
258 RDEBUG("Insufficient string space");
259 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
260 sql_release_socket(inst,sqlsocket);
264 strlcpy(out,row[0],freespace);
266 RDEBUG("sql_xlat finished");
268 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
269 sql_release_socket(inst,sqlsocket);
273 static int generate_sql_clients(SQL_INST *inst)
277 char querystr[MAX_QUERY_LEN];
279 char *prefix_ptr = NULL;
283 DEBUG("rlm_sql (%s): Processing generate_sql_clients",
284 inst->config->xlat_name);
286 /* NAS query isn't xlat'ed */
287 strlcpy(querystr, inst->config->nas_query, sizeof(querystr));
288 DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s",
289 inst->config->xlat_name, querystr);
291 sqlsocket = sql_get_socket(inst);
292 if (sqlsocket == NULL)
294 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
295 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
296 inst->config->xlat_name,querystr,
297 (inst->module->sql_error)(sqlsocket, inst->config));
298 sql_release_socket(inst,sqlsocket);
302 while(rlm_sql_fetch_row(sqlsocket, inst) == 0) {
304 row = sqlsocket->row;
308 * The return data for each row MUST be in the following order:
310 * 0. Row ID (currently unused)
311 * 1. Name (or IP address)
315 * 5. Virtual Server (optional)
318 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
322 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
326 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
330 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
334 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
335 row[1],row[2],row[4]);
337 c = rad_malloc(sizeof(*c));
338 memset(c, 0, sizeof(*c));
340 #ifdef WITH_DYNAMIC_CLIENTS
348 prefix_ptr = strchr(row[1], '/');
350 c->prefix = atoi(prefix_ptr + 1);
351 if ((c->prefix < 0) || (c->prefix > 128)) {
352 radlog(L_ERR, "rlm_sql (%s): Invalid Prefix value '%s' for IP.",
353 inst->config->xlat_name, prefix_ptr + 1);
357 /* Replace '/' with '\0' */
362 * Always get the numeric representation of IP
364 if (ip_hton(row[1], AF_UNSPEC, &c->ipaddr) < 0) {
365 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s: %s",
366 inst->config->xlat_name,
367 row[1], fr_strerror());
372 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
373 c->longname = strdup(buffer);
376 if (c->prefix < 0) switch (c->ipaddr.af) {
388 * Other values (secret, shortname, nastype, virtual_server)
390 c->secret = strdup(row[4]);
391 c->shortname = strdup(row[2]);
393 c->nastype = strdup(row[3]);
395 numf = (inst->module->sql_num_fields)(sqlsocket, inst->config);
396 if ((numf > 5) && (row[5] != NULL)) c->server = strdup(row[5]);
398 DEBUG("rlm_sql (%s): Adding client %s (%s, server=%s) to clients list",
399 inst->config->xlat_name,
400 c->longname,c->shortname, c->server ? c->server : "<none>");
401 if (!client_add(NULL, c)) {
402 DEBUG("rlm_sql (%s): Failed to add client %s (%s) to clients list. Maybe there's a duplicate?",
403 inst->config->xlat_name,
404 c->longname,c->shortname);
409 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
410 sql_release_socket(inst, sqlsocket);
417 * Translate the SQL queries.
419 static size_t sql_escape_func(char *out, size_t outlen, const char *in)
425 * Non-printable characters get replaced with their
426 * mime-encoded equivalents.
429 strchr(allowed_chars, *in) == NULL) {
431 * Only 3 or less bytes available.
437 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
446 * Only one byte left.
466 * Set the SQL user name.
468 * We don't call the escape function here. The resulting string
469 * will be escaped later in the queries xlat so we don't need to
470 * escape it twice. (it will make things wrong if we have an
471 * escape candidate character in the username)
473 int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
476 char tmpuser[MAX_STRING_LEN];
479 sqlusername[0]= '\0';
481 /* Remove any user attr we added previously */
482 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
484 if (username != NULL) {
485 strlcpy(tmpuser, username, sizeof(tmpuser));
486 } else if (strlen(inst->config->query_user)) {
487 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
492 strlcpy(sqlusername, tmpuser, MAX_STRING_LEN);
493 RDEBUG2("sql_set_user escaped user --> '%s'", sqlusername);
494 vp = radius_pairmake(request, &request->packet->vps,
495 "SQL-User-Name", NULL, 0);
497 radlog(L_ERR, "%s", fr_strerror());
501 strlcpy(vp->vp_strvalue, tmpuser, sizeof(vp->vp_strvalue));
502 vp->length = strlen(vp->vp_strvalue);
509 static void sql_grouplist_free (SQL_GROUPLIST **group_list)
515 *group_list = (*group_list)->next;
521 static int sql_get_grouplist (SQL_INST *inst, SQLSOCK *sqlsocket, REQUEST *request, SQL_GROUPLIST **group_list)
523 char querystr[MAX_QUERY_LEN];
526 SQL_GROUPLIST *group_list_tmp;
528 /* NOTE: sql_set_user should have been run before calling this function */
530 group_list_tmp = *group_list = NULL;
532 if (!inst->config->groupmemb_query ||
533 (inst->config->groupmemb_query[0] == 0))
536 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, request, sql_escape_func)) {
537 radlog_request(L_ERR, 0, request, "xlat \"%s\" failed.",
538 inst->config->groupmemb_query);
542 if (rlm_sql_select_query(sqlsocket, inst, querystr) < 0) {
543 radlog_request(L_ERR, 0, request,
544 "database query error, %s: %s",
546 (inst->module->sql_error)(sqlsocket,inst->config));
549 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
550 row = sqlsocket->row;
554 RDEBUG("row[0] returned NULL");
555 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
556 sql_grouplist_free(group_list);
559 if (*group_list == NULL) {
560 *group_list = rad_malloc(sizeof(SQL_GROUPLIST));
561 group_list_tmp = *group_list;
563 rad_assert(group_list_tmp != NULL);
564 group_list_tmp->next = rad_malloc(sizeof(SQL_GROUPLIST));
565 group_list_tmp = group_list_tmp->next;
567 group_list_tmp->next = NULL;
568 strlcpy(group_list_tmp->groupname, row[0], MAX_STRING_LEN);
571 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
578 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
579 * with the group memberships reciding in sql
580 * The group membership query should only return one element which is the username. The returned
581 * username will then be checked with the passed check string.
584 static int sql_groupcmp(void *instance, REQUEST *request, VALUE_PAIR *request_vp, VALUE_PAIR *check,
585 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
588 SQL_INST *inst = instance;
589 char sqlusername[MAX_STRING_LEN];
590 SQL_GROUPLIST *group_list, *group_list_tmp;
592 check_pairs = check_pairs;
593 reply_pairs = reply_pairs;
594 request_vp = request_vp;
596 RDEBUG("sql_groupcmp");
597 if (!check || !check->vp_strvalue || !check->length){
598 RDEBUG("sql_groupcmp: Illegal group name");
602 RDEBUG("sql_groupcmp: NULL request");
606 * Set, escape, and check the user attr here
608 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
612 * Get a socket for this lookup
614 sqlsocket = sql_get_socket(inst);
615 if (sqlsocket == NULL) {
616 /* Remove the username we (maybe) added above */
617 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
622 * Get the list of groups this user is a member of
624 if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
625 radlog_request(L_ERR, 0, request,
626 "Error getting group membership");
627 /* Remove the username we (maybe) added above */
628 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
629 sql_release_socket(inst, sqlsocket);
633 for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) {
634 if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){
635 RDEBUG("sql_groupcmp finished: User is a member of group %s",
637 /* Free the grouplist */
638 sql_grouplist_free(&group_list);
639 /* Remove the username we (maybe) added above */
640 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
641 sql_release_socket(inst, sqlsocket);
646 /* Free the grouplist */
647 sql_grouplist_free(&group_list);
648 /* Remove the username we (maybe) added above */
649 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
650 sql_release_socket(inst,sqlsocket);
652 RDEBUG("sql_groupcmp finished: User is NOT a member of group %s",
660 static int rlm_sql_process_groups(SQL_INST *inst, REQUEST *request, SQLSOCK *sqlsocket, int *dofallthrough)
662 VALUE_PAIR *check_tmp = NULL;
663 VALUE_PAIR *reply_tmp = NULL;
664 SQL_GROUPLIST *group_list, *group_list_tmp;
665 VALUE_PAIR *sql_group = NULL;
666 char querystr[MAX_QUERY_LEN];
671 * Get the list of groups this user is a member of
673 if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
674 radlog_request(L_ERR, 0, request, "Error retrieving group list");
678 for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) {
680 * Add the Sql-Group attribute to the request list so we know
681 * which group we're retrieving attributes for
683 sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ);
685 radlog_request(L_ERR, 0, request,
686 "Error creating Sql-Group attribute");
689 pairadd(&request->packet->vps, sql_group);
690 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func)) {
691 radlog_request(L_ERR, 0, request,
692 "Error generating query; rejecting user");
693 /* Remove the grouup we added above */
694 pairdelete(&request->packet->vps, PW_SQL_GROUP);
697 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
699 radlog_request(L_ERR, 0, request, "Error retrieving check pairs for group %s",
700 group_list_tmp->groupname);
701 /* Remove the grouup we added above */
702 pairdelete(&request->packet->vps, PW_SQL_GROUP);
703 pairfree(&check_tmp);
705 } else if (rows > 0) {
707 * Only do this if *some* check pairs were returned
709 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
711 RDEBUG2("User found in group %s",
712 group_list_tmp->groupname);
714 * Now get the reply pairs since the paircompare matched
716 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
717 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
718 /* Remove the grouup we added above */
719 pairdelete(&request->packet->vps, PW_SQL_GROUP);
720 pairfree(&check_tmp);
723 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
724 radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
725 group_list_tmp->groupname);
726 /* Remove the grouup we added above */
727 pairdelete(&request->packet->vps, PW_SQL_GROUP);
728 pairfree(&check_tmp);
729 pairfree(&reply_tmp);
732 *dofallthrough = fallthrough(reply_tmp);
733 pairxlatmove(request, &request->reply->vps, &reply_tmp);
734 pairxlatmove(request, &request->config_items, &check_tmp);
738 * rows == 0. This is like having the username on a line
739 * in the user's file with no check vp's. As such, we treat
740 * it as found and add the reply attributes, so that we
741 * match expected behavior
744 RDEBUG2("User found in group %s",
745 group_list_tmp->groupname);
747 * Now get the reply pairs since the paircompare matched
749 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
750 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
751 /* Remove the grouup we added above */
752 pairdelete(&request->packet->vps, PW_SQL_GROUP);
753 pairfree(&check_tmp);
756 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
757 radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
758 group_list_tmp->groupname);
759 /* Remove the grouup we added above */
760 pairdelete(&request->packet->vps, PW_SQL_GROUP);
761 pairfree(&check_tmp);
762 pairfree(&reply_tmp);
765 *dofallthrough = fallthrough(reply_tmp);
766 pairxlatmove(request, &request->reply->vps, &reply_tmp);
767 pairxlatmove(request, &request->config_items, &check_tmp);
771 * Delete the Sql-Group we added above
772 * And clear out the pairlists
774 pairdelete(&request->packet->vps, PW_SQL_GROUP);
775 pairfree(&check_tmp);
776 pairfree(&reply_tmp);
779 sql_grouplist_free(&group_list);
784 static int rlm_sql_detach(void *instance)
786 SQL_INST *inst = instance;
788 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
797 if (inst->config->xlat_name) {
798 xlat_unregister(inst->config->xlat_name,(RAD_XLAT_FUNC)sql_xlat);
799 free(inst->config->xlat_name);
803 * Free up dynamically allocated string pointers.
805 for (i = 0; module_config[i].name != NULL; i++) {
807 if (module_config[i].type != PW_TYPE_STRING_PTR) {
812 * Treat 'config' as an opaque array of bytes,
813 * and take the offset into it. There's a
814 * (char*) pointer at that offset, and we want
817 p = (char **) (((char *)inst->config) + module_config[i].offset);
818 if (!*p) { /* nothing allocated */
825 * Catch multiple instances of the module.
827 if (allowed_chars == inst->config->allowed_chars) {
828 allowed_chars = NULL;
837 * FIXME: Call the modules 'destroy' function?
839 lt_dlclose(inst->handle); /* ignore any errors */
846 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
849 const char *xlat_name;
851 inst = rad_malloc(sizeof(SQL_INST));
852 memset(inst, 0, sizeof(SQL_INST));
854 inst->config = rad_malloc(sizeof(SQL_CONFIG));
855 memset(inst->config, 0, sizeof(SQL_CONFIG));
858 * Export these methods, too. This avoids RTDL_GLOBAL.
860 inst->sql_set_user = sql_set_user;
861 inst->sql_get_socket = sql_get_socket;
862 inst->sql_release_socket = sql_release_socket;
863 inst->sql_escape_func = sql_escape_func;
864 inst->sql_query = rlm_sql_query;
865 inst->sql_select_query = rlm_sql_select_query;
866 inst->sql_fetch_row = rlm_sql_fetch_row;
869 * If the configuration parameters can't be parsed, then
872 if (cf_section_parse(conf, inst->config, module_config) < 0) {
873 rlm_sql_detach(inst);
877 xlat_name = cf_section_name2(conf);
878 if (xlat_name == NULL)
879 xlat_name = cf_section_name1(conf);
881 inst->config->xlat_name = strdup(xlat_name);
882 xlat_register(xlat_name, (RAD_XLAT_FUNC)sql_xlat, inst);
885 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
886 radlog(L_ERR, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
887 inst->config->xlat_name, MAX_SQL_SOCKS);
888 rlm_sql_detach(inst);
893 * Sanity check for crazy people.
895 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
896 radlog(L_ERR, "\"%s\" is NOT an SQL driver!",
897 inst->config->sql_driver);
898 rlm_sql_detach(inst);
902 inst->handle = lt_dlopenext(inst->config->sql_driver);
903 if (inst->handle == NULL) {
904 radlog(L_ERR, "Could not link driver %s: %s",
905 inst->config->sql_driver,
907 radlog(L_ERR, "Make sure it (and all its dependent libraries!) are in the search path of your system's ld.");
908 rlm_sql_detach(inst);
912 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
914 radlog(L_ERR, "Could not link symbol %s: %s",
915 inst->config->sql_driver,
917 rlm_sql_detach(inst);
921 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
922 inst->config->xlat_name, inst->config->sql_driver,
924 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
925 inst->config->xlat_name, inst->config->sql_login,
926 inst->config->sql_server, inst->config->sql_port,
927 inst->config->sql_db);
929 if (sql_init_socketpool(inst) < 0) {
930 rlm_sql_detach(inst);
934 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
936 if (inst->config->do_clients){
937 if (generate_sql_clients(inst) == -1){
938 radlog(L_ERR, "Failed to load clients from SQL.");
939 rlm_sql_detach(inst);
943 allowed_chars = inst->config->allowed_chars;
947 return RLM_MODULE_OK;
951 static int rlm_sql_authorize(void *instance, REQUEST * request)
953 VALUE_PAIR *check_tmp = NULL;
954 VALUE_PAIR *reply_tmp = NULL;
955 VALUE_PAIR *user_profile = NULL;
957 int dofallthrough = 1;
960 SQL_INST *inst = instance;
961 char querystr[MAX_QUERY_LEN];
962 char sqlusername[MAX_STRING_LEN];
964 * the profile username is used as the sqlusername during
965 * profile checking so that we don't overwrite the orignal
968 char profileusername[MAX_STRING_LEN];
971 * Set, escape, and check the user attr here
973 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
974 return RLM_MODULE_FAIL;
980 sqlsocket = sql_get_socket(inst);
981 if (sqlsocket == NULL) {
982 /* Remove the username we (maybe) added above */
983 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
984 return RLM_MODULE_FAIL;
989 * After this point, ALL 'return's MUST release the SQL socket!
993 * Alright, start by getting the specific entry for the user
995 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func)) {
996 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
997 sql_release_socket(inst, sqlsocket);
998 /* Remove the username we (maybe) added above */
999 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1000 return RLM_MODULE_FAIL;
1002 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
1004 radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
1005 sql_release_socket(inst, sqlsocket);
1006 /* Remove the username we (maybe) added above */
1007 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1008 pairfree(&check_tmp);
1009 return RLM_MODULE_FAIL;
1010 } else if (rows > 0) {
1012 * Only do this if *some* check pairs were returned
1014 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
1016 RDEBUG2("User found in radcheck table");
1018 if (inst->config->authorize_reply_query &&
1019 *inst->config->authorize_reply_query) {
1022 * Now get the reply pairs since the paircompare matched
1024 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func)) {
1025 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
1026 sql_release_socket(inst, sqlsocket);
1027 /* Remove the username we (maybe) added above */
1028 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1029 pairfree(&check_tmp);
1030 return RLM_MODULE_FAIL;
1032 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
1033 radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
1034 sql_release_socket(inst, sqlsocket);
1035 /* Remove the username we (maybe) added above */
1036 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1037 pairfree(&check_tmp);
1038 pairfree(&reply_tmp);
1039 return RLM_MODULE_FAIL;
1042 if (!inst->config->read_groups)
1043 dofallthrough = fallthrough(reply_tmp);
1044 pairxlatmove(request, &request->reply->vps, &reply_tmp);
1046 pairxlatmove(request, &request->config_items, &check_tmp);
1051 * Clear out the pairlists
1053 pairfree(&check_tmp);
1054 pairfree(&reply_tmp);
1057 * dofallthrough is set to 1 by default so that if the user information
1058 * is not found, we will still process groups. If the user information,
1059 * however, *is* found, Fall-Through must be set in order to process
1060 * the groups as well
1062 if (dofallthrough) {
1063 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1065 radlog_request(L_ERR, 0, request, "Error processing groups; rejecting user");
1066 sql_release_socket(inst, sqlsocket);
1067 /* Remove the username we (maybe) added above */
1068 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1069 return RLM_MODULE_FAIL;
1070 } else if (rows > 0) {
1076 * repeat the above process with the default profile or User-Profile
1078 if (dofallthrough) {
1079 int profile_found = 0;
1081 * Check for a default_profile or for a User-Profile.
1083 user_profile = pairfind(request->config_items, PW_USER_PROFILE);
1084 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
1085 char *profile = inst->config->default_profile;
1087 if (user_profile != NULL)
1088 profile = user_profile->vp_strvalue;
1089 if (profile && strlen(profile)){
1090 RDEBUG("Checking profile %s", profile);
1091 if (sql_set_user(inst, request, profileusername, profile) < 0) {
1092 radlog_request(L_ERR, 0, request, "Error setting profile; rejecting user");
1093 sql_release_socket(inst, sqlsocket);
1094 /* Remove the username we (maybe) added above */
1095 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1096 return RLM_MODULE_FAIL;
1103 if (profile_found) {
1104 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1106 radlog_request(L_ERR, 0, request, "Error processing profile groups; rejecting user");
1107 sql_release_socket(inst, sqlsocket);
1108 /* Remove the username we (maybe) added above */
1109 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1110 return RLM_MODULE_FAIL;
1111 } else if (rows > 0) {
1117 /* Remove the username we (maybe) added above */
1118 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1119 sql_release_socket(inst, sqlsocket);
1122 RDEBUG("User %s not found", sqlusername);
1123 return RLM_MODULE_NOTFOUND;
1125 return RLM_MODULE_OK;
1130 * Accounting: save the account data to our sql table
1132 static int rlm_sql_accounting(void *instance, REQUEST * request) {
1134 SQLSOCK *sqlsocket = NULL;
1136 SQL_INST *inst = instance;
1137 int ret = RLM_MODULE_OK;
1138 int numaffected = 0;
1139 int acctstatustype = 0;
1140 char querystr[MAX_QUERY_LEN];
1141 char logstr[MAX_QUERY_LEN];
1142 char sqlusername[MAX_STRING_LEN];
1144 #ifdef CISCO_ACCOUNTING_HACK
1145 int acctsessiontime = 0;
1148 memset(querystr, 0, MAX_QUERY_LEN);
1151 * Find the Acct Status Type
1153 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
1154 acctstatustype = pair->vp_integer;
1156 radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1157 radlog_request(L_ERR, 0, request, "%s", logstr);
1158 return RLM_MODULE_INVALID;
1161 switch (acctstatustype) {
1163 * The Terminal server informed us that it was rebooted
1164 * STOP all records from this NAS
1166 case PW_STATUS_ACCOUNTING_ON:
1167 case PW_STATUS_ACCOUNTING_OFF:
1168 RDEBUG("Received Acct On/Off packet");
1169 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_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_request(L_ERR, 0, request, "Couldn't update SQL accounting for Acct On/Off packet - %s",
1178 (inst->module->sql_error)(sqlsocket, inst->config));
1179 ret = RLM_MODULE_FAIL;
1181 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1187 * Got an update accounting packet
1189 case PW_STATUS_ALIVE:
1192 * Set, escape, and check the user attr here
1194 sql_set_user(inst, request, sqlusername, NULL);
1196 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
1197 query_log(request, inst, querystr);
1199 sqlsocket = sql_get_socket(inst);
1200 if (sqlsocket == NULL)
1201 return(RLM_MODULE_FAIL);
1202 if (*querystr) { /* non-empty query */
1203 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1204 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting ALIVE record - %s",
1205 (inst->module->sql_error)(sqlsocket, inst->config));
1206 ret = RLM_MODULE_FAIL;
1209 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1210 if (numaffected < 1) {
1213 * If our update above didn't match anything
1214 * we assume it's because we haven't seen a
1215 * matching Start record. So we have to
1216 * insert this update rather than do an update
1218 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
1219 query_log(request, inst, querystr);
1220 if (*querystr) { /* non-empty query */
1221 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1222 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting ALIVE record - %s",
1223 (inst->module->sql_error)(sqlsocket, inst->config));
1224 ret = RLM_MODULE_FAIL;
1226 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1230 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1235 * Got accounting start packet
1237 case PW_STATUS_START:
1240 * Set, escape, and check the user attr here
1242 sql_set_user(inst, request, sqlusername, NULL);
1244 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
1245 query_log(request, inst, querystr);
1247 sqlsocket = sql_get_socket(inst);
1248 if (sqlsocket == NULL)
1249 return(RLM_MODULE_FAIL);
1250 if (*querystr) { /* non-empty query */
1251 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1252 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting START record - %s",
1253 (inst->module->sql_error)(sqlsocket, inst->config));
1256 * We failed the insert above. It's probably because
1257 * the stop record came before the start. We try
1258 * our alternate query now (typically an UPDATE)
1260 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1261 query_log(request, inst, querystr);
1263 if (*querystr) { /* non-empty query */
1264 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1265 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting START record - %s",
1266 (inst->module->sql_error)(sqlsocket, inst->config));
1267 ret = RLM_MODULE_FAIL;
1269 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1272 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1277 * Got accounting stop packet
1279 case PW_STATUS_STOP:
1282 * Set, escape, and check the user attr here
1284 sql_set_user(inst, request, sqlusername, NULL);
1286 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1287 query_log(request, inst, querystr);
1289 sqlsocket = sql_get_socket(inst);
1290 if (sqlsocket == NULL)
1291 return(RLM_MODULE_FAIL);
1292 if (*querystr) { /* non-empty query */
1293 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1294 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting STOP record - %s",
1295 (inst->module->sql_error)(sqlsocket, inst->config));
1296 ret = RLM_MODULE_FAIL;
1299 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1300 if (numaffected < 1) {
1302 * If our update above didn't match anything
1303 * we assume it's because we haven't seen a
1304 * matching Start record. So we have to
1305 * insert this stop rather than do an update
1307 #ifdef CISCO_ACCOUNTING_HACK
1309 * If stop but zero session length AND no previous
1310 * session found, drop it as in invalid packet
1311 * This is to fix CISCO's aaa from filling our
1312 * table with bogus crap
1314 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
1315 acctsessiontime = pair->vp_integer;
1317 if (acctsessiontime <= 0) {
1318 radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1319 radlog_request(L_ERR, 0, request, "%s", logstr);
1320 sql_release_socket(inst, sqlsocket);
1321 ret = RLM_MODULE_NOOP;
1325 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1326 query_log(request, inst, querystr);
1328 if (*querystr) { /* non-empty query */
1329 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1330 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting STOP record - %s",
1332 (inst->module->sql_error)(sqlsocket, inst->config));
1333 ret = RLM_MODULE_FAIL;
1335 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1339 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1344 * Anything else is ignored.
1347 RDEBUG("Unsupported Acct-Status-Type = %d",
1349 return RLM_MODULE_NOOP;
1354 sql_release_socket(inst, sqlsocket);
1361 * See if a user is already logged in. Sets request->simul_count to the
1362 * current session count for this user.
1364 * Check twice. If on the first pass the user exceeds his
1365 * max. number of logins, do a second pass and validate all
1366 * logins by querying the terminal server (using eg. SNMP).
1369 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1371 SQL_INST *inst = instance;
1373 char querystr[MAX_QUERY_LEN];
1374 char sqlusername[MAX_STRING_LEN];
1377 char *call_num = NULL;
1380 uint32_t nas_addr = 0;
1383 /* If simul_count_query is not defined, we don't do any checking */
1384 if (!inst->config->simul_count_query ||
1385 (inst->config->simul_count_query[0] == 0)) {
1386 return RLM_MODULE_NOOP;
1389 if((request->username == NULL) || (request->username->length == 0)) {
1390 radlog_request(L_ERR, 0, request, "Zero Length username not permitted\n");
1391 return RLM_MODULE_INVALID;
1395 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1396 return RLM_MODULE_FAIL;
1398 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1400 /* initialize the sql socket */
1401 sqlsocket = sql_get_socket(inst);
1402 if(sqlsocket == NULL)
1403 return RLM_MODULE_FAIL;
1405 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1406 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1407 sql_release_socket(inst, sqlsocket);
1408 return RLM_MODULE_FAIL;
1411 ret = rlm_sql_fetch_row(sqlsocket, inst);
1414 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1415 sql_release_socket(inst, sqlsocket);
1416 return RLM_MODULE_FAIL;
1419 row = sqlsocket->row;
1421 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1422 sql_release_socket(inst, sqlsocket);
1423 return RLM_MODULE_FAIL;
1426 request->simul_count = atoi(row[0]);
1427 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1429 if(request->simul_count < request->simul_max) {
1430 sql_release_socket(inst, sqlsocket);
1431 return RLM_MODULE_OK;
1435 * Looks like too many sessions, so let's start verifying
1436 * them, unless told to rely on count query only.
1438 if (!inst->config->simul_verify_query ||
1439 (inst->config->simul_verify_query[0] == '\0')) {
1440 sql_release_socket(inst, sqlsocket);
1441 return RLM_MODULE_OK;
1444 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1445 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1446 radlog_request(L_ERR, 0, request, "Database query error");
1447 sql_release_socket(inst, sqlsocket);
1448 return RLM_MODULE_FAIL;
1452 * Setup some stuff, like for MPP detection.
1454 request->simul_count = 0;
1456 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1457 ipno = vp->vp_ipaddr;
1458 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1459 call_num = vp->vp_strvalue;
1462 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1463 row = sqlsocket->row;
1467 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1468 sql_release_socket(inst, sqlsocket);
1469 RDEBUG("Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1470 return RLM_MODULE_FAIL;
1473 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1474 sql_release_socket(inst, sqlsocket);
1475 RDEBUG("Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1476 return RLM_MODULE_FAIL;
1479 nas_addr = inet_addr(row[3]);
1481 nas_port = atoi(row[4]);
1483 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1487 * Stale record - zap it.
1489 if (inst->config->deletestalesessions == TRUE) {
1490 uint32_t framed_addr = 0;
1495 framed_addr = inet_addr(row[5]);
1497 if (strcmp(row[7], "PPP") == 0)
1499 else if (strcmp(row[7], "SLIP") == 0)
1503 sess_time = atoi(row[8]);
1504 session_zap(request, nas_addr, nas_port,
1505 row[2], row[1], framed_addr,
1509 else if (check == 1) {
1511 * User is still logged in.
1513 ++request->simul_count;
1516 * Does it look like a MPP attempt?
1518 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1519 request->simul_mpp = 2;
1520 else if (row[6] && call_num &&
1521 !strncmp(row[6],call_num,16))
1522 request->simul_mpp = 2;
1526 * Failed to check the terminal server for
1527 * duplicate logins: return an error.
1529 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1530 sql_release_socket(inst, sqlsocket);
1531 radlog_request(L_ERR, 0, request, "Failed to check the terminal server for user '%s'.", row[2]);
1532 return RLM_MODULE_FAIL;
1536 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1537 sql_release_socket(inst, sqlsocket);
1540 * The Auth module apparently looks at request->simul_count,
1541 * not the return value of this module when deciding to deny
1542 * a call for too many sessions.
1544 return RLM_MODULE_OK;
1548 * Execute postauth_query after authentication
1550 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1551 SQLSOCK *sqlsocket = NULL;
1552 SQL_INST *inst = instance;
1553 char querystr[MAX_QUERY_LEN];
1554 char sqlusername[MAX_STRING_LEN];
1556 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1557 return RLM_MODULE_FAIL;
1559 /* If postauth_query is not defined, we stop here */
1560 if (!inst->config->postauth_query ||
1561 (inst->config->postauth_query[0] == '\0'))
1562 return RLM_MODULE_NOOP;
1564 /* Expand variables in the query */
1565 memset(querystr, 0, MAX_QUERY_LEN);
1566 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1567 request, sql_escape_func);
1568 query_log(request, inst, querystr);
1569 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1570 inst->config->xlat_name, querystr);
1572 /* Initialize the sql socket */
1573 sqlsocket = sql_get_socket(inst);
1574 if (sqlsocket == NULL)
1575 return RLM_MODULE_FAIL;
1577 /* Process the query */
1578 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1579 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1580 inst->config->xlat_name,
1581 (inst->module->sql_error)(sqlsocket, inst->config));
1582 sql_release_socket(inst, sqlsocket);
1583 return RLM_MODULE_FAIL;
1585 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1587 sql_release_socket(inst, sqlsocket);
1588 return RLM_MODULE_OK;
1591 /* globally exported name */
1592 module_t rlm_sql = {
1595 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1596 rlm_sql_instantiate, /* instantiation */
1597 rlm_sql_detach, /* detach */
1599 NULL, /* authentication */
1600 rlm_sql_authorize, /* authorization */
1601 NULL, /* preaccounting */
1602 rlm_sql_accounting, /* accounting */
1603 rlm_sql_checksimul, /* checksimul */
1604 NULL, /* pre-proxy */
1605 NULL, /* post-proxy */
1606 rlm_sql_postauth /* post-auth */