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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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[] =
37 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
49 #include "rad_assert.h"
51 static CONF_PARSER module_config[] = {
52 {"driver",PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_driver), NULL, "mysql"},
53 {"server",PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_server), NULL, "localhost"},
54 {"port",PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_port), NULL, ""},
55 {"login", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_login), NULL, ""},
56 {"password", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_password), NULL, ""},
57 {"radius_db", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_db), NULL, "radius"},
58 {"acct_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_acct_table), NULL, "radacct"},
59 {"acct_table2", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_acct_table2), NULL, "radacct"},
60 {"authcheck_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_authcheck_table), NULL, "radcheck"},
61 {"authreply_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_authreply_table), NULL, "radreply"},
62 {"groupcheck_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_groupcheck_table), NULL, "radgroupcheck"},
63 {"groupreply_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_groupreply_table), NULL, "radgroupreply"},
64 {"usergroup_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_usergroup_table), NULL, "usergroup"},
65 {"nas_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_nas_table), NULL, "nas"},
66 {"dict_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_dict_table), NULL, "dictionary"},
67 {"sqltrace", PW_TYPE_BOOLEAN, offsetof(SQL_CONFIG,sqltrace), NULL, "0"},
68 {"sqltracefile", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE},
69 {"deletestalesessions", PW_TYPE_BOOLEAN, offsetof(SQL_CONFIG,deletestalesessions), NULL, "0"},
70 {"num_sql_socks", PW_TYPE_INTEGER, offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"},
71 {"sql_user_name", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,query_user), NULL, ""},
72 {"authorize_check_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
73 {"authorize_reply_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,authorize_reply_query), NULL, ""},
74 {"authorize_group_check_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
75 {"authorize_group_reply_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
76 {"accounting_onoff_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
77 {"accounting_update_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
78 {"accounting_start_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
79 {"accounting_start_query_alt", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
80 {"accounting_stop_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
81 {"accounting_stop_query_alt", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
82 {"group_membership_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,groupmemb_query), NULL, ""},
83 {"connect_failure_retry_delay", PW_TYPE_INTEGER, offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
84 {"simul_count_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
85 {"simul_verify_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
87 {NULL, -1, 0, NULL, NULL}
90 /***********************************************************************
91 * start of main routines
92 ***********************************************************************/
93 static int rlm_sql_init(void) {
97 * We should put the sqlsocket array here once
98 * the module code is reworked to not unload
99 * modules on HUP. This way we can have
100 * persistant connections. -jcarneal
106 * sql xlat function. Right now only SELECTs are supported. Only
107 * the first element of the SELECT result will be used.
109 static int sql_xlat(void *instance, REQUEST *request, char *fmt, char *out, int freespace,
110 RADIUS_ESCAPE_STRING func)
114 SQL_INST *inst=instance;
115 char querystr[MAX_QUERY_LEN];
118 DEBUG("rlm_sql: - sql_xlat");
120 * Do an xlat on the provided string (nice recursive operation).
122 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, func)){
123 radlog(L_ERR, "rlm_sql: xlat failed.");
127 sqlsocket = sql_get_socket(inst);
128 if (sqlsocket == NULL)
130 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
131 radlog(L_ERR, "rlm_sql: database query error");
132 sql_release_socket(inst,sqlsocket);
136 ret = rlm_sql_fetch_row(sqlsocket, inst);
137 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
140 DEBUG("rlm_sql: SQL query did not succeed");
141 sql_release_socket(inst,sqlsocket);
145 row = sqlsocket->row;
147 DEBUG("rlm_sql: SQL query did not return any results");
148 sql_release_socket(inst,sqlsocket);
153 DEBUG("rlm_sql: row[0] returned NULL");
154 sql_release_socket(inst,sqlsocket);
157 ret = strlen(row[0]);
158 if (ret > freespace){
159 DEBUG("rlm_sql: sql_xlat:: Insufficient string space");
160 sql_release_socket(inst,sqlsocket);
164 strncpy(out,row[0],ret);
166 DEBUG("rlm_sql: - sql_xlat finished");
168 sql_release_socket(inst,sqlsocket);
173 * Translate the SQL queries.
175 static int sql_escape_func(char *out, int outlen, const char *in)
181 * Only one byte left.
188 * Non-printable characters get replaced with their
189 * mime-encoded equivalents.
192 strchr("@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: =/", *in) == NULL) {
193 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
202 * Else it's a nice character.
215 * Set the SQl user name.
217 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username) {
219 char tmpuser[MAX_STRING_LEN];
224 /* Remove any user attr we added previously */
225 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
227 if (username != NULL) {
228 strNcpy(tmpuser, username, MAX_STRING_LEN);
229 } else if (strlen(inst->config->query_user)) {
230 radius_xlat(tmpuser, MAX_STRING_LEN, inst->config->query_user, request, sql_escape_func);
236 strNcpy(sqlusername, tmpuser, MAX_STRING_LEN * 2);
237 DEBUG2("sql_set_user: escaped user --> '%s'", sqlusername);
238 vp = pairmake("SQL-User-Name", sqlusername, 0);
240 radlog(L_ERR, "%s", librad_errstr);
244 pairadd(&request->packet->vps, vp);
251 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
252 * with the group memberships reciding in sql
253 * The group membership query should only return one element which is the username. The returned
254 * username will then be checked with the passed check string.
257 static int sql_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
258 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
262 SQL_INST *inst=instance;
263 char querystr[MAX_QUERY_LEN];
264 char sqlusername[2 * MAX_STRING_LEN + 10];
266 check_pairs = check_pairs;
267 reply_pairs = reply_pairs;
269 DEBUG("rlm_sql: - sql_groupcmp");
270 if (!check || !check->strvalue || !check->length){
271 DEBUG("rlm_sql::sql_groupcmp: Illegal group name");
275 DEBUG("rlm_sql::sql_groupcmp: NULL request");
278 if (inst->config->groupmemb_query[0] == 0)
281 * Set, escape, and check the user attr here
283 if (sql_set_user(inst, req, sqlusername, 0) < 0)
285 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, req, NULL)){
286 radlog(L_ERR, "rlm_sql: xlat failed.");
287 /* Remove the username we (maybe) added above */
288 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
291 /* Remove the username we (maybe) added above */
292 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
294 sqlsocket = sql_get_socket(inst);
295 if (sqlsocket == NULL)
297 if ((inst->module->sql_select_query)(sqlsocket,inst->config,querystr) <0){
298 radlog(L_ERR, "rlm_sql: database query error");
299 sql_release_socket(inst,sqlsocket);
302 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
303 row = sqlsocket->row;
307 DEBUG("rlm_sql: row[0] returned NULL");
308 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
309 sql_release_socket(inst, sqlsocket);
312 if (strcmp(row[0],check->strvalue) == 0){
313 DEBUG("rlm_sql: - sql_groupcmp finished: User belongs in group %s",(char *)check->strvalue);
314 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
315 sql_release_socket(inst, sqlsocket);
320 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
321 sql_release_socket(inst,sqlsocket);
323 DEBUG("rlm_sql: - sql_groupcmp finished: User does not belong in group %s",(char *)check->strvalue);
329 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance) {
335 inst = rad_malloc(sizeof(SQL_INST));
336 memset(inst, 0, sizeof(SQL_INST));
338 inst->config = rad_malloc(sizeof(SQL_CONFIG));
339 memset(inst->config, 0, sizeof(SQL_CONFIG));
342 * If the configuration parameters can't be parsed, then
345 if (cf_section_parse(conf, inst->config, module_config) < 0) {
351 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
352 radlog(L_ERR | L_CONS, "sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d", MAX_SQL_SOCKS);
358 handle = lt_dlopenext(inst->config->sql_driver);
359 if (handle == NULL) {
360 radlog(L_ERR, "rlm_sql: Could not link driver %s: %s", inst->config->sql_driver, lt_dlerror());
361 radlog(L_ERR, "rlm_sql: Make sure it (and all its dependent libraries!) are in the search path of your system's ld.");
365 inst->module = (rlm_sql_module_t *) lt_dlsym(handle, inst->config->sql_driver);
367 radlog(L_ERR, "rlm_sql: Could not link symbol %s: %s", inst->config->sql_driver, lt_dlerror());
371 radlog(L_INFO, "rlm_sql: Driver %s loaded and linked", inst->config->sql_driver);
372 radlog(L_INFO, "rlm_sql: Attempting to connect to %s@%s:%s/%s", inst->config->sql_login, inst->config->sql_server, inst->config->sql_port, inst->config->sql_db);
374 if (sql_init_socketpool(inst) < 0) {
379 xlat_name = cf_section_name2(conf);
380 if (xlat_name == NULL)
381 xlat_name = cf_section_name1(conf);
383 inst->config->xlat_name = strdup(xlat_name);
384 xlat_register(xlat_name, sql_xlat, inst);
386 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
390 return RLM_MODULE_OK;
393 static int rlm_sql_destroy(void) {
398 static int rlm_sql_detach(void *instance) {
400 SQL_INST *inst = instance;
403 if (inst->config->xlat_name)
404 xlat_unregister(inst->config->xlat_name,sql_xlat);
405 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
413 static int rlm_sql_authorize(void *instance, REQUEST * request) {
415 VALUE_PAIR *check_tmp = NULL;
416 VALUE_PAIR *reply_tmp = NULL;
419 SQL_INST *inst = instance;
420 char querystr[MAX_QUERY_LEN];
422 /* sqlusername holds the sql escaped username. The original
423 * username is at most MAX_STRING_LEN chars long and
424 * *sql_escape_string doubles its length in the worst case.
425 * Throw in an extra 10 to account for trailing NULs and to have
426 * a safety margin. */
427 char sqlusername[2 * MAX_STRING_LEN + 10];
430 * They MUST have a user name to do SQL authorization.
432 if ((request->username == NULL) ||
433 (request->username->length == 0)) {
434 radlog(L_ERR, "zero length username not permitted\n");
435 return RLM_MODULE_INVALID;
440 * After this point, ALL 'return's MUST release the SQL socket!
444 * Set, escape, and check the user attr here
446 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
447 return RLM_MODULE_FAIL;
448 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->authorize_check_query, request, sql_escape_func);
450 sqlsocket = sql_get_socket(inst);
451 if (sqlsocket == NULL) {
452 /* Remove the username we (maybe) added above */
453 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
454 return(RLM_MODULE_FAIL);
457 found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_USERDATA);
459 * Find the entry for the user.
462 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->authorize_group_check_query, request, sql_escape_func);
463 sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
464 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->authorize_reply_query, request, sql_escape_func);
465 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_USERDATA);
466 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->authorize_group_reply_query, request, sql_escape_func);
467 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
468 } else if (found < 0) {
469 radlog(L_ERR, "rlm_sql: SQL query error; rejecting user");
470 sql_release_socket(inst, sqlsocket);
471 /* Remove the username we (maybe) added above */
472 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
473 return RLM_MODULE_FAIL;
478 radlog(L_DBG, "rlm_sql: User %s not found", sqlusername);
481 * We didn't find the user in radcheck, so we try looking
482 * for radgroupcheck entry
484 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->authorize_group_check_query, request, sql_escape_func);
485 gcheck = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
486 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->authorize_group_reply_query, request, sql_escape_func);
487 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
492 * We didn't find the user, so we try looking
493 * for a DEFAULT entry
495 if (sql_set_user(inst, request, sqlusername, "DEFAULT") < 0) {
496 sql_release_socket(inst, sqlsocket);
497 return RLM_MODULE_FAIL;
499 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->authorize_group_check_query, request, sql_escape_func);
500 gcheck = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
501 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->authorize_group_reply_query, request, sql_escape_func);
502 gcheck = sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
508 radlog(L_DBG, "rlm_sql: DEFAULT not found");
509 sql_release_socket(inst, sqlsocket);
510 /* Remove the username we (maybe) added above */
511 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
512 return RLM_MODULE_NOTFOUND;
516 * Uncomment these lines for debugging
517 * Recompile, and run 'radiusd -X'
521 DEBUG2("rlm_sql: check items");
522 vp_listdebug(check_tmp);
523 DEBUG2("rlm_sql: reply items");
524 vp_listdebug(reply_tmp);
527 if (paircmp(request, request->packet->vps, check_tmp, &reply_tmp) != 0) {
528 radlog(L_INFO, "rlm_sql: Pairs do not match [%s]", sqlusername);
529 /* Remove the username we (maybe) added above */
530 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
531 sql_release_socket(inst, sqlsocket);
532 pairfree(&reply_tmp);
533 pairfree(&check_tmp);
534 return RLM_MODULE_NOTFOUND;
537 pairmove(&request->reply->vps, &reply_tmp);
538 pairmove(&request->config_items, &check_tmp);
539 pairfree(&reply_tmp);
540 pairfree(&check_tmp);
542 /* Remove the username we (maybe) added above */
543 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
544 sql_release_socket(inst, sqlsocket);
546 return RLM_MODULE_OK;
550 * Accounting: save the account data to our sql table
552 static int rlm_sql_accounting(void *instance, REQUEST * request) {
554 SQLSOCK *sqlsocket = NULL;
556 SQL_INST *inst = instance;
558 int acctstatustype = 0;
559 char querystr[MAX_QUERY_LEN];
560 char logstr[MAX_QUERY_LEN];
561 char sqlusername[MAX_STRING_LEN];
563 #ifdef CISCO_ACCOUNTING_HACK
564 int acctsessiontime = 0;
567 memset(querystr, 0, MAX_QUERY_LEN);
570 * Find the Acct Status Type
572 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
573 acctstatustype = pair->lvalue;
575 radius_xlat(logstr, MAX_QUERY_LEN, "rlm_sql: packet has no account status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, sql_escape_func);
576 radlog(L_ERR, logstr);
577 return RLM_MODULE_INVALID;
580 switch (acctstatustype) {
582 * The Terminal server informed us that it was rebooted
583 * STOP all records from this NAS
585 case PW_STATUS_ACCOUNTING_ON:
586 case PW_STATUS_ACCOUNTING_OFF:
587 radlog(L_INFO, "rlm_sql: received Acct On/Off packet");
588 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->accounting_onoff_query, request, sql_escape_func);
589 query_log(inst, querystr);
591 sqlsocket = sql_get_socket(inst);
592 if (sqlsocket == NULL)
593 return(RLM_MODULE_FAIL);
595 if (rlm_sql_query(sqlsocket, inst, querystr))
596 radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting for Acct On/Off packet - %s", (char *)(inst->module->sql_error)(sqlsocket, inst->config));
597 (inst->module->sql_finish_query)(sqlsocket, inst->config);
603 * Got an update accounting packet
605 case PW_STATUS_ALIVE:
608 * Set, escape, and check the user attr here
610 sql_set_user(inst, request, sqlusername, NULL);
612 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->accounting_update_query, request, sql_escape_func);
613 query_log(inst, querystr);
615 sqlsocket = sql_get_socket(inst);
616 if (sqlsocket == NULL)
617 return(RLM_MODULE_FAIL);
619 if (rlm_sql_query(sqlsocket, inst, querystr))
620 radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting for ALIVE packet - %s", (char *)(inst->module->sql_error)(sqlsocket, inst->config));
621 (inst->module->sql_finish_query)(sqlsocket, inst->config);
627 * Got accounting start packet
629 case PW_STATUS_START:
632 * Set, escape, and check the user attr here
634 sql_set_user(inst, request, sqlusername, NULL);
636 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->accounting_start_query, request, sql_escape_func);
637 query_log(inst, querystr);
639 sqlsocket = sql_get_socket(inst);
640 if (sqlsocket == NULL)
641 return(RLM_MODULE_FAIL);
643 if (rlm_sql_query(sqlsocket, inst, querystr)) {
644 radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting" " for START packet - %s", (char *)(inst->module->sql_error)(sqlsocket, inst->config));
647 * We failed the insert above. It's probably because
648 * the stop record came before the start. We try an
649 * our alternate query now (typically an UPDATE)
651 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->accounting_start_query_alt, request, sql_escape_func);
652 query_log(inst, querystr);
655 if (rlm_sql_query(sqlsocket, inst, querystr)) {
656 radlog(L_ERR, "rlm_sql: Couldn't update SQL" "accounting START record - %s", (char *)(inst->module->sql_error)(sqlsocket, inst->config));
658 (inst->module->sql_finish_query)(sqlsocket, inst->config);
661 (inst->module->sql_finish_query)(sqlsocket, inst->config);
666 * Got accounting stop packet
671 * Set, escape, and check the user attr here
673 sql_set_user(inst, request, sqlusername, NULL);
675 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->accounting_stop_query, request, sql_escape_func);
676 query_log(inst, querystr);
678 sqlsocket = sql_get_socket(inst);
679 if (sqlsocket == NULL)
680 return(RLM_MODULE_FAIL);
682 if (rlm_sql_query(sqlsocket, inst, querystr)) {
683 radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting STOP record - %s", (char *)(inst->module->sql_error)(sqlsocket, inst->config));
686 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
687 if (numaffected < 1) {
689 * If our update above didn't match anything
690 * we assume it's because we haven't seen a
691 * matching Start record. So we have to
692 * insert this stop rather than do an update
694 #ifdef CISCO_ACCOUNTING_HACK
696 * If stop but zero session length AND no previous
697 * session found, drop it as in invalid packet
698 * This is to fix CISCO's aaa from filling our
699 * table with bogus crap
701 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
702 acctsessiontime = pair->lvalue;
704 if (acctsessiontime <= 0) {
705 radius_xlat(logstr, MAX_QUERY_LEN, "rlm_sql: Stop packet with zero session length. (user '%{User-Name}', nas '%{NAS-IP-Address}')", request, sql_escape_func);
706 radlog(L_ERR, logstr);
707 sql_release_socket(inst, sqlsocket);
708 return RLM_MODULE_NOOP;
712 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->accounting_stop_query_alt, request, sql_escape_func);
713 query_log(inst, querystr);
716 if (rlm_sql_query(sqlsocket, inst, querystr)) {
717 radlog(L_ERR, "rlm_sql: Couldn't insert SQL accounting STOP record - %s", (char *)(inst->module->sql_error)(sqlsocket, inst->config));
719 (inst->module->sql_finish_query)(sqlsocket, inst->config);
723 (inst->module->sql_finish_query)(sqlsocket, inst->config);
728 sql_release_socket(inst, sqlsocket);
730 return RLM_MODULE_OK;
735 * See if a user is already logged in. Sets request->simul_count to the
736 * current session count for this user.
738 * Check twice. If on the first pass the user exceeds his
739 * max. number of logins, do a second pass and validate all
740 * logins by querying the terminal server (using eg. SNMP).
743 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
745 SQL_INST *inst = instance;
747 char querystr[MAX_QUERY_LEN];
748 char sqlusername[2*MAX_STRING_LEN+10];
751 char *call_num = NULL;
754 uint32_t nas_addr = 0;
757 /* If simul_count_query is not defined, we don't do any checking */
758 if (inst->config->simul_count_query[0] == 0) {
759 return RLM_MODULE_NOOP;
762 if((request->username == NULL) || (request->username->length == 0)) {
763 radlog(L_ERR, "Zero Length username not permitted\n");
764 return RLM_MODULE_INVALID;
768 if(sql_set_user(inst, request, sqlusername, 0) <0)
769 return RLM_MODULE_FAIL;
771 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->simul_count_query, request, NULL);
773 /* initialize the sql socket */
774 sqlsocket = sql_get_socket(inst);
775 if(sqlsocket == NULL)
776 return RLM_MODULE_FAIL;
778 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
779 radlog(L_ERR, "sql_checksimul: Database query failed");
780 sql_release_socket(inst, sqlsocket);
781 return RLM_MODULE_FAIL;
784 ret = rlm_sql_fetch_row(sqlsocket, inst);
787 row = sqlsocket->row;
791 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
792 sql_release_socket(inst, sqlsocket);
793 return RLM_MODULE_FAIL;
797 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
798 sql_release_socket(inst, sqlsocket);
799 return RLM_MODULE_FAIL;
802 request->simul_count = atoi(row[0]);
803 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
805 if(request->simul_count < request->simul_max) {
806 sql_release_socket(inst, sqlsocket);
807 return RLM_MODULE_OK;
810 /* Looks like too many sessions, so lets start verifying them */
812 if (inst->config->simul_verify_query[0] == 0) {
813 /* No verify query defined, so skip verify step and rely on count query only */
814 sql_release_socket(inst, sqlsocket);
815 return RLM_MODULE_OK;
818 radius_xlat(querystr, MAX_QUERY_LEN, inst->config->simul_verify_query, request, NULL);
819 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
820 radlog(L_ERR, "sql_checksimul: Database query error");
821 sql_release_socket(inst, sqlsocket);
822 return RLM_MODULE_FAIL;
826 * Setup some stuff, like for MPP detection.
828 request->simul_count = 0;
830 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
832 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
833 call_num = vp->strvalue;
836 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
837 row = sqlsocket->row;
841 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
842 sql_release_socket(inst, sqlsocket);
843 DEBUG("rlm_sql: Cannot zap stale entry. No username present in entry.");
844 return RLM_MODULE_FAIL;
847 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
848 sql_release_socket(inst, sqlsocket);
849 DEBUG("rlm_sql: Cannot zap stale entry. No session id in entry.");
850 return RLM_MODULE_FAIL;
853 nas_addr = inet_addr(row[3]);
855 nas_port = atoi(row[4]);
857 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
860 * Failed to check the terminal server for
861 * duplicate logins: Return an error.
864 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
865 sql_release_socket(inst, sqlsocket);
866 DEBUG("rlm_sql: rad_check_ts() failed.");
867 return RLM_MODULE_FAIL;
871 ++request->simul_count;
874 * Does it look like a MPP attempt?
876 if (row[5] && ipno && inet_addr(row[5]) == ipno)
877 request->simul_mpp = 2;
878 else if (row[6] && call_num &&
879 !strncmp(row[6],call_num,16))
880 request->simul_mpp = 2;
884 * Stale record - zap it.
886 uint32_t framed_addr = 0;
890 framed_addr = inet_addr(row[5]);
892 if (strcmp(row[7],"SLIP") == 0)
895 session_zap(request->packet->sockfd,
896 nas_addr,nas_port,row[2],row[1],
897 framed_addr, proto,0);
902 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
903 sql_release_socket(inst, sqlsocket);
905 /* The Auth module apparently looks at request->simul_count, not the return value
906 of this module when deciding to deny a call for too many sessions */
907 return RLM_MODULE_OK;
911 /* globally exported name */
914 RLM_TYPE_THREAD_SAFE, /* type: reserved */
915 rlm_sql_init, /* initialization */
916 rlm_sql_instantiate, /* instantiation */
918 NULL, /* authentication */
919 rlm_sql_authorize, /* authorization */
920 NULL, /* preaccounting */
921 rlm_sql_accounting, /* accounting */
922 rlm_sql_checksimul, /* checksimul */
923 NULL, /* pre-proxy */
924 NULL, /* post-proxy */
927 rlm_sql_detach, /* detach */
928 rlm_sql_destroy, /* destroy */