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 char *allowed_chars = NULL;
53 static CONF_PARSER module_config[] = {
54 {"driver",PW_TYPE_STRING_PTR,
55 offsetof(SQL_CONFIG,sql_driver), NULL, "mysql"},
56 {"server",PW_TYPE_STRING_PTR,
57 offsetof(SQL_CONFIG,sql_server), NULL, "localhost"},
58 {"port",PW_TYPE_STRING_PTR,
59 offsetof(SQL_CONFIG,sql_port), NULL, ""},
60 {"login", PW_TYPE_STRING_PTR,
61 offsetof(SQL_CONFIG,sql_login), NULL, ""},
62 {"password", PW_TYPE_STRING_PTR,
63 offsetof(SQL_CONFIG,sql_password), NULL, ""},
64 {"radius_db", PW_TYPE_STRING_PTR,
65 offsetof(SQL_CONFIG,sql_db), NULL, "radius"},
66 {"nas_table", PW_TYPE_STRING_PTR,
67 offsetof(SQL_CONFIG,sql_nas_table), NULL, "nas"},
68 {"sqltrace", PW_TYPE_BOOLEAN,
69 offsetof(SQL_CONFIG,sqltrace), NULL, "no"},
70 {"sqltracefile", PW_TYPE_STRING_PTR,
71 offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE},
72 {"readclients", PW_TYPE_BOOLEAN,
73 offsetof(SQL_CONFIG,do_clients), NULL, "no"},
74 {"deletestalesessions", PW_TYPE_BOOLEAN,
75 offsetof(SQL_CONFIG,deletestalesessions), NULL, "no"},
76 {"num_sql_socks", PW_TYPE_INTEGER,
77 offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"},
78 {"sql_user_name", PW_TYPE_STRING_PTR,
79 offsetof(SQL_CONFIG,query_user), NULL, ""},
80 {"default_user_profile", PW_TYPE_STRING_PTR,
81 offsetof(SQL_CONFIG,default_profile), NULL, ""},
82 {"query_on_not_found", PW_TYPE_BOOLEAN,
83 offsetof(SQL_CONFIG,query_on_not_found), NULL, "no"},
84 {"authorize_check_query", PW_TYPE_STRING_PTR,
85 offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
86 {"authorize_reply_query", PW_TYPE_STRING_PTR,
87 offsetof(SQL_CONFIG,authorize_reply_query), NULL, ""},
88 {"authorize_group_check_query", PW_TYPE_STRING_PTR,
89 offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
90 {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
91 offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
92 {"accounting_onoff_query", PW_TYPE_STRING_PTR,
93 offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
94 {"accounting_update_query", PW_TYPE_STRING_PTR,
95 offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
96 {"accounting_update_query_alt", PW_TYPE_STRING_PTR,
97 offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""},
98 {"accounting_start_query", PW_TYPE_STRING_PTR,
99 offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
100 {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
101 offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
102 {"accounting_stop_query", PW_TYPE_STRING_PTR,
103 offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
104 {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
105 offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
106 {"group_membership_query", PW_TYPE_STRING_PTR,
107 offsetof(SQL_CONFIG,groupmemb_query), NULL, ""},
108 {"connect_failure_retry_delay", PW_TYPE_INTEGER,
109 offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
110 {"simul_count_query", PW_TYPE_STRING_PTR,
111 offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
112 {"simul_verify_query", PW_TYPE_STRING_PTR,
113 offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
114 {"postauth_query", PW_TYPE_STRING_PTR,
115 offsetof(SQL_CONFIG,postauth_query), NULL, ""},
116 {"safe-characters", PW_TYPE_STRING_PTR,
117 offsetof(SQL_CONFIG,allowed_chars), NULL,
118 "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
120 {NULL, -1, 0, NULL, NULL}
123 /***********************************************************************
124 * start of main routines
125 ***********************************************************************/
126 static int rlm_sql_init(void) {
130 * We should put the sqlsocket array here once
131 * the module code is reworked to not unload
132 * modules on HUP. This way we can have
133 * persistant connections. -jcarneal
141 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username);
142 static int generate_sql_clients(SQL_INST *inst);
143 static int sql_escape_func(char *out, int outlen, const char *in);
146 * sql xlat function. Right now only SELECTs are supported. Only
147 * the first element of the SELECT result will be used.
149 static int sql_xlat(void *instance, REQUEST *request,
150 char *fmt, char *out, size_t freespace,
151 RADIUS_ESCAPE_STRING func)
155 SQL_INST *inst = instance;
156 char querystr[MAX_QUERY_LEN];
157 char sqlusername[MAX_STRING_LEN];
160 DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name);
162 * Add SQL-User-Name attribute just in case it is needed
163 * We could search the string fmt for SQL-User-Name to see if this is
166 sql_set_user(inst, request, sqlusername, NULL);
168 * Do an xlat on the provided string (nice recursive operation).
170 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
171 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
172 inst->config->xlat_name);
176 query_log(request, inst,querystr);
177 sqlsocket = sql_get_socket(inst);
178 if (sqlsocket == NULL)
180 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
181 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
182 inst->config->xlat_name,querystr,
183 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
184 sql_release_socket(inst,sqlsocket);
188 ret = rlm_sql_fetch_row(sqlsocket, inst);
191 DEBUG("rlm_sql (%s): SQL query did not succeed",
192 inst->config->xlat_name);
193 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
194 sql_release_socket(inst,sqlsocket);
198 row = sqlsocket->row;
200 DEBUG("rlm_sql (%s): SQL query did not return any results",
201 inst->config->xlat_name);
202 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
203 sql_release_socket(inst,sqlsocket);
208 DEBUG("rlm_sql (%s): row[0] returned NULL",
209 inst->config->xlat_name);
210 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
211 sql_release_socket(inst,sqlsocket);
214 ret = strlen(row[0]);
215 if (ret > freespace){
216 DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space",
217 inst->config->xlat_name);
218 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
219 sql_release_socket(inst,sqlsocket);
223 strncpy(out,row[0],ret);
225 DEBUG("rlm_sql (%s): - sql_xlat finished",
226 inst->config->xlat_name);
228 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
229 sql_release_socket(inst,sqlsocket);
233 static int generate_sql_clients(SQL_INST *inst)
237 char querystr[MAX_QUERY_LEN];
242 DEBUG("rlm_sql (%s): - generate_sql_clients",inst->config->xlat_name);
244 if (inst->config->sql_nas_table == NULL){
245 radlog(L_ERR, "rlm_sql (%s): sql_nas_table is NULL.",inst->config->xlat_name);
248 snprintf(querystr,MAX_QUERY_LEN - 1,"SELECT * FROM %s",inst->config->sql_nas_table);
250 DEBUG("rlm_sql (%s): Query: %s",inst->config->xlat_name,querystr);
251 sqlsocket = sql_get_socket(inst);
252 if (sqlsocket == NULL)
254 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
255 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
256 inst->config->xlat_name,querystr,
257 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
258 sql_release_socket(inst,sqlsocket);
262 while(rlm_sql_fetch_row(sqlsocket, inst) == 0) {
264 row = sqlsocket->row;
269 * Row1 Row2 Row3 Row4 Row5 Row6 Row7 Row8
271 * id nasname shortname type ports secret community description
276 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
280 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
283 if (strlen(row[1]) >= sizeof(c->longname)){
284 radlog(L_ERR, "rlm_sql (%s): nasname of length %d is greater than the allowed maximum of %d",
285 inst->config->xlat_name,strlen(row[1]),sizeof(c->longname) - 1);
290 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
293 if (strlen(row[2]) >= sizeof(c->shortname)){
294 radlog(L_ERR, "rlm_sql (%s): shortname of length %d is greater than the allowed maximum of %d",
295 inst->config->xlat_name,strlen(row[2]),sizeof(c->shortname) - 1);
298 if (row[3] && strlen(row[3]) >= sizeof(c->nastype)){
299 radlog(L_ERR, "rlm_sql (%s): nastype of length %d is greater than the allowed maximum of %d",
300 inst->config->xlat_name,strlen(row[3]),sizeof(c->nastype) - 1);
304 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
307 if (strlen(row[5]) >= sizeof(c->secret)){
308 radlog(L_ERR, "rlm_sql (%s): secret of length %d is greater than the allowed maximum of %d",
309 inst->config->xlat_name,strlen(row[5]),sizeof(c->secret) - 1);
313 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
314 row[1],row[2],row[5]);
316 c = rad_malloc(sizeof(RADCLIENT));
317 memset(c, 0, sizeof(RADCLIENT));
320 netmask = strchr(row[1], '/');
329 mask_length = atoi(netmask + 1);
330 if ((mask_length < 0) || (mask_length > 32)) {
331 radlog(L_ERR, "rlm_sql (%s): Invalid value '%s' for IP network mask for nasname %s.",
332 inst->config->xlat_name, netmask + 1,row[1]);
337 if (mask_length == 0) {
340 c->netmask = ~0 << (32 - mask_length);
344 c->netmask = htonl(c->netmask);
347 c->ipaddr = ip_getaddr(row[1]);
348 if (c->ipaddr == INADDR_NONE) {
349 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s",
350 inst->config->xlat_name, row[1]);
356 * Update the client name again...
360 c->ipaddr &= c->netmask;
361 strcpy(c->longname, row[1]);
363 ip_hostname(c->longname, sizeof(c->longname),
367 strcpy((char *)c->secret, row[5]);
368 strcpy(c->shortname, row[2]);
370 strcpy(c->nastype, row[3]);
372 DEBUG("rlm_sql (%s): Adding client %s (%s) to clients list",inst->config->xlat_name,
373 c->longname,c->shortname);
375 c->next = mainconfig.clients;
376 mainconfig.clients = c;
379 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
380 sql_release_socket(inst, sqlsocket);
387 * Translate the SQL queries.
389 static int sql_escape_func(char *out, int outlen, const char *in)
395 * Non-printable characters get replaced with their
396 * mime-encoded equivalents.
399 strchr(allowed_chars, *in) == NULL) {
401 * Only 3 or less bytes available.
407 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
416 * Only one byte left.
436 * Set the SQL user name.
438 * We don't call the escape function here. The resulting string
439 * will be escaped later in the queries xlat so we don't need to
440 * escape it twice. (it will make things wrong if we have an
441 * escape candidate character in the username)
443 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
446 char tmpuser[MAX_STRING_LEN];
449 sqlusername[0]= '\0';
451 /* Remove any user attr we added previously */
452 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
454 if (username != NULL) {
455 strNcpy(tmpuser, username, MAX_STRING_LEN);
456 } else if (strlen(inst->config->query_user)) {
457 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
463 strNcpy(sqlusername, tmpuser, MAX_STRING_LEN);
464 DEBUG2("rlm_sql (%s): sql_set_user escaped user --> '%s'",
465 inst->config->xlat_name, sqlusername);
466 vp = pairmake("SQL-User-Name", sqlusername, 0);
468 radlog(L_ERR, "%s", librad_errstr);
472 pairadd(&request->packet->vps, vp);
479 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
480 * with the group memberships reciding in sql
481 * The group membership query should only return one element which is the username. The returned
482 * username will then be checked with the passed check string.
485 static int sql_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
486 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
490 SQL_INST *inst = instance;
491 char querystr[MAX_QUERY_LEN];
492 char sqlusername[MAX_STRING_LEN];
494 check_pairs = check_pairs;
495 reply_pairs = reply_pairs;
497 DEBUG("rlm_sql (%s): - sql_groupcmp", inst->config->xlat_name);
498 if (!check || !check->strvalue || !check->length){
499 DEBUG("rlm_sql (%s): sql_groupcmp: Illegal group name",
500 inst->config->xlat_name);
504 DEBUG("rlm_sql (%s): sql_groupcmp: NULL request",
505 inst->config->xlat_name);
508 if (inst->config->groupmemb_query[0] == 0)
511 * Set, escape, and check the user attr here
513 if (sql_set_user(inst, req, sqlusername, NULL) < 0)
515 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, req, sql_escape_func)){
516 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
517 inst->config->xlat_name);
518 /* Remove the username we (maybe) added above */
519 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
522 /* Remove the username we (maybe) added above */
523 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
525 sqlsocket = sql_get_socket(inst);
526 if (sqlsocket == NULL)
528 if ((inst->module->sql_select_query)(sqlsocket,inst->config,querystr) <0){
529 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
530 inst->config->xlat_name,querystr,
531 (char *)(inst->module->sql_error)(sqlsocket,inst->config));
532 sql_release_socket(inst,sqlsocket);
535 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
536 row = sqlsocket->row;
540 DEBUG("rlm_sql (%s): row[0] returned NULL",
541 inst->config->xlat_name);
542 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
543 sql_release_socket(inst, sqlsocket);
546 if (strcmp(row[0],check->strvalue) == 0){
547 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User belongs in group %s",
548 inst->config->xlat_name,
549 (char *)check->strvalue);
550 (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst, sqlsocket);
555 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
556 sql_release_socket(inst,sqlsocket);
558 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User does not belong in group %s",
559 inst->config->xlat_name, (char *)check->strvalue);
565 static int rlm_sql_detach(void *instance)
567 SQL_INST *inst = instance;
573 if (inst->config->xlat_name) {
574 xlat_unregister(inst->config->xlat_name,sql_xlat);
575 free(inst->config->xlat_name);
578 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
584 * Free up dynamically allocated string pointers.
586 for (i = 0; module_config[i].name != NULL; i++) {
588 if (module_config[i].type != PW_TYPE_STRING_PTR) {
593 * Treat 'config' as an opaque array of bytes,
594 * and take the offset into it. There's a
595 * (char*) pointer at that offset, and we want
598 p = (char **) (((char *)inst->config) + module_config[i].offset);
599 if (!*p) { /* nothing allocated */
605 allowed_chars = NULL;
613 * FIXME: Call the modules 'destroy' function?
615 lt_dlclose(inst->handle); /* ignore any errors */
622 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
627 inst = rad_malloc(sizeof(SQL_INST));
628 memset(inst, 0, sizeof(SQL_INST));
630 inst->config = rad_malloc(sizeof(SQL_CONFIG));
631 memset(inst->config, 0, sizeof(SQL_CONFIG));
634 * If the configuration parameters can't be parsed, then
637 if (cf_section_parse(conf, inst->config, module_config) < 0) {
638 rlm_sql_detach(inst);
642 xlat_name = cf_section_name2(conf);
643 if (xlat_name == NULL)
644 xlat_name = cf_section_name1(conf);
646 inst->config->xlat_name = strdup(xlat_name);
647 xlat_register(xlat_name, sql_xlat, inst);
650 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
651 radlog(L_ERR | L_CONS, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
652 inst->config->xlat_name, MAX_SQL_SOCKS);
653 rlm_sql_detach(inst);
658 * Sanity check for crazy people.
660 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
661 radlog(L_ERR, "rlm_sql (%s): \"%s\" is NOT an SQL driver!",
662 inst->config->xlat_name, inst->config->sql_driver);
663 rlm_sql_detach(inst);
667 inst->handle = lt_dlopenext(inst->config->sql_driver);
668 if (inst->handle == NULL) {
669 radlog(L_ERR, "rlm_sql (%s): Could not link driver %s: %s",
670 inst->config->xlat_name, inst->config->sql_driver,
672 radlog(L_ERR, "rlm_sql (%s): Make sure it (and all its dependent libraries!) are in the search path of your system's ld.",
673 inst->config->xlat_name);
674 rlm_sql_detach(inst);
678 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
680 radlog(L_ERR, "rlm_sql (%s): Could not link symbol %s: %s",
681 inst->config->xlat_name, inst->config->sql_driver,
683 rlm_sql_detach(inst);
687 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
688 inst->config->xlat_name, inst->config->sql_driver,
690 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
691 inst->config->xlat_name, inst->config->sql_login,
692 inst->config->sql_server, inst->config->sql_port,
693 inst->config->sql_db);
695 if (sql_init_socketpool(inst) < 0) {
696 rlm_sql_detach(inst);
699 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
701 if (inst->config->do_clients){
702 if (generate_sql_clients(inst) == -1){
703 radlog(L_ERR, "rlm_sql (%s): generate_sql_clients() returned error",inst->config->xlat_name);
704 rlm_sql_detach(inst);
708 allowed_chars = inst->config->allowed_chars;
712 return RLM_MODULE_OK;
715 static int rlm_sql_destroy(void)
721 static int rlm_sql_authorize(void *instance, REQUEST * request)
723 VALUE_PAIR *check_tmp = NULL;
724 VALUE_PAIR *reply_tmp = NULL;
725 VALUE_PAIR *user_profile = NULL;
728 SQL_INST *inst = instance;
729 char querystr[MAX_QUERY_LEN];
730 char sqlusername[MAX_STRING_LEN];
733 * They MUST have a user name to do SQL authorization.
735 if ((request->username == NULL) ||
736 (request->username->length == 0)) {
737 radlog(L_ERR, "rlm_sql (%s): zero length username not permitted\n", inst->config->xlat_name);
738 return RLM_MODULE_INVALID;
742 * Set, escape, and check the user attr here.
744 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
745 return RLM_MODULE_FAIL;
746 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func);
748 sqlsocket = sql_get_socket(inst);
749 if (sqlsocket == NULL) {
750 /* Remove the username we (maybe) added above */
751 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
752 return RLM_MODULE_FAIL;
756 * After this point, ALL 'return's MUST release the SQL socket!
759 found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_USERDATA);
761 * Find the entry for the user.
764 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func);
765 sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
766 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func);
767 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_USERDATA);
768 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func);
769 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
770 } else if (found < 0) {
771 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
772 inst->config->xlat_name);
773 sql_release_socket(inst, sqlsocket);
774 /* Remove the username we (maybe) added above */
775 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
776 pairfree(&check_tmp);
777 return RLM_MODULE_FAIL;
780 radlog(L_DBG, "rlm_sql (%s): User %s not found in radcheck",
781 inst->config->xlat_name, sqlusername);
784 * We didn't find the user in radcheck, so we try looking
785 * for radgroupcheck entry
787 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func);
788 found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
789 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func);
790 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
793 radlog(L_DBG, "rlm_sql (%s): User %s not found in radgroupcheck",
794 inst->config->xlat_name, sqlusername);
795 if (found || (!found && inst->config->query_on_not_found)){
799 * Check for a default_profile or for a User-Profile.
801 user_profile = pairfind(request->config_items, PW_USER_PROFILE);
802 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
803 char *profile = inst->config->default_profile;
805 if (user_profile != NULL)
806 profile = user_profile->strvalue;
807 if (profile && strlen(profile)){
808 radlog(L_DBG, "rlm_sql (%s): Checking profile %s",
809 inst->config->xlat_name, profile);
810 if (sql_set_user(inst, request, sqlusername, profile) < 0) {
811 sql_release_socket(inst, sqlsocket);
812 pairfree(&reply_tmp);
813 pairfree(&check_tmp);
814 return RLM_MODULE_FAIL;
816 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query,
817 request, sql_escape_func);
818 def_found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
821 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query,
822 request, sql_escape_func);
823 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
829 * We don't need the SQL socket anymore.
831 sql_release_socket(inst, sqlsocket);
834 radlog(L_DBG, "rlm_sql (%s): User not found",
835 inst->config->xlat_name);
836 /* Remove the username we (maybe) added above */
837 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
838 pairfree(&reply_tmp);
839 pairfree(&check_tmp);
840 return RLM_MODULE_NOTFOUND;
844 * Uncomment these lines for debugging
845 * Recompile, and run 'radiusd -X'
849 DEBUG2("rlm_sql: check items");
850 vp_listdebug(check_tmp);
851 DEBUG2("rlm_sql: reply items");
852 vp_listdebug(reply_tmp);
855 if (paircmp(request, request->packet->vps, check_tmp, &reply_tmp) != 0) {
856 radlog(L_INFO, "rlm_sql (%s): No matching entry in the database for request from user [%s]",
857 inst->config->xlat_name, sqlusername);
858 /* Remove the username we (maybe) added above */
859 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
860 pairfree(&reply_tmp);
861 pairfree(&check_tmp);
862 return RLM_MODULE_NOTFOUND;
865 pairxlatmove(request, &request->reply->vps, &reply_tmp);
866 pairxlatmove(request, &request->config_items, &check_tmp);
867 pairfree(&reply_tmp);
868 pairfree(&check_tmp);
870 /* Remove the username we (maybe) added above */
871 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
873 return RLM_MODULE_OK;
877 * Accounting: save the account data to our sql table
879 static int rlm_sql_accounting(void *instance, REQUEST * request) {
881 SQLSOCK *sqlsocket = NULL;
883 SQL_INST *inst = instance;
884 int ret = RLM_MODULE_OK;
886 int acctstatustype = 0;
887 char querystr[MAX_QUERY_LEN];
888 char logstr[MAX_QUERY_LEN];
889 char sqlusername[MAX_STRING_LEN];
891 #ifdef CISCO_ACCOUNTING_HACK
892 int acctsessiontime = 0;
895 memset(querystr, 0, MAX_QUERY_LEN);
898 * Find the Acct Status Type
900 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
901 acctstatustype = pair->lvalue;
903 radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
904 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s",
905 inst->config->xlat_name, logstr);
906 return RLM_MODULE_INVALID;
909 switch (acctstatustype) {
911 * The Terminal server informed us that it was rebooted
912 * STOP all records from this NAS
914 case PW_STATUS_ACCOUNTING_ON:
915 case PW_STATUS_ACCOUNTING_OFF:
916 radlog(L_INFO, "rlm_sql (%s): received Acct On/Off packet", inst->config->xlat_name);
917 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
918 query_log(request, inst, querystr);
920 sqlsocket = sql_get_socket(inst);
921 if (sqlsocket == NULL)
922 return(RLM_MODULE_FAIL);
923 if (*querystr) { /* non-empty query */
924 if (rlm_sql_query(sqlsocket, inst, querystr)) {
925 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting for Acct On/Off packet - %s",
926 inst->config->xlat_name,
927 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
928 ret = RLM_MODULE_FAIL;
930 (inst->module->sql_finish_query)(sqlsocket, inst->config);
936 * Got an update accounting packet
938 case PW_STATUS_ALIVE:
941 * Set, escape, and check the user attr here
943 sql_set_user(inst, request, sqlusername, NULL);
945 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
946 query_log(request, inst, querystr);
948 sqlsocket = sql_get_socket(inst);
949 if (sqlsocket == NULL)
950 return(RLM_MODULE_FAIL);
951 if (*querystr) { /* non-empty query */
952 if (rlm_sql_query(sqlsocket, inst, querystr)) {
953 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting ALIVE record - %s",
954 inst->config->xlat_name,
955 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
956 ret = RLM_MODULE_FAIL;
959 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
960 if (numaffected < 1) {
963 * If our update above didn't match anything
964 * we assume it's because we haven't seen a
965 * matching Start record. So we have to
966 * insert this update rather than do an update
968 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
969 query_log(request, inst, querystr);
970 if (*querystr) { /* non-empty query */
971 if (rlm_sql_query(sqlsocket, inst, querystr)) {
972 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting ALIVE record - %s",
973 inst->config->xlat_name,
974 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
975 ret = RLM_MODULE_FAIL;
977 (inst->module->sql_finish_query)(sqlsocket, inst->config);
981 (inst->module->sql_finish_query)(sqlsocket, inst->config);
986 * Got accounting start packet
988 case PW_STATUS_START:
991 * Set, escape, and check the user attr here
993 sql_set_user(inst, request, sqlusername, NULL);
995 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
996 query_log(request, inst, querystr);
998 sqlsocket = sql_get_socket(inst);
999 if (sqlsocket == NULL)
1000 return(RLM_MODULE_FAIL);
1001 if (*querystr) { /* non-empty query */
1002 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1003 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting START record - %s",
1004 inst->config->xlat_name,
1005 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1008 * We failed the insert above. It's probably because
1009 * the stop record came before the start. We try
1010 * our alternate query now (typically an UPDATE)
1012 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1013 query_log(request, inst, querystr);
1015 if (*querystr) { /* non-empty query */
1016 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1017 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting START record - %s",
1018 inst->config->xlat_name,
1019 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1020 ret = RLM_MODULE_FAIL;
1022 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1025 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1030 * Got accounting stop packet
1032 case PW_STATUS_STOP:
1035 * Set, escape, and check the user attr here
1037 sql_set_user(inst, request, sqlusername, NULL);
1039 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1040 query_log(request, inst, querystr);
1042 sqlsocket = sql_get_socket(inst);
1043 if (sqlsocket == NULL)
1044 return(RLM_MODULE_FAIL);
1045 if (*querystr) { /* non-empty query */
1046 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1047 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting STOP record - %s",
1048 inst->config->xlat_name,
1049 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1050 ret = RLM_MODULE_FAIL;
1053 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1054 if (numaffected < 1) {
1056 * If our update above didn't match anything
1057 * we assume it's because we haven't seen a
1058 * matching Start record. So we have to
1059 * insert this stop rather than do an update
1061 #ifdef CISCO_ACCOUNTING_HACK
1063 * If stop but zero session length AND no previous
1064 * session found, drop it as in invalid packet
1065 * This is to fix CISCO's aaa from filling our
1066 * table with bogus crap
1068 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
1069 acctsessiontime = pair->lvalue;
1071 if (acctsessiontime <= 0) {
1072 radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1073 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s", inst->config->xlat_name, logstr);
1074 sql_release_socket(inst, sqlsocket);
1075 ret = RLM_MODULE_NOOP;
1079 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1080 query_log(request, inst, querystr);
1082 if (*querystr) { /* non-empty query */
1083 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1084 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting STOP record - %s",
1085 inst->config->xlat_name,
1086 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1087 ret = RLM_MODULE_FAIL;
1089 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1093 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1098 * Anything else is ignored.
1101 radlog(L_INFO, "rlm_sql (%s): Unsupported Acct-Status-Type = %d", inst->config->xlat_name, acctstatustype);
1102 return RLM_MODULE_NOOP;
1107 sql_release_socket(inst, sqlsocket);
1114 * See if a user is already logged in. Sets request->simul_count to the
1115 * current session count for this user.
1117 * Check twice. If on the first pass the user exceeds his
1118 * max. number of logins, do a second pass and validate all
1119 * logins by querying the terminal server (using eg. SNMP).
1122 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1124 SQL_INST *inst = instance;
1126 char querystr[MAX_QUERY_LEN];
1127 char sqlusername[MAX_STRING_LEN];
1130 char *call_num = NULL;
1133 uint32_t nas_addr = 0;
1136 /* If simul_count_query is not defined, we don't do any checking */
1137 if (inst->config->simul_count_query[0] == 0) {
1138 return RLM_MODULE_NOOP;
1141 if((request->username == NULL) || (request->username->length == 0)) {
1142 radlog(L_ERR, "rlm_sql (%s): Zero Length username not permitted\n", inst->config->xlat_name);
1143 return RLM_MODULE_INVALID;
1147 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1148 return RLM_MODULE_FAIL;
1150 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1152 /* initialize the sql socket */
1153 sqlsocket = sql_get_socket(inst);
1154 if(sqlsocket == NULL)
1155 return RLM_MODULE_FAIL;
1157 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1158 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1159 sql_release_socket(inst, sqlsocket);
1160 return RLM_MODULE_FAIL;
1163 ret = rlm_sql_fetch_row(sqlsocket, inst);
1166 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1167 sql_release_socket(inst, sqlsocket);
1168 return RLM_MODULE_FAIL;
1171 row = sqlsocket->row;
1173 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1174 sql_release_socket(inst, sqlsocket);
1175 return RLM_MODULE_FAIL;
1178 request->simul_count = atoi(row[0]);
1179 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1181 if(request->simul_count < request->simul_max) {
1182 sql_release_socket(inst, sqlsocket);
1183 return RLM_MODULE_OK;
1186 /* Looks like too many sessions, so lets start verifying them */
1188 if (inst->config->simul_verify_query[0] == 0) {
1189 /* No verify query defined, so skip verify step and rely on count query only */
1190 sql_release_socket(inst, sqlsocket);
1191 return RLM_MODULE_OK;
1194 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1195 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1196 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Database query error", inst->config->xlat_name);
1197 sql_release_socket(inst, sqlsocket);
1198 return RLM_MODULE_FAIL;
1202 * Setup some stuff, like for MPP detection.
1204 request->simul_count = 0;
1206 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1208 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1209 call_num = vp->strvalue;
1212 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1213 row = sqlsocket->row;
1217 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1218 sql_release_socket(inst, sqlsocket);
1219 DEBUG("rlm_sql (%s): Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1220 return RLM_MODULE_FAIL;
1223 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1224 sql_release_socket(inst, sqlsocket);
1225 DEBUG("rlm_sql (%s): Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1226 return RLM_MODULE_FAIL;
1229 nas_addr = inet_addr(row[3]);
1231 nas_port = atoi(row[4]);
1233 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1236 * Failed to check the terminal server for
1237 * duplicate logins: Return an error.
1240 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1241 sql_release_socket(inst, sqlsocket);
1242 DEBUG("rlm_sql (%s) rad_check_ts() failed.",
1243 inst->config->xlat_name);
1244 return RLM_MODULE_FAIL;
1248 ++request->simul_count;
1251 * Does it look like a MPP attempt?
1253 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1254 request->simul_mpp = 2;
1255 else if (row[6] && call_num &&
1256 !strncmp(row[6],call_num,16))
1257 request->simul_mpp = 2;
1261 * Stale record - zap it.
1263 uint32_t framed_addr = 0;
1267 framed_addr = inet_addr(row[5]);
1269 if (strcmp(row[7],"SLIP") == 0)
1272 session_zap(request,
1273 nas_addr,nas_port,row[2],row[1],
1274 framed_addr, proto);
1278 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1279 sql_release_socket(inst, sqlsocket);
1281 /* The Auth module apparently looks at request->simul_count, not the return value
1282 of this module when deciding to deny a call for too many sessions */
1283 return RLM_MODULE_OK;
1288 * Execute postauth_query after authentication
1290 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1291 SQLSOCK *sqlsocket = NULL;
1292 SQL_INST *inst = instance;
1293 char querystr[MAX_QUERY_LEN];
1294 char sqlusername[MAX_STRING_LEN];
1296 DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name);
1298 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1299 return RLM_MODULE_FAIL;
1301 /* If postauth_query is not defined, we stop here */
1302 if (inst->config->postauth_query[0] == '\0')
1303 return RLM_MODULE_NOOP;
1305 /* Expand variables in the query */
1306 memset(querystr, 0, MAX_QUERY_LEN);
1307 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1308 request, sql_escape_func);
1309 query_log(request, inst, querystr);
1310 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1311 inst->config->xlat_name, querystr);
1313 /* Initialize the sql socket */
1314 sqlsocket = sql_get_socket(inst);
1315 if (sqlsocket == NULL)
1316 return RLM_MODULE_FAIL;
1318 /* Process the query */
1319 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1320 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1321 inst->config->xlat_name,
1322 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1323 sql_release_socket(inst, sqlsocket);
1324 return RLM_MODULE_FAIL;
1326 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1328 sql_release_socket(inst, sqlsocket);
1329 return RLM_MODULE_OK;
1332 /* globally exported name */
1333 module_t rlm_sql = {
1335 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1336 rlm_sql_init, /* initialization */
1337 rlm_sql_instantiate, /* instantiation */
1339 NULL, /* authentication */
1340 rlm_sql_authorize, /* authorization */
1341 NULL, /* preaccounting */
1342 rlm_sql_accounting, /* accounting */
1343 rlm_sql_checksimul, /* checksimul */
1344 NULL, /* pre-proxy */
1345 NULL, /* post-proxy */
1346 rlm_sql_postauth /* post-auth */
1348 rlm_sql_detach, /* detach */
1349 rlm_sql_destroy, /* destroy */