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 #ifdef WITH_ACCOUNTING
85 {"accounting_onoff_query", PW_TYPE_STRING_PTR,
86 offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
87 {"accounting_update_query", PW_TYPE_STRING_PTR,
88 offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
89 {"accounting_update_query_alt", PW_TYPE_STRING_PTR,
90 offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""},
91 {"accounting_start_query", PW_TYPE_STRING_PTR,
92 offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
93 {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
94 offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
95 {"accounting_stop_query", PW_TYPE_STRING_PTR,
96 offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
97 {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
98 offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
100 {"group_membership_query", PW_TYPE_STRING_PTR,
101 offsetof(SQL_CONFIG,groupmemb_query), NULL, NULL},
102 {"connect_failure_retry_delay", PW_TYPE_INTEGER,
103 offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
104 #ifdef WITH_SESSION_MGMT
105 {"simul_count_query", PW_TYPE_STRING_PTR,
106 offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
107 {"simul_verify_query", PW_TYPE_STRING_PTR,
108 offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
110 {"postauth_query", PW_TYPE_STRING_PTR,
111 offsetof(SQL_CONFIG,postauth_query), NULL, ""},
112 {"safe-characters", PW_TYPE_STRING_PTR,
113 offsetof(SQL_CONFIG,allowed_chars), NULL,
114 "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
117 * This only works for a few drivers.
119 {"query_timeout", PW_TYPE_INTEGER,
120 offsetof(SQL_CONFIG,query_timeout), NULL, NULL},
122 {NULL, -1, 0, NULL, NULL}
126 * Fall-Through checking function from rlm_files.c
128 static int fallthrough(VALUE_PAIR *vp)
131 tmp = pairfind(vp, PW_FALL_THROUGH, 0);
133 return tmp ? tmp->vp_integer : 0;
141 static int generate_sql_clients(SQL_INST *inst);
142 static size_t sql_escape_func(char *out, size_t outlen, const char *in);
145 * sql xlat function. Right now only SELECTs are supported. Only
146 * the first element of the SELECT result will be used.
148 * For other statements (insert, update, delete, etc.), the
149 * number of affected rows will be returned.
151 static int sql_xlat(void *instance, REQUEST *request,
152 char *fmt, char *out, size_t freespace,
153 UNUSED RADIUS_ESCAPE_STRING func)
157 SQL_INST *inst = instance;
158 char querystr[MAX_QUERY_LEN];
159 char sqlusername[MAX_STRING_LEN];
165 * Add SQL-User-Name attribute just in case it is needed
166 * We could search the string fmt for SQL-User-Name to see if this is
169 sql_set_user(inst, request, sqlusername, NULL);
171 * Do an xlat on the provided string (nice recursive operation).
173 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
174 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
175 inst->config->xlat_name);
179 query_log(request, inst,querystr);
180 sqlsocket = sql_get_socket(inst);
181 if (sqlsocket == NULL)
185 * If the query starts with any of the following prefixes,
186 * then return the number of rows affected
188 if ((strncasecmp(querystr, "insert", 6) == 0) ||
189 (strncasecmp(querystr, "update", 6) == 0) ||
190 (strncasecmp(querystr, "delete", 6) == 0)) {
192 char buffer[21]; /* 64bit max is 20 decimal chars + null byte */
194 if (rlm_sql_query(sqlsocket,inst,querystr)) {
195 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
196 inst->config->xlat_name, querystr,
197 (inst->module->sql_error)(sqlsocket,
199 sql_release_socket(inst,sqlsocket);
203 numaffected = (inst->module->sql_affected_rows)(sqlsocket,
205 if (numaffected < 1) {
206 RDEBUG("rlm_sql (%s): SQL query affected no rows",
207 inst->config->xlat_name);
211 * Don't chop the returned number if freespace is
212 * too small. This hack is necessary because
213 * some implementations of snprintf return the
214 * size of the written data, and others return
215 * the size of the data they *would* have written
216 * if the output buffer was large enough.
218 snprintf(buffer, sizeof(buffer), "%d", numaffected);
219 ret = strlen(buffer);
220 if (ret >= freespace){
221 RDEBUG("rlm_sql (%s): Can't write result, insufficient string space",
222 inst->config->xlat_name);
223 (inst->module->sql_finish_query)(sqlsocket,
225 sql_release_socket(inst,sqlsocket);
229 memcpy(out, buffer, ret + 1); /* we did bounds checking above */
231 (inst->module->sql_finish_query)(sqlsocket, inst->config);
232 sql_release_socket(inst,sqlsocket);
234 } /* else it's a SELECT statement */
236 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
237 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
238 inst->config->xlat_name,querystr,
239 (inst->module->sql_error)(sqlsocket, inst->config));
240 sql_release_socket(inst,sqlsocket);
244 ret = rlm_sql_fetch_row(sqlsocket, inst);
247 RDEBUG("SQL query did not succeed");
248 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
249 sql_release_socket(inst,sqlsocket);
253 row = sqlsocket->row;
255 RDEBUG("SQL query did not return any results");
256 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
257 sql_release_socket(inst,sqlsocket);
262 RDEBUG("row[0] returned NULL");
263 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
264 sql_release_socket(inst,sqlsocket);
267 ret = strlen(row[0]);
268 if (ret >= freespace){
269 RDEBUG("Insufficient string space");
270 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
271 sql_release_socket(inst,sqlsocket);
275 strlcpy(out,row[0],freespace);
277 RDEBUG("sql_xlat finished");
279 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
280 sql_release_socket(inst,sqlsocket);
284 static int generate_sql_clients(SQL_INST *inst)
288 char querystr[MAX_QUERY_LEN];
290 char *prefix_ptr = NULL;
294 DEBUG("rlm_sql (%s): Processing generate_sql_clients",
295 inst->config->xlat_name);
297 /* NAS query isn't xlat'ed */
298 strlcpy(querystr, inst->config->nas_query, sizeof(querystr));
299 DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s",
300 inst->config->xlat_name, querystr);
302 sqlsocket = sql_get_socket(inst);
303 if (sqlsocket == NULL)
305 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
306 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
307 inst->config->xlat_name,querystr,
308 (inst->module->sql_error)(sqlsocket, inst->config));
309 sql_release_socket(inst,sqlsocket);
313 while(rlm_sql_fetch_row(sqlsocket, inst) == 0) {
315 row = sqlsocket->row;
319 * The return data for each row MUST be in the following order:
321 * 0. Row ID (currently unused)
322 * 1. Name (or IP address)
326 * 5. Virtual Server (optional)
329 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
333 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
337 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
341 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
345 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
346 row[1],row[2],row[4]);
348 c = rad_malloc(sizeof(*c));
349 memset(c, 0, sizeof(*c));
351 #ifdef WITH_DYNAMIC_CLIENTS
359 prefix_ptr = strchr(row[1], '/');
361 c->prefix = atoi(prefix_ptr + 1);
362 if ((c->prefix < 0) || (c->prefix > 128)) {
363 radlog(L_ERR, "rlm_sql (%s): Invalid Prefix value '%s' for IP.",
364 inst->config->xlat_name, prefix_ptr + 1);
368 /* Replace '/' with '\0' */
373 * Always get the numeric representation of IP
375 if (ip_hton(row[1], AF_UNSPEC, &c->ipaddr) < 0) {
376 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s: %s",
377 inst->config->xlat_name,
378 row[1], fr_strerror());
383 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
384 c->longname = strdup(buffer);
387 if (c->prefix < 0) switch (c->ipaddr.af) {
399 * Other values (secret, shortname, nastype, virtual_server)
401 c->secret = strdup(row[4]);
402 c->shortname = strdup(row[2]);
404 c->nastype = strdup(row[3]);
406 numf = (inst->module->sql_num_fields)(sqlsocket, inst->config);
407 if ((numf > 5) && (row[5] != NULL) && *row[5]) c->server = strdup(row[5]);
409 DEBUG("rlm_sql (%s): Adding client %s (%s, server=%s) to clients list",
410 inst->config->xlat_name,
411 c->longname,c->shortname, c->server ? c->server : "<none>");
412 if (!client_add(NULL, c)) {
413 sql_release_socket(inst, sqlsocket);
414 DEBUG("rlm_sql (%s): Failed to add client %s (%s) to clients list. Maybe there's a duplicate?",
415 inst->config->xlat_name,
416 c->longname,c->shortname);
421 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
422 sql_release_socket(inst, sqlsocket);
429 * Translate the SQL queries.
431 static size_t sql_escape_func(char *out, size_t outlen, const char *in)
437 * Non-printable characters get replaced with their
438 * mime-encoded equivalents.
441 strchr(allowed_chars, *in) == NULL) {
443 * Only 3 or less bytes available.
449 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
458 * Only one byte left.
478 * Set the SQL user name.
480 * We don't call the escape function here. The resulting string
481 * will be escaped later in the queries xlat so we don't need to
482 * escape it twice. (it will make things wrong if we have an
483 * escape candidate character in the username)
485 int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
488 char tmpuser[MAX_STRING_LEN];
491 sqlusername[0]= '\0';
493 /* Remove any user attr we added previously */
494 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
496 if (username != NULL) {
497 strlcpy(tmpuser, username, sizeof(tmpuser));
498 } else if (strlen(inst->config->query_user)) {
499 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
504 strlcpy(sqlusername, tmpuser, MAX_STRING_LEN);
505 RDEBUG2("sql_set_user escaped user --> '%s'", sqlusername);
506 vp = radius_pairmake(request, &request->packet->vps,
507 "SQL-User-Name", NULL, 0);
509 radlog(L_ERR, "%s", fr_strerror());
513 strlcpy(vp->vp_strvalue, tmpuser, sizeof(vp->vp_strvalue));
514 vp->length = strlen(vp->vp_strvalue);
521 static void sql_grouplist_free (SQL_GROUPLIST **group_list)
527 *group_list = (*group_list)->next;
533 static int sql_get_grouplist (SQL_INST *inst, SQLSOCK *sqlsocket, REQUEST *request, SQL_GROUPLIST **group_list)
535 char querystr[MAX_QUERY_LEN];
538 SQL_GROUPLIST *group_list_tmp;
540 /* NOTE: sql_set_user should have been run before calling this function */
542 group_list_tmp = *group_list = NULL;
544 if (!inst->config->groupmemb_query ||
545 (inst->config->groupmemb_query[0] == 0))
548 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, request, sql_escape_func)) {
549 radlog_request(L_ERR, 0, request, "xlat \"%s\" failed.",
550 inst->config->groupmemb_query);
554 if (rlm_sql_select_query(sqlsocket, inst, querystr) < 0) {
555 radlog_request(L_ERR, 0, request,
556 "database query error, %s: %s",
558 (inst->module->sql_error)(sqlsocket,inst->config));
561 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
562 row = sqlsocket->row;
566 RDEBUG("row[0] returned NULL");
567 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
568 sql_grouplist_free(group_list);
571 if (*group_list == NULL) {
572 *group_list = rad_malloc(sizeof(SQL_GROUPLIST));
573 group_list_tmp = *group_list;
575 rad_assert(group_list_tmp != NULL);
576 group_list_tmp->next = rad_malloc(sizeof(SQL_GROUPLIST));
577 group_list_tmp = group_list_tmp->next;
579 group_list_tmp->next = NULL;
580 strlcpy(group_list_tmp->groupname, row[0], MAX_STRING_LEN);
583 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
590 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
591 * with the group memberships reciding in sql
592 * The group membership query should only return one element which is the username. The returned
593 * username will then be checked with the passed check string.
596 static int sql_groupcmp(void *instance, REQUEST *request, VALUE_PAIR *request_vp, VALUE_PAIR *check,
597 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
600 SQL_INST *inst = instance;
601 char sqlusername[MAX_STRING_LEN];
602 SQL_GROUPLIST *group_list, *group_list_tmp;
604 check_pairs = check_pairs;
605 reply_pairs = reply_pairs;
606 request_vp = request_vp;
608 RDEBUG("sql_groupcmp");
609 if (!check || !check->vp_strvalue || !check->length){
610 RDEBUG("sql_groupcmp: Illegal group name");
614 RDEBUG("sql_groupcmp: NULL request");
618 * Set, escape, and check the user attr here
620 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
624 * Get a socket for this lookup
626 sqlsocket = sql_get_socket(inst);
627 if (sqlsocket == NULL) {
628 /* Remove the username we (maybe) added above */
629 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
634 * Get the list of groups this user is a member of
636 if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
637 radlog_request(L_ERR, 0, request,
638 "Error getting group membership");
639 /* Remove the username we (maybe) added above */
640 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
641 sql_release_socket(inst, sqlsocket);
645 for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) {
646 if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){
647 RDEBUG("sql_groupcmp finished: User is a member of group %s",
649 /* Free the grouplist */
650 sql_grouplist_free(&group_list);
651 /* Remove the username we (maybe) added above */
652 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
653 sql_release_socket(inst, sqlsocket);
658 /* Free the grouplist */
659 sql_grouplist_free(&group_list);
660 /* Remove the username we (maybe) added above */
661 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
662 sql_release_socket(inst,sqlsocket);
664 RDEBUG("sql_groupcmp finished: User is NOT a member of group %s",
672 static int rlm_sql_process_groups(SQL_INST *inst, REQUEST *request, SQLSOCK *sqlsocket, int *dofallthrough)
674 VALUE_PAIR *check_tmp = NULL;
675 VALUE_PAIR *reply_tmp = NULL;
676 SQL_GROUPLIST *group_list, *group_list_tmp;
677 VALUE_PAIR *sql_group = NULL;
678 char querystr[MAX_QUERY_LEN];
683 * Get the list of groups this user is a member of
685 if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
686 radlog_request(L_ERR, 0, request, "Error retrieving group list");
690 for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) {
692 * Add the Sql-Group attribute to the request list so we know
693 * which group we're retrieving attributes for
695 sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ);
697 radlog_request(L_ERR, 0, request,
698 "Error creating Sql-Group attribute");
699 sql_grouplist_free(&group_list);
702 pairadd(&request->packet->vps, sql_group);
703 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func)) {
704 radlog_request(L_ERR, 0, request,
705 "Error generating query; rejecting user");
706 /* Remove the grouup we added above */
707 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
708 sql_grouplist_free(&group_list);
711 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
713 radlog_request(L_ERR, 0, request, "Error retrieving check pairs for group %s",
714 group_list_tmp->groupname);
715 /* Remove the grouup we added above */
716 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
717 pairfree(&check_tmp);
718 sql_grouplist_free(&group_list);
720 } else if (rows > 0) {
722 * Only do this if *some* check pairs were returned
724 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
726 RDEBUG2("User found in group %s",
727 group_list_tmp->groupname);
729 * Now get the reply pairs since the paircompare matched
731 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
732 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
733 /* Remove the grouup we added above */
734 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
735 pairfree(&check_tmp);
736 sql_grouplist_free(&group_list);
739 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
740 radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
741 group_list_tmp->groupname);
742 /* Remove the grouup we added above */
743 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
744 pairfree(&check_tmp);
745 pairfree(&reply_tmp);
746 sql_grouplist_free(&group_list);
749 *dofallthrough = fallthrough(reply_tmp);
750 pairxlatmove(request, &request->reply->vps, &reply_tmp);
751 pairxlatmove(request, &request->config_items, &check_tmp);
755 * rows == 0. This is like having the username on a line
756 * in the user's file with no check vp's. As such, we treat
757 * it as found and add the reply attributes, so that we
758 * match expected behavior
761 RDEBUG2("User found in group %s",
762 group_list_tmp->groupname);
764 * Now get the reply pairs since the paircompare matched
766 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
767 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
768 /* Remove the grouup we added above */
769 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
770 pairfree(&check_tmp);
771 sql_grouplist_free(&group_list);
774 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
775 radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
776 group_list_tmp->groupname);
777 /* Remove the grouup we added above */
778 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
779 pairfree(&check_tmp);
780 pairfree(&reply_tmp);
781 sql_grouplist_free(&group_list);
784 *dofallthrough = fallthrough(reply_tmp);
785 pairxlatmove(request, &request->reply->vps, &reply_tmp);
786 pairxlatmove(request, &request->config_items, &check_tmp);
790 * Delete the Sql-Group we added above
791 * And clear out the pairlists
793 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
794 pairfree(&check_tmp);
795 pairfree(&reply_tmp);
798 sql_grouplist_free(&group_list);
803 static int rlm_sql_detach(void *instance)
805 SQL_INST *inst = instance;
807 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
816 if (inst->config->xlat_name) {
817 xlat_unregister(inst->config->xlat_name,(RAD_XLAT_FUNC)sql_xlat);
818 free(inst->config->xlat_name);
822 * Free up dynamically allocated string pointers.
824 for (i = 0; module_config[i].name != NULL; i++) {
826 if (module_config[i].type != PW_TYPE_STRING_PTR) {
831 * Treat 'config' as an opaque array of bytes,
832 * and take the offset into it. There's a
833 * (char*) pointer at that offset, and we want
836 p = (char **) (((char *)inst->config) + module_config[i].offset);
837 if (!*p) { /* nothing allocated */
844 * Catch multiple instances of the module.
846 if (allowed_chars == inst->config->allowed_chars) {
847 allowed_chars = NULL;
856 * FIXME: Call the modules 'destroy' function?
858 lt_dlclose(inst->handle); /* ignore any errors */
865 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
868 const char *xlat_name;
870 inst = rad_malloc(sizeof(SQL_INST));
871 memset(inst, 0, sizeof(SQL_INST));
873 inst->config = rad_malloc(sizeof(SQL_CONFIG));
874 memset(inst->config, 0, sizeof(SQL_CONFIG));
878 * Export these methods, too. This avoids RTDL_GLOBAL.
880 inst->sql_set_user = sql_set_user;
881 inst->sql_get_socket = sql_get_socket;
882 inst->sql_release_socket = sql_release_socket;
883 inst->sql_escape_func = sql_escape_func;
884 inst->sql_query = rlm_sql_query;
885 inst->sql_select_query = rlm_sql_select_query;
886 inst->sql_fetch_row = rlm_sql_fetch_row;
889 * If the configuration parameters can't be parsed, then
892 if (cf_section_parse(conf, inst->config, module_config) < 0) {
893 rlm_sql_detach(inst);
897 xlat_name = cf_section_name2(conf);
898 if (xlat_name == NULL) {
899 xlat_name = cf_section_name1(conf);
906 * Allocate room for <instance>-SQL-Group
908 group_name = rad_malloc((strlen(xlat_name) + 1 + 11) * sizeof(char));
909 sprintf(group_name,"%s-SQL-Group",xlat_name);
910 DEBUG("rlm_sql Creating new attribute %s",group_name);
912 memset(&flags, 0, sizeof(flags));
913 dict_addattr(group_name, 0, PW_TYPE_STRING, -1, flags);
914 dattr = dict_attrbyname(group_name);
916 radlog(L_ERR, "rlm_sql: Failed to create attribute %s",group_name);
918 free(inst); /* FIXME: detach */
922 if (inst->config->groupmemb_query &&
923 inst->config->groupmemb_query[0]) {
924 DEBUG("rlm_sql: Registering sql_groupcmp for %s",group_name);
925 paircompare_register(dattr->attr, PW_USER_NAME, sql_groupcmp, inst);
931 inst->config->xlat_name = strdup(xlat_name);
932 xlat_register(xlat_name, (RAD_XLAT_FUNC)sql_xlat, inst);
935 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
936 radlog(L_ERR, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
937 inst->config->xlat_name, MAX_SQL_SOCKS);
938 rlm_sql_detach(inst);
943 * Sanity check for crazy people.
945 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
946 radlog(L_ERR, "\"%s\" is NOT an SQL driver!",
947 inst->config->sql_driver);
948 rlm_sql_detach(inst);
952 inst->handle = lt_dlopenext(inst->config->sql_driver);
953 if (inst->handle == NULL) {
954 radlog(L_ERR, "Could not link driver %s: %s",
955 inst->config->sql_driver,
957 radlog(L_ERR, "Make sure it (and all its dependent libraries!) are in the search path of your system's ld.");
958 rlm_sql_detach(inst);
962 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
964 radlog(L_ERR, "Could not link symbol %s: %s",
965 inst->config->sql_driver,
967 rlm_sql_detach(inst);
971 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
972 inst->config->xlat_name, inst->config->sql_driver,
974 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
975 inst->config->xlat_name, inst->config->sql_login,
976 inst->config->sql_server, inst->config->sql_port,
977 inst->config->sql_db);
979 if (sql_init_socketpool(inst) < 0) {
980 rlm_sql_detach(inst);
984 if (inst->config->groupmemb_query &&
985 inst->config->groupmemb_query[0]) {
986 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
989 if (inst->config->do_clients){
990 if (generate_sql_clients(inst) == -1){
991 radlog(L_ERR, "Failed to load clients from SQL.");
992 rlm_sql_detach(inst);
996 allowed_chars = inst->config->allowed_chars;
1000 return RLM_MODULE_OK;
1004 static int rlm_sql_authorize(void *instance, REQUEST * request)
1006 VALUE_PAIR *check_tmp = NULL;
1007 VALUE_PAIR *reply_tmp = NULL;
1008 VALUE_PAIR *user_profile = NULL;
1010 int dofallthrough = 1;
1013 SQL_INST *inst = instance;
1014 char querystr[MAX_QUERY_LEN];
1015 char sqlusername[MAX_STRING_LEN];
1017 * the profile username is used as the sqlusername during
1018 * profile checking so that we don't overwrite the orignal
1019 * sqlusername string
1021 char profileusername[MAX_STRING_LEN];
1024 * Set, escape, and check the user attr here
1026 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
1027 return RLM_MODULE_FAIL;
1033 sqlsocket = sql_get_socket(inst);
1034 if (sqlsocket == NULL) {
1035 /* Remove the username we (maybe) added above */
1036 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1037 return RLM_MODULE_FAIL;
1042 * After this point, ALL 'return's MUST release the SQL socket!
1046 * Alright, start by getting the specific entry for the user
1048 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func)) {
1049 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
1050 sql_release_socket(inst, sqlsocket);
1051 /* Remove the username we (maybe) added above */
1052 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1053 return RLM_MODULE_FAIL;
1055 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
1057 radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
1058 sql_release_socket(inst, sqlsocket);
1059 /* Remove the username we (maybe) added above */
1060 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1061 pairfree(&check_tmp);
1062 return RLM_MODULE_FAIL;
1063 } else if (rows > 0) {
1065 * Only do this if *some* check pairs were returned
1067 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
1069 RDEBUG2("User found in radcheck table");
1071 if (inst->config->authorize_reply_query &&
1072 *inst->config->authorize_reply_query) {
1075 * Now get the reply pairs since the paircompare matched
1077 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func)) {
1078 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
1079 sql_release_socket(inst, sqlsocket);
1080 /* Remove the username we (maybe) added above */
1081 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1082 pairfree(&check_tmp);
1083 return RLM_MODULE_FAIL;
1085 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
1086 radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
1087 sql_release_socket(inst, sqlsocket);
1088 /* Remove the username we (maybe) added above */
1089 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1090 pairfree(&check_tmp);
1091 pairfree(&reply_tmp);
1092 return RLM_MODULE_FAIL;
1095 if (!inst->config->read_groups)
1096 dofallthrough = fallthrough(reply_tmp);
1097 pairxlatmove(request, &request->reply->vps, &reply_tmp);
1099 pairxlatmove(request, &request->config_items, &check_tmp);
1104 * Clear out the pairlists
1106 pairfree(&check_tmp);
1107 pairfree(&reply_tmp);
1110 * dofallthrough is set to 1 by default so that if the user information
1111 * is not found, we will still process groups. If the user information,
1112 * however, *is* found, Fall-Through must be set in order to process
1113 * the groups as well
1115 if (dofallthrough) {
1116 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1118 radlog_request(L_ERR, 0, request, "Error processing groups; rejecting user");
1119 sql_release_socket(inst, sqlsocket);
1120 /* Remove the username we (maybe) added above */
1121 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1122 return RLM_MODULE_FAIL;
1123 } else if (rows > 0) {
1129 * repeat the above process with the default profile or User-Profile
1131 if (dofallthrough) {
1132 int profile_found = 0;
1134 * Check for a default_profile or for a User-Profile.
1136 user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0);
1137 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
1138 char *profile = inst->config->default_profile;
1140 if (user_profile != NULL)
1141 profile = user_profile->vp_strvalue;
1142 if (profile && strlen(profile)){
1143 RDEBUG("Checking profile %s", profile);
1144 if (sql_set_user(inst, request, profileusername, profile) < 0) {
1145 radlog_request(L_ERR, 0, request, "Error setting profile; rejecting user");
1146 sql_release_socket(inst, sqlsocket);
1147 /* Remove the username we (maybe) added above */
1148 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1149 return RLM_MODULE_FAIL;
1156 if (profile_found) {
1157 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1159 radlog_request(L_ERR, 0, request, "Error processing profile groups; rejecting user");
1160 sql_release_socket(inst, sqlsocket);
1161 /* Remove the username we (maybe) added above */
1162 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1163 return RLM_MODULE_FAIL;
1164 } else if (rows > 0) {
1170 /* Remove the username we (maybe) added above */
1171 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1172 sql_release_socket(inst, sqlsocket);
1175 RDEBUG("User %s not found", sqlusername);
1176 return RLM_MODULE_NOTFOUND;
1178 return RLM_MODULE_OK;
1182 #ifdef WITH_ACCOUNTING
1184 * Accounting: save the account data to our sql table
1186 static int rlm_sql_accounting(void *instance, REQUEST * request) {
1188 SQLSOCK *sqlsocket = NULL;
1190 SQL_INST *inst = instance;
1191 int ret = RLM_MODULE_OK;
1192 int numaffected = 0;
1193 int acctstatustype = 0;
1194 char querystr[MAX_QUERY_LEN];
1195 char logstr[MAX_QUERY_LEN];
1196 char sqlusername[MAX_STRING_LEN];
1198 #ifdef CISCO_ACCOUNTING_HACK
1199 int acctsessiontime = 0;
1202 memset(querystr, 0, MAX_QUERY_LEN);
1205 * Find the Acct Status Type
1207 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0)) != NULL) {
1208 acctstatustype = pair->vp_integer;
1210 radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1211 radlog_request(L_ERR, 0, request, "%s", logstr);
1212 return RLM_MODULE_INVALID;
1215 switch (acctstatustype) {
1217 * The Terminal server informed us that it was rebooted
1218 * STOP all records from this NAS
1220 case PW_STATUS_ACCOUNTING_ON:
1221 case PW_STATUS_ACCOUNTING_OFF:
1222 RDEBUG("Received Acct On/Off packet");
1223 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
1224 query_log(request, inst, querystr);
1226 sqlsocket = sql_get_socket(inst);
1227 if (sqlsocket == NULL)
1228 return(RLM_MODULE_FAIL);
1229 if (*querystr) { /* non-empty query */
1230 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1231 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting for Acct On/Off packet - %s",
1232 (inst->module->sql_error)(sqlsocket, inst->config));
1233 ret = RLM_MODULE_FAIL;
1235 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1241 * Got an update accounting packet
1243 case PW_STATUS_ALIVE:
1246 * Set, escape, and check the user attr here
1248 sql_set_user(inst, request, sqlusername, NULL);
1250 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
1251 query_log(request, inst, querystr);
1253 sqlsocket = sql_get_socket(inst);
1254 if (sqlsocket == NULL)
1255 return(RLM_MODULE_FAIL);
1256 if (*querystr) { /* non-empty query */
1257 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1258 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting ALIVE record - %s",
1259 (inst->module->sql_error)(sqlsocket, inst->config));
1260 ret = RLM_MODULE_FAIL;
1263 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1264 if (numaffected < 1) {
1267 * If our update above didn't match anything
1268 * we assume it's because we haven't seen a
1269 * matching Start record. So we have to
1270 * insert this update rather than do an update
1272 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
1273 query_log(request, inst, querystr);
1274 if (*querystr) { /* non-empty query */
1275 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1276 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting ALIVE record - %s",
1277 (inst->module->sql_error)(sqlsocket, inst->config));
1278 ret = RLM_MODULE_FAIL;
1280 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1284 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1289 * Got accounting start packet
1291 case PW_STATUS_START:
1294 * Set, escape, and check the user attr here
1296 sql_set_user(inst, request, sqlusername, NULL);
1298 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
1299 query_log(request, inst, querystr);
1301 sqlsocket = sql_get_socket(inst);
1302 if (sqlsocket == NULL)
1303 return(RLM_MODULE_FAIL);
1304 if (*querystr) { /* non-empty query */
1305 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1306 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting START record - %s",
1307 (inst->module->sql_error)(sqlsocket, inst->config));
1310 * We failed the insert above. It's probably because
1311 * the stop record came before the start. We try
1312 * our alternate query now (typically an UPDATE)
1314 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1315 query_log(request, inst, querystr);
1317 if (*querystr) { /* non-empty query */
1318 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1319 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting START record - %s",
1320 (inst->module->sql_error)(sqlsocket, inst->config));
1321 ret = RLM_MODULE_FAIL;
1323 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1326 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1331 * Got accounting stop packet
1333 case PW_STATUS_STOP:
1336 * Set, escape, and check the user attr here
1338 sql_set_user(inst, request, sqlusername, NULL);
1340 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1341 query_log(request, inst, querystr);
1343 sqlsocket = sql_get_socket(inst);
1344 if (sqlsocket == NULL)
1345 return(RLM_MODULE_FAIL);
1346 if (*querystr) { /* non-empty query */
1347 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1348 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting STOP record - %s",
1349 (inst->module->sql_error)(sqlsocket, inst->config));
1350 ret = RLM_MODULE_FAIL;
1353 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1354 if (numaffected < 1) {
1356 * If our update above didn't match anything
1357 * we assume it's because we haven't seen a
1358 * matching Start record. So we have to
1359 * insert this stop rather than do an update
1361 #ifdef CISCO_ACCOUNTING_HACK
1363 * If stop but zero session length AND no previous
1364 * session found, drop it as in invalid packet
1365 * This is to fix CISCO's aaa from filling our
1366 * table with bogus crap
1368 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME, 0)) != NULL)
1369 acctsessiontime = pair->vp_integer;
1371 if (acctsessiontime <= 0) {
1372 radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1373 radlog_request(L_DBG, 0, request, "%s", logstr);
1374 sql_release_socket(inst, sqlsocket);
1375 return RLM_MODULE_NOOP;
1379 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1380 query_log(request, inst, querystr);
1382 if (*querystr) { /* non-empty query */
1383 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1384 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting STOP record - %s",
1386 (inst->module->sql_error)(sqlsocket, inst->config));
1387 ret = RLM_MODULE_FAIL;
1389 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1393 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1398 * Anything else is ignored.
1401 RDEBUG("Unsupported Acct-Status-Type = %d",
1403 return RLM_MODULE_NOOP;
1408 sql_release_socket(inst, sqlsocket);
1415 #ifdef WITH_SESSION_MGMT
1417 * See if a user is already logged in. Sets request->simul_count to the
1418 * current session count for this user.
1420 * Check twice. If on the first pass the user exceeds his
1421 * max. number of logins, do a second pass and validate all
1422 * logins by querying the terminal server (using eg. SNMP).
1425 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1427 SQL_INST *inst = instance;
1429 char querystr[MAX_QUERY_LEN];
1430 char sqlusername[MAX_STRING_LEN];
1433 char *call_num = NULL;
1436 uint32_t nas_addr = 0;
1439 /* If simul_count_query is not defined, we don't do any checking */
1440 if (!inst->config->simul_count_query ||
1441 (inst->config->simul_count_query[0] == 0)) {
1442 return RLM_MODULE_NOOP;
1445 if((request->username == NULL) || (request->username->length == 0)) {
1446 radlog_request(L_ERR, 0, request, "Zero Length username not permitted\n");
1447 return RLM_MODULE_INVALID;
1451 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1452 return RLM_MODULE_FAIL;
1454 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1456 /* initialize the sql socket */
1457 sqlsocket = sql_get_socket(inst);
1458 if(sqlsocket == NULL)
1459 return RLM_MODULE_FAIL;
1461 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1462 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1463 sql_release_socket(inst, sqlsocket);
1464 return RLM_MODULE_FAIL;
1467 ret = rlm_sql_fetch_row(sqlsocket, inst);
1470 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1471 sql_release_socket(inst, sqlsocket);
1472 return RLM_MODULE_FAIL;
1475 row = sqlsocket->row;
1477 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1478 sql_release_socket(inst, sqlsocket);
1479 return RLM_MODULE_FAIL;
1482 request->simul_count = atoi(row[0]);
1483 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1485 if(request->simul_count < request->simul_max) {
1486 sql_release_socket(inst, sqlsocket);
1487 return RLM_MODULE_OK;
1491 * Looks like too many sessions, so let's start verifying
1492 * them, unless told to rely on count query only.
1494 if (!inst->config->simul_verify_query ||
1495 (inst->config->simul_verify_query[0] == '\0')) {
1496 sql_release_socket(inst, sqlsocket);
1497 return RLM_MODULE_OK;
1500 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1501 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1502 radlog_request(L_ERR, 0, request, "Database query error");
1503 sql_release_socket(inst, sqlsocket);
1504 return RLM_MODULE_FAIL;
1508 * Setup some stuff, like for MPP detection.
1510 request->simul_count = 0;
1512 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0)) != NULL)
1513 ipno = vp->vp_ipaddr;
1514 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0)) != NULL)
1515 call_num = vp->vp_strvalue;
1518 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1519 row = sqlsocket->row;
1523 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1524 sql_release_socket(inst, sqlsocket);
1525 RDEBUG("Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1526 return RLM_MODULE_FAIL;
1529 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1530 sql_release_socket(inst, sqlsocket);
1531 RDEBUG("Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1532 return RLM_MODULE_FAIL;
1535 nas_addr = inet_addr(row[3]);
1537 nas_port = atoi(row[4]);
1539 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1543 * Stale record - zap it.
1545 if (inst->config->deletestalesessions == TRUE) {
1546 uint32_t framed_addr = 0;
1551 framed_addr = inet_addr(row[5]);
1553 if (strcmp(row[7], "PPP") == 0)
1555 else if (strcmp(row[7], "SLIP") == 0)
1559 sess_time = atoi(row[8]);
1560 session_zap(request, nas_addr, nas_port,
1561 row[2], row[1], framed_addr,
1565 else if (check == 1) {
1567 * User is still logged in.
1569 ++request->simul_count;
1572 * Does it look like a MPP attempt?
1574 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1575 request->simul_mpp = 2;
1576 else if (row[6] && call_num &&
1577 !strncmp(row[6],call_num,16))
1578 request->simul_mpp = 2;
1582 * Failed to check the terminal server for
1583 * duplicate logins: return an error.
1585 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1586 sql_release_socket(inst, sqlsocket);
1587 radlog_request(L_ERR, 0, request, "Failed to check the terminal server for user '%s'.", row[2]);
1588 return RLM_MODULE_FAIL;
1592 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1593 sql_release_socket(inst, sqlsocket);
1596 * The Auth module apparently looks at request->simul_count,
1597 * not the return value of this module when deciding to deny
1598 * a call for too many sessions.
1600 return RLM_MODULE_OK;
1605 * Execute postauth_query after authentication
1607 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1608 SQLSOCK *sqlsocket = NULL;
1609 SQL_INST *inst = instance;
1610 char querystr[MAX_QUERY_LEN];
1611 char sqlusername[MAX_STRING_LEN];
1613 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1614 return RLM_MODULE_FAIL;
1616 /* If postauth_query is not defined, we stop here */
1617 if (!inst->config->postauth_query ||
1618 (inst->config->postauth_query[0] == '\0'))
1619 return RLM_MODULE_NOOP;
1621 /* Expand variables in the query */
1622 memset(querystr, 0, MAX_QUERY_LEN);
1623 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1624 request, sql_escape_func);
1625 query_log(request, inst, querystr);
1626 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1627 inst->config->xlat_name, querystr);
1629 /* Initialize the sql socket */
1630 sqlsocket = sql_get_socket(inst);
1631 if (sqlsocket == NULL)
1632 return RLM_MODULE_FAIL;
1634 /* Process the query */
1635 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1636 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1637 inst->config->xlat_name,
1638 (inst->module->sql_error)(sqlsocket, inst->config));
1639 sql_release_socket(inst, sqlsocket);
1640 return RLM_MODULE_FAIL;
1642 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1644 sql_release_socket(inst, sqlsocket);
1645 return RLM_MODULE_OK;
1648 /* globally exported name */
1649 module_t rlm_sql = {
1652 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1653 rlm_sql_instantiate, /* instantiation */
1654 rlm_sql_detach, /* detach */
1656 NULL, /* authentication */
1657 rlm_sql_authorize, /* authorization */
1658 NULL, /* preaccounting */
1659 #ifdef WITH_ACCOUNTING
1660 rlm_sql_accounting, /* accounting */
1664 #ifdef WITH_SESSION_MGMT
1665 rlm_sql_checksimul, /* checksimul */
1669 NULL, /* pre-proxy */
1670 NULL, /* post-proxy */
1671 rlm_sql_postauth /* post-auth */