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 The FreeRADIUS server project
22 * Copyright 2000 Mike Machado <mike@innercite.com>
23 * Copyright 2000 Alan DeKok <aland@ox.org>
26 static const char rcsid[] =
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 {"acct_table", PW_TYPE_STRING_PTR,
66 offsetof(SQL_CONFIG,sql_acct_table), NULL, "radacct"},
67 {"acct_table2", PW_TYPE_STRING_PTR,
68 offsetof(SQL_CONFIG,sql_acct_table2), NULL, "radacct"},
69 {"authcheck_table", PW_TYPE_STRING_PTR,
70 offsetof(SQL_CONFIG,sql_authcheck_table), NULL, "radcheck"},
71 {"authreply_table", PW_TYPE_STRING_PTR,
72 offsetof(SQL_CONFIG,sql_authreply_table), NULL, "radreply"},
73 {"groupcheck_table", PW_TYPE_STRING_PTR,
74 offsetof(SQL_CONFIG,sql_groupcheck_table), NULL, "radgroupcheck"},
75 {"groupreply_table", PW_TYPE_STRING_PTR,
76 offsetof(SQL_CONFIG,sql_groupreply_table), NULL, "radgroupreply"},
77 {"usergroup_table", PW_TYPE_STRING_PTR,
78 offsetof(SQL_CONFIG,sql_usergroup_table), NULL, "usergroup"},
79 {"read_groups", PW_TYPE_BOOLEAN,
80 offsetof(SQL_CONFIG,read_groups), NULL, "yes"},
81 {"nas_table", PW_TYPE_STRING_PTR,
82 offsetof(SQL_CONFIG,sql_nas_table), NULL, "nas"},
83 {"dict_table", PW_TYPE_STRING_PTR,
84 offsetof(SQL_CONFIG,sql_dict_table), NULL, "dictionary"},
85 {"sqltrace", PW_TYPE_BOOLEAN,
86 offsetof(SQL_CONFIG,sqltrace), NULL, "no"},
87 {"sqltracefile", PW_TYPE_STRING_PTR,
88 offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE},
89 {"readclients", PW_TYPE_BOOLEAN,
90 offsetof(SQL_CONFIG,do_clients), NULL, "no"},
91 {"deletestalesessions", PW_TYPE_BOOLEAN,
92 offsetof(SQL_CONFIG,deletestalesessions), NULL, "yes"},
93 {"num_sql_socks", PW_TYPE_INTEGER,
94 offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"},
95 {"sql_user_name", PW_TYPE_STRING_PTR,
96 offsetof(SQL_CONFIG,query_user), NULL, ""},
97 {"default_user_profile", PW_TYPE_STRING_PTR,
98 offsetof(SQL_CONFIG,default_profile), NULL, ""},
99 {"nas_query", PW_TYPE_STRING_PTR,
100 offsetof(SQL_CONFIG,nas_query), NULL, "SELECT id,nasname,shortname,type,secret FROM nas"},
101 {"authorize_check_query", PW_TYPE_STRING_PTR,
102 offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
103 {"authorize_reply_query", PW_TYPE_STRING_PTR,
104 offsetof(SQL_CONFIG,authorize_reply_query), NULL, ""},
105 {"authorize_group_check_query", PW_TYPE_STRING_PTR,
106 offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
107 {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
108 offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
109 {"accounting_onoff_query", PW_TYPE_STRING_PTR,
110 offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
111 {"accounting_update_query", PW_TYPE_STRING_PTR,
112 offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
113 {"accounting_update_query_alt", PW_TYPE_STRING_PTR,
114 offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""},
115 {"accounting_start_query", PW_TYPE_STRING_PTR,
116 offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
117 {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
118 offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
119 {"accounting_stop_query", PW_TYPE_STRING_PTR,
120 offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
121 {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
122 offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
123 {"group_membership_query", PW_TYPE_STRING_PTR,
124 offsetof(SQL_CONFIG,groupmemb_query), NULL, ""},
125 {"connect_failure_retry_delay", PW_TYPE_INTEGER,
126 offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
127 {"simul_count_query", PW_TYPE_STRING_PTR,
128 offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
129 {"simul_verify_query", PW_TYPE_STRING_PTR,
130 offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
131 {"postauth_table", PW_TYPE_STRING_PTR,
132 offsetof(SQL_CONFIG,sql_postauth_table), NULL, "radpostauth"},
133 {"postauth_query", PW_TYPE_STRING_PTR,
134 offsetof(SQL_CONFIG,postauth_query), NULL, ""},
135 {"safe-characters", PW_TYPE_STRING_PTR,
136 offsetof(SQL_CONFIG,allowed_chars), NULL,
137 "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
139 {NULL, -1, 0, NULL, NULL}
143 * Fall-Through checking function from rlm_files.c
145 static int fallthrough(VALUE_PAIR *vp)
148 tmp = pairfind(vp, PW_FALL_THROUGH);
150 return tmp ? tmp->lvalue : 0;
158 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username);
159 static int generate_sql_clients(SQL_INST *inst);
160 static int sql_escape_func(char *out, int outlen, const char *in);
163 * sql xlat function. Right now only SELECTs are supported. Only
164 * the first element of the SELECT result will be used.
166 static int sql_xlat(void *instance, REQUEST *request,
167 char *fmt, char *out, size_t freespace,
168 UNUSED RADIUS_ESCAPE_STRING func)
172 SQL_INST *inst = instance;
173 char querystr[MAX_QUERY_LEN];
174 char sqlusername[MAX_STRING_LEN];
177 DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name);
179 * Add SQL-User-Name attribute just in case it is needed
180 * We could search the string fmt for SQL-User-Name to see if this is
183 sql_set_user(inst, request, sqlusername, NULL);
185 * Do an xlat on the provided string (nice recursive operation).
187 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
188 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
189 inst->config->xlat_name);
193 query_log(request, inst,querystr);
194 sqlsocket = sql_get_socket(inst);
195 if (sqlsocket == NULL)
197 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
198 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
199 inst->config->xlat_name,querystr,
200 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
201 sql_release_socket(inst,sqlsocket);
205 ret = rlm_sql_fetch_row(sqlsocket, inst);
208 DEBUG("rlm_sql (%s): SQL query did not succeed",
209 inst->config->xlat_name);
210 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
211 sql_release_socket(inst,sqlsocket);
215 row = sqlsocket->row;
217 DEBUG("rlm_sql (%s): SQL query did not return any results",
218 inst->config->xlat_name);
219 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
220 sql_release_socket(inst,sqlsocket);
225 DEBUG("rlm_sql (%s): row[0] returned NULL",
226 inst->config->xlat_name);
227 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
228 sql_release_socket(inst,sqlsocket);
231 ret = strlen(row[0]);
232 if (ret > freespace){
233 DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space",
234 inst->config->xlat_name);
235 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
236 sql_release_socket(inst,sqlsocket);
240 strncpy(out,row[0],ret);
242 DEBUG("rlm_sql (%s): - sql_xlat finished",
243 inst->config->xlat_name);
245 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
246 sql_release_socket(inst,sqlsocket);
250 static int generate_sql_clients(SQL_INST *inst)
254 char querystr[MAX_QUERY_LEN];
256 char *prefix_ptr = NULL;
259 DEBUG("rlm_sql (%s): Processing generate_sql_clients",
260 inst->config->xlat_name);
262 if (inst->config->sql_nas_table == NULL){
263 radlog(L_ERR, "rlm_sql (%s): sql_nas_table is NULL.",inst->config->xlat_name);
267 /* NAS query isn't xlat'ed */
268 strNcpy(querystr, inst->config->nas_query, sizeof(querystr));
269 DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s",
270 inst->config->xlat_name, querystr);
272 sqlsocket = sql_get_socket(inst);
273 if (sqlsocket == NULL)
275 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
276 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
277 inst->config->xlat_name,querystr,
278 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
279 sql_release_socket(inst,sqlsocket);
283 while(rlm_sql_fetch_row(sqlsocket, inst) == 0) {
285 row = sqlsocket->row;
290 * Row1 Row2 Row3 Row4 Row5 Row6 Row7 Row8
292 * id nasname shortname type ports secret community description
297 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
301 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
305 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
309 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
313 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
314 row[1],row[2],row[4]);
316 c = rad_malloc(sizeof(RADCLIENT));
317 memset(c, 0, sizeof(RADCLIENT));
323 prefix_ptr = strchr(row[1], '/');
325 c->prefix = atoi(prefix_ptr + 1);
326 if ((c->prefix < 0) || (c->prefix > 128)) {
327 radlog(L_ERR, "rlm_sql (%s): Invalid Prefix value '%s' for IP.",
328 inst->config->xlat_name, prefix_ptr + 1);
332 /* Replace '/' with '\0' */
337 * Always get the numeric representation of IP
339 if (ip_hton(row[1], AF_UNSPEC, &c->ipaddr) < 0) {
340 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s: %s",
341 inst->config->xlat_name,
342 row[1], librad_errstr);
347 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
348 c->longname = strdup(buffer);
351 if (c->prefix < 0) switch (c->ipaddr.af) {
363 * Other values (secret, shortname, nastype)
365 c->secret = (u_char *)strdup(row[4]);
366 c->shortname = strdup(row[2]);
368 c->nastype = strdup(row[3]);
370 DEBUG("rlm_sql (%s): Adding client %s (%s) to clients list",
371 inst->config->xlat_name,
372 c->longname,c->shortname);
373 if (!client_add(mainconfig.clients, c)) {
378 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
379 sql_release_socket(inst, sqlsocket);
386 * Translate the SQL queries.
388 static int sql_escape_func(char *out, int outlen, const char *in)
394 * Non-printable characters get replaced with their
395 * mime-encoded equivalents.
398 strchr(allowed_chars, *in) == NULL) {
400 * Only 3 or less bytes available.
406 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
415 * Only one byte left.
435 * Set the SQL user name.
437 * We don't call the escape function here. The resulting string
438 * will be escaped later in the queries xlat so we don't need to
439 * escape it twice. (it will make things wrong if we have an
440 * escape candidate character in the username)
442 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
445 char tmpuser[MAX_STRING_LEN];
448 sqlusername[0]= '\0';
450 /* Remove any user attr we added previously */
451 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
453 if (username != NULL) {
454 strNcpy(tmpuser, username, MAX_STRING_LEN);
455 } else if (strlen(inst->config->query_user)) {
456 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
462 strNcpy(sqlusername, tmpuser, MAX_STRING_LEN);
463 DEBUG2("rlm_sql (%s): sql_set_user escaped user --> '%s'",
464 inst->config->xlat_name, sqlusername);
465 vp = pairmake("SQL-User-Name", sqlusername, 0);
467 radlog(L_ERR, "%s", librad_errstr);
471 pairadd(&request->packet->vps, vp);
478 static void sql_grouplist_free (SQL_GROUPLIST **group_list)
484 *group_list = (*group_list)->next;
490 static int sql_get_grouplist (SQL_INST *inst, SQLSOCK *sqlsocket, REQUEST *request, SQL_GROUPLIST **group_list)
492 char querystr[MAX_QUERY_LEN];
495 SQL_GROUPLIST *group_list_tmp;
497 /* NOTE: sql_set_user should have been run before calling this function */
499 group_list_tmp = *group_list = NULL;
501 if (inst->config->groupmemb_query[0] == 0)
504 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, request, sql_escape_func)) {
505 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
506 inst->config->xlat_name);
510 if (rlm_sql_select_query(sqlsocket, inst, querystr) < 0) {
511 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
512 inst->config->xlat_name,querystr,
513 (char *)(inst->module->sql_error)(sqlsocket,inst->config));
516 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
517 row = sqlsocket->row;
521 DEBUG("rlm_sql (%s): row[0] returned NULL",
522 inst->config->xlat_name);
523 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
524 sql_grouplist_free(group_list);
527 if (*group_list == NULL) {
528 *group_list = rad_malloc(sizeof(SQL_GROUPLIST));
529 group_list_tmp = *group_list;
531 group_list_tmp->next = rad_malloc(sizeof(SQL_GROUPLIST));
532 group_list_tmp = group_list_tmp->next;
534 group_list_tmp->next = NULL;
535 strNcpy(group_list_tmp->groupname, row[0], MAX_STRING_LEN);
538 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
545 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
546 * with the group memberships reciding in sql
547 * The group membership query should only return one element which is the username. The returned
548 * username will then be checked with the passed check string.
551 static int sql_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
552 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
555 SQL_INST *inst = instance;
556 char sqlusername[MAX_STRING_LEN];
557 SQL_GROUPLIST *group_list, *group_list_tmp;
559 check_pairs = check_pairs;
560 reply_pairs = reply_pairs;
563 DEBUG("rlm_sql (%s): - sql_groupcmp", inst->config->xlat_name);
564 if (!check || !check->vp_strvalue || !check->length){
565 DEBUG("rlm_sql (%s): sql_groupcmp: Illegal group name",
566 inst->config->xlat_name);
570 DEBUG("rlm_sql (%s): sql_groupcmp: NULL request",
571 inst->config->xlat_name);
575 * Set, escape, and check the user attr here
577 if (sql_set_user(inst, req, sqlusername, NULL) < 0)
581 * Get a socket for this lookup
583 sqlsocket = sql_get_socket(inst);
584 if (sqlsocket == NULL) {
585 /* Remove the username we (maybe) added above */
586 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
591 * Get the list of groups this user is a member of
593 if (sql_get_grouplist(inst, sqlsocket, req, &group_list)) {
594 radlog(L_ERR, "rlm_sql (%s): Error getting group membership",
595 inst->config->xlat_name);
596 /* Remove the username we (maybe) added above */
597 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
598 sql_release_socket(inst, sqlsocket);
602 for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) {
603 if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){
604 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User is a member of group %s",
605 inst->config->xlat_name,
606 (char *)check->vp_strvalue);
607 /* Free the grouplist */
608 sql_grouplist_free(&group_list);
609 /* Remove the username we (maybe) added above */
610 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
611 sql_release_socket(inst, sqlsocket);
616 /* Free the grouplist */
617 sql_grouplist_free(&group_list);
618 /* Remove the username we (maybe) added above */
619 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
620 sql_release_socket(inst,sqlsocket);
622 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User is NOT a member of group %s",
623 inst->config->xlat_name, (char *)check->vp_strvalue);
630 static int rlm_sql_process_groups(SQL_INST *inst, REQUEST *request, SQLSOCK *sqlsocket, int *dofallthrough)
632 VALUE_PAIR *check_tmp = NULL;
633 VALUE_PAIR *reply_tmp = NULL;
634 SQL_GROUPLIST *group_list, *group_list_tmp;
635 VALUE_PAIR *sql_group = NULL;
636 char querystr[MAX_QUERY_LEN];
641 * Get the list of groups this user is a member of
643 if (sql_get_grouplist(inst, sqlsocket, request, &group_list)) {
644 radlog(L_ERR, "rlm_sql (%s): Error retrieving group list",
645 inst->config->xlat_name);
649 for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) {
651 * Add the Sql-Group attribute to the request list so we know
652 * which group we're retrieving attributes for
654 sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ);
656 radlog(L_ERR, "rlm_sql (%s): Error creating Sql-Group attribute",
657 inst->config->xlat_name);
660 pairadd(&request->packet->vps, sql_group);
661 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func)) {
662 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
663 inst->config->xlat_name);
664 /* Remove the grouup we added above */
665 pairdelete(&request->packet->vps, PW_SQL_GROUP);
668 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
670 radlog(L_ERR, "rlm_sql (%s): Error retrieving check pairs for group %s",
671 inst->config->xlat_name, group_list_tmp->groupname);
672 /* Remove the grouup we added above */
673 pairdelete(&request->packet->vps, PW_SQL_GROUP);
674 pairfree(&check_tmp);
676 } else if (rows > 0) {
678 * Only do this if *some* check pairs were returned
680 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
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 * rows == 0. This is like having the username on a line
711 * in the user's file with no check vp's. As such, we treat
712 * it as found and add the reply attributes, so that we
713 * match expected behavior
716 DEBUG2("rlm_sql (%s): User found in group %s",
717 inst->config->xlat_name, group_list_tmp->groupname);
719 * Now get the reply pairs since the paircompare matched
721 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
722 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
723 inst->config->xlat_name);
724 /* Remove the grouup we added above */
725 pairdelete(&request->packet->vps, PW_SQL_GROUP);
726 pairfree(&check_tmp);
729 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
730 radlog(L_ERR, "rlm_sql (%s): Error retrieving reply pairs for group %s",
731 inst->config->xlat_name, group_list_tmp->groupname);
732 /* Remove the grouup we added above */
733 pairdelete(&request->packet->vps, PW_SQL_GROUP);
734 pairfree(&check_tmp);
735 pairfree(&reply_tmp);
738 *dofallthrough = fallthrough(reply_tmp);
739 pairxlatmove(request, &request->reply->vps, &reply_tmp);
740 pairxlatmove(request, &request->config_items, &check_tmp);
744 * Delete the Sql-Group we added above
745 * And clear out the pairlists
747 pairdelete(&request->packet->vps, PW_SQL_GROUP);
748 pairfree(&check_tmp);
749 pairfree(&reply_tmp);
752 sql_grouplist_free(&group_list);
757 static int rlm_sql_detach(void *instance)
759 SQL_INST *inst = instance;
765 if (inst->config->xlat_name) {
766 xlat_unregister(inst->config->xlat_name,(RAD_XLAT_FUNC)sql_xlat);
767 free(inst->config->xlat_name);
770 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
776 * Free up dynamically allocated string pointers.
778 for (i = 0; module_config[i].name != NULL; i++) {
780 if (module_config[i].type != PW_TYPE_STRING_PTR) {
785 * Treat 'config' as an opaque array of bytes,
786 * and take the offset into it. There's a
787 * (char*) pointer at that offset, and we want
790 p = (char **) (((char *)inst->config) + module_config[i].offset);
791 if (!*p) { /* nothing allocated */
797 allowed_chars = NULL;
805 * FIXME: Call the modules 'destroy' function?
807 lt_dlclose(inst->handle); /* ignore any errors */
814 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
817 const char *xlat_name;
819 inst = rad_malloc(sizeof(SQL_INST));
820 memset(inst, 0, sizeof(SQL_INST));
822 inst->config = rad_malloc(sizeof(SQL_CONFIG));
823 memset(inst->config, 0, sizeof(SQL_CONFIG));
826 * If the configuration parameters can't be parsed, then
829 if (cf_section_parse(conf, inst->config, module_config) < 0) {
830 rlm_sql_detach(inst);
834 xlat_name = cf_section_name2(conf);
835 if (xlat_name == NULL)
836 xlat_name = cf_section_name1(conf);
838 inst->config->xlat_name = strdup(xlat_name);
839 xlat_register(xlat_name, (RAD_XLAT_FUNC)sql_xlat, inst);
842 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
843 radlog(L_ERR | L_CONS, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
844 inst->config->xlat_name, MAX_SQL_SOCKS);
845 rlm_sql_detach(inst);
850 * Sanity check for crazy people.
852 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
853 radlog(L_ERR, "rlm_sql (%s): \"%s\" is NOT an SQL driver!",
854 inst->config->xlat_name, inst->config->sql_driver);
855 rlm_sql_detach(inst);
859 inst->handle = lt_dlopenext(inst->config->sql_driver);
860 if (inst->handle == NULL) {
861 radlog(L_ERR, "rlm_sql (%s): Could not link driver %s: %s",
862 inst->config->xlat_name, inst->config->sql_driver,
864 radlog(L_ERR, "rlm_sql (%s): Make sure it (and all its dependent libraries!) are in the search path of your system's ld.",
865 inst->config->xlat_name);
866 rlm_sql_detach(inst);
870 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
872 radlog(L_ERR, "rlm_sql (%s): Could not link symbol %s: %s",
873 inst->config->xlat_name, inst->config->sql_driver,
875 rlm_sql_detach(inst);
879 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
880 inst->config->xlat_name, inst->config->sql_driver,
882 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
883 inst->config->xlat_name, inst->config->sql_login,
884 inst->config->sql_server, inst->config->sql_port,
885 inst->config->sql_db);
887 if (sql_init_socketpool(inst) < 0) {
888 rlm_sql_detach(inst);
891 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
893 if (inst->config->do_clients){
894 if (generate_sql_clients(inst) == -1){
895 radlog(L_ERR, "rlm_sql (%s): generate_sql_clients() returned error",inst->config->xlat_name);
896 rlm_sql_detach(inst);
900 allowed_chars = inst->config->allowed_chars;
904 return RLM_MODULE_OK;
908 static int rlm_sql_authorize(void *instance, REQUEST * request)
910 VALUE_PAIR *check_tmp = NULL;
911 VALUE_PAIR *reply_tmp = NULL;
912 VALUE_PAIR *user_profile = NULL;
914 int dofallthrough = 1;
917 SQL_INST *inst = instance;
918 char querystr[MAX_QUERY_LEN];
919 char sqlusername[MAX_STRING_LEN];
921 * the profile username is used as the sqlusername during
922 * profile checking so that we don't overwrite the orignal
925 char profileusername[MAX_STRING_LEN];
928 * They MUST have a user name to do SQL authorization.
930 if ((request->username == NULL) ||
931 (request->username->length == 0)) {
932 radlog(L_ERR, "rlm_sql (%s): zero length username not permitted\n", inst->config->xlat_name);
933 return RLM_MODULE_INVALID;
938 * Set, escape, and check the user attr here
940 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
941 return RLM_MODULE_FAIL;
947 sqlsocket = sql_get_socket(inst);
948 if (sqlsocket == NULL) {
949 /* Remove the username we (maybe) added above */
950 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
951 return RLM_MODULE_FAIL;
956 * After this point, ALL 'return's MUST release the SQL socket!
960 * Alright, start by getting the specific entry for the user
962 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func)) {
963 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
964 inst->config->xlat_name);
965 sql_release_socket(inst, sqlsocket);
966 /* Remove the username we (maybe) added above */
967 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
968 return RLM_MODULE_FAIL;
970 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
972 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
973 inst->config->xlat_name);
974 sql_release_socket(inst, sqlsocket);
975 /* Remove the username we (maybe) added above */
976 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
977 pairfree(&check_tmp);
978 return RLM_MODULE_FAIL;
979 } else if (rows > 0) {
981 * Only do this if *some* check pairs were returned
983 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
985 DEBUG2("rlm_sql (%s): User found in radcheck table", inst->config->xlat_name);
987 * Now get the reply pairs since the paircompare matched
989 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func)) {
990 radlog(L_ERR, "rlm_sql (%s): Error generating query; rejecting user",
991 inst->config->xlat_name);
992 sql_release_socket(inst, sqlsocket);
993 /* Remove the username we (maybe) added above */
994 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
995 pairfree(&check_tmp);
996 return RLM_MODULE_FAIL;
998 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
999 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
1000 inst->config->xlat_name);
1001 sql_release_socket(inst, sqlsocket);
1002 /* Remove the username we (maybe) added above */
1003 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1004 pairfree(&check_tmp);
1005 pairfree(&reply_tmp);
1006 return RLM_MODULE_FAIL;
1008 if (!inst->config->read_groups)
1009 dofallthrough = fallthrough(reply_tmp);
1010 pairxlatmove(request, &request->reply->vps, &reply_tmp);
1011 pairxlatmove(request, &request->config_items, &check_tmp);
1016 * Clear out the pairlists
1018 pairfree(&check_tmp);
1019 pairfree(&reply_tmp);
1022 * dofallthrough is set to 1 by default so that if the user information
1023 * is not found, we will still process groups. If the user information,
1024 * however, *is* found, Fall-Through must be set in order to process
1025 * the groups as well
1027 if (dofallthrough) {
1028 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1030 radlog(L_ERR, "rlm_sql (%s): Error processing 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 * repeat the above process with the default profile or User-Profile
1044 if (dofallthrough) {
1045 int profile_found = 0;
1047 * Check for a default_profile or for a User-Profile.
1049 user_profile = pairfind(request->config_items, PW_USER_PROFILE);
1050 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
1051 char *profile = inst->config->default_profile;
1053 if (user_profile != NULL)
1054 profile = user_profile->vp_strvalue;
1055 if (profile && strlen(profile)){
1056 radlog(L_DBG, "rlm_sql (%s): Checking profile %s",
1057 inst->config->xlat_name, profile);
1058 if (sql_set_user(inst, request, profileusername, profile) < 0) {
1059 radlog(L_ERR, "rlm_sql (%s): Error setting profile; rejecting user",
1060 inst->config->xlat_name);
1061 sql_release_socket(inst, sqlsocket);
1062 /* Remove the username we (maybe) added above */
1063 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1064 return RLM_MODULE_FAIL;
1071 if (profile_found) {
1072 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1074 radlog(L_ERR, "rlm_sql (%s): Error processing profile groups; rejecting user",
1075 inst->config->xlat_name);
1076 sql_release_socket(inst, sqlsocket);
1077 /* Remove the username we (maybe) added above */
1078 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1079 return RLM_MODULE_FAIL;
1080 } else if (rows > 0) {
1086 /* Remove the username we (maybe) added above */
1087 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
1088 sql_release_socket(inst, sqlsocket);
1091 radlog(L_DBG, "rlm_sql (%s): User %s not found",
1092 inst->config->xlat_name, sqlusername);
1093 return RLM_MODULE_NOTFOUND;
1095 return RLM_MODULE_OK;
1100 * Accounting: save the account data to our sql table
1102 static int rlm_sql_accounting(void *instance, REQUEST * request) {
1104 SQLSOCK *sqlsocket = NULL;
1106 SQL_INST *inst = instance;
1107 int ret = RLM_MODULE_OK;
1108 int numaffected = 0;
1109 int acctstatustype = 0;
1110 char querystr[MAX_QUERY_LEN];
1111 char logstr[MAX_QUERY_LEN];
1112 char sqlusername[MAX_STRING_LEN];
1114 #ifdef CISCO_ACCOUNTING_HACK
1115 int acctsessiontime = 0;
1118 memset(querystr, 0, MAX_QUERY_LEN);
1121 * Find the Acct Status Type
1123 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
1124 acctstatustype = pair->lvalue;
1126 radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1127 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s",
1128 inst->config->xlat_name, logstr);
1129 return RLM_MODULE_INVALID;
1132 switch (acctstatustype) {
1134 * The Terminal server informed us that it was rebooted
1135 * STOP all records from this NAS
1137 case PW_STATUS_ACCOUNTING_ON:
1138 case PW_STATUS_ACCOUNTING_OFF:
1139 radlog(L_INFO, "rlm_sql (%s): received Acct On/Off packet", inst->config->xlat_name);
1140 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
1141 query_log(request, inst, querystr);
1143 sqlsocket = sql_get_socket(inst);
1144 if (sqlsocket == NULL)
1145 return(RLM_MODULE_FAIL);
1146 if (*querystr) { /* non-empty query */
1147 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1148 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting for Acct On/Off packet - %s",
1149 inst->config->xlat_name,
1150 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1151 ret = RLM_MODULE_FAIL;
1153 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1159 * Got an update accounting packet
1161 case PW_STATUS_ALIVE:
1164 * Set, escape, and check the user attr here
1166 sql_set_user(inst, request, sqlusername, NULL);
1168 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
1169 query_log(request, inst, querystr);
1171 sqlsocket = sql_get_socket(inst);
1172 if (sqlsocket == NULL)
1173 return(RLM_MODULE_FAIL);
1174 if (*querystr) { /* non-empty query */
1175 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1176 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting ALIVE record - %s",
1177 inst->config->xlat_name,
1178 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1179 ret = RLM_MODULE_FAIL;
1182 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1183 if (numaffected < 1) {
1186 * If our update above didn't match anything
1187 * we assume it's because we haven't seen a
1188 * matching Start record. So we have to
1189 * insert this update rather than do an update
1191 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
1192 query_log(request, inst, querystr);
1193 if (*querystr) { /* non-empty query */
1194 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1195 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting ALIVE record - %s",
1196 inst->config->xlat_name,
1197 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1198 ret = RLM_MODULE_FAIL;
1200 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1204 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1209 * Got accounting start packet
1211 case PW_STATUS_START:
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_start_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 insert SQL accounting START record - %s",
1227 inst->config->xlat_name,
1228 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1231 * We failed the insert above. It's probably because
1232 * the stop record came before the start. We try
1233 * our alternate query now (typically an UPDATE)
1235 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1236 query_log(request, inst, querystr);
1238 if (*querystr) { /* non-empty query */
1239 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1240 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting START record - %s",
1241 inst->config->xlat_name,
1242 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1243 ret = RLM_MODULE_FAIL;
1245 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1248 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1253 * Got accounting stop packet
1255 case PW_STATUS_STOP:
1258 * Set, escape, and check the user attr here
1260 sql_set_user(inst, request, sqlusername, NULL);
1262 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1263 query_log(request, inst, querystr);
1265 sqlsocket = sql_get_socket(inst);
1266 if (sqlsocket == NULL)
1267 return(RLM_MODULE_FAIL);
1268 if (*querystr) { /* non-empty query */
1269 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1270 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting STOP record - %s",
1271 inst->config->xlat_name,
1272 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1273 ret = RLM_MODULE_FAIL;
1276 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1277 if (numaffected < 1) {
1279 * If our update above didn't match anything
1280 * we assume it's because we haven't seen a
1281 * matching Start record. So we have to
1282 * insert this stop rather than do an update
1284 #ifdef CISCO_ACCOUNTING_HACK
1286 * If stop but zero session length AND no previous
1287 * session found, drop it as in invalid packet
1288 * This is to fix CISCO's aaa from filling our
1289 * table with bogus crap
1291 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
1292 acctsessiontime = pair->lvalue;
1294 if (acctsessiontime <= 0) {
1295 radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1296 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s", inst->config->xlat_name, logstr);
1297 sql_release_socket(inst, sqlsocket);
1298 ret = RLM_MODULE_NOOP;
1302 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1303 query_log(request, inst, querystr);
1305 if (*querystr) { /* non-empty query */
1306 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1307 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting STOP record - %s",
1308 inst->config->xlat_name,
1309 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1310 ret = RLM_MODULE_FAIL;
1312 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1316 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1321 * Anything else is ignored.
1324 radlog(L_INFO, "rlm_sql (%s): Unsupported Acct-Status-Type = %d", inst->config->xlat_name, acctstatustype);
1325 return RLM_MODULE_NOOP;
1330 sql_release_socket(inst, sqlsocket);
1337 * See if a user is already logged in. Sets request->simul_count to the
1338 * current session count for this user.
1340 * Check twice. If on the first pass the user exceeds his
1341 * max. number of logins, do a second pass and validate all
1342 * logins by querying the terminal server (using eg. SNMP).
1345 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1347 SQL_INST *inst = instance;
1349 char querystr[MAX_QUERY_LEN];
1350 char sqlusername[MAX_STRING_LEN];
1353 char *call_num = NULL;
1356 uint32_t nas_addr = 0;
1359 /* If simul_count_query is not defined, we don't do any checking */
1360 if (inst->config->simul_count_query[0] == 0) {
1361 return RLM_MODULE_NOOP;
1364 if((request->username == NULL) || (request->username->length == 0)) {
1365 radlog(L_ERR, "rlm_sql (%s): Zero Length username not permitted\n", inst->config->xlat_name);
1366 return RLM_MODULE_INVALID;
1370 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1371 return RLM_MODULE_FAIL;
1373 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1375 /* initialize the sql socket */
1376 sqlsocket = sql_get_socket(inst);
1377 if(sqlsocket == NULL)
1378 return RLM_MODULE_FAIL;
1380 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1381 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1382 sql_release_socket(inst, sqlsocket);
1383 return RLM_MODULE_FAIL;
1386 ret = rlm_sql_fetch_row(sqlsocket, inst);
1389 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1390 sql_release_socket(inst, sqlsocket);
1391 return RLM_MODULE_FAIL;
1394 row = sqlsocket->row;
1396 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1397 sql_release_socket(inst, sqlsocket);
1398 return RLM_MODULE_FAIL;
1401 request->simul_count = atoi(row[0]);
1402 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1404 if(request->simul_count < request->simul_max) {
1405 sql_release_socket(inst, sqlsocket);
1406 return RLM_MODULE_OK;
1410 * Looks like too many sessions, so let's start verifying
1411 * them, unless told to rely on count query only.
1413 if (inst->config->deletestalesessions == FALSE ||
1414 inst->config->simul_verify_query[0] == '\0') {
1415 sql_release_socket(inst, sqlsocket);
1416 return RLM_MODULE_OK;
1419 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1420 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1421 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Database query error", inst->config->xlat_name);
1422 sql_release_socket(inst, sqlsocket);
1423 return RLM_MODULE_FAIL;
1427 * Setup some stuff, like for MPP detection.
1429 request->simul_count = 0;
1431 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1433 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1434 call_num = vp->vp_strvalue;
1437 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1438 row = sqlsocket->row;
1442 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1443 sql_release_socket(inst, sqlsocket);
1444 DEBUG("rlm_sql (%s): Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1445 return RLM_MODULE_FAIL;
1448 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1449 sql_release_socket(inst, sqlsocket);
1450 DEBUG("rlm_sql (%s): Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1451 return RLM_MODULE_FAIL;
1454 nas_addr = inet_addr(row[3]);
1456 nas_port = atoi(row[4]);
1458 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1462 * Stale record - zap it.
1464 uint32_t framed_addr = 0;
1469 framed_addr = inet_addr(row[5]);
1471 if (strcmp(row[7], "PPP") == 0)
1473 else if (strcmp(row[7], "SLIP") == 0)
1477 sess_time = atoi(row[8]);
1479 session_zap(request, nas_addr, nas_port, row[2],
1480 row[1], framed_addr, proto, sess_time);
1482 else if (check == 1) {
1484 * User is still logged in.
1486 ++request->simul_count;
1489 * Does it look like a MPP attempt?
1491 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1492 request->simul_mpp = 2;
1493 else if (row[6] && call_num &&
1494 !strncmp(row[6],call_num,16))
1495 request->simul_mpp = 2;
1499 * Failed to check the terminal server for
1500 * duplicate logins: return an error.
1502 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1503 sql_release_socket(inst, sqlsocket);
1504 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Failed to check the terminal server for user '%s'.", inst->config->xlat_name, row[2]);
1505 return RLM_MODULE_FAIL;
1509 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1510 sql_release_socket(inst, sqlsocket);
1513 * The Auth module apparently looks at request->simul_count,
1514 * not the return value of this module when deciding to deny
1515 * a call for too many sessions.
1517 return RLM_MODULE_OK;
1521 * Execute postauth_query after authentication
1523 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1524 SQLSOCK *sqlsocket = NULL;
1525 SQL_INST *inst = instance;
1526 char querystr[MAX_QUERY_LEN];
1527 char sqlusername[MAX_STRING_LEN];
1529 DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name);
1531 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1532 return RLM_MODULE_FAIL;
1534 /* If postauth_query is not defined, we stop here */
1535 if (inst->config->postauth_query[0] == '\0')
1536 return RLM_MODULE_NOOP;
1538 /* Expand variables in the query */
1539 memset(querystr, 0, MAX_QUERY_LEN);
1540 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1541 request, sql_escape_func);
1542 query_log(request, inst, querystr);
1543 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1544 inst->config->xlat_name, querystr);
1546 /* Initialize the sql socket */
1547 sqlsocket = sql_get_socket(inst);
1548 if (sqlsocket == NULL)
1549 return RLM_MODULE_FAIL;
1551 /* Process the query */
1552 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1553 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1554 inst->config->xlat_name,
1555 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1556 sql_release_socket(inst, sqlsocket);
1557 return RLM_MODULE_FAIL;
1559 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1561 sql_release_socket(inst, sqlsocket);
1562 return RLM_MODULE_OK;
1565 /* globally exported name */
1566 module_t rlm_sql = {
1569 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1570 rlm_sql_instantiate, /* instantiation */
1571 rlm_sql_detach, /* detach */
1573 NULL, /* authentication */
1574 rlm_sql_authorize, /* authorization */
1575 NULL, /* preaccounting */
1576 rlm_sql_accounting, /* accounting */
1577 rlm_sql_checksimul, /* checksimul */
1578 NULL, /* pre-proxy */
1579 NULL, /* post-proxy */
1580 rlm_sql_postauth /* post-auth */