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>
37 static char *allowed_chars = NULL;
39 static const CONF_PARSER module_config[] = {
40 {"driver",PW_TYPE_STRING_PTR,
41 offsetof(SQL_CONFIG,sql_driver), NULL, "mysql"},
42 {"server",PW_TYPE_STRING_PTR,
43 offsetof(SQL_CONFIG,sql_server), NULL, "localhost"},
44 {"port",PW_TYPE_STRING_PTR,
45 offsetof(SQL_CONFIG,sql_port), NULL, ""},
46 {"login", PW_TYPE_STRING_PTR,
47 offsetof(SQL_CONFIG,sql_login), NULL, ""},
48 {"password", PW_TYPE_STRING_PTR,
49 offsetof(SQL_CONFIG,sql_password), NULL, ""},
50 {"radius_db", PW_TYPE_STRING_PTR,
51 offsetof(SQL_CONFIG,sql_db), NULL, "radius"},
52 {"read_groups", PW_TYPE_BOOLEAN,
53 offsetof(SQL_CONFIG,read_groups), NULL, "yes"},
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, 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->vp_integer : 0;
125 static int generate_sql_clients(SQL_INST *inst);
126 static size_t sql_escape_func(char *out, size_t outlen, const char *in);
129 * sql xlat function. Right now only SELECTs are supported. Only
130 * the first element of the SELECT result will be used.
132 static int sql_xlat(void *instance, REQUEST *request,
133 char *fmt, char *out, size_t freespace,
134 UNUSED RADIUS_ESCAPE_STRING func)
138 SQL_INST *inst = instance;
139 char querystr[MAX_QUERY_LEN];
140 char sqlusername[MAX_STRING_LEN];
143 DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name);
145 * Add SQL-User-Name attribute just in case it is needed
146 * We could search the string fmt for SQL-User-Name to see if this is
149 sql_set_user(inst, request, sqlusername, NULL);
151 * Do an xlat on the provided string (nice recursive operation).
153 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
154 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
155 inst->config->xlat_name);
159 query_log(request, inst,querystr);
160 sqlsocket = sql_get_socket(inst);
161 if (sqlsocket == NULL)
163 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
164 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
165 inst->config->xlat_name,querystr,
166 (inst->module->sql_error)(sqlsocket, inst->config));
167 sql_release_socket(inst,sqlsocket);
171 ret = rlm_sql_fetch_row(sqlsocket, inst);
174 DEBUG("rlm_sql (%s): SQL query did not succeed",
175 inst->config->xlat_name);
176 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
177 sql_release_socket(inst,sqlsocket);
181 row = sqlsocket->row;
183 DEBUG("rlm_sql (%s): SQL query did not return any results",
184 inst->config->xlat_name);
185 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
186 sql_release_socket(inst,sqlsocket);
191 DEBUG("rlm_sql (%s): row[0] returned NULL",
192 inst->config->xlat_name);
193 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
194 sql_release_socket(inst,sqlsocket);
197 ret = strlen(row[0]);
198 if (ret >= freespace){
199 DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space",
200 inst->config->xlat_name);
201 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
202 sql_release_socket(inst,sqlsocket);
206 strlcpy(out,row[0],freespace);
208 DEBUG("rlm_sql (%s): - sql_xlat finished",
209 inst->config->xlat_name);
211 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
212 sql_release_socket(inst,sqlsocket);
216 static int generate_sql_clients(SQL_INST *inst)
220 char querystr[MAX_QUERY_LEN];
222 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 (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)
258 * 5. Virtual Server (optional)
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, virtual_server)
329 c->secret = strdup(row[4]);
330 c->shortname = strdup(row[2]);
332 c->nastype = strdup(row[3]);
334 numf = (inst->module->sql_num_fields)(sqlsocket, inst->config);
335 if ((numf > 5) && (row[5] != NULL)) c->server = strdup(row[5]);
337 DEBUG("rlm_sql (%s): Adding client %s (%s, server=%s) to clients list",
338 inst->config->xlat_name,
339 c->longname,c->shortname, c->server ? c->server : "<none>");
340 if (!client_add(NULL, c)) {
341 DEBUG("rlm_sql (%s): Failed to add client %s (%s) to clients list. Maybe there's a duplicate?",
342 inst->config->xlat_name,
343 c->longname,c->shortname);
348 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
349 sql_release_socket(inst, sqlsocket);
356 * Translate the SQL queries.
358 static size_t sql_escape_func(char *out, size_t outlen, const char *in)
364 * Non-printable characters get replaced with their
365 * mime-encoded equivalents.
368 strchr(allowed_chars, *in) == NULL) {
370 * Only 3 or less bytes available.
376 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
385 * Only one byte left.
405 * Set the SQL user name.
407 * We don't call the escape function here. The resulting string
408 * will be escaped later in the queries xlat so we don't need to
409 * escape it twice. (it will make things wrong if we have an
410 * escape candidate character in the username)
412 int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
415 char tmpuser[MAX_STRING_LEN];
418 sqlusername[0]= '\0';
420 /* Remove any user attr we added previously */
421 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
423 if (username != NULL) {
424 strlcpy(tmpuser, username, sizeof(tmpuser));
425 } else if (strlen(inst->config->query_user)) {
426 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
431 strlcpy(sqlusername, tmpuser, MAX_STRING_LEN);
432 DEBUG2("rlm_sql (%s): sql_set_user escaped user --> '%s'",
433 inst->config->xlat_name, sqlusername);
434 vp = pairmake("SQL-User-Name", sqlusername, 0);
436 radlog(L_ERR, "%s", librad_errstr);
440 pairadd(&request->packet->vps, vp);
446 static void sql_grouplist_free (SQL_GROUPLIST **group_list)
452 *group_list = (*group_list)->next;
458 static int sql_get_grouplist (SQL_INST *inst, SQLSOCK *sqlsocket, REQUEST *request, SQL_GROUPLIST **group_list)
460 char querystr[MAX_QUERY_LEN];
463 SQL_GROUPLIST *group_list_tmp;
465 /* NOTE: sql_set_user should have been run before calling this function */
467 group_list_tmp = *group_list = NULL;
469 if (!inst->config->groupmemb_query ||
470 (inst->config->groupmemb_query[0] == 0))
473 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, request, sql_escape_func)) {
474 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
475 inst->config->xlat_name);
479 if (rlm_sql_select_query(sqlsocket, inst, querystr) < 0) {
480 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
481 inst->config->xlat_name,querystr,
482 (inst->module->sql_error)(sqlsocket,inst->config));
485 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
486 row = sqlsocket->row;
490 DEBUG("rlm_sql (%s): row[0] returned NULL",
491 inst->config->xlat_name);
492 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
493 sql_grouplist_free(group_list);
496 if (*group_list == NULL) {
497 *group_list = rad_malloc(sizeof(SQL_GROUPLIST));
498 group_list_tmp = *group_list;
500 group_list_tmp->next = rad_malloc(sizeof(SQL_GROUPLIST));
501 group_list_tmp = group_list_tmp->next;
503 group_list_tmp->next = NULL;
504 strlcpy(group_list_tmp->groupname, row[0], MAX_STRING_LEN);
507 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
514 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
515 * with the group memberships reciding in sql
516 * The group membership query should only return one element which is the username. The returned
517 * username will then be checked with the passed check string.
520 static int sql_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
521 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
524 SQL_INST *inst = instance;
525 char sqlusername[MAX_STRING_LEN];
526 SQL_GROUPLIST *group_list, *group_list_tmp;
528 check_pairs = check_pairs;
529 reply_pairs = reply_pairs;
532 DEBUG("rlm_sql (%s): - sql_groupcmp", inst->config->xlat_name);
533 if (!check || !check->vp_strvalue || !check->length){
534 DEBUG("rlm_sql (%s): sql_groupcmp: Illegal group name",
535 inst->config->xlat_name);
539 DEBUG("rlm_sql (%s): sql_groupcmp: NULL request",
540 inst->config->xlat_name);
544 * Set, escape, and check the user attr here
546 if (sql_set_user(inst, req, sqlusername, NULL) < 0)
550 * Get a socket for this lookup
552 sqlsocket = sql_get_socket(inst);
553 if (sqlsocket == NULL) {
554 /* Remove the username we (maybe) added above */
555 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
560 * Get the list of groups this user is a member of
562 if (sql_get_grouplist(inst, sqlsocket, req, &group_list) < 0) {
563 radlog(L_ERR, "rlm_sql (%s): Error getting group membership",
564 inst->config->xlat_name);
565 /* Remove the username we (maybe) added above */
566 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
567 sql_release_socket(inst, sqlsocket);
571 for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) {
572 if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){
573 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User is a member of group %s",
574 inst->config->xlat_name,
575 (char *)check->vp_strvalue);
576 /* Free the grouplist */
577 sql_grouplist_free(&group_list);
578 /* Remove the username we (maybe) added above */
579 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
580 sql_release_socket(inst, sqlsocket);
585 /* Free the grouplist */
586 sql_grouplist_free(&group_list);
587 /* Remove the username we (maybe) added above */
588 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
589 sql_release_socket(inst,sqlsocket);
591 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User is NOT a member of group %s",
592 inst->config->xlat_name, (char *)check->vp_strvalue);
599 static int rlm_sql_process_groups(SQL_INST *inst, REQUEST *request, SQLSOCK *sqlsocket, int *dofallthrough)
601 VALUE_PAIR *check_tmp = NULL;
602 VALUE_PAIR *reply_tmp = NULL;
603 SQL_GROUPLIST *group_list, *group_list_tmp;
604 VALUE_PAIR *sql_group = NULL;
605 char querystr[MAX_QUERY_LEN];
610 * Get the list of groups this user is a member of
612 if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
613 radlog(L_ERR, "rlm_sql (%s): Error retrieving group list",
614 inst->config->xlat_name);
618 for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) {
620 * Add the Sql-Group attribute to the request list so we know
621 * which group we're retrieving attributes for
623 sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ);
625 radlog(L_ERR, "rlm_sql (%s): Error creating Sql-Group attribute",
626 inst->config->xlat_name);
629 pairadd(&request->packet->vps, sql_group);
630 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func)) {
631 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
632 inst->config->xlat_name);
633 /* Remove the grouup we added above */
634 pairdelete(&request->packet->vps, PW_SQL_GROUP);
637 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
639 radlog(L_ERR, "rlm_sql (%s): Error retrieving check pairs for group %s",
640 inst->config->xlat_name, group_list_tmp->groupname);
641 /* Remove the grouup we added above */
642 pairdelete(&request->packet->vps, PW_SQL_GROUP);
643 pairfree(&check_tmp);
645 } else if (rows > 0) {
647 * Only do this if *some* check pairs were returned
649 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
651 DEBUG2("rlm_sql (%s): User found in group %s",
652 inst->config->xlat_name, group_list_tmp->groupname);
654 * Now get the reply pairs since the paircompare matched
656 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
657 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
658 inst->config->xlat_name);
659 /* Remove the grouup we added above */
660 pairdelete(&request->packet->vps, PW_SQL_GROUP);
661 pairfree(&check_tmp);
664 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
665 radlog(L_ERR, "rlm_sql (%s): Error retrieving reply pairs for group %s",
666 inst->config->xlat_name, group_list_tmp->groupname);
667 /* Remove the grouup we added above */
668 pairdelete(&request->packet->vps, PW_SQL_GROUP);
669 pairfree(&check_tmp);
670 pairfree(&reply_tmp);
673 *dofallthrough = fallthrough(reply_tmp);
674 pairxlatmove(request, &request->reply->vps, &reply_tmp);
675 pairxlatmove(request, &request->config_items, &check_tmp);
679 * rows == 0. This is like having the username on a line
680 * in the user's file with no check vp's. As such, we treat
681 * it as found and add the reply attributes, so that we
682 * match expected behavior
685 DEBUG2("rlm_sql (%s): User found in group %s",
686 inst->config->xlat_name, group_list_tmp->groupname);
688 * Now get the reply pairs since the paircompare matched
690 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
691 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
692 inst->config->xlat_name);
693 /* Remove the grouup we added above */
694 pairdelete(&request->packet->vps, PW_SQL_GROUP);
695 pairfree(&check_tmp);
698 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
699 radlog(L_ERR, "rlm_sql (%s): Error retrieving reply pairs for group %s",
700 inst->config->xlat_name, group_list_tmp->groupname);
701 /* Remove the grouup we added above */
702 pairdelete(&request->packet->vps, PW_SQL_GROUP);
703 pairfree(&check_tmp);
704 pairfree(&reply_tmp);
707 *dofallthrough = fallthrough(reply_tmp);
708 pairxlatmove(request, &request->reply->vps, &reply_tmp);
709 pairxlatmove(request, &request->config_items, &check_tmp);
713 * Delete the Sql-Group we added above
714 * And clear out the pairlists
716 pairdelete(&request->packet->vps, PW_SQL_GROUP);
717 pairfree(&check_tmp);
718 pairfree(&reply_tmp);
721 sql_grouplist_free(&group_list);
726 static int rlm_sql_detach(void *instance)
728 SQL_INST *inst = instance;
730 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
739 if (inst->config->xlat_name) {
740 xlat_unregister(inst->config->xlat_name,(RAD_XLAT_FUNC)sql_xlat);
741 free(inst->config->xlat_name);
745 * Free up dynamically allocated string pointers.
747 for (i = 0; module_config[i].name != NULL; i++) {
749 if (module_config[i].type != PW_TYPE_STRING_PTR) {
754 * Treat 'config' as an opaque array of bytes,
755 * and take the offset into it. There's a
756 * (char*) pointer at that offset, and we want
759 p = (char **) (((char *)inst->config) + module_config[i].offset);
760 if (!*p) { /* nothing allocated */
766 allowed_chars = NULL;
774 * FIXME: Call the modules 'destroy' function?
776 lt_dlclose(inst->handle); /* ignore any errors */
783 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
786 const char *xlat_name;
788 inst = rad_malloc(sizeof(SQL_INST));
789 memset(inst, 0, sizeof(SQL_INST));
791 inst->config = rad_malloc(sizeof(SQL_CONFIG));
792 memset(inst->config, 0, sizeof(SQL_CONFIG));
795 * If the configuration parameters can't be parsed, then
798 if (cf_section_parse(conf, inst->config, module_config) < 0) {
799 rlm_sql_detach(inst);
803 xlat_name = cf_section_name2(conf);
804 if (xlat_name == NULL)
805 xlat_name = cf_section_name1(conf);
807 inst->config->xlat_name = strdup(xlat_name);
808 xlat_register(xlat_name, (RAD_XLAT_FUNC)sql_xlat, inst);
811 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
812 radlog(L_ERR | L_CONS, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
813 inst->config->xlat_name, MAX_SQL_SOCKS);
814 rlm_sql_detach(inst);
819 * Sanity check for crazy people.
821 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
822 radlog(L_ERR, "rlm_sql (%s): \"%s\" is NOT an SQL driver!",
823 inst->config->xlat_name, inst->config->sql_driver);
824 rlm_sql_detach(inst);
828 inst->handle = lt_dlopenext(inst->config->sql_driver);
829 if (inst->handle == NULL) {
830 radlog(L_ERR, "rlm_sql (%s): Could not link driver %s: %s",
831 inst->config->xlat_name, inst->config->sql_driver,
833 radlog(L_ERR, "rlm_sql (%s): Make sure it (and all its dependent libraries!) are in the search path of your system's ld.",
834 inst->config->xlat_name);
835 rlm_sql_detach(inst);
839 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
841 radlog(L_ERR, "rlm_sql (%s): Could not link symbol %s: %s",
842 inst->config->xlat_name, inst->config->sql_driver,
844 rlm_sql_detach(inst);
848 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
849 inst->config->xlat_name, inst->config->sql_driver,
851 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
852 inst->config->xlat_name, inst->config->sql_login,
853 inst->config->sql_server, inst->config->sql_port,
854 inst->config->sql_db);
856 if (sql_init_socketpool(inst) < 0) {
857 rlm_sql_detach(inst);
860 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
862 if (inst->config->do_clients){
863 if (generate_sql_clients(inst) == -1){
864 radlog(L_ERR, "rlm_sql (%s): generate_sql_clients() returned error",inst->config->xlat_name);
865 rlm_sql_detach(inst);
869 allowed_chars = inst->config->allowed_chars;
873 return RLM_MODULE_OK;
877 static int rlm_sql_authorize(void *instance, REQUEST * request)
879 VALUE_PAIR *check_tmp = NULL;
880 VALUE_PAIR *reply_tmp = NULL;
881 VALUE_PAIR *user_profile = NULL;
883 int dofallthrough = 1;
886 SQL_INST *inst = instance;
887 char querystr[MAX_QUERY_LEN];
888 char sqlusername[MAX_STRING_LEN];
890 * the profile username is used as the sqlusername during
891 * profile checking so that we don't overwrite the orignal
894 char profileusername[MAX_STRING_LEN];
897 * Set, escape, and check the user attr here
899 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
900 return RLM_MODULE_FAIL;
906 sqlsocket = sql_get_socket(inst);
907 if (sqlsocket == NULL) {
908 /* Remove the username we (maybe) added above */
909 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
910 return RLM_MODULE_FAIL;
915 * After this point, ALL 'return's MUST release the SQL socket!
919 * Alright, start by getting the specific entry for the user
921 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func)) {
922 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
923 inst->config->xlat_name);
924 sql_release_socket(inst, sqlsocket);
925 /* Remove the username we (maybe) added above */
926 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
927 return RLM_MODULE_FAIL;
929 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
931 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
932 inst->config->xlat_name);
933 sql_release_socket(inst, sqlsocket);
934 /* Remove the username we (maybe) added above */
935 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
936 pairfree(&check_tmp);
937 return RLM_MODULE_FAIL;
938 } else if (rows > 0) {
940 * Only do this if *some* check pairs were returned
942 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
944 DEBUG2("rlm_sql (%s): User found in radcheck table", inst->config->xlat_name);
946 * Now get the reply pairs since the paircompare matched
948 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func)) {
949 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
950 inst->config->xlat_name);
951 sql_release_socket(inst, sqlsocket);
952 /* Remove the username we (maybe) added above */
953 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
954 pairfree(&check_tmp);
955 return RLM_MODULE_FAIL;
957 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
958 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
959 inst->config->xlat_name);
960 sql_release_socket(inst, sqlsocket);
961 /* Remove the username we (maybe) added above */
962 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
963 pairfree(&check_tmp);
964 pairfree(&reply_tmp);
965 return RLM_MODULE_FAIL;
967 if (!inst->config->read_groups)
968 dofallthrough = fallthrough(reply_tmp);
969 pairxlatmove(request, &request->reply->vps, &reply_tmp);
970 pairxlatmove(request, &request->config_items, &check_tmp);
975 * Clear out the pairlists
977 pairfree(&check_tmp);
978 pairfree(&reply_tmp);
981 * dofallthrough is set to 1 by default so that if the user information
982 * is not found, we will still process groups. If the user information,
983 * however, *is* found, Fall-Through must be set in order to process
987 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
989 radlog(L_ERR, "rlm_sql (%s): Error processing groups; rejecting user",
990 inst->config->xlat_name);
991 sql_release_socket(inst, sqlsocket);
992 /* Remove the username we (maybe) added above */
993 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
994 return RLM_MODULE_FAIL;
995 } else if (rows > 0) {
1001 * repeat the above process with the default profile or User-Profile
1003 if (dofallthrough) {
1004 int profile_found = 0;
1006 * Check for a default_profile or for a User-Profile.
1008 user_profile = pairfind(request->config_items, PW_USER_PROFILE);
1009 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
1010 char *profile = inst->config->default_profile;
1012 if (user_profile != NULL)
1013 profile = user_profile->vp_strvalue;
1014 if (profile && strlen(profile)){
1015 radlog(L_DBG, "rlm_sql (%s): Checking profile %s",
1016 inst->config->xlat_name, profile);
1017 if (sql_set_user(inst, request, profileusername, profile) < 0) {
1018 radlog(L_ERR, "rlm_sql (%s): Error setting profile; rejecting user",
1019 inst->config->xlat_name);
1020 sql_release_socket(inst, sqlsocket);
1021 /* Remove the username we (maybe) added above */
1022 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1023 return RLM_MODULE_FAIL;
1030 if (profile_found) {
1031 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1033 radlog(L_ERR, "rlm_sql (%s): Error processing profile groups; rejecting user",
1034 inst->config->xlat_name);
1035 sql_release_socket(inst, sqlsocket);
1036 /* Remove the username we (maybe) added above */
1037 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1038 return RLM_MODULE_FAIL;
1039 } else if (rows > 0) {
1045 /* Remove the username we (maybe) added above */
1046 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1047 sql_release_socket(inst, sqlsocket);
1050 radlog(L_DBG, "rlm_sql (%s): User %s not found",
1051 inst->config->xlat_name, sqlusername);
1052 return RLM_MODULE_NOTFOUND;
1054 return RLM_MODULE_OK;
1059 * Accounting: save the account data to our sql table
1061 static int rlm_sql_accounting(void *instance, REQUEST * request) {
1063 SQLSOCK *sqlsocket = NULL;
1065 SQL_INST *inst = instance;
1066 int ret = RLM_MODULE_OK;
1067 int numaffected = 0;
1068 int acctstatustype = 0;
1069 char querystr[MAX_QUERY_LEN];
1070 char logstr[MAX_QUERY_LEN];
1071 char sqlusername[MAX_STRING_LEN];
1073 #ifdef CISCO_ACCOUNTING_HACK
1074 int acctsessiontime = 0;
1077 memset(querystr, 0, MAX_QUERY_LEN);
1080 * Find the Acct Status Type
1082 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
1083 acctstatustype = pair->vp_integer;
1085 radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1086 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s",
1087 inst->config->xlat_name, logstr);
1088 return RLM_MODULE_INVALID;
1091 switch (acctstatustype) {
1093 * The Terminal server informed us that it was rebooted
1094 * STOP all records from this NAS
1096 case PW_STATUS_ACCOUNTING_ON:
1097 case PW_STATUS_ACCOUNTING_OFF:
1098 radlog(L_INFO, "rlm_sql (%s): received Acct On/Off packet", inst->config->xlat_name);
1099 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
1100 query_log(request, inst, querystr);
1102 sqlsocket = sql_get_socket(inst);
1103 if (sqlsocket == NULL)
1104 return(RLM_MODULE_FAIL);
1105 if (*querystr) { /* non-empty query */
1106 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1107 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting for Acct On/Off packet - %s",
1108 inst->config->xlat_name,
1109 (inst->module->sql_error)(sqlsocket, inst->config));
1110 ret = RLM_MODULE_FAIL;
1112 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1118 * Got an update accounting packet
1120 case PW_STATUS_ALIVE:
1123 * Set, escape, and check the user attr here
1125 sql_set_user(inst, request, sqlusername, NULL);
1127 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
1128 query_log(request, inst, querystr);
1130 sqlsocket = sql_get_socket(inst);
1131 if (sqlsocket == NULL)
1132 return(RLM_MODULE_FAIL);
1133 if (*querystr) { /* non-empty query */
1134 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1135 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting ALIVE record - %s",
1136 inst->config->xlat_name,
1137 (inst->module->sql_error)(sqlsocket, inst->config));
1138 ret = RLM_MODULE_FAIL;
1141 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1142 if (numaffected < 1) {
1145 * If our update above didn't match anything
1146 * we assume it's because we haven't seen a
1147 * matching Start record. So we have to
1148 * insert this update rather than do an update
1150 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
1151 query_log(request, inst, querystr);
1152 if (*querystr) { /* non-empty query */
1153 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1154 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting ALIVE record - %s",
1155 inst->config->xlat_name,
1156 (inst->module->sql_error)(sqlsocket, inst->config));
1157 ret = RLM_MODULE_FAIL;
1159 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1163 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1168 * Got accounting start packet
1170 case PW_STATUS_START:
1173 * Set, escape, and check the user attr here
1175 sql_set_user(inst, request, sqlusername, NULL);
1177 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
1178 query_log(request, inst, querystr);
1180 sqlsocket = sql_get_socket(inst);
1181 if (sqlsocket == NULL)
1182 return(RLM_MODULE_FAIL);
1183 if (*querystr) { /* non-empty query */
1184 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1185 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting START record - %s",
1186 inst->config->xlat_name,
1187 (inst->module->sql_error)(sqlsocket, inst->config));
1190 * We failed the insert above. It's probably because
1191 * the stop record came before the start. We try
1192 * our alternate query now (typically an UPDATE)
1194 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1195 query_log(request, inst, querystr);
1197 if (*querystr) { /* non-empty query */
1198 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1199 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting START record - %s",
1200 inst->config->xlat_name,
1201 (inst->module->sql_error)(sqlsocket, inst->config));
1202 ret = RLM_MODULE_FAIL;
1204 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1207 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1212 * Got accounting stop packet
1214 case PW_STATUS_STOP:
1217 * Set, escape, and check the user attr here
1219 sql_set_user(inst, request, sqlusername, NULL);
1221 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1222 query_log(request, inst, querystr);
1224 sqlsocket = sql_get_socket(inst);
1225 if (sqlsocket == NULL)
1226 return(RLM_MODULE_FAIL);
1227 if (*querystr) { /* non-empty query */
1228 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1229 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting STOP record - %s",
1230 inst->config->xlat_name,
1231 (inst->module->sql_error)(sqlsocket, inst->config));
1232 ret = RLM_MODULE_FAIL;
1235 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1236 if (numaffected < 1) {
1238 * If our update above didn't match anything
1239 * we assume it's because we haven't seen a
1240 * matching Start record. So we have to
1241 * insert this stop rather than do an update
1243 #ifdef CISCO_ACCOUNTING_HACK
1245 * If stop but zero session length AND no previous
1246 * session found, drop it as in invalid packet
1247 * This is to fix CISCO's aaa from filling our
1248 * table with bogus crap
1250 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
1251 acctsessiontime = pair->vp_integer;
1253 if (acctsessiontime <= 0) {
1254 radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1255 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s", inst->config->xlat_name, logstr);
1256 sql_release_socket(inst, sqlsocket);
1257 ret = RLM_MODULE_NOOP;
1261 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1262 query_log(request, inst, querystr);
1264 if (*querystr) { /* non-empty query */
1265 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1266 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting STOP record - %s",
1267 inst->config->xlat_name,
1268 (inst->module->sql_error)(sqlsocket, inst->config));
1269 ret = RLM_MODULE_FAIL;
1271 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1275 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1280 * Anything else is ignored.
1283 radlog(L_INFO, "rlm_sql (%s): Unsupported Acct-Status-Type = %d", inst->config->xlat_name, acctstatustype);
1284 return RLM_MODULE_NOOP;
1289 sql_release_socket(inst, sqlsocket);
1296 * See if a user is already logged in. Sets request->simul_count to the
1297 * current session count for this user.
1299 * Check twice. If on the first pass the user exceeds his
1300 * max. number of logins, do a second pass and validate all
1301 * logins by querying the terminal server (using eg. SNMP).
1304 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1306 SQL_INST *inst = instance;
1308 char querystr[MAX_QUERY_LEN];
1309 char sqlusername[MAX_STRING_LEN];
1312 char *call_num = NULL;
1315 uint32_t nas_addr = 0;
1318 /* If simul_count_query is not defined, we don't do any checking */
1319 if (!inst->config->simul_count_query ||
1320 (inst->config->simul_count_query[0] == 0)) {
1321 return RLM_MODULE_NOOP;
1324 if((request->username == NULL) || (request->username->length == 0)) {
1325 radlog(L_ERR, "rlm_sql (%s): Zero Length username not permitted\n", inst->config->xlat_name);
1326 return RLM_MODULE_INVALID;
1330 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1331 return RLM_MODULE_FAIL;
1333 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1335 /* initialize the sql socket */
1336 sqlsocket = sql_get_socket(inst);
1337 if(sqlsocket == NULL)
1338 return RLM_MODULE_FAIL;
1340 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1341 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1342 sql_release_socket(inst, sqlsocket);
1343 return RLM_MODULE_FAIL;
1346 ret = rlm_sql_fetch_row(sqlsocket, inst);
1349 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1350 sql_release_socket(inst, sqlsocket);
1351 return RLM_MODULE_FAIL;
1354 row = sqlsocket->row;
1356 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1357 sql_release_socket(inst, sqlsocket);
1358 return RLM_MODULE_FAIL;
1361 request->simul_count = atoi(row[0]);
1362 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1364 if(request->simul_count < request->simul_max) {
1365 sql_release_socket(inst, sqlsocket);
1366 return RLM_MODULE_OK;
1370 * Looks like too many sessions, so let's start verifying
1371 * them, unless told to rely on count query only.
1373 if (!inst->config->simul_verify_query ||
1374 (inst->config->simul_verify_query[0] == '\0')) {
1375 sql_release_socket(inst, sqlsocket);
1376 return RLM_MODULE_OK;
1379 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1380 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1381 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Database query error", inst->config->xlat_name);
1382 sql_release_socket(inst, sqlsocket);
1383 return RLM_MODULE_FAIL;
1387 * Setup some stuff, like for MPP detection.
1389 request->simul_count = 0;
1391 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1392 ipno = vp->vp_ipaddr;
1393 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1394 call_num = vp->vp_strvalue;
1397 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1398 row = sqlsocket->row;
1402 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1403 sql_release_socket(inst, sqlsocket);
1404 DEBUG("rlm_sql (%s): Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1405 return RLM_MODULE_FAIL;
1408 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1409 sql_release_socket(inst, sqlsocket);
1410 DEBUG("rlm_sql (%s): Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1411 return RLM_MODULE_FAIL;
1414 nas_addr = inet_addr(row[3]);
1416 nas_port = atoi(row[4]);
1418 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1422 * Stale record - zap it.
1424 if (inst->config->deletestalesessions == TRUE) {
1425 uint32_t framed_addr = 0;
1430 framed_addr = inet_addr(row[5]);
1432 if (strcmp(row[7], "PPP") == 0)
1434 else if (strcmp(row[7], "SLIP") == 0)
1438 sess_time = atoi(row[8]);
1439 session_zap(request, nas_addr, nas_port,
1440 row[2], row[1], framed_addr,
1444 else if (check == 1) {
1446 * User is still logged in.
1448 ++request->simul_count;
1451 * Does it look like a MPP attempt?
1453 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1454 request->simul_mpp = 2;
1455 else if (row[6] && call_num &&
1456 !strncmp(row[6],call_num,16))
1457 request->simul_mpp = 2;
1461 * Failed to check the terminal server for
1462 * duplicate logins: return an error.
1464 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1465 sql_release_socket(inst, sqlsocket);
1466 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Failed to check the terminal server for user '%s'.", inst->config->xlat_name, row[2]);
1467 return RLM_MODULE_FAIL;
1471 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1472 sql_release_socket(inst, sqlsocket);
1475 * The Auth module apparently looks at request->simul_count,
1476 * not the return value of this module when deciding to deny
1477 * a call for too many sessions.
1479 return RLM_MODULE_OK;
1483 * Execute postauth_query after authentication
1485 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1486 SQLSOCK *sqlsocket = NULL;
1487 SQL_INST *inst = instance;
1488 char querystr[MAX_QUERY_LEN];
1489 char sqlusername[MAX_STRING_LEN];
1491 DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name);
1493 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1494 return RLM_MODULE_FAIL;
1496 /* If postauth_query is not defined, we stop here */
1497 if (!inst->config->postauth_query ||
1498 (inst->config->postauth_query[0] == '\0'))
1499 return RLM_MODULE_NOOP;
1501 /* Expand variables in the query */
1502 memset(querystr, 0, MAX_QUERY_LEN);
1503 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1504 request, sql_escape_func);
1505 query_log(request, inst, querystr);
1506 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1507 inst->config->xlat_name, querystr);
1509 /* Initialize the sql socket */
1510 sqlsocket = sql_get_socket(inst);
1511 if (sqlsocket == NULL)
1512 return RLM_MODULE_FAIL;
1514 /* Process the query */
1515 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1516 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1517 inst->config->xlat_name,
1518 (inst->module->sql_error)(sqlsocket, inst->config));
1519 sql_release_socket(inst, sqlsocket);
1520 return RLM_MODULE_FAIL;
1522 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1524 sql_release_socket(inst, sqlsocket);
1525 return RLM_MODULE_OK;
1528 /* globally exported name */
1529 module_t rlm_sql = {
1532 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1533 rlm_sql_instantiate, /* instantiation */
1534 rlm_sql_detach, /* detach */
1536 NULL, /* authentication */
1537 rlm_sql_authorize, /* authorization */
1538 NULL, /* preaccounting */
1539 rlm_sql_accounting, /* accounting */
1540 rlm_sql_checksimul, /* checksimul */
1541 NULL, /* pre-proxy */
1542 NULL, /* post-proxy */
1543 rlm_sql_postauth /* post-auth */