3 * Main SQL module file. Most ICRADIUS code is located in sql.c
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * Copyright 2000,2006 The FreeRADIUS server project
22 * Copyright 2000 Mike Machado <mike@innercite.com>
23 * Copyright 2000 Alan DeKok <aland@ox.org>
26 #include <freeradius-devel/ident.h>
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/modules.h>
31 #include <freeradius-devel/rad_assert.h>
39 static char *allowed_chars = NULL;
41 static const CONF_PARSER module_config[] = {
42 {"driver",PW_TYPE_STRING_PTR,
43 offsetof(SQL_CONFIG,sql_driver), NULL, "mysql"},
44 {"server",PW_TYPE_STRING_PTR,
45 offsetof(SQL_CONFIG,sql_server), NULL, "localhost"},
46 {"port",PW_TYPE_STRING_PTR,
47 offsetof(SQL_CONFIG,sql_port), NULL, ""},
48 {"login", PW_TYPE_STRING_PTR,
49 offsetof(SQL_CONFIG,sql_login), NULL, ""},
50 {"password", PW_TYPE_STRING_PTR,
51 offsetof(SQL_CONFIG,sql_password), NULL, ""},
52 {"radius_db", PW_TYPE_STRING_PTR,
53 offsetof(SQL_CONFIG,sql_db), NULL, "radius"},
54 {"read_groups", PW_TYPE_BOOLEAN,
55 offsetof(SQL_CONFIG,read_groups), NULL, "yes"},
56 {"sqltrace", PW_TYPE_BOOLEAN,
57 offsetof(SQL_CONFIG,sqltrace), NULL, "no"},
58 {"sqltracefile", PW_TYPE_STRING_PTR,
59 offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE},
60 {"readclients", PW_TYPE_BOOLEAN,
61 offsetof(SQL_CONFIG,do_clients), NULL, "no"},
62 {"deletestalesessions", PW_TYPE_BOOLEAN,
63 offsetof(SQL_CONFIG,deletestalesessions), NULL, "yes"},
64 {"num_sql_socks", PW_TYPE_INTEGER,
65 offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"},
66 {"sql_user_name", PW_TYPE_STRING_PTR,
67 offsetof(SQL_CONFIG,query_user), NULL, ""},
68 {"default_user_profile", PW_TYPE_STRING_PTR,
69 offsetof(SQL_CONFIG,default_profile), NULL, ""},
70 {"nas_query", PW_TYPE_STRING_PTR,
71 offsetof(SQL_CONFIG,nas_query), NULL, "SELECT id,nasname,shortname,type,secret FROM nas"},
72 {"authorize_check_query", PW_TYPE_STRING_PTR,
73 offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
74 {"authorize_reply_query", PW_TYPE_STRING_PTR,
75 offsetof(SQL_CONFIG,authorize_reply_query), NULL, ""},
76 {"authorize_group_check_query", PW_TYPE_STRING_PTR,
77 offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
78 {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
79 offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
80 {"accounting_onoff_query", PW_TYPE_STRING_PTR,
81 offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
82 {"accounting_update_query", PW_TYPE_STRING_PTR,
83 offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
84 {"accounting_update_query_alt", PW_TYPE_STRING_PTR,
85 offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""},
86 {"accounting_start_query", PW_TYPE_STRING_PTR,
87 offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
88 {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
89 offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
90 {"accounting_stop_query", PW_TYPE_STRING_PTR,
91 offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
92 {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
93 offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
94 {"group_membership_query", PW_TYPE_STRING_PTR,
95 offsetof(SQL_CONFIG,groupmemb_query), NULL, NULL},
96 {"connect_failure_retry_delay", PW_TYPE_INTEGER,
97 offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
98 {"simul_count_query", PW_TYPE_STRING_PTR,
99 offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
100 {"simul_verify_query", PW_TYPE_STRING_PTR,
101 offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
102 {"postauth_query", PW_TYPE_STRING_PTR,
103 offsetof(SQL_CONFIG,postauth_query), NULL, ""},
104 {"safe-characters", PW_TYPE_STRING_PTR,
105 offsetof(SQL_CONFIG,allowed_chars), NULL,
106 "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
108 {NULL, -1, 0, NULL, NULL}
112 * Fall-Through checking function from rlm_files.c
114 static int fallthrough(VALUE_PAIR *vp)
117 tmp = pairfind(vp, PW_FALL_THROUGH);
119 return tmp ? tmp->vp_integer : 0;
127 static int generate_sql_clients(SQL_INST *inst);
128 static int sql_escape_func(char *out, int outlen, const char *in);
131 * sql xlat function. Right now only SELECTs are supported. Only
132 * the first element of the SELECT result will be used.
134 static int sql_xlat(void *instance, REQUEST *request,
135 char *fmt, char *out, size_t freespace,
136 UNUSED RADIUS_ESCAPE_STRING func)
140 SQL_INST *inst = instance;
141 char querystr[MAX_QUERY_LEN];
142 char sqlusername[MAX_STRING_LEN];
145 DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name);
147 * Add SQL-User-Name attribute just in case it is needed
148 * We could search the string fmt for SQL-User-Name to see if this is
151 sql_set_user(inst, request, sqlusername, NULL);
153 * Do an xlat on the provided string (nice recursive operation).
155 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
156 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
157 inst->config->xlat_name);
161 query_log(request, inst,querystr);
162 sqlsocket = sql_get_socket(inst);
163 if (sqlsocket == NULL)
165 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
166 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
167 inst->config->xlat_name,querystr,
168 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
169 sql_release_socket(inst,sqlsocket);
173 ret = rlm_sql_fetch_row(sqlsocket, inst);
176 DEBUG("rlm_sql (%s): SQL query did not succeed",
177 inst->config->xlat_name);
178 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
179 sql_release_socket(inst,sqlsocket);
183 row = sqlsocket->row;
185 DEBUG("rlm_sql (%s): SQL query did not return any results",
186 inst->config->xlat_name);
187 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
188 sql_release_socket(inst,sqlsocket);
193 DEBUG("rlm_sql (%s): row[0] returned NULL",
194 inst->config->xlat_name);
195 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
196 sql_release_socket(inst,sqlsocket);
199 ret = strlen(row[0]);
200 if (ret >= freespace){
201 DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space",
202 inst->config->xlat_name);
203 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
204 sql_release_socket(inst,sqlsocket);
208 strlcpy(out,row[0],freespace);
210 DEBUG("rlm_sql (%s): - sql_xlat finished",
211 inst->config->xlat_name);
213 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
214 sql_release_socket(inst,sqlsocket);
218 static int generate_sql_clients(SQL_INST *inst)
222 char querystr[MAX_QUERY_LEN];
224 char *prefix_ptr = NULL;
227 DEBUG("rlm_sql (%s): Processing generate_sql_clients",
228 inst->config->xlat_name);
230 /* NAS query isn't xlat'ed */
231 strlcpy(querystr, inst->config->nas_query, sizeof(querystr));
232 DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s",
233 inst->config->xlat_name, querystr);
235 sqlsocket = sql_get_socket(inst);
236 if (sqlsocket == NULL)
238 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
239 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
240 inst->config->xlat_name,querystr,
241 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
242 sql_release_socket(inst,sqlsocket);
246 while(rlm_sql_fetch_row(sqlsocket, inst) == 0) {
248 row = sqlsocket->row;
252 * The return data for each row MUST be in the following order:
254 * 0. Row ID (currently unused)
255 * 1. Name (or IP address)
261 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
265 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
269 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
273 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
277 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
278 row[1],row[2],row[4]);
280 c = rad_malloc(sizeof(*c));
281 memset(c, 0, sizeof(*c));
287 prefix_ptr = strchr(row[1], '/');
289 c->prefix = atoi(prefix_ptr + 1);
290 if ((c->prefix < 0) || (c->prefix > 128)) {
291 radlog(L_ERR, "rlm_sql (%s): Invalid Prefix value '%s' for IP.",
292 inst->config->xlat_name, prefix_ptr + 1);
296 /* Replace '/' with '\0' */
301 * Always get the numeric representation of IP
303 if (ip_hton(row[1], AF_UNSPEC, &c->ipaddr) < 0) {
304 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s: %s",
305 inst->config->xlat_name,
306 row[1], librad_errstr);
311 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
312 c->longname = strdup(buffer);
315 if (c->prefix < 0) switch (c->ipaddr.af) {
327 * Other values (secret, shortname, nastype)
329 c->secret = (u_char *)strdup(row[4]);
330 c->shortname = strdup(row[2]);
332 c->nastype = strdup(row[3]);
334 DEBUG("rlm_sql (%s): Adding client %s (%s) to clients list",
335 inst->config->xlat_name,
336 c->longname,c->shortname);
337 if (!client_add(mainconfig.clients, c)) {
338 DEBUG("rlm_sql (%s): Failed to add client %s (%s) to clients list. Maybe there's a duplicate?",
339 inst->config->xlat_name,
340 c->longname,c->shortname);
345 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
346 sql_release_socket(inst, sqlsocket);
353 * Translate the SQL queries.
355 static int sql_escape_func(char *out, int outlen, const char *in)
361 * Non-printable characters get replaced with their
362 * mime-encoded equivalents.
365 strchr(allowed_chars, *in) == NULL) {
367 * Only 3 or less bytes available.
373 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
382 * Only one byte left.
402 * Set the SQL user name.
404 * We don't call the escape function here. The resulting string
405 * will be escaped later in the queries xlat so we don't need to
406 * escape it twice. (it will make things wrong if we have an
407 * escape candidate character in the username)
409 int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
412 char tmpuser[MAX_STRING_LEN];
415 sqlusername[0]= '\0';
417 /* Remove any user attr we added previously */
418 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
420 if (username != NULL) {
421 strlcpy(tmpuser, username, sizeof(tmpuser));
422 } else if (strlen(inst->config->query_user)) {
423 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
428 strlcpy(sqlusername, tmpuser, MAX_STRING_LEN);
429 DEBUG2("rlm_sql (%s): sql_set_user escaped user --> '%s'",
430 inst->config->xlat_name, sqlusername);
431 vp = pairmake("SQL-User-Name", sqlusername, 0);
433 radlog(L_ERR, "%s", librad_errstr);
437 pairadd(&request->packet->vps, vp);
443 static void sql_grouplist_free (SQL_GROUPLIST **group_list)
449 *group_list = (*group_list)->next;
455 static int sql_get_grouplist (SQL_INST *inst, SQLSOCK *sqlsocket, REQUEST *request, SQL_GROUPLIST **group_list)
457 char querystr[MAX_QUERY_LEN];
460 SQL_GROUPLIST *group_list_tmp;
462 /* NOTE: sql_set_user should have been run before calling this function */
464 group_list_tmp = *group_list = NULL;
466 if (!inst->config->groupmemb_query ||
467 (inst->config->groupmemb_query[0] == 0))
470 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, request, sql_escape_func)) {
471 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
472 inst->config->xlat_name);
476 if (rlm_sql_select_query(sqlsocket, inst, querystr) < 0) {
477 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
478 inst->config->xlat_name,querystr,
479 (char *)(inst->module->sql_error)(sqlsocket,inst->config));
482 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
483 row = sqlsocket->row;
487 DEBUG("rlm_sql (%s): row[0] returned NULL",
488 inst->config->xlat_name);
489 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
490 sql_grouplist_free(group_list);
493 if (*group_list == NULL) {
494 *group_list = rad_malloc(sizeof(SQL_GROUPLIST));
495 group_list_tmp = *group_list;
497 group_list_tmp->next = rad_malloc(sizeof(SQL_GROUPLIST));
498 group_list_tmp = group_list_tmp->next;
500 group_list_tmp->next = NULL;
501 strlcpy(group_list_tmp->groupname, row[0], MAX_STRING_LEN);
504 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
511 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
512 * with the group memberships reciding in sql
513 * The group membership query should only return one element which is the username. The returned
514 * username will then be checked with the passed check string.
517 static int sql_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
518 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
521 SQL_INST *inst = instance;
522 char sqlusername[MAX_STRING_LEN];
523 SQL_GROUPLIST *group_list, *group_list_tmp;
525 check_pairs = check_pairs;
526 reply_pairs = reply_pairs;
529 DEBUG("rlm_sql (%s): - sql_groupcmp", inst->config->xlat_name);
530 if (!check || !check->vp_strvalue || !check->length){
531 DEBUG("rlm_sql (%s): sql_groupcmp: Illegal group name",
532 inst->config->xlat_name);
536 DEBUG("rlm_sql (%s): sql_groupcmp: NULL request",
537 inst->config->xlat_name);
541 * Set, escape, and check the user attr here
543 if (sql_set_user(inst, req, sqlusername, NULL) < 0)
547 * Get a socket for this lookup
549 sqlsocket = sql_get_socket(inst);
550 if (sqlsocket == NULL) {
551 /* Remove the username we (maybe) added above */
552 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
557 * Get the list of groups this user is a member of
559 if (sql_get_grouplist(inst, sqlsocket, req, &group_list)) {
560 radlog(L_ERR, "rlm_sql (%s): Error getting group membership",
561 inst->config->xlat_name);
562 /* Remove the username we (maybe) added above */
563 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
564 sql_release_socket(inst, sqlsocket);
568 for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) {
569 if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){
570 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User is a member of group %s",
571 inst->config->xlat_name,
572 (char *)check->vp_strvalue);
573 /* Free the grouplist */
574 sql_grouplist_free(&group_list);
575 /* Remove the username we (maybe) added above */
576 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
577 sql_release_socket(inst, sqlsocket);
582 /* Free the grouplist */
583 sql_grouplist_free(&group_list);
584 /* Remove the username we (maybe) added above */
585 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
586 sql_release_socket(inst,sqlsocket);
588 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User is NOT a member of group %s",
589 inst->config->xlat_name, (char *)check->vp_strvalue);
596 static int rlm_sql_process_groups(SQL_INST *inst, REQUEST *request, SQLSOCK *sqlsocket, int *dofallthrough)
598 VALUE_PAIR *check_tmp = NULL;
599 VALUE_PAIR *reply_tmp = NULL;
600 SQL_GROUPLIST *group_list, *group_list_tmp;
601 VALUE_PAIR *sql_group = NULL;
602 char querystr[MAX_QUERY_LEN];
607 * Get the list of groups this user is a member of
609 if (sql_get_grouplist(inst, sqlsocket, request, &group_list)) {
610 radlog(L_ERR, "rlm_sql (%s): Error retrieving group list",
611 inst->config->xlat_name);
615 for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) {
617 * Add the Sql-Group attribute to the request list so we know
618 * which group we're retrieving attributes for
620 sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ);
622 radlog(L_ERR, "rlm_sql (%s): Error creating Sql-Group attribute",
623 inst->config->xlat_name);
626 pairadd(&request->packet->vps, sql_group);
627 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func)) {
628 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
629 inst->config->xlat_name);
630 /* Remove the grouup we added above */
631 pairdelete(&request->packet->vps, PW_SQL_GROUP);
634 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
636 radlog(L_ERR, "rlm_sql (%s): Error retrieving check pairs for group %s",
637 inst->config->xlat_name, group_list_tmp->groupname);
638 /* Remove the grouup we added above */
639 pairdelete(&request->packet->vps, PW_SQL_GROUP);
640 pairfree(&check_tmp);
642 } else if (rows > 0) {
644 * Only do this if *some* check pairs were returned
646 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
648 DEBUG2("rlm_sql (%s): User found in group %s",
649 inst->config->xlat_name, group_list_tmp->groupname);
651 * Now get the reply pairs since the paircompare matched
653 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
654 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
655 inst->config->xlat_name);
656 /* Remove the grouup we added above */
657 pairdelete(&request->packet->vps, PW_SQL_GROUP);
658 pairfree(&check_tmp);
661 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
662 radlog(L_ERR, "rlm_sql (%s): Error retrieving reply pairs for group %s",
663 inst->config->xlat_name, group_list_tmp->groupname);
664 /* Remove the grouup we added above */
665 pairdelete(&request->packet->vps, PW_SQL_GROUP);
666 pairfree(&check_tmp);
667 pairfree(&reply_tmp);
670 *dofallthrough = fallthrough(reply_tmp);
671 pairxlatmove(request, &request->reply->vps, &reply_tmp);
672 pairxlatmove(request, &request->config_items, &check_tmp);
676 * rows == 0. This is like having the username on a line
677 * in the user's file with no check vp's. As such, we treat
678 * it as found and add the reply attributes, so that we
679 * match expected behavior
682 DEBUG2("rlm_sql (%s): User found in group %s",
683 inst->config->xlat_name, group_list_tmp->groupname);
685 * Now get the reply pairs since the paircompare matched
687 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
688 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
689 inst->config->xlat_name);
690 /* Remove the grouup we added above */
691 pairdelete(&request->packet->vps, PW_SQL_GROUP);
692 pairfree(&check_tmp);
695 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
696 radlog(L_ERR, "rlm_sql (%s): Error retrieving reply pairs for group %s",
697 inst->config->xlat_name, group_list_tmp->groupname);
698 /* Remove the grouup we added above */
699 pairdelete(&request->packet->vps, PW_SQL_GROUP);
700 pairfree(&check_tmp);
701 pairfree(&reply_tmp);
704 *dofallthrough = fallthrough(reply_tmp);
705 pairxlatmove(request, &request->reply->vps, &reply_tmp);
706 pairxlatmove(request, &request->config_items, &check_tmp);
710 * Delete the Sql-Group we added above
711 * And clear out the pairlists
713 pairdelete(&request->packet->vps, PW_SQL_GROUP);
714 pairfree(&check_tmp);
715 pairfree(&reply_tmp);
718 sql_grouplist_free(&group_list);
723 static int rlm_sql_detach(void *instance)
725 SQL_INST *inst = instance;
727 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
736 if (inst->config->xlat_name) {
737 xlat_unregister(inst->config->xlat_name,(RAD_XLAT_FUNC)sql_xlat);
738 free(inst->config->xlat_name);
742 * Free up dynamically allocated string pointers.
744 for (i = 0; module_config[i].name != NULL; i++) {
746 if (module_config[i].type != PW_TYPE_STRING_PTR) {
751 * Treat 'config' as an opaque array of bytes,
752 * and take the offset into it. There's a
753 * (char*) pointer at that offset, and we want
756 p = (char **) (((char *)inst->config) + module_config[i].offset);
757 if (!*p) { /* nothing allocated */
763 allowed_chars = NULL;
771 * FIXME: Call the modules 'destroy' function?
773 lt_dlclose(inst->handle); /* ignore any errors */
780 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
783 const char *xlat_name;
785 inst = rad_malloc(sizeof(SQL_INST));
786 memset(inst, 0, sizeof(SQL_INST));
788 inst->config = rad_malloc(sizeof(SQL_CONFIG));
789 memset(inst->config, 0, sizeof(SQL_CONFIG));
792 * If the configuration parameters can't be parsed, then
795 if (cf_section_parse(conf, inst->config, module_config) < 0) {
796 rlm_sql_detach(inst);
800 xlat_name = cf_section_name2(conf);
801 if (xlat_name == NULL)
802 xlat_name = cf_section_name1(conf);
804 inst->config->xlat_name = strdup(xlat_name);
805 xlat_register(xlat_name, (RAD_XLAT_FUNC)sql_xlat, inst);
808 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
809 radlog(L_ERR | L_CONS, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
810 inst->config->xlat_name, MAX_SQL_SOCKS);
811 rlm_sql_detach(inst);
816 * Sanity check for crazy people.
818 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
819 radlog(L_ERR, "rlm_sql (%s): \"%s\" is NOT an SQL driver!",
820 inst->config->xlat_name, inst->config->sql_driver);
821 rlm_sql_detach(inst);
825 inst->handle = lt_dlopenext(inst->config->sql_driver);
826 if (inst->handle == NULL) {
827 radlog(L_ERR, "rlm_sql (%s): Could not link driver %s: %s",
828 inst->config->xlat_name, inst->config->sql_driver,
830 radlog(L_ERR, "rlm_sql (%s): Make sure it (and all its dependent libraries!) are in the search path of your system's ld.",
831 inst->config->xlat_name);
832 rlm_sql_detach(inst);
836 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
838 radlog(L_ERR, "rlm_sql (%s): Could not link symbol %s: %s",
839 inst->config->xlat_name, inst->config->sql_driver,
841 rlm_sql_detach(inst);
845 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
846 inst->config->xlat_name, inst->config->sql_driver,
848 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
849 inst->config->xlat_name, inst->config->sql_login,
850 inst->config->sql_server, inst->config->sql_port,
851 inst->config->sql_db);
853 if (sql_init_socketpool(inst) < 0) {
854 rlm_sql_detach(inst);
857 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
859 if (inst->config->do_clients){
860 if (generate_sql_clients(inst) == -1){
861 radlog(L_ERR, "rlm_sql (%s): generate_sql_clients() returned error",inst->config->xlat_name);
862 rlm_sql_detach(inst);
866 allowed_chars = inst->config->allowed_chars;
870 return RLM_MODULE_OK;
874 static int rlm_sql_authorize(void *instance, REQUEST * request)
876 VALUE_PAIR *check_tmp = NULL;
877 VALUE_PAIR *reply_tmp = NULL;
878 VALUE_PAIR *user_profile = NULL;
880 int dofallthrough = 1;
883 SQL_INST *inst = instance;
884 char querystr[MAX_QUERY_LEN];
885 char sqlusername[MAX_STRING_LEN];
887 * the profile username is used as the sqlusername during
888 * profile checking so that we don't overwrite the orignal
891 char profileusername[MAX_STRING_LEN];
894 * Set, escape, and check the user attr here
896 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
897 return RLM_MODULE_FAIL;
903 sqlsocket = sql_get_socket(inst);
904 if (sqlsocket == NULL) {
905 /* Remove the username we (maybe) added above */
906 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
907 return RLM_MODULE_FAIL;
912 * After this point, ALL 'return's MUST release the SQL socket!
916 * Alright, start by getting the specific entry for the user
918 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func)) {
919 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
920 inst->config->xlat_name);
921 sql_release_socket(inst, sqlsocket);
922 /* Remove the username we (maybe) added above */
923 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
924 return RLM_MODULE_FAIL;
926 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
928 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
929 inst->config->xlat_name);
930 sql_release_socket(inst, sqlsocket);
931 /* Remove the username we (maybe) added above */
932 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
933 pairfree(&check_tmp);
934 return RLM_MODULE_FAIL;
935 } else if (rows > 0) {
937 * Only do this if *some* check pairs were returned
939 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
941 DEBUG2("rlm_sql (%s): User found in radcheck table", inst->config->xlat_name);
943 * Now get the reply pairs since the paircompare matched
945 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func)) {
946 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
947 inst->config->xlat_name);
948 sql_release_socket(inst, sqlsocket);
949 /* Remove the username we (maybe) added above */
950 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
951 pairfree(&check_tmp);
952 return RLM_MODULE_FAIL;
954 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
955 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
956 inst->config->xlat_name);
957 sql_release_socket(inst, sqlsocket);
958 /* Remove the username we (maybe) added above */
959 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
960 pairfree(&check_tmp);
961 pairfree(&reply_tmp);
962 return RLM_MODULE_FAIL;
964 if (!inst->config->read_groups)
965 dofallthrough = fallthrough(reply_tmp);
966 pairxlatmove(request, &request->reply->vps, &reply_tmp);
967 pairxlatmove(request, &request->config_items, &check_tmp);
972 * Clear out the pairlists
974 pairfree(&check_tmp);
975 pairfree(&reply_tmp);
978 * dofallthrough is set to 1 by default so that if the user information
979 * is not found, we will still process groups. If the user information,
980 * however, *is* found, Fall-Through must be set in order to process
984 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
986 radlog(L_ERR, "rlm_sql (%s): Error processing groups; rejecting user",
987 inst->config->xlat_name);
988 sql_release_socket(inst, sqlsocket);
989 /* Remove the username we (maybe) added above */
990 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
991 return RLM_MODULE_FAIL;
992 } else if (rows > 0) {
998 * repeat the above process with the default profile or User-Profile
1000 if (dofallthrough) {
1001 int profile_found = 0;
1003 * Check for a default_profile or for a User-Profile.
1005 user_profile = pairfind(request->config_items, PW_USER_PROFILE);
1006 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
1007 char *profile = inst->config->default_profile;
1009 if (user_profile != NULL)
1010 profile = user_profile->vp_strvalue;
1011 if (profile && strlen(profile)){
1012 radlog(L_DBG, "rlm_sql (%s): Checking profile %s",
1013 inst->config->xlat_name, profile);
1014 if (sql_set_user(inst, request, profileusername, profile) < 0) {
1015 radlog(L_ERR, "rlm_sql (%s): Error setting profile; rejecting user",
1016 inst->config->xlat_name);
1017 sql_release_socket(inst, sqlsocket);
1018 /* Remove the username we (maybe) added above */
1019 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1020 return RLM_MODULE_FAIL;
1027 if (profile_found) {
1028 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1030 radlog(L_ERR, "rlm_sql (%s): Error processing profile groups; rejecting user",
1031 inst->config->xlat_name);
1032 sql_release_socket(inst, sqlsocket);
1033 /* Remove the username we (maybe) added above */
1034 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1035 return RLM_MODULE_FAIL;
1036 } else if (rows > 0) {
1042 /* Remove the username we (maybe) added above */
1043 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1044 sql_release_socket(inst, sqlsocket);
1047 radlog(L_DBG, "rlm_sql (%s): User %s not found",
1048 inst->config->xlat_name, sqlusername);
1049 return RLM_MODULE_NOTFOUND;
1051 return RLM_MODULE_OK;
1056 * Accounting: save the account data to our sql table
1058 static int rlm_sql_accounting(void *instance, REQUEST * request) {
1060 SQLSOCK *sqlsocket = NULL;
1062 SQL_INST *inst = instance;
1063 int ret = RLM_MODULE_OK;
1064 int numaffected = 0;
1065 int acctstatustype = 0;
1066 char querystr[MAX_QUERY_LEN];
1067 char logstr[MAX_QUERY_LEN];
1068 char sqlusername[MAX_STRING_LEN];
1070 #ifdef CISCO_ACCOUNTING_HACK
1071 int acctsessiontime = 0;
1074 memset(querystr, 0, MAX_QUERY_LEN);
1077 * Find the Acct Status Type
1079 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
1080 acctstatustype = pair->vp_integer;
1082 radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1083 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s",
1084 inst->config->xlat_name, logstr);
1085 return RLM_MODULE_INVALID;
1088 switch (acctstatustype) {
1090 * The Terminal server informed us that it was rebooted
1091 * STOP all records from this NAS
1093 case PW_STATUS_ACCOUNTING_ON:
1094 case PW_STATUS_ACCOUNTING_OFF:
1095 radlog(L_INFO, "rlm_sql (%s): received Acct On/Off packet", inst->config->xlat_name);
1096 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
1097 query_log(request, inst, querystr);
1099 sqlsocket = sql_get_socket(inst);
1100 if (sqlsocket == NULL)
1101 return(RLM_MODULE_FAIL);
1102 if (*querystr) { /* non-empty query */
1103 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1104 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting for Acct On/Off packet - %s",
1105 inst->config->xlat_name,
1106 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1107 ret = RLM_MODULE_FAIL;
1109 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1115 * Got an update accounting packet
1117 case PW_STATUS_ALIVE:
1120 * Set, escape, and check the user attr here
1122 sql_set_user(inst, request, sqlusername, NULL);
1124 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
1125 query_log(request, inst, querystr);
1127 sqlsocket = sql_get_socket(inst);
1128 if (sqlsocket == NULL)
1129 return(RLM_MODULE_FAIL);
1130 if (*querystr) { /* non-empty query */
1131 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1132 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting ALIVE record - %s",
1133 inst->config->xlat_name,
1134 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1135 ret = RLM_MODULE_FAIL;
1138 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1139 if (numaffected < 1) {
1142 * If our update above didn't match anything
1143 * we assume it's because we haven't seen a
1144 * matching Start record. So we have to
1145 * insert this update rather than do an update
1147 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
1148 query_log(request, inst, querystr);
1149 if (*querystr) { /* non-empty query */
1150 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1151 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting ALIVE record - %s",
1152 inst->config->xlat_name,
1153 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1154 ret = RLM_MODULE_FAIL;
1156 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1160 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1165 * Got accounting start packet
1167 case PW_STATUS_START:
1170 * Set, escape, and check the user attr here
1172 sql_set_user(inst, request, sqlusername, NULL);
1174 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
1175 query_log(request, inst, querystr);
1177 sqlsocket = sql_get_socket(inst);
1178 if (sqlsocket == NULL)
1179 return(RLM_MODULE_FAIL);
1180 if (*querystr) { /* non-empty query */
1181 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1182 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting START record - %s",
1183 inst->config->xlat_name,
1184 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1187 * We failed the insert above. It's probably because
1188 * the stop record came before the start. We try
1189 * our alternate query now (typically an UPDATE)
1191 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1192 query_log(request, inst, querystr);
1194 if (*querystr) { /* non-empty query */
1195 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1196 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting START record - %s",
1197 inst->config->xlat_name,
1198 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1199 ret = RLM_MODULE_FAIL;
1201 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1204 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1209 * Got accounting stop packet
1211 case PW_STATUS_STOP:
1214 * Set, escape, and check the user attr here
1216 sql_set_user(inst, request, sqlusername, NULL);
1218 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1219 query_log(request, inst, querystr);
1221 sqlsocket = sql_get_socket(inst);
1222 if (sqlsocket == NULL)
1223 return(RLM_MODULE_FAIL);
1224 if (*querystr) { /* non-empty query */
1225 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1226 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting STOP record - %s",
1227 inst->config->xlat_name,
1228 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1229 ret = RLM_MODULE_FAIL;
1232 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1233 if (numaffected < 1) {
1235 * If our update above didn't match anything
1236 * we assume it's because we haven't seen a
1237 * matching Start record. So we have to
1238 * insert this stop rather than do an update
1240 #ifdef CISCO_ACCOUNTING_HACK
1242 * If stop but zero session length AND no previous
1243 * session found, drop it as in invalid packet
1244 * This is to fix CISCO's aaa from filling our
1245 * table with bogus crap
1247 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
1248 acctsessiontime = pair->vp_integer;
1250 if (acctsessiontime <= 0) {
1251 radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1252 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s", inst->config->xlat_name, logstr);
1253 sql_release_socket(inst, sqlsocket);
1254 ret = RLM_MODULE_NOOP;
1258 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1259 query_log(request, inst, querystr);
1261 if (*querystr) { /* non-empty query */
1262 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1263 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting STOP record - %s",
1264 inst->config->xlat_name,
1265 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1266 ret = RLM_MODULE_FAIL;
1268 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1272 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1277 * Anything else is ignored.
1280 radlog(L_INFO, "rlm_sql (%s): Unsupported Acct-Status-Type = %d", inst->config->xlat_name, acctstatustype);
1281 return RLM_MODULE_NOOP;
1286 sql_release_socket(inst, sqlsocket);
1293 * See if a user is already logged in. Sets request->simul_count to the
1294 * current session count for this user.
1296 * Check twice. If on the first pass the user exceeds his
1297 * max. number of logins, do a second pass and validate all
1298 * logins by querying the terminal server (using eg. SNMP).
1301 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1303 SQL_INST *inst = instance;
1305 char querystr[MAX_QUERY_LEN];
1306 char sqlusername[MAX_STRING_LEN];
1309 char *call_num = NULL;
1312 uint32_t nas_addr = 0;
1315 /* If simul_count_query is not defined, we don't do any checking */
1316 if (!inst->config->simul_count_query ||
1317 (inst->config->simul_count_query[0] == 0)) {
1318 return RLM_MODULE_NOOP;
1321 if((request->username == NULL) || (request->username->length == 0)) {
1322 radlog(L_ERR, "rlm_sql (%s): Zero Length username not permitted\n", inst->config->xlat_name);
1323 return RLM_MODULE_INVALID;
1327 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1328 return RLM_MODULE_FAIL;
1330 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1332 /* initialize the sql socket */
1333 sqlsocket = sql_get_socket(inst);
1334 if(sqlsocket == NULL)
1335 return RLM_MODULE_FAIL;
1337 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1338 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1339 sql_release_socket(inst, sqlsocket);
1340 return RLM_MODULE_FAIL;
1343 ret = rlm_sql_fetch_row(sqlsocket, inst);
1346 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1347 sql_release_socket(inst, sqlsocket);
1348 return RLM_MODULE_FAIL;
1351 row = sqlsocket->row;
1353 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1354 sql_release_socket(inst, sqlsocket);
1355 return RLM_MODULE_FAIL;
1358 request->simul_count = atoi(row[0]);
1359 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1361 if(request->simul_count < request->simul_max) {
1362 sql_release_socket(inst, sqlsocket);
1363 return RLM_MODULE_OK;
1367 * Looks like too many sessions, so let's start verifying
1368 * them, unless told to rely on count query only.
1370 if (!inst->config->simul_verify_query ||
1371 (inst->config->simul_verify_query[0] == '\0')) {
1372 sql_release_socket(inst, sqlsocket);
1373 return RLM_MODULE_OK;
1376 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1377 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1378 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Database query error", inst->config->xlat_name);
1379 sql_release_socket(inst, sqlsocket);
1380 return RLM_MODULE_FAIL;
1384 * Setup some stuff, like for MPP detection.
1386 request->simul_count = 0;
1388 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1389 ipno = vp->vp_ipaddr;
1390 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1391 call_num = vp->vp_strvalue;
1394 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1395 row = sqlsocket->row;
1399 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1400 sql_release_socket(inst, sqlsocket);
1401 DEBUG("rlm_sql (%s): Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1402 return RLM_MODULE_FAIL;
1405 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1406 sql_release_socket(inst, sqlsocket);
1407 DEBUG("rlm_sql (%s): Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1408 return RLM_MODULE_FAIL;
1411 nas_addr = inet_addr(row[3]);
1413 nas_port = atoi(row[4]);
1415 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1419 * Stale record - zap it.
1421 if (inst->config->deletestalesessions == TRUE) {
1422 uint32_t framed_addr = 0;
1427 framed_addr = inet_addr(row[5]);
1429 if (strcmp(row[7], "PPP") == 0)
1431 else if (strcmp(row[7], "SLIP") == 0)
1435 sess_time = atoi(row[8]);
1436 session_zap(request, nas_addr, nas_port,
1437 row[2], row[1], framed_addr,
1441 else if (check == 1) {
1443 * User is still logged in.
1445 ++request->simul_count;
1448 * Does it look like a MPP attempt?
1450 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1451 request->simul_mpp = 2;
1452 else if (row[6] && call_num &&
1453 !strncmp(row[6],call_num,16))
1454 request->simul_mpp = 2;
1458 * Failed to check the terminal server for
1459 * duplicate logins: return an error.
1461 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1462 sql_release_socket(inst, sqlsocket);
1463 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Failed to check the terminal server for user '%s'.", inst->config->xlat_name, row[2]);
1464 return RLM_MODULE_FAIL;
1468 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1469 sql_release_socket(inst, sqlsocket);
1472 * The Auth module apparently looks at request->simul_count,
1473 * not the return value of this module when deciding to deny
1474 * a call for too many sessions.
1476 return RLM_MODULE_OK;
1480 * Execute postauth_query after authentication
1482 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1483 SQLSOCK *sqlsocket = NULL;
1484 SQL_INST *inst = instance;
1485 char querystr[MAX_QUERY_LEN];
1486 char sqlusername[MAX_STRING_LEN];
1488 DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name);
1490 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1491 return RLM_MODULE_FAIL;
1493 /* If postauth_query is not defined, we stop here */
1494 if (!inst->config->postauth_query ||
1495 (inst->config->postauth_query[0] == '\0'))
1496 return RLM_MODULE_NOOP;
1498 /* Expand variables in the query */
1499 memset(querystr, 0, MAX_QUERY_LEN);
1500 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1501 request, sql_escape_func);
1502 query_log(request, inst, querystr);
1503 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1504 inst->config->xlat_name, querystr);
1506 /* Initialize the sql socket */
1507 sqlsocket = sql_get_socket(inst);
1508 if (sqlsocket == NULL)
1509 return RLM_MODULE_FAIL;
1511 /* Process the query */
1512 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1513 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1514 inst->config->xlat_name,
1515 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1516 sql_release_socket(inst, sqlsocket);
1517 return RLM_MODULE_FAIL;
1519 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1521 sql_release_socket(inst, sqlsocket);
1522 return RLM_MODULE_OK;
1525 /* globally exported name */
1526 module_t rlm_sql = {
1529 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1530 rlm_sql_instantiate, /* instantiation */
1531 rlm_sql_detach, /* detach */
1533 NULL, /* authentication */
1534 rlm_sql_authorize, /* authorization */
1535 NULL, /* preaccounting */
1536 rlm_sql_accounting, /* accounting */
1537 rlm_sql_checksimul, /* checksimul */
1538 NULL, /* pre-proxy */
1539 NULL, /* post-proxy */
1540 rlm_sql_postauth /* post-auth */