3 * Main SQL module file. Most ICRADIUS code is located in sql.c
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * Copyright 2000,2006 The FreeRADIUS server project
22 * Copyright 2000 Mike Machado <mike@innercite.com>
23 * Copyright 2000 Alan DeKok <aland@ox.org>
26 #include <freeradius-devel/ident.h>
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/modules.h>
31 #include <freeradius-devel/rad_assert.h>
37 static char *allowed_chars = NULL;
39 static const CONF_PARSER module_config[] = {
40 {"driver",PW_TYPE_STRING_PTR,
41 offsetof(SQL_CONFIG,sql_driver), NULL, "mysql"},
42 {"server",PW_TYPE_STRING_PTR,
43 offsetof(SQL_CONFIG,sql_server), NULL, "localhost"},
44 {"port",PW_TYPE_STRING_PTR,
45 offsetof(SQL_CONFIG,sql_port), NULL, ""},
46 {"login", PW_TYPE_STRING_PTR,
47 offsetof(SQL_CONFIG,sql_login), NULL, ""},
48 {"password", PW_TYPE_STRING_PTR,
49 offsetof(SQL_CONFIG,sql_password), NULL, ""},
50 {"radius_db", PW_TYPE_STRING_PTR,
51 offsetof(SQL_CONFIG,sql_db), NULL, "radius"},
52 {"filename", PW_TYPE_FILENAME, /* for sqlite */
53 offsetof(SQL_CONFIG,sql_file), NULL, NULL},
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 {"lifetime", PW_TYPE_INTEGER,
67 offsetof(SQL_CONFIG,lifetime), NULL, "0"},
68 {"max_queries", PW_TYPE_INTEGER,
69 offsetof(SQL_CONFIG,max_queries), NULL, "0"},
70 {"sql_user_name", PW_TYPE_STRING_PTR,
71 offsetof(SQL_CONFIG,query_user), NULL, ""},
72 {"default_user_profile", PW_TYPE_STRING_PTR,
73 offsetof(SQL_CONFIG,default_profile), NULL, ""},
74 {"nas_query", PW_TYPE_STRING_PTR,
75 offsetof(SQL_CONFIG,nas_query), NULL, "SELECT id,nasname,shortname,type,secret FROM nas"},
76 {"authorize_check_query", PW_TYPE_STRING_PTR,
77 offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
78 {"authorize_reply_query", PW_TYPE_STRING_PTR,
79 offsetof(SQL_CONFIG,authorize_reply_query), NULL, NULL},
80 {"authorize_group_check_query", PW_TYPE_STRING_PTR,
81 offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
82 {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
83 offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
84 {"accounting_onoff_query", PW_TYPE_STRING_PTR,
85 offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
86 {"accounting_update_query", PW_TYPE_STRING_PTR,
87 offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
88 {"accounting_update_query_alt", PW_TYPE_STRING_PTR,
89 offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""},
90 {"accounting_start_query", PW_TYPE_STRING_PTR,
91 offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
92 {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
93 offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
94 {"accounting_stop_query", PW_TYPE_STRING_PTR,
95 offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
96 {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
97 offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
98 {"group_membership_query", PW_TYPE_STRING_PTR,
99 offsetof(SQL_CONFIG,groupmemb_query), NULL, NULL},
100 {"connect_failure_retry_delay", PW_TYPE_INTEGER,
101 offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
102 {"simul_count_query", PW_TYPE_STRING_PTR,
103 offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
104 {"simul_verify_query", PW_TYPE_STRING_PTR,
105 offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
106 {"postauth_query", PW_TYPE_STRING_PTR,
107 offsetof(SQL_CONFIG,postauth_query), NULL, ""},
108 {"safe-characters", PW_TYPE_STRING_PTR,
109 offsetof(SQL_CONFIG,allowed_chars), NULL,
110 "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
113 * This only works for a few drivers.
115 {"query_timeout", PW_TYPE_INTEGER,
116 offsetof(SQL_CONFIG,query_timeout), NULL, NULL},
118 {NULL, -1, 0, NULL, NULL}
122 * Fall-Through checking function from rlm_files.c
124 static int fallthrough(VALUE_PAIR *vp)
127 tmp = pairfind(vp, PW_FALL_THROUGH, 0);
129 return tmp ? tmp->vp_integer : 0;
137 static int generate_sql_clients(SQL_INST *inst);
138 static size_t sql_escape_func(char *out, size_t 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 * For other statements (insert, update, delete, etc.), the
145 * number of affected rows will be returned.
147 static int sql_xlat(void *instance, REQUEST *request,
148 char *fmt, char *out, size_t freespace,
149 UNUSED RADIUS_ESCAPE_STRING func)
153 SQL_INST *inst = instance;
154 char querystr[MAX_QUERY_LEN];
155 char sqlusername[MAX_STRING_LEN];
161 * Add SQL-User-Name attribute just in case it is needed
162 * We could search the string fmt for SQL-User-Name to see if this is
165 sql_set_user(inst, request, sqlusername, NULL);
167 * Do an xlat on the provided string (nice recursive operation).
169 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
170 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
171 inst->config->xlat_name);
175 query_log(request, inst,querystr);
176 sqlsocket = sql_get_socket(inst);
177 if (sqlsocket == NULL)
181 * If the query starts with any of the following prefixes,
182 * then return the number of rows affected
184 if ((strncasecmp(querystr, "insert", 6) == 0) ||
185 (strncasecmp(querystr, "update", 6) == 0) ||
186 (strncasecmp(querystr, "delete", 6) == 0)) {
188 char buffer[21]; /* 64bit max is 20 decimal chars + null byte */
190 if (rlm_sql_query(sqlsocket,inst,querystr)) {
191 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
192 inst->config->xlat_name, querystr,
193 (inst->module->sql_error)(sqlsocket,
195 sql_release_socket(inst,sqlsocket);
199 numaffected = (inst->module->sql_affected_rows)(sqlsocket,
201 if (numaffected < 1) {
202 RDEBUG("rlm_sql (%s): SQL query affected no rows",
203 inst->config->xlat_name);
207 * Don't chop the returned number if freespace is
208 * too small. This hack is necessary because
209 * some implementations of snprintf return the
210 * size of the written data, and others return
211 * the size of the data they *would* have written
212 * if the output buffer was large enough.
214 snprintf(buffer, sizeof(buffer), "%d", numaffected);
215 ret = strlen(buffer);
216 if (ret >= freespace){
217 RDEBUG("rlm_sql (%s): Can't write result, insufficient string space",
218 inst->config->xlat_name);
219 (inst->module->sql_finish_query)(sqlsocket,
221 sql_release_socket(inst,sqlsocket);
225 memcpy(out, buffer, ret + 1); /* we did bounds checking above */
227 (inst->module->sql_finish_query)(sqlsocket, inst->config);
228 sql_release_socket(inst,sqlsocket);
230 } /* else it's a SELECT statement */
232 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
233 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
234 inst->config->xlat_name,querystr,
235 (inst->module->sql_error)(sqlsocket, inst->config));
236 sql_release_socket(inst,sqlsocket);
240 ret = rlm_sql_fetch_row(sqlsocket, inst);
243 RDEBUG("SQL query did not succeed");
244 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
245 sql_release_socket(inst,sqlsocket);
249 row = sqlsocket->row;
251 RDEBUG("SQL query did not return any results");
252 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
253 sql_release_socket(inst,sqlsocket);
258 RDEBUG("row[0] returned NULL");
259 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
260 sql_release_socket(inst,sqlsocket);
263 ret = strlen(row[0]);
264 if (ret >= freespace){
265 RDEBUG("Insufficient string space");
266 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
267 sql_release_socket(inst,sqlsocket);
271 strlcpy(out,row[0],freespace);
273 RDEBUG("sql_xlat finished");
275 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
276 sql_release_socket(inst,sqlsocket);
280 static int generate_sql_clients(SQL_INST *inst)
284 char querystr[MAX_QUERY_LEN];
286 char *prefix_ptr = NULL;
290 DEBUG("rlm_sql (%s): Processing generate_sql_clients",
291 inst->config->xlat_name);
293 /* NAS query isn't xlat'ed */
294 strlcpy(querystr, inst->config->nas_query, sizeof(querystr));
295 DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s",
296 inst->config->xlat_name, querystr);
298 sqlsocket = sql_get_socket(inst);
299 if (sqlsocket == NULL)
301 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
302 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
303 inst->config->xlat_name,querystr,
304 (inst->module->sql_error)(sqlsocket, inst->config));
305 sql_release_socket(inst,sqlsocket);
309 while(rlm_sql_fetch_row(sqlsocket, inst) == 0) {
311 row = sqlsocket->row;
315 * The return data for each row MUST be in the following order:
317 * 0. Row ID (currently unused)
318 * 1. Name (or IP address)
322 * 5. Virtual Server (optional)
325 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
329 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
333 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
337 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
341 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
342 row[1],row[2],row[4]);
344 c = rad_malloc(sizeof(*c));
345 memset(c, 0, sizeof(*c));
347 #ifdef WITH_DYNAMIC_CLIENTS
355 prefix_ptr = strchr(row[1], '/');
357 c->prefix = atoi(prefix_ptr + 1);
358 if ((c->prefix < 0) || (c->prefix > 128)) {
359 radlog(L_ERR, "rlm_sql (%s): Invalid Prefix value '%s' for IP.",
360 inst->config->xlat_name, prefix_ptr + 1);
364 /* Replace '/' with '\0' */
369 * Always get the numeric representation of IP
371 if (ip_hton(row[1], AF_UNSPEC, &c->ipaddr) < 0) {
372 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s: %s",
373 inst->config->xlat_name,
374 row[1], fr_strerror());
379 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
380 c->longname = strdup(buffer);
383 if (c->prefix < 0) switch (c->ipaddr.af) {
395 * Other values (secret, shortname, nastype, virtual_server)
397 c->secret = strdup(row[4]);
398 c->shortname = strdup(row[2]);
400 c->nastype = strdup(row[3]);
402 numf = (inst->module->sql_num_fields)(sqlsocket, inst->config);
403 if ((numf > 5) && (row[5] != NULL) && *row[5]) c->server = strdup(row[5]);
405 DEBUG("rlm_sql (%s): Adding client %s (%s, server=%s) to clients list",
406 inst->config->xlat_name,
407 c->longname,c->shortname, c->server ? c->server : "<none>");
408 if (!client_add(NULL, c)) {
409 sql_release_socket(inst, sqlsocket);
410 DEBUG("rlm_sql (%s): Failed to add client %s (%s) to clients list. Maybe there's a duplicate?",
411 inst->config->xlat_name,
412 c->longname,c->shortname);
417 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
418 sql_release_socket(inst, sqlsocket);
425 * Translate the SQL queries.
427 static size_t sql_escape_func(char *out, size_t outlen, const char *in)
433 * Non-printable characters get replaced with their
434 * mime-encoded equivalents.
437 strchr(allowed_chars, *in) == NULL) {
439 * Only 3 or less bytes available.
445 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
454 * Only one byte left.
474 * Set the SQL user name.
476 * We don't call the escape function here. The resulting string
477 * will be escaped later in the queries xlat so we don't need to
478 * escape it twice. (it will make things wrong if we have an
479 * escape candidate character in the username)
481 int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
484 char tmpuser[MAX_STRING_LEN];
487 sqlusername[0]= '\0';
489 /* Remove any user attr we added previously */
490 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
492 if (username != NULL) {
493 strlcpy(tmpuser, username, sizeof(tmpuser));
494 } else if (strlen(inst->config->query_user)) {
495 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
500 strlcpy(sqlusername, tmpuser, MAX_STRING_LEN);
501 RDEBUG2("sql_set_user escaped user --> '%s'", sqlusername);
502 vp = radius_pairmake(request, &request->packet->vps,
503 "SQL-User-Name", NULL, 0);
505 radlog(L_ERR, "%s", fr_strerror());
509 strlcpy(vp->vp_strvalue, tmpuser, sizeof(vp->vp_strvalue));
510 vp->length = strlen(vp->vp_strvalue);
517 static void sql_grouplist_free (SQL_GROUPLIST **group_list)
523 *group_list = (*group_list)->next;
529 static int sql_get_grouplist (SQL_INST *inst, SQLSOCK *sqlsocket, REQUEST *request, SQL_GROUPLIST **group_list)
531 char querystr[MAX_QUERY_LEN];
534 SQL_GROUPLIST *group_list_tmp;
536 /* NOTE: sql_set_user should have been run before calling this function */
538 group_list_tmp = *group_list = NULL;
540 if (!inst->config->groupmemb_query ||
541 (inst->config->groupmemb_query[0] == 0))
544 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, request, sql_escape_func)) {
545 radlog_request(L_ERR, 0, request, "xlat \"%s\" failed.",
546 inst->config->groupmemb_query);
550 if (rlm_sql_select_query(sqlsocket, inst, querystr) < 0) {
551 radlog_request(L_ERR, 0, request,
552 "database query error, %s: %s",
554 (inst->module->sql_error)(sqlsocket,inst->config));
557 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
558 row = sqlsocket->row;
562 RDEBUG("row[0] returned NULL");
563 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
564 sql_grouplist_free(group_list);
567 if (*group_list == NULL) {
568 *group_list = rad_malloc(sizeof(SQL_GROUPLIST));
569 group_list_tmp = *group_list;
571 rad_assert(group_list_tmp != NULL);
572 group_list_tmp->next = rad_malloc(sizeof(SQL_GROUPLIST));
573 group_list_tmp = group_list_tmp->next;
575 group_list_tmp->next = NULL;
576 strlcpy(group_list_tmp->groupname, row[0], MAX_STRING_LEN);
579 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
586 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
587 * with the group memberships reciding in sql
588 * The group membership query should only return one element which is the username. The returned
589 * username will then be checked with the passed check string.
592 static int sql_groupcmp(void *instance, REQUEST *request, VALUE_PAIR *request_vp, VALUE_PAIR *check,
593 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
596 SQL_INST *inst = instance;
597 char sqlusername[MAX_STRING_LEN];
598 SQL_GROUPLIST *group_list, *group_list_tmp;
600 check_pairs = check_pairs;
601 reply_pairs = reply_pairs;
602 request_vp = request_vp;
604 RDEBUG("sql_groupcmp");
605 if (!check || !check->vp_strvalue || !check->length){
606 RDEBUG("sql_groupcmp: Illegal group name");
610 RDEBUG("sql_groupcmp: NULL request");
614 * Set, escape, and check the user attr here
616 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
620 * Get a socket for this lookup
622 sqlsocket = sql_get_socket(inst);
623 if (sqlsocket == NULL) {
624 /* Remove the username we (maybe) added above */
625 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
630 * Get the list of groups this user is a member of
632 if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
633 radlog_request(L_ERR, 0, request,
634 "Error getting group membership");
635 /* Remove the username we (maybe) added above */
636 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
637 sql_release_socket(inst, sqlsocket);
641 for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) {
642 if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){
643 RDEBUG("sql_groupcmp finished: User is a member of group %s",
645 /* Free the grouplist */
646 sql_grouplist_free(&group_list);
647 /* Remove the username we (maybe) added above */
648 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
649 sql_release_socket(inst, sqlsocket);
654 /* Free the grouplist */
655 sql_grouplist_free(&group_list);
656 /* Remove the username we (maybe) added above */
657 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
658 sql_release_socket(inst,sqlsocket);
660 RDEBUG("sql_groupcmp finished: User is NOT a member of group %s",
668 static int rlm_sql_process_groups(SQL_INST *inst, REQUEST *request, SQLSOCK *sqlsocket, int *dofallthrough)
670 VALUE_PAIR *check_tmp = NULL;
671 VALUE_PAIR *reply_tmp = NULL;
672 SQL_GROUPLIST *group_list, *group_list_tmp;
673 VALUE_PAIR *sql_group = NULL;
674 char querystr[MAX_QUERY_LEN];
679 * Get the list of groups this user is a member of
681 if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
682 radlog_request(L_ERR, 0, request, "Error retrieving group list");
686 for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) {
688 * Add the Sql-Group attribute to the request list so we know
689 * which group we're retrieving attributes for
691 sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ);
693 radlog_request(L_ERR, 0, request,
694 "Error creating Sql-Group attribute");
697 pairadd(&request->packet->vps, sql_group);
698 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func)) {
699 radlog_request(L_ERR, 0, request,
700 "Error generating query; rejecting user");
701 /* Remove the grouup we added above */
702 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
705 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
707 radlog_request(L_ERR, 0, request, "Error retrieving check pairs for group %s",
708 group_list_tmp->groupname);
709 /* Remove the grouup we added above */
710 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
711 pairfree(&check_tmp);
713 } else if (rows > 0) {
715 * Only do this if *some* check pairs were returned
717 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
719 RDEBUG2("User found in group %s",
720 group_list_tmp->groupname);
722 * Now get the reply pairs since the paircompare matched
724 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
725 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
726 /* Remove the grouup we added above */
727 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
728 pairfree(&check_tmp);
731 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
732 radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
733 group_list_tmp->groupname);
734 /* Remove the grouup we added above */
735 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
736 pairfree(&check_tmp);
737 pairfree(&reply_tmp);
740 *dofallthrough = fallthrough(reply_tmp);
741 pairxlatmove(request, &request->reply->vps, &reply_tmp);
742 pairxlatmove(request, &request->config_items, &check_tmp);
746 * rows == 0. This is like having the username on a line
747 * in the user's file with no check vp's. As such, we treat
748 * it as found and add the reply attributes, so that we
749 * match expected behavior
752 RDEBUG2("User found in group %s",
753 group_list_tmp->groupname);
755 * Now get the reply pairs since the paircompare matched
757 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
758 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
759 /* Remove the grouup we added above */
760 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
761 pairfree(&check_tmp);
764 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
765 radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
766 group_list_tmp->groupname);
767 /* Remove the grouup we added above */
768 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
769 pairfree(&check_tmp);
770 pairfree(&reply_tmp);
773 *dofallthrough = fallthrough(reply_tmp);
774 pairxlatmove(request, &request->reply->vps, &reply_tmp);
775 pairxlatmove(request, &request->config_items, &check_tmp);
779 * Delete the Sql-Group we added above
780 * And clear out the pairlists
782 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
783 pairfree(&check_tmp);
784 pairfree(&reply_tmp);
787 sql_grouplist_free(&group_list);
792 static int rlm_sql_detach(void *instance)
794 SQL_INST *inst = instance;
796 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
805 if (inst->config->xlat_name) {
806 xlat_unregister(inst->config->xlat_name,(RAD_XLAT_FUNC)sql_xlat);
807 free(inst->config->xlat_name);
811 * Free up dynamically allocated string pointers.
813 for (i = 0; module_config[i].name != NULL; i++) {
815 if (module_config[i].type != PW_TYPE_STRING_PTR) {
820 * Treat 'config' as an opaque array of bytes,
821 * and take the offset into it. There's a
822 * (char*) pointer at that offset, and we want
825 p = (char **) (((char *)inst->config) + module_config[i].offset);
826 if (!*p) { /* nothing allocated */
833 * Catch multiple instances of the module.
835 if (allowed_chars == inst->config->allowed_chars) {
836 allowed_chars = NULL;
845 * FIXME: Call the modules 'destroy' function?
847 lt_dlclose(inst->handle); /* ignore any errors */
854 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
857 const char *xlat_name;
859 inst = rad_malloc(sizeof(SQL_INST));
860 memset(inst, 0, sizeof(SQL_INST));
862 inst->config = rad_malloc(sizeof(SQL_CONFIG));
863 memset(inst->config, 0, sizeof(SQL_CONFIG));
866 * Export these methods, too. This avoids RTDL_GLOBAL.
868 inst->sql_set_user = sql_set_user;
869 inst->sql_get_socket = sql_get_socket;
870 inst->sql_release_socket = sql_release_socket;
871 inst->sql_escape_func = sql_escape_func;
872 inst->sql_query = rlm_sql_query;
873 inst->sql_select_query = rlm_sql_select_query;
874 inst->sql_fetch_row = rlm_sql_fetch_row;
877 * If the configuration parameters can't be parsed, then
880 if (cf_section_parse(conf, inst->config, module_config) < 0) {
881 rlm_sql_detach(inst);
885 xlat_name = cf_section_name2(conf);
886 if (xlat_name == NULL) {
887 xlat_name = cf_section_name1(conf);
894 * Allocate room for <instance>-SQL-Group
896 group_name = rad_malloc((strlen(xlat_name) + 1 + 11) * sizeof(char));
897 sprintf(group_name,"%s-SQL-Group",xlat_name);
898 DEBUG("rlm_sql Creating new attribute %s",group_name);
900 memset(&flags, 0, sizeof(flags));
901 dict_addattr(group_name, 0, PW_TYPE_STRING, -1, flags);
902 dattr = dict_attrbyname(group_name);
904 radlog(L_ERR, "rlm_ldap: Failed to create attribute %s",group_name);
906 free(inst); /* FIXME: detach */
910 if (inst->config->groupmemb_query &&
911 inst->config->groupmemb_query[0]) {
912 DEBUG("rlm_sql: Registering sql_groupcmp for %s",group_name);
913 paircompare_register(dattr->attr, PW_USER_NAME, sql_groupcmp, inst);
919 inst->config->xlat_name = strdup(xlat_name);
920 xlat_register(xlat_name, (RAD_XLAT_FUNC)sql_xlat, inst);
923 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
924 radlog(L_ERR, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
925 inst->config->xlat_name, MAX_SQL_SOCKS);
926 rlm_sql_detach(inst);
931 * Sanity check for crazy people.
933 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
934 radlog(L_ERR, "\"%s\" is NOT an SQL driver!",
935 inst->config->sql_driver);
936 rlm_sql_detach(inst);
940 inst->handle = lt_dlopenext(inst->config->sql_driver);
941 if (inst->handle == NULL) {
942 radlog(L_ERR, "Could not link driver %s: %s",
943 inst->config->sql_driver,
945 radlog(L_ERR, "Make sure it (and all its dependent libraries!) are in the search path of your system's ld.");
946 rlm_sql_detach(inst);
950 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
952 radlog(L_ERR, "Could not link symbol %s: %s",
953 inst->config->sql_driver,
955 rlm_sql_detach(inst);
959 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
960 inst->config->xlat_name, inst->config->sql_driver,
962 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
963 inst->config->xlat_name, inst->config->sql_login,
964 inst->config->sql_server, inst->config->sql_port,
965 inst->config->sql_db);
967 if (sql_init_socketpool(inst) < 0) {
968 rlm_sql_detach(inst);
972 if (inst->config->groupmemb_query &&
973 inst->config->groupmemb_query[0]) {
974 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
977 if (inst->config->do_clients){
978 if (generate_sql_clients(inst) == -1){
979 radlog(L_ERR, "Failed to load clients from SQL.");
980 rlm_sql_detach(inst);
984 allowed_chars = inst->config->allowed_chars;
988 return RLM_MODULE_OK;
992 static int rlm_sql_authorize(void *instance, REQUEST * request)
994 VALUE_PAIR *check_tmp = NULL;
995 VALUE_PAIR *reply_tmp = NULL;
996 VALUE_PAIR *user_profile = NULL;
998 int dofallthrough = 1;
1001 SQL_INST *inst = instance;
1002 char querystr[MAX_QUERY_LEN];
1003 char sqlusername[MAX_STRING_LEN];
1005 * the profile username is used as the sqlusername during
1006 * profile checking so that we don't overwrite the orignal
1007 * sqlusername string
1009 char profileusername[MAX_STRING_LEN];
1012 * Set, escape, and check the user attr here
1014 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
1015 return RLM_MODULE_FAIL;
1021 sqlsocket = sql_get_socket(inst);
1022 if (sqlsocket == NULL) {
1023 /* Remove the username we (maybe) added above */
1024 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1025 return RLM_MODULE_FAIL;
1030 * After this point, ALL 'return's MUST release the SQL socket!
1034 * Alright, start by getting the specific entry for the user
1036 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func)) {
1037 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
1038 sql_release_socket(inst, sqlsocket);
1039 /* Remove the username we (maybe) added above */
1040 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1041 return RLM_MODULE_FAIL;
1043 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
1045 radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
1046 sql_release_socket(inst, sqlsocket);
1047 /* Remove the username we (maybe) added above */
1048 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1049 pairfree(&check_tmp);
1050 return RLM_MODULE_FAIL;
1051 } else if (rows > 0) {
1053 * Only do this if *some* check pairs were returned
1055 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
1057 RDEBUG2("User found in radcheck table");
1059 if (inst->config->authorize_reply_query &&
1060 *inst->config->authorize_reply_query) {
1063 * Now get the reply pairs since the paircompare matched
1065 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func)) {
1066 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
1067 sql_release_socket(inst, sqlsocket);
1068 /* Remove the username we (maybe) added above */
1069 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1070 pairfree(&check_tmp);
1071 return RLM_MODULE_FAIL;
1073 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
1074 radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
1075 sql_release_socket(inst, sqlsocket);
1076 /* Remove the username we (maybe) added above */
1077 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1078 pairfree(&check_tmp);
1079 pairfree(&reply_tmp);
1080 return RLM_MODULE_FAIL;
1083 if (!inst->config->read_groups)
1084 dofallthrough = fallthrough(reply_tmp);
1085 pairxlatmove(request, &request->reply->vps, &reply_tmp);
1087 pairxlatmove(request, &request->config_items, &check_tmp);
1092 * Clear out the pairlists
1094 pairfree(&check_tmp);
1095 pairfree(&reply_tmp);
1098 * dofallthrough is set to 1 by default so that if the user information
1099 * is not found, we will still process groups. If the user information,
1100 * however, *is* found, Fall-Through must be set in order to process
1101 * the groups as well
1103 if (dofallthrough) {
1104 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1106 radlog_request(L_ERR, 0, request, "Error processing groups; rejecting user");
1107 sql_release_socket(inst, sqlsocket);
1108 /* Remove the username we (maybe) added above */
1109 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1110 return RLM_MODULE_FAIL;
1111 } else if (rows > 0) {
1117 * repeat the above process with the default profile or User-Profile
1119 if (dofallthrough) {
1120 int profile_found = 0;
1122 * Check for a default_profile or for a User-Profile.
1124 user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0);
1125 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
1126 char *profile = inst->config->default_profile;
1128 if (user_profile != NULL)
1129 profile = user_profile->vp_strvalue;
1130 if (profile && strlen(profile)){
1131 RDEBUG("Checking profile %s", profile);
1132 if (sql_set_user(inst, request, profileusername, profile) < 0) {
1133 radlog_request(L_ERR, 0, request, "Error setting profile; rejecting user");
1134 sql_release_socket(inst, sqlsocket);
1135 /* Remove the username we (maybe) added above */
1136 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1137 return RLM_MODULE_FAIL;
1144 if (profile_found) {
1145 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1147 radlog_request(L_ERR, 0, request, "Error processing profile groups; rejecting user");
1148 sql_release_socket(inst, sqlsocket);
1149 /* Remove the username we (maybe) added above */
1150 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1151 return RLM_MODULE_FAIL;
1152 } else if (rows > 0) {
1158 /* Remove the username we (maybe) added above */
1159 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1160 sql_release_socket(inst, sqlsocket);
1163 RDEBUG("User %s not found", sqlusername);
1164 return RLM_MODULE_NOTFOUND;
1166 return RLM_MODULE_OK;
1171 * Accounting: save the account data to our sql table
1173 static int rlm_sql_accounting(void *instance, REQUEST * request) {
1175 SQLSOCK *sqlsocket = NULL;
1177 SQL_INST *inst = instance;
1178 int ret = RLM_MODULE_OK;
1179 int numaffected = 0;
1180 int acctstatustype = 0;
1181 char querystr[MAX_QUERY_LEN];
1182 char logstr[MAX_QUERY_LEN];
1183 char sqlusername[MAX_STRING_LEN];
1185 #ifdef CISCO_ACCOUNTING_HACK
1186 int acctsessiontime = 0;
1189 memset(querystr, 0, MAX_QUERY_LEN);
1192 * Find the Acct Status Type
1194 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0)) != NULL) {
1195 acctstatustype = pair->vp_integer;
1197 radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1198 radlog_request(L_ERR, 0, request, "%s", logstr);
1199 return RLM_MODULE_INVALID;
1202 switch (acctstatustype) {
1204 * The Terminal server informed us that it was rebooted
1205 * STOP all records from this NAS
1207 case PW_STATUS_ACCOUNTING_ON:
1208 case PW_STATUS_ACCOUNTING_OFF:
1209 RDEBUG("Received Acct On/Off packet");
1210 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
1211 query_log(request, inst, querystr);
1213 sqlsocket = sql_get_socket(inst);
1214 if (sqlsocket == NULL)
1215 return(RLM_MODULE_FAIL);
1216 if (*querystr) { /* non-empty query */
1217 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1218 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting for Acct On/Off packet - %s",
1219 (inst->module->sql_error)(sqlsocket, inst->config));
1220 ret = RLM_MODULE_FAIL;
1222 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1228 * Got an update accounting packet
1230 case PW_STATUS_ALIVE:
1233 * Set, escape, and check the user attr here
1235 sql_set_user(inst, request, sqlusername, NULL);
1237 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
1238 query_log(request, inst, querystr);
1240 sqlsocket = sql_get_socket(inst);
1241 if (sqlsocket == NULL)
1242 return(RLM_MODULE_FAIL);
1243 if (*querystr) { /* non-empty query */
1244 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1245 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting ALIVE record - %s",
1246 (inst->module->sql_error)(sqlsocket, inst->config));
1247 ret = RLM_MODULE_FAIL;
1250 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1251 if (numaffected < 1) {
1254 * If our update above didn't match anything
1255 * we assume it's because we haven't seen a
1256 * matching Start record. So we have to
1257 * insert this update rather than do an update
1259 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
1260 query_log(request, inst, querystr);
1261 if (*querystr) { /* non-empty query */
1262 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1263 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting ALIVE record - %s",
1264 (inst->module->sql_error)(sqlsocket, inst->config));
1265 ret = RLM_MODULE_FAIL;
1267 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1271 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1276 * Got accounting start packet
1278 case PW_STATUS_START:
1281 * Set, escape, and check the user attr here
1283 sql_set_user(inst, request, sqlusername, NULL);
1285 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
1286 query_log(request, inst, querystr);
1288 sqlsocket = sql_get_socket(inst);
1289 if (sqlsocket == NULL)
1290 return(RLM_MODULE_FAIL);
1291 if (*querystr) { /* non-empty query */
1292 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1293 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting START record - %s",
1294 (inst->module->sql_error)(sqlsocket, inst->config));
1297 * We failed the insert above. It's probably because
1298 * the stop record came before the start. We try
1299 * our alternate query now (typically an UPDATE)
1301 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1302 query_log(request, inst, querystr);
1304 if (*querystr) { /* non-empty query */
1305 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1306 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting START record - %s",
1307 (inst->module->sql_error)(sqlsocket, inst->config));
1308 ret = RLM_MODULE_FAIL;
1310 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1313 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1318 * Got accounting stop packet
1320 case PW_STATUS_STOP:
1323 * Set, escape, and check the user attr here
1325 sql_set_user(inst, request, sqlusername, NULL);
1327 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1328 query_log(request, inst, querystr);
1330 sqlsocket = sql_get_socket(inst);
1331 if (sqlsocket == NULL)
1332 return(RLM_MODULE_FAIL);
1333 if (*querystr) { /* non-empty query */
1334 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1335 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting STOP record - %s",
1336 (inst->module->sql_error)(sqlsocket, inst->config));
1337 ret = RLM_MODULE_FAIL;
1340 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1341 if (numaffected < 1) {
1343 * If our update above didn't match anything
1344 * we assume it's because we haven't seen a
1345 * matching Start record. So we have to
1346 * insert this stop rather than do an update
1348 #ifdef CISCO_ACCOUNTING_HACK
1350 * If stop but zero session length AND no previous
1351 * session found, drop it as in invalid packet
1352 * This is to fix CISCO's aaa from filling our
1353 * table with bogus crap
1355 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME, 0)) != NULL)
1356 acctsessiontime = pair->vp_integer;
1358 if (acctsessiontime <= 0) {
1359 radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1360 radlog_request(L_DBG, 0, request, "%s", logstr);
1361 sql_release_socket(inst, sqlsocket);
1362 return RLM_MODULE_NOOP;
1366 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1367 query_log(request, inst, querystr);
1369 if (*querystr) { /* non-empty query */
1370 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1371 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting STOP record - %s",
1373 (inst->module->sql_error)(sqlsocket, inst->config));
1374 ret = RLM_MODULE_FAIL;
1376 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1380 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1385 * Anything else is ignored.
1388 RDEBUG("Unsupported Acct-Status-Type = %d",
1390 return RLM_MODULE_NOOP;
1395 sql_release_socket(inst, sqlsocket);
1402 * See if a user is already logged in. Sets request->simul_count to the
1403 * current session count for this user.
1405 * Check twice. If on the first pass the user exceeds his
1406 * max. number of logins, do a second pass and validate all
1407 * logins by querying the terminal server (using eg. SNMP).
1410 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1412 SQL_INST *inst = instance;
1414 char querystr[MAX_QUERY_LEN];
1415 char sqlusername[MAX_STRING_LEN];
1418 char *call_num = NULL;
1421 uint32_t nas_addr = 0;
1424 /* If simul_count_query is not defined, we don't do any checking */
1425 if (!inst->config->simul_count_query ||
1426 (inst->config->simul_count_query[0] == 0)) {
1427 return RLM_MODULE_NOOP;
1430 if((request->username == NULL) || (request->username->length == 0)) {
1431 radlog_request(L_ERR, 0, request, "Zero Length username not permitted\n");
1432 return RLM_MODULE_INVALID;
1436 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1437 return RLM_MODULE_FAIL;
1439 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1441 /* initialize the sql socket */
1442 sqlsocket = sql_get_socket(inst);
1443 if(sqlsocket == NULL)
1444 return RLM_MODULE_FAIL;
1446 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1447 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1448 sql_release_socket(inst, sqlsocket);
1449 return RLM_MODULE_FAIL;
1452 ret = rlm_sql_fetch_row(sqlsocket, inst);
1455 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1456 sql_release_socket(inst, sqlsocket);
1457 return RLM_MODULE_FAIL;
1460 row = sqlsocket->row;
1462 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1463 sql_release_socket(inst, sqlsocket);
1464 return RLM_MODULE_FAIL;
1467 request->simul_count = atoi(row[0]);
1468 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1470 if(request->simul_count < request->simul_max) {
1471 sql_release_socket(inst, sqlsocket);
1472 return RLM_MODULE_OK;
1476 * Looks like too many sessions, so let's start verifying
1477 * them, unless told to rely on count query only.
1479 if (!inst->config->simul_verify_query ||
1480 (inst->config->simul_verify_query[0] == '\0')) {
1481 sql_release_socket(inst, sqlsocket);
1482 return RLM_MODULE_OK;
1485 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1486 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1487 radlog_request(L_ERR, 0, request, "Database query error");
1488 sql_release_socket(inst, sqlsocket);
1489 return RLM_MODULE_FAIL;
1493 * Setup some stuff, like for MPP detection.
1495 request->simul_count = 0;
1497 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0)) != NULL)
1498 ipno = vp->vp_ipaddr;
1499 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0)) != NULL)
1500 call_num = vp->vp_strvalue;
1503 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1504 row = sqlsocket->row;
1508 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1509 sql_release_socket(inst, sqlsocket);
1510 RDEBUG("Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1511 return RLM_MODULE_FAIL;
1514 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1515 sql_release_socket(inst, sqlsocket);
1516 RDEBUG("Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1517 return RLM_MODULE_FAIL;
1520 nas_addr = inet_addr(row[3]);
1522 nas_port = atoi(row[4]);
1524 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1528 * Stale record - zap it.
1530 if (inst->config->deletestalesessions == TRUE) {
1531 uint32_t framed_addr = 0;
1536 framed_addr = inet_addr(row[5]);
1538 if (strcmp(row[7], "PPP") == 0)
1540 else if (strcmp(row[7], "SLIP") == 0)
1544 sess_time = atoi(row[8]);
1545 session_zap(request, nas_addr, nas_port,
1546 row[2], row[1], framed_addr,
1550 else if (check == 1) {
1552 * User is still logged in.
1554 ++request->simul_count;
1557 * Does it look like a MPP attempt?
1559 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1560 request->simul_mpp = 2;
1561 else if (row[6] && call_num &&
1562 !strncmp(row[6],call_num,16))
1563 request->simul_mpp = 2;
1567 * Failed to check the terminal server for
1568 * duplicate logins: return an error.
1570 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1571 sql_release_socket(inst, sqlsocket);
1572 radlog_request(L_ERR, 0, request, "Failed to check the terminal server for user '%s'.", row[2]);
1573 return RLM_MODULE_FAIL;
1577 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1578 sql_release_socket(inst, sqlsocket);
1581 * The Auth module apparently looks at request->simul_count,
1582 * not the return value of this module when deciding to deny
1583 * a call for too many sessions.
1585 return RLM_MODULE_OK;
1589 * Execute postauth_query after authentication
1591 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1592 SQLSOCK *sqlsocket = NULL;
1593 SQL_INST *inst = instance;
1594 char querystr[MAX_QUERY_LEN];
1595 char sqlusername[MAX_STRING_LEN];
1597 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1598 return RLM_MODULE_FAIL;
1600 /* If postauth_query is not defined, we stop here */
1601 if (!inst->config->postauth_query ||
1602 (inst->config->postauth_query[0] == '\0'))
1603 return RLM_MODULE_NOOP;
1605 /* Expand variables in the query */
1606 memset(querystr, 0, MAX_QUERY_LEN);
1607 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1608 request, sql_escape_func);
1609 query_log(request, inst, querystr);
1610 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1611 inst->config->xlat_name, querystr);
1613 /* Initialize the sql socket */
1614 sqlsocket = sql_get_socket(inst);
1615 if (sqlsocket == NULL)
1616 return RLM_MODULE_FAIL;
1618 /* Process the query */
1619 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1620 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1621 inst->config->xlat_name,
1622 (inst->module->sql_error)(sqlsocket, inst->config));
1623 sql_release_socket(inst, sqlsocket);
1624 return RLM_MODULE_FAIL;
1626 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1628 sql_release_socket(inst, sqlsocket);
1629 return RLM_MODULE_OK;
1632 /* globally exported name */
1633 module_t rlm_sql = {
1636 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1637 rlm_sql_instantiate, /* instantiation */
1638 rlm_sql_detach, /* detach */
1640 NULL, /* authentication */
1641 rlm_sql_authorize, /* authorization */
1642 NULL, /* preaccounting */
1643 rlm_sql_accounting, /* accounting */
1644 rlm_sql_checksimul, /* checksimul */
1645 NULL, /* pre-proxy */
1646 NULL, /* post-proxy */
1647 rlm_sql_postauth /* post-auth */