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/autoconf.h>
37 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
45 #include <freeradius-devel/radiusd.h>
46 #include <freeradius-devel/modules.h>
48 #include <freeradius-devel/rad_assert.h>
50 static char *allowed_chars = NULL;
52 static const CONF_PARSER module_config[] = {
53 {"driver",PW_TYPE_STRING_PTR,
54 offsetof(SQL_CONFIG,sql_driver), NULL, "mysql"},
55 {"server",PW_TYPE_STRING_PTR,
56 offsetof(SQL_CONFIG,sql_server), NULL, "localhost"},
57 {"port",PW_TYPE_STRING_PTR,
58 offsetof(SQL_CONFIG,sql_port), NULL, ""},
59 {"login", PW_TYPE_STRING_PTR,
60 offsetof(SQL_CONFIG,sql_login), NULL, ""},
61 {"password", PW_TYPE_STRING_PTR,
62 offsetof(SQL_CONFIG,sql_password), NULL, ""},
63 {"radius_db", PW_TYPE_STRING_PTR,
64 offsetof(SQL_CONFIG,sql_db), NULL, "radius"},
65 {"sqltrace", PW_TYPE_BOOLEAN,
66 offsetof(SQL_CONFIG,sqltrace), NULL, "no"},
67 {"sqltracefile", PW_TYPE_STRING_PTR,
68 offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE},
69 {"readclients", PW_TYPE_BOOLEAN,
70 offsetof(SQL_CONFIG,do_clients), NULL, "no"},
71 {"deletestalesessions", PW_TYPE_BOOLEAN,
72 offsetof(SQL_CONFIG,deletestalesessions), NULL, "yes"},
73 {"num_sql_socks", PW_TYPE_INTEGER,
74 offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"},
75 {"sql_user_name", PW_TYPE_STRING_PTR,
76 offsetof(SQL_CONFIG,query_user), NULL, ""},
77 {"default_user_profile", PW_TYPE_STRING_PTR,
78 offsetof(SQL_CONFIG,default_profile), NULL, ""},
79 {"nas_query", PW_TYPE_STRING_PTR,
80 offsetof(SQL_CONFIG,nas_query), NULL, "SELECT id,nasname,shortname,type,secret FROM nas"},
81 {"authorize_check_query", PW_TYPE_STRING_PTR,
82 offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
83 {"authorize_reply_query", PW_TYPE_STRING_PTR,
84 offsetof(SQL_CONFIG,authorize_reply_query), NULL, ""},
85 {"authorize_group_check_query", PW_TYPE_STRING_PTR,
86 offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
87 {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
88 offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
89 {"accounting_onoff_query", PW_TYPE_STRING_PTR,
90 offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
91 {"accounting_update_query", PW_TYPE_STRING_PTR,
92 offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
93 {"accounting_update_query_alt", PW_TYPE_STRING_PTR,
94 offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""},
95 {"accounting_start_query", PW_TYPE_STRING_PTR,
96 offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
97 {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
98 offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
99 {"accounting_stop_query", PW_TYPE_STRING_PTR,
100 offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
101 {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
102 offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
103 {"group_membership_query", PW_TYPE_STRING_PTR,
104 offsetof(SQL_CONFIG,groupmemb_query), NULL, ""},
105 {"connect_failure_retry_delay", PW_TYPE_INTEGER,
106 offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
107 {"simul_count_query", PW_TYPE_STRING_PTR,
108 offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
109 {"simul_verify_query", PW_TYPE_STRING_PTR,
110 offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
111 {"postauth_query", PW_TYPE_STRING_PTR,
112 offsetof(SQL_CONFIG,postauth_query), NULL, ""},
113 {"safe-characters", PW_TYPE_STRING_PTR,
114 offsetof(SQL_CONFIG,allowed_chars), NULL,
115 "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
117 {NULL, -1, 0, NULL, NULL}
121 * Fall-Through checking function from rlm_files.c
123 static int fallthrough(VALUE_PAIR *vp)
126 tmp = pairfind(vp, PW_FALL_THROUGH);
128 return tmp ? tmp->lvalue : 0;
136 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username);
137 static int generate_sql_clients(SQL_INST *inst);
138 static int sql_escape_func(char *out, int outlen, const char *in);
141 * sql xlat function. Right now only SELECTs are supported. Only
142 * the first element of the SELECT result will be used.
144 static int sql_xlat(void *instance, REQUEST *request,
145 char *fmt, char *out, size_t freespace,
146 UNUSED RADIUS_ESCAPE_STRING func)
150 SQL_INST *inst = instance;
151 char querystr[MAX_QUERY_LEN];
152 char sqlusername[MAX_STRING_LEN];
155 DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name);
157 * Add SQL-User-Name attribute just in case it is needed
158 * We could search the string fmt for SQL-User-Name to see if this is
161 sql_set_user(inst, request, sqlusername, NULL);
163 * Do an xlat on the provided string (nice recursive operation).
165 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
166 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
167 inst->config->xlat_name);
171 query_log(request, inst,querystr);
172 sqlsocket = sql_get_socket(inst);
173 if (sqlsocket == NULL)
175 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
176 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
177 inst->config->xlat_name,querystr,
178 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
179 sql_release_socket(inst,sqlsocket);
183 ret = rlm_sql_fetch_row(sqlsocket, inst);
186 DEBUG("rlm_sql (%s): SQL query did not succeed",
187 inst->config->xlat_name);
188 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
189 sql_release_socket(inst,sqlsocket);
193 row = sqlsocket->row;
195 DEBUG("rlm_sql (%s): SQL query did not return any results",
196 inst->config->xlat_name);
197 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
198 sql_release_socket(inst,sqlsocket);
203 DEBUG("rlm_sql (%s): row[0] returned NULL",
204 inst->config->xlat_name);
205 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
206 sql_release_socket(inst,sqlsocket);
209 ret = strlen(row[0]);
210 if (ret > freespace){
211 DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space",
212 inst->config->xlat_name);
213 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
214 sql_release_socket(inst,sqlsocket);
218 strlcpy(out,row[0],ret);
220 DEBUG("rlm_sql (%s): - sql_xlat finished",
221 inst->config->xlat_name);
223 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
224 sql_release_socket(inst,sqlsocket);
228 static int generate_sql_clients(SQL_INST *inst)
232 char querystr[MAX_QUERY_LEN];
234 char *prefix_ptr = NULL;
237 DEBUG("rlm_sql (%s): Processing generate_sql_clients",
238 inst->config->xlat_name);
240 /* NAS query isn't xlat'ed */
241 strlcpy(querystr, inst->config->nas_query, sizeof(querystr));
242 DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s",
243 inst->config->xlat_name, querystr);
245 sqlsocket = sql_get_socket(inst);
246 if (sqlsocket == NULL)
248 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
249 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
250 inst->config->xlat_name,querystr,
251 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
252 sql_release_socket(inst,sqlsocket);
256 while(rlm_sql_fetch_row(sqlsocket, inst) == 0) {
258 row = sqlsocket->row;
262 * The return data for each row MUST be in the following order:
264 * 0. Row ID (currently unused)
265 * 1. Name (or IP address)
271 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
275 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
279 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
283 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
287 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
288 row[1],row[2],row[4]);
290 c = rad_malloc(sizeof(RADCLIENT));
291 memset(c, 0, sizeof(RADCLIENT));
297 prefix_ptr = strchr(row[1], '/');
299 c->prefix = atoi(prefix_ptr + 1);
300 if ((c->prefix < 0) || (c->prefix > 128)) {
301 radlog(L_ERR, "rlm_sql (%s): Invalid Prefix value '%s' for IP.",
302 inst->config->xlat_name, prefix_ptr + 1);
306 /* Replace '/' with '\0' */
311 * Always get the numeric representation of IP
313 if (ip_hton(row[1], AF_UNSPEC, &c->ipaddr) < 0) {
314 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s: %s",
315 inst->config->xlat_name,
316 row[1], librad_errstr);
321 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
322 c->longname = strdup(buffer);
325 if (c->prefix < 0) switch (c->ipaddr.af) {
337 * Other values (secret, shortname, nastype)
339 c->secret = (u_char *)strdup(row[4]);
340 c->shortname = strdup(row[2]);
342 c->nastype = strdup(row[3]);
344 DEBUG("rlm_sql (%s): Adding client %s (%s) to clients list",
345 inst->config->xlat_name,
346 c->longname,c->shortname);
347 if (!client_add(mainconfig.clients, c)) {
352 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
353 sql_release_socket(inst, sqlsocket);
360 * Translate the SQL queries.
362 static int sql_escape_func(char *out, int outlen, const char *in)
368 * Non-printable characters get replaced with their
369 * mime-encoded equivalents.
372 strchr(allowed_chars, *in) == NULL) {
374 * Only 3 or less bytes available.
380 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
389 * Only one byte left.
409 * Set the SQL user name.
411 * We don't call the escape function here. The resulting string
412 * will be escaped later in the queries xlat so we don't need to
413 * escape it twice. (it will make things wrong if we have an
414 * escape candidate character in the username)
416 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
419 char tmpuser[MAX_STRING_LEN];
422 sqlusername[0]= '\0';
424 /* Remove any user attr we added previously */
425 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
427 if (username != NULL) {
428 strlcpy(tmpuser, username, MAX_STRING_LEN);
429 } else if (strlen(inst->config->query_user)) {
430 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
435 strlcpy(sqlusername, tmpuser, MAX_STRING_LEN);
436 DEBUG2("rlm_sql (%s): sql_set_user escaped user --> '%s'",
437 inst->config->xlat_name, sqlusername);
438 vp = pairmake("SQL-User-Name", sqlusername, 0);
440 radlog(L_ERR, "%s", librad_errstr);
444 pairadd(&request->packet->vps, vp);
450 static void sql_grouplist_free (SQL_GROUPLIST **group_list)
456 *group_list = (*group_list)->next;
462 static int sql_get_grouplist (SQL_INST *inst, SQLSOCK *sqlsocket, REQUEST *request, SQL_GROUPLIST **group_list)
464 char querystr[MAX_QUERY_LEN];
467 SQL_GROUPLIST *group_list_tmp;
469 /* NOTE: sql_set_user should have been run before calling this function */
471 group_list_tmp = *group_list = NULL;
473 if (inst->config->groupmemb_query[0] == 0)
476 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, request, sql_escape_func)) {
477 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
478 inst->config->xlat_name);
482 if (rlm_sql_select_query(sqlsocket, inst, querystr) < 0) {
483 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
484 inst->config->xlat_name,querystr,
485 (char *)(inst->module->sql_error)(sqlsocket,inst->config));
488 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
489 row = sqlsocket->row;
493 DEBUG("rlm_sql (%s): row[0] returned NULL",
494 inst->config->xlat_name);
495 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
496 sql_grouplist_free(group_list);
499 if (*group_list == NULL) {
500 *group_list = rad_malloc(sizeof(SQL_GROUPLIST));
501 group_list_tmp = *group_list;
503 group_list_tmp->next = rad_malloc(sizeof(SQL_GROUPLIST));
504 group_list_tmp = group_list_tmp->next;
506 group_list_tmp->next = NULL;
507 strlcpy(group_list_tmp->groupname, row[0], MAX_STRING_LEN);
510 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
517 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
518 * with the group memberships reciding in sql
519 * The group membership query should only return one element which is the username. The returned
520 * username will then be checked with the passed check string.
523 static int sql_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
524 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
527 SQL_INST *inst = instance;
528 char sqlusername[MAX_STRING_LEN];
529 SQL_GROUPLIST *group_list, *group_list_tmp;
531 check_pairs = check_pairs;
532 reply_pairs = reply_pairs;
535 DEBUG("rlm_sql (%s): - sql_groupcmp", inst->config->xlat_name);
536 if (!check || !check->vp_strvalue || !check->length){
537 DEBUG("rlm_sql (%s): sql_groupcmp: Illegal group name",
538 inst->config->xlat_name);
542 DEBUG("rlm_sql (%s): sql_groupcmp: NULL request",
543 inst->config->xlat_name);
547 * Set, escape, and check the user attr here
549 if (sql_set_user(inst, req, sqlusername, NULL) < 0)
553 * Get a socket for this lookup
555 sqlsocket = sql_get_socket(inst);
556 if (sqlsocket == NULL) {
557 /* Remove the username we (maybe) added above */
558 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
563 * Get the list of groups this user is a member of
565 if (sql_get_grouplist(inst, sqlsocket, req, &group_list)) {
566 radlog(L_ERR, "rlm_sql (%s): Error getting group membership",
567 inst->config->xlat_name);
568 /* Remove the username we (maybe) added above */
569 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
570 sql_release_socket(inst, sqlsocket);
574 for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) {
575 if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){
576 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User is a member of group %s",
577 inst->config->xlat_name,
578 (char *)check->vp_strvalue);
579 /* Free the grouplist */
580 sql_grouplist_free(&group_list);
581 /* Remove the username we (maybe) added above */
582 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
583 sql_release_socket(inst, sqlsocket);
588 /* Free the grouplist */
589 sql_grouplist_free(&group_list);
590 /* Remove the username we (maybe) added above */
591 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
592 sql_release_socket(inst,sqlsocket);
594 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User is NOT a member of group %s",
595 inst->config->xlat_name, (char *)check->vp_strvalue);
602 static int rlm_sql_process_groups(SQL_INST *inst, REQUEST *request, SQLSOCK *sqlsocket, int *dofallthrough)
604 VALUE_PAIR *check_tmp = NULL;
605 VALUE_PAIR *reply_tmp = NULL;
606 SQL_GROUPLIST *group_list, *group_list_tmp;
607 VALUE_PAIR *sql_group = NULL;
608 char querystr[MAX_QUERY_LEN];
613 * Get the list of groups this user is a member of
615 if (sql_get_grouplist(inst, sqlsocket, request, &group_list)) {
616 radlog(L_ERR, "rlm_sql (%s): Error retrieving group list",
617 inst->config->xlat_name);
621 for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) {
623 * Add the Sql-Group attribute to the request list so we know
624 * which group we're retrieving attributes for
626 sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ);
628 radlog(L_ERR, "rlm_sql (%s): Error creating Sql-Group attribute",
629 inst->config->xlat_name);
632 pairadd(&request->packet->vps, sql_group);
633 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func)) {
634 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
635 inst->config->xlat_name);
636 /* Remove the grouup we added above */
637 pairdelete(&request->packet->vps, PW_SQL_GROUP);
640 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
642 radlog(L_ERR, "rlm_sql (%s): Error retrieving check pairs for group %s",
643 inst->config->xlat_name, group_list_tmp->groupname);
644 /* Remove the grouup we added above */
645 pairdelete(&request->packet->vps, PW_SQL_GROUP);
646 pairfree(&check_tmp);
648 } else if (rows > 0) {
650 * Only do this if *some* check pairs were returned
652 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
654 DEBUG2("rlm_sql (%s): User found in group %s",
655 inst->config->xlat_name, group_list_tmp->groupname);
657 * Now get the reply pairs since the paircompare matched
659 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
660 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
661 inst->config->xlat_name);
662 /* Remove the grouup we added above */
663 pairdelete(&request->packet->vps, PW_SQL_GROUP);
664 pairfree(&check_tmp);
667 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
668 radlog(L_ERR, "rlm_sql (%s): Error retrieving reply pairs for group %s",
669 inst->config->xlat_name, group_list_tmp->groupname);
670 /* Remove the grouup we added above */
671 pairdelete(&request->packet->vps, PW_SQL_GROUP);
672 pairfree(&check_tmp);
673 pairfree(&reply_tmp);
676 *dofallthrough = fallthrough(reply_tmp);
677 pairxlatmove(request, &request->reply->vps, &reply_tmp);
678 pairxlatmove(request, &request->config_items, &check_tmp);
682 * rows == 0. This is like having the username on a line
683 * in the user's file with no check vp's. As such, we treat
684 * it as found and add the reply attributes, so that we
685 * match expected behavior
688 DEBUG2("rlm_sql (%s): User found in group %s",
689 inst->config->xlat_name, group_list_tmp->groupname);
691 * Now get the reply pairs since the paircompare matched
693 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
694 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
695 inst->config->xlat_name);
696 /* Remove the grouup we added above */
697 pairdelete(&request->packet->vps, PW_SQL_GROUP);
698 pairfree(&check_tmp);
701 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
702 radlog(L_ERR, "rlm_sql (%s): Error retrieving reply pairs for group %s",
703 inst->config->xlat_name, group_list_tmp->groupname);
704 /* Remove the grouup we added above */
705 pairdelete(&request->packet->vps, PW_SQL_GROUP);
706 pairfree(&check_tmp);
707 pairfree(&reply_tmp);
710 *dofallthrough = fallthrough(reply_tmp);
711 pairxlatmove(request, &request->reply->vps, &reply_tmp);
712 pairxlatmove(request, &request->config_items, &check_tmp);
716 * Delete the Sql-Group we added above
717 * And clear out the pairlists
719 pairdelete(&request->packet->vps, PW_SQL_GROUP);
720 pairfree(&check_tmp);
721 pairfree(&reply_tmp);
724 sql_grouplist_free(&group_list);
729 static int rlm_sql_detach(void *instance)
731 SQL_INST *inst = instance;
737 if (inst->config->xlat_name) {
738 xlat_unregister(inst->config->xlat_name,(RAD_XLAT_FUNC)sql_xlat);
739 free(inst->config->xlat_name);
742 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
748 * Free up dynamically allocated string pointers.
750 for (i = 0; module_config[i].name != NULL; i++) {
752 if (module_config[i].type != PW_TYPE_STRING_PTR) {
757 * Treat 'config' as an opaque array of bytes,
758 * and take the offset into it. There's a
759 * (char*) pointer at that offset, and we want
762 p = (char **) (((char *)inst->config) + module_config[i].offset);
763 if (!*p) { /* nothing allocated */
769 allowed_chars = NULL;
777 * FIXME: Call the modules 'destroy' function?
779 lt_dlclose(inst->handle); /* ignore any errors */
786 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
789 const char *xlat_name;
791 inst = rad_malloc(sizeof(SQL_INST));
792 memset(inst, 0, sizeof(SQL_INST));
794 inst->config = rad_malloc(sizeof(SQL_CONFIG));
795 memset(inst->config, 0, sizeof(SQL_CONFIG));
798 * If the configuration parameters can't be parsed, then
801 if (cf_section_parse(conf, inst->config, module_config) < 0) {
802 rlm_sql_detach(inst);
806 xlat_name = cf_section_name2(conf);
807 if (xlat_name == NULL)
808 xlat_name = cf_section_name1(conf);
810 inst->config->xlat_name = strdup(xlat_name);
811 xlat_register(xlat_name, (RAD_XLAT_FUNC)sql_xlat, inst);
814 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
815 radlog(L_ERR | L_CONS, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
816 inst->config->xlat_name, MAX_SQL_SOCKS);
817 rlm_sql_detach(inst);
822 * Sanity check for crazy people.
824 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
825 radlog(L_ERR, "rlm_sql (%s): \"%s\" is NOT an SQL driver!",
826 inst->config->xlat_name, inst->config->sql_driver);
827 rlm_sql_detach(inst);
831 inst->handle = lt_dlopenext(inst->config->sql_driver);
832 if (inst->handle == NULL) {
833 radlog(L_ERR, "rlm_sql (%s): Could not link driver %s: %s",
834 inst->config->xlat_name, inst->config->sql_driver,
836 radlog(L_ERR, "rlm_sql (%s): Make sure it (and all its dependent libraries!) are in the search path of your system's ld.",
837 inst->config->xlat_name);
838 rlm_sql_detach(inst);
842 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
844 radlog(L_ERR, "rlm_sql (%s): Could not link symbol %s: %s",
845 inst->config->xlat_name, inst->config->sql_driver,
847 rlm_sql_detach(inst);
851 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
852 inst->config->xlat_name, inst->config->sql_driver,
854 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
855 inst->config->xlat_name, inst->config->sql_login,
856 inst->config->sql_server, inst->config->sql_port,
857 inst->config->sql_db);
859 if (sql_init_socketpool(inst) < 0) {
860 rlm_sql_detach(inst);
863 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
865 if (inst->config->do_clients){
866 if (generate_sql_clients(inst) == -1){
867 radlog(L_ERR, "rlm_sql (%s): generate_sql_clients() returned error",inst->config->xlat_name);
868 rlm_sql_detach(inst);
872 allowed_chars = inst->config->allowed_chars;
876 return RLM_MODULE_OK;
880 static int rlm_sql_authorize(void *instance, REQUEST * request)
882 VALUE_PAIR *check_tmp = NULL;
883 VALUE_PAIR *reply_tmp = NULL;
884 VALUE_PAIR *user_profile = NULL;
886 int dofallthrough = 1;
889 SQL_INST *inst = instance;
890 char querystr[MAX_QUERY_LEN];
891 char sqlusername[MAX_STRING_LEN];
893 * the profile username is used as the sqlusername during
894 * profile checking so that we don't overwrite the orignal
897 char profileusername[MAX_STRING_LEN];
900 * Set, escape, and check the user attr here
902 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
903 return RLM_MODULE_FAIL;
909 sqlsocket = sql_get_socket(inst);
910 if (sqlsocket == NULL) {
911 /* Remove the username we (maybe) added above */
912 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
913 return RLM_MODULE_FAIL;
918 * After this point, ALL 'return's MUST release the SQL socket!
922 * Alright, start by getting the specific entry for the user
924 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func)) {
925 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
926 inst->config->xlat_name);
927 sql_release_socket(inst, sqlsocket);
928 /* Remove the username we (maybe) added above */
929 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
930 return RLM_MODULE_FAIL;
932 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
934 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
935 inst->config->xlat_name);
936 sql_release_socket(inst, sqlsocket);
937 /* Remove the username we (maybe) added above */
938 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
939 pairfree(&check_tmp);
940 return RLM_MODULE_FAIL;
941 } else if (rows > 0) {
943 * Only do this if *some* check pairs were returned
945 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
947 DEBUG2("rlm_sql (%s): User found in radcheck table", inst->config->xlat_name);
949 * Now get the reply pairs since the paircompare matched
951 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func)) {
952 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
953 inst->config->xlat_name);
954 sql_release_socket(inst, sqlsocket);
955 /* Remove the username we (maybe) added above */
956 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
957 pairfree(&check_tmp);
958 return RLM_MODULE_FAIL;
960 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
961 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
962 inst->config->xlat_name);
963 sql_release_socket(inst, sqlsocket);
964 /* Remove the username we (maybe) added above */
965 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
966 pairfree(&check_tmp);
967 pairfree(&reply_tmp);
968 return RLM_MODULE_FAIL;
970 if (!inst->config->read_groups)
971 dofallthrough = fallthrough(reply_tmp);
972 pairxlatmove(request, &request->reply->vps, &reply_tmp);
973 pairxlatmove(request, &request->config_items, &check_tmp);
978 * Clear out the pairlists
980 pairfree(&check_tmp);
981 pairfree(&reply_tmp);
984 * dofallthrough is set to 1 by default so that if the user information
985 * is not found, we will still process groups. If the user information,
986 * however, *is* found, Fall-Through must be set in order to process
990 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
992 radlog(L_ERR, "rlm_sql (%s): Error processing groups; rejecting user",
993 inst->config->xlat_name);
994 sql_release_socket(inst, sqlsocket);
995 /* Remove the username we (maybe) added above */
996 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
997 return RLM_MODULE_FAIL;
998 } else if (rows > 0) {
1004 * repeat the above process with the default profile or User-Profile
1006 if (dofallthrough) {
1007 int profile_found = 0;
1009 * Check for a default_profile or for a User-Profile.
1011 user_profile = pairfind(request->config_items, PW_USER_PROFILE);
1012 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
1013 char *profile = inst->config->default_profile;
1015 if (user_profile != NULL)
1016 profile = user_profile->vp_strvalue;
1017 if (profile && strlen(profile)){
1018 radlog(L_DBG, "rlm_sql (%s): Checking profile %s",
1019 inst->config->xlat_name, profile);
1020 if (sql_set_user(inst, request, profileusername, profile) < 0) {
1021 radlog(L_ERR, "rlm_sql (%s): Error setting profile; rejecting user",
1022 inst->config->xlat_name);
1023 sql_release_socket(inst, sqlsocket);
1024 /* Remove the username we (maybe) added above */
1025 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1026 return RLM_MODULE_FAIL;
1033 if (profile_found) {
1034 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1036 radlog(L_ERR, "rlm_sql (%s): Error processing profile groups; rejecting user",
1037 inst->config->xlat_name);
1038 sql_release_socket(inst, sqlsocket);
1039 /* Remove the username we (maybe) added above */
1040 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1041 return RLM_MODULE_FAIL;
1042 } else if (rows > 0) {
1048 /* Remove the username we (maybe) added above */
1049 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1050 sql_release_socket(inst, sqlsocket);
1053 radlog(L_DBG, "rlm_sql (%s): User %s not found",
1054 inst->config->xlat_name, sqlusername);
1055 return RLM_MODULE_NOTFOUND;
1057 return RLM_MODULE_OK;
1062 * Accounting: save the account data to our sql table
1064 static int rlm_sql_accounting(void *instance, REQUEST * request) {
1066 SQLSOCK *sqlsocket = NULL;
1068 SQL_INST *inst = instance;
1069 int ret = RLM_MODULE_OK;
1070 int numaffected = 0;
1071 int acctstatustype = 0;
1072 char querystr[MAX_QUERY_LEN];
1073 char logstr[MAX_QUERY_LEN];
1074 char sqlusername[MAX_STRING_LEN];
1076 #ifdef CISCO_ACCOUNTING_HACK
1077 int acctsessiontime = 0;
1080 memset(querystr, 0, MAX_QUERY_LEN);
1083 * Find the Acct Status Type
1085 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
1086 acctstatustype = pair->lvalue;
1088 radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1089 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s",
1090 inst->config->xlat_name, logstr);
1091 return RLM_MODULE_INVALID;
1094 switch (acctstatustype) {
1096 * The Terminal server informed us that it was rebooted
1097 * STOP all records from this NAS
1099 case PW_STATUS_ACCOUNTING_ON:
1100 case PW_STATUS_ACCOUNTING_OFF:
1101 radlog(L_INFO, "rlm_sql (%s): received Acct On/Off packet", inst->config->xlat_name);
1102 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
1103 query_log(request, inst, querystr);
1105 sqlsocket = sql_get_socket(inst);
1106 if (sqlsocket == NULL)
1107 return(RLM_MODULE_FAIL);
1108 if (*querystr) { /* non-empty query */
1109 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1110 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting for Acct On/Off packet - %s",
1111 inst->config->xlat_name,
1112 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1113 ret = RLM_MODULE_FAIL;
1115 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1121 * Got an update accounting packet
1123 case PW_STATUS_ALIVE:
1126 * Set, escape, and check the user attr here
1128 sql_set_user(inst, request, sqlusername, NULL);
1130 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
1131 query_log(request, inst, querystr);
1133 sqlsocket = sql_get_socket(inst);
1134 if (sqlsocket == NULL)
1135 return(RLM_MODULE_FAIL);
1136 if (*querystr) { /* non-empty query */
1137 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1138 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting ALIVE record - %s",
1139 inst->config->xlat_name,
1140 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1141 ret = RLM_MODULE_FAIL;
1144 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1145 if (numaffected < 1) {
1148 * If our update above didn't match anything
1149 * we assume it's because we haven't seen a
1150 * matching Start record. So we have to
1151 * insert this update rather than do an update
1153 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
1154 query_log(request, inst, querystr);
1155 if (*querystr) { /* non-empty query */
1156 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1157 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting ALIVE record - %s",
1158 inst->config->xlat_name,
1159 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1160 ret = RLM_MODULE_FAIL;
1162 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1166 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1171 * Got accounting start packet
1173 case PW_STATUS_START:
1176 * Set, escape, and check the user attr here
1178 sql_set_user(inst, request, sqlusername, NULL);
1180 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
1181 query_log(request, inst, querystr);
1183 sqlsocket = sql_get_socket(inst);
1184 if (sqlsocket == NULL)
1185 return(RLM_MODULE_FAIL);
1186 if (*querystr) { /* non-empty query */
1187 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1188 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting START record - %s",
1189 inst->config->xlat_name,
1190 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1193 * We failed the insert above. It's probably because
1194 * the stop record came before the start. We try
1195 * our alternate query now (typically an UPDATE)
1197 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1198 query_log(request, inst, querystr);
1200 if (*querystr) { /* non-empty query */
1201 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1202 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting START record - %s",
1203 inst->config->xlat_name,
1204 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1205 ret = RLM_MODULE_FAIL;
1207 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1210 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1215 * Got accounting stop packet
1217 case PW_STATUS_STOP:
1220 * Set, escape, and check the user attr here
1222 sql_set_user(inst, request, sqlusername, NULL);
1224 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1225 query_log(request, inst, querystr);
1227 sqlsocket = sql_get_socket(inst);
1228 if (sqlsocket == NULL)
1229 return(RLM_MODULE_FAIL);
1230 if (*querystr) { /* non-empty query */
1231 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1232 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting STOP record - %s",
1233 inst->config->xlat_name,
1234 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1235 ret = RLM_MODULE_FAIL;
1238 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1239 if (numaffected < 1) {
1241 * If our update above didn't match anything
1242 * we assume it's because we haven't seen a
1243 * matching Start record. So we have to
1244 * insert this stop rather than do an update
1246 #ifdef CISCO_ACCOUNTING_HACK
1248 * If stop but zero session length AND no previous
1249 * session found, drop it as in invalid packet
1250 * This is to fix CISCO's aaa from filling our
1251 * table with bogus crap
1253 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
1254 acctsessiontime = pair->lvalue;
1256 if (acctsessiontime <= 0) {
1257 radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1258 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s", inst->config->xlat_name, logstr);
1259 sql_release_socket(inst, sqlsocket);
1260 ret = RLM_MODULE_NOOP;
1264 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1265 query_log(request, inst, querystr);
1267 if (*querystr) { /* non-empty query */
1268 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1269 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting STOP record - %s",
1270 inst->config->xlat_name,
1271 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1272 ret = RLM_MODULE_FAIL;
1274 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1278 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1283 * Anything else is ignored.
1286 radlog(L_INFO, "rlm_sql (%s): Unsupported Acct-Status-Type = %d", inst->config->xlat_name, acctstatustype);
1287 return RLM_MODULE_NOOP;
1292 sql_release_socket(inst, sqlsocket);
1299 * See if a user is already logged in. Sets request->simul_count to the
1300 * current session count for this user.
1302 * Check twice. If on the first pass the user exceeds his
1303 * max. number of logins, do a second pass and validate all
1304 * logins by querying the terminal server (using eg. SNMP).
1307 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1309 SQL_INST *inst = instance;
1311 char querystr[MAX_QUERY_LEN];
1312 char sqlusername[MAX_STRING_LEN];
1315 char *call_num = NULL;
1318 uint32_t nas_addr = 0;
1321 /* If simul_count_query is not defined, we don't do any checking */
1322 if (inst->config->simul_count_query[0] == 0) {
1323 return RLM_MODULE_NOOP;
1326 if((request->username == NULL) || (request->username->length == 0)) {
1327 radlog(L_ERR, "rlm_sql (%s): Zero Length username not permitted\n", inst->config->xlat_name);
1328 return RLM_MODULE_INVALID;
1332 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1333 return RLM_MODULE_FAIL;
1335 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1337 /* initialize the sql socket */
1338 sqlsocket = sql_get_socket(inst);
1339 if(sqlsocket == NULL)
1340 return RLM_MODULE_FAIL;
1342 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1343 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1344 sql_release_socket(inst, sqlsocket);
1345 return RLM_MODULE_FAIL;
1348 ret = rlm_sql_fetch_row(sqlsocket, inst);
1351 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1352 sql_release_socket(inst, sqlsocket);
1353 return RLM_MODULE_FAIL;
1356 row = sqlsocket->row;
1358 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1359 sql_release_socket(inst, sqlsocket);
1360 return RLM_MODULE_FAIL;
1363 request->simul_count = atoi(row[0]);
1364 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1366 if(request->simul_count < request->simul_max) {
1367 sql_release_socket(inst, sqlsocket);
1368 return RLM_MODULE_OK;
1372 * Looks like too many sessions, so let's start verifying
1373 * them, unless told to rely on count query only.
1375 if (inst->config->simul_verify_query[0] == '\0') {
1376 sql_release_socket(inst, sqlsocket);
1377 return RLM_MODULE_OK;
1380 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1381 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1382 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Database query error", inst->config->xlat_name);
1383 sql_release_socket(inst, sqlsocket);
1384 return RLM_MODULE_FAIL;
1388 * Setup some stuff, like for MPP detection.
1390 request->simul_count = 0;
1392 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1394 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1395 call_num = vp->vp_strvalue;
1398 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1399 row = sqlsocket->row;
1403 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1404 sql_release_socket(inst, sqlsocket);
1405 DEBUG("rlm_sql (%s): Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1406 return RLM_MODULE_FAIL;
1409 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1410 sql_release_socket(inst, sqlsocket);
1411 DEBUG("rlm_sql (%s): Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1412 return RLM_MODULE_FAIL;
1415 nas_addr = inet_addr(row[3]);
1417 nas_port = atoi(row[4]);
1419 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1423 * Stale record - zap it.
1425 if (inst->config->deletestalesessions == TRUE) {
1426 uint32_t framed_addr = 0;
1431 framed_addr = inet_addr(row[5]);
1433 if (strcmp(row[7], "PPP") == 0)
1435 else if (strcmp(row[7], "SLIP") == 0)
1439 sess_time = atoi(row[8]);
1440 session_zap(request, nas_addr, nas_port,
1441 row[2], row[1], framed_addr,
1445 else if (check == 1) {
1447 * User is still logged in.
1449 ++request->simul_count;
1452 * Does it look like a MPP attempt?
1454 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1455 request->simul_mpp = 2;
1456 else if (row[6] && call_num &&
1457 !strncmp(row[6],call_num,16))
1458 request->simul_mpp = 2;
1462 * Failed to check the terminal server for
1463 * duplicate logins: return an error.
1465 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1466 sql_release_socket(inst, sqlsocket);
1467 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Failed to check the terminal server for user '%s'.", inst->config->xlat_name, row[2]);
1468 return RLM_MODULE_FAIL;
1472 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1473 sql_release_socket(inst, sqlsocket);
1476 * The Auth module apparently looks at request->simul_count,
1477 * not the return value of this module when deciding to deny
1478 * a call for too many sessions.
1480 return RLM_MODULE_OK;
1484 * Execute postauth_query after authentication
1486 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1487 SQLSOCK *sqlsocket = NULL;
1488 SQL_INST *inst = instance;
1489 char querystr[MAX_QUERY_LEN];
1490 char sqlusername[MAX_STRING_LEN];
1492 DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name);
1494 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1495 return RLM_MODULE_FAIL;
1497 /* If postauth_query is not defined, we stop here */
1498 if (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 (char *)(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 */