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,
53 offsetof(SQL_CONFIG,sql_driver), NULL, "mysql"},
54 {"server",PW_TYPE_STRING_PTR,
55 offsetof(SQL_CONFIG,sql_server), NULL, "localhost"},
56 {"port",PW_TYPE_STRING_PTR,
57 offsetof(SQL_CONFIG,sql_port), NULL, ""},
58 {"login", PW_TYPE_STRING_PTR,
59 offsetof(SQL_CONFIG,sql_login), NULL, ""},
60 {"password", PW_TYPE_STRING_PTR,
61 offsetof(SQL_CONFIG,sql_password), NULL, ""},
62 {"radius_db", PW_TYPE_STRING_PTR,
63 offsetof(SQL_CONFIG,sql_db), NULL, "radius"},
64 {"acct_table", PW_TYPE_STRING_PTR,
65 offsetof(SQL_CONFIG,sql_acct_table), NULL, "radacct"},
66 {"acct_table2", PW_TYPE_STRING_PTR,
67 offsetof(SQL_CONFIG,sql_acct_table2), NULL, "radacct"},
68 {"authcheck_table", PW_TYPE_STRING_PTR,
69 offsetof(SQL_CONFIG,sql_authcheck_table), NULL, "radcheck"},
70 {"authreply_table", PW_TYPE_STRING_PTR,
71 offsetof(SQL_CONFIG,sql_authreply_table), NULL, "radreply"},
72 {"groupcheck_table", PW_TYPE_STRING_PTR,
73 offsetof(SQL_CONFIG,sql_groupcheck_table), NULL, "radgroupcheck"},
74 {"groupreply_table", PW_TYPE_STRING_PTR,
75 offsetof(SQL_CONFIG,sql_groupreply_table), NULL, "radgroupreply"},
76 {"usergroup_table", PW_TYPE_STRING_PTR,
77 offsetof(SQL_CONFIG,sql_usergroup_table), NULL, "usergroup"},
78 {"nas_table", PW_TYPE_STRING_PTR,
79 offsetof(SQL_CONFIG,sql_nas_table), NULL, "nas"},
80 {"dict_table", PW_TYPE_STRING_PTR,
81 offsetof(SQL_CONFIG,sql_dict_table), NULL, "dictionary"},
82 {"sqltrace", PW_TYPE_BOOLEAN,
83 offsetof(SQL_CONFIG,sqltrace), NULL, "no"},
84 {"sqltracefile", PW_TYPE_STRING_PTR,
85 offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE},
86 {"readclients", PW_TYPE_BOOLEAN,
87 offsetof(SQL_CONFIG,do_clients), NULL, "no"},
88 {"deletestalesessions", PW_TYPE_BOOLEAN,
89 offsetof(SQL_CONFIG,deletestalesessions), NULL, "no"},
90 {"num_sql_socks", PW_TYPE_INTEGER,
91 offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"},
92 {"sql_user_name", PW_TYPE_STRING_PTR,
93 offsetof(SQL_CONFIG,query_user), NULL, ""},
94 {"default_user_profile", PW_TYPE_STRING_PTR,
95 offsetof(SQL_CONFIG,default_profile), NULL, ""},
96 {"query_on_not_found", PW_TYPE_BOOLEAN,
97 offsetof(SQL_CONFIG,query_on_not_found), NULL, "no"},
98 {"authorize_check_query", PW_TYPE_STRING_PTR,
99 offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
100 {"authorize_reply_query", PW_TYPE_STRING_PTR,
101 offsetof(SQL_CONFIG,authorize_reply_query), NULL, ""},
102 {"authorize_group_check_query", PW_TYPE_STRING_PTR,
103 offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
104 {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
105 offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
106 {"accounting_onoff_query", PW_TYPE_STRING_PTR,
107 offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
108 {"accounting_update_query", PW_TYPE_STRING_PTR,
109 offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
110 {"accounting_update_query_alt", PW_TYPE_STRING_PTR,
111 offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""},
112 {"accounting_start_query", PW_TYPE_STRING_PTR,
113 offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
114 {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
115 offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
116 {"accounting_stop_query", PW_TYPE_STRING_PTR,
117 offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
118 {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
119 offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
120 {"group_membership_query", PW_TYPE_STRING_PTR,
121 offsetof(SQL_CONFIG,groupmemb_query), NULL, ""},
122 {"connect_failure_retry_delay", PW_TYPE_INTEGER,
123 offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
124 {"simul_count_query", PW_TYPE_STRING_PTR,
125 offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
126 {"simul_verify_query", PW_TYPE_STRING_PTR,
127 offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
128 {"postauth_table", PW_TYPE_STRING_PTR,
129 offsetof(SQL_CONFIG,sql_postauth_table), NULL, "radpostauth"},
130 {"postauth_query", PW_TYPE_STRING_PTR,
131 offsetof(SQL_CONFIG,postauth_query), NULL, ""},
133 {NULL, -1, 0, NULL, NULL}
136 /***********************************************************************
137 * start of main routines
138 ***********************************************************************/
139 static int rlm_sql_init(void) {
143 * We should put the sqlsocket array here once
144 * the module code is reworked to not unload
145 * modules on HUP. This way we can have
146 * persistant connections. -jcarneal
154 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username);
155 static int generate_sql_clients(SQL_INST *inst);
158 * sql xlat function. Right now only SELECTs are supported. Only
159 * the first element of the SELECT result will be used.
161 static int sql_xlat(void *instance, REQUEST *request,
162 char *fmt, char *out, int freespace,
163 RADIUS_ESCAPE_STRING func)
167 SQL_INST *inst = instance;
168 char querystr[MAX_QUERY_LEN];
169 char sqlusername[2 * MAX_STRING_LEN + 10];
172 DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name);
174 * Add SQL-User-Name attribute just in case it is needed
175 * We could search the string fmt for SQL-User-Name to see if this is
178 sql_set_user(inst, request, sqlusername, NULL);
180 * Do an xlat on the provided string (nice recursive operation).
182 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, func)) {
183 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
184 inst->config->xlat_name);
188 query_log(request, inst,querystr);
189 sqlsocket = sql_get_socket(inst);
190 if (sqlsocket == NULL)
192 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
193 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
194 inst->config->xlat_name,querystr,
195 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
196 sql_release_socket(inst,sqlsocket);
200 ret = rlm_sql_fetch_row(sqlsocket, inst);
203 DEBUG("rlm_sql (%s): SQL query did not succeed",
204 inst->config->xlat_name);
205 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
206 sql_release_socket(inst,sqlsocket);
210 row = sqlsocket->row;
212 DEBUG("rlm_sql (%s): SQL query did not return any results",
213 inst->config->xlat_name);
214 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
215 sql_release_socket(inst,sqlsocket);
220 DEBUG("rlm_sql (%s): row[0] returned NULL",
221 inst->config->xlat_name);
222 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
223 sql_release_socket(inst,sqlsocket);
226 ret = strlen(row[0]);
227 if (ret > freespace){
228 DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space",
229 inst->config->xlat_name);
230 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
231 sql_release_socket(inst,sqlsocket);
235 strncpy(out,row[0],ret);
237 DEBUG("rlm_sql (%s): - sql_xlat finished",
238 inst->config->xlat_name);
240 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
241 sql_release_socket(inst,sqlsocket);
245 static int generate_sql_clients(SQL_INST *inst)
249 char querystr[MAX_QUERY_LEN];
254 DEBUG("rlm_sql (%s): - generate_sql_clients",inst->config->xlat_name);
256 if (inst->config->sql_nas_table == NULL){
257 radlog(L_ERR, "rlm_sql (%s): sql_nas_table is NULL.",inst->config->xlat_name);
260 snprintf(querystr,MAX_QUERY_LEN - 1,"SELECT * FROM %s",inst->config->sql_nas_table);
262 DEBUG("rlm_sql (%s): Query: %s",inst->config->xlat_name,querystr);
263 sqlsocket = sql_get_socket(inst);
264 if (sqlsocket == NULL)
266 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
267 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
268 inst->config->xlat_name,querystr,
269 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
270 sql_release_socket(inst,sqlsocket);
274 while(rlm_sql_fetch_row(sqlsocket, inst) == 0) {
276 row = sqlsocket->row;
281 * Row1 Row2 Row3 Row4 Row5 Row6 Row7 Row8
283 * id nasname shortname type ports secret community description
288 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
292 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
295 if (strlen(row[1]) >= sizeof(c->longname)){
296 radlog(L_ERR, "rlm_sql (%s): nasname of length %d is greater than the allowed maximum of %d",
297 inst->config->xlat_name,strlen(row[1]),sizeof(c->longname) - 1);
302 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
305 if (strlen(row[2]) >= sizeof(c->shortname)){
306 radlog(L_ERR, "rlm_sql (%s): shortname of length %d is greater than the allowed maximum of %d",
307 inst->config->xlat_name,strlen(row[2]),sizeof(c->shortname) - 1);
310 if (row[3] && strlen(row[3]) >= sizeof(c->nastype)){
311 radlog(L_ERR, "rlm_sql (%s): nastype of length %d is greater than the allowed maximum of %d",
312 inst->config->xlat_name,strlen(row[3]),sizeof(c->nastype) - 1);
316 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
319 if (strlen(row[5]) >= sizeof(c->secret)){
320 radlog(L_ERR, "rlm_sql (%s): secret of length %d is greater than the allowed maximum of %d",
321 inst->config->xlat_name,strlen(row[5]),sizeof(c->secret) - 1);
325 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
326 row[1],row[2],row[5]);
328 c = rad_malloc(sizeof(RADCLIENT));
329 memset(c, 0, sizeof(RADCLIENT));
332 netmask = strchr(row[1], '/');
341 mask_length = atoi(netmask + 1);
342 if ((mask_length < 0) || (mask_length > 32)) {
343 radlog(L_ERR, "rlm_sql (%s): Invalid value '%s' for IP network mask for nasname %s.",
344 inst->config->xlat_name, netmask + 1,row[1]);
349 if (mask_length == 0) {
352 c->netmask = ~0 << (32 - mask_length);
356 c->netmask = htonl(c->netmask);
359 c->ipaddr = ip_getaddr(row[1]);
360 if (c->ipaddr == INADDR_NONE) {
361 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s",
362 inst->config->xlat_name, row[1]);
368 * Update the client name again...
372 c->ipaddr &= c->netmask;
373 strcpy(c->longname, row[1]);
375 ip_hostname(c->longname, sizeof(c->longname),
379 strcpy((char *)c->secret, row[5]);
380 strcpy(c->shortname, row[2]);
382 strcpy(c->nastype, row[3]);
384 DEBUG("rlm_sql (%s): Adding client %s (%s) to clients list",inst->config->xlat_name,
385 c->longname,c->shortname);
387 c->next = mainconfig.clients;
388 mainconfig.clients = c;
391 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
392 sql_release_socket(inst, sqlsocket);
399 * Translate the SQL queries.
401 static int sql_escape_func(char *out, int outlen, const char *in)
407 * Only one byte left.
414 * Non-printable characters get replaced with their
415 * mime-encoded equivalents.
418 strchr("@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: =/", *in) == NULL) {
419 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
428 * Else it's a nice character.
441 * Set the SQl user name.
443 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
446 char tmpuser[MAX_STRING_LEN];
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, sql_escape_func);
463 strNcpy(sqlusername, tmpuser, sizeof(tmpuser));
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[2 * MAX_STRING_LEN + 10];
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, 0) < 0)
515 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, req, NULL)){
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 */
612 * FIXME: Call the modules 'destroy' function?
614 lt_dlclose(inst->handle); /* ignore any errors */
621 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
626 inst = rad_malloc(sizeof(SQL_INST));
627 memset(inst, 0, sizeof(SQL_INST));
629 inst->config = rad_malloc(sizeof(SQL_CONFIG));
630 memset(inst->config, 0, sizeof(SQL_CONFIG));
633 * If the configuration parameters can't be parsed, then
636 if (cf_section_parse(conf, inst->config, module_config) < 0) {
637 rlm_sql_detach(inst);
641 xlat_name = cf_section_name2(conf);
642 if (xlat_name == NULL)
643 xlat_name = cf_section_name1(conf);
645 inst->config->xlat_name = strdup(xlat_name);
646 xlat_register(xlat_name, sql_xlat, inst);
649 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
650 radlog(L_ERR | L_CONS, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
651 inst->config->xlat_name, MAX_SQL_SOCKS);
652 rlm_sql_detach(inst);
657 * Sanity check for crazy people.
659 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
660 radlog(L_ERR, "rlm_sql (%s): \"%s\" is NOT an SQL driver!",
661 inst->config->xlat_name, inst->config->sql_driver);
662 rlm_sql_detach(inst);
666 inst->handle = lt_dlopenext(inst->config->sql_driver);
667 if (inst->handle == NULL) {
668 radlog(L_ERR, "rlm_sql (%s): Could not link driver %s: %s",
669 inst->config->xlat_name, inst->config->sql_driver,
671 radlog(L_ERR, "rlm_sql (%s): Make sure it (and all its dependent libraries!) are in the search path of your system's ld.",
672 inst->config->xlat_name);
673 rlm_sql_detach(inst);
677 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
679 radlog(L_ERR, "rlm_sql (%s): Could not link symbol %s: %s",
680 inst->config->xlat_name, inst->config->sql_driver,
682 rlm_sql_detach(inst);
686 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
687 inst->config->xlat_name, inst->config->sql_driver,
689 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
690 inst->config->xlat_name, inst->config->sql_login,
691 inst->config->sql_server, inst->config->sql_port,
692 inst->config->sql_db);
694 if (sql_init_socketpool(inst) < 0) {
695 rlm_sql_detach(inst);
698 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
700 if (inst->config->do_clients){
701 if (generate_sql_clients(inst) == -1){
702 radlog(L_ERR, "rlm_sql (%s): generate_sql_clients() returned error");
703 rlm_sql_detach(inst);
710 return RLM_MODULE_OK;
713 static int rlm_sql_destroy(void)
719 static int rlm_sql_authorize(void *instance, REQUEST * request)
721 VALUE_PAIR *check_tmp = NULL;
722 VALUE_PAIR *reply_tmp = NULL;
723 VALUE_PAIR *user_profile = NULL;
726 SQL_INST *inst = instance;
727 char querystr[MAX_QUERY_LEN];
729 /* sqlusername holds the sql escaped username. The original
730 * username is at most MAX_STRING_LEN chars long and
731 * *sql_escape_string doubles its length in the worst case.
732 * Throw in an extra 10 to account for trailing NULs and to have
733 * a safety margin. */
734 char sqlusername[2 * MAX_STRING_LEN + 10];
737 * They MUST have a user name to do SQL authorization.
739 if ((request->username == NULL) ||
740 (request->username->length == 0)) {
741 radlog(L_ERR, "rlm_sql (%s): zero length username not permitted\n", inst->config->xlat_name);
742 return RLM_MODULE_INVALID;
747 * After this point, ALL 'return's MUST release the SQL socket!
752 * Set, escape, and check the user attr here
754 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
755 return RLM_MODULE_FAIL;
756 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func);
758 sqlsocket = sql_get_socket(inst);
759 if (sqlsocket == NULL) {
760 /* Remove the username we (maybe) added above */
761 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
762 return(RLM_MODULE_FAIL);
765 found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_USERDATA);
767 * Find the entry for the user.
770 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func);
771 sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
772 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func);
773 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_USERDATA);
774 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func);
775 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
776 } else if (found < 0) {
777 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
778 inst->config->xlat_name);
779 sql_release_socket(inst, sqlsocket);
780 /* Remove the username we (maybe) added above */
781 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
782 return RLM_MODULE_FAIL;
785 radlog(L_DBG, "rlm_sql (%s): User %s not found in radcheck",
786 inst->config->xlat_name, sqlusername);
789 * We didn't find the user in radcheck, so we try looking
790 * for radgroupcheck entry
792 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func);
793 found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
794 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func);
795 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
798 radlog(L_DBG, "rlm_sql (%s): User %s not found in radgroupcheck",
799 inst->config->xlat_name, sqlusername);
800 if (found || (!found && inst->config->query_on_not_found)){
802 * Check for a default_profile or for a User-Profile.
804 user_profile = pairfind(request->config_items, PW_USER_PROFILE);
805 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
806 char *profile = inst->config->default_profile;
808 if (user_profile != NULL)
809 profile = user_profile->strvalue;
810 if (profile && strlen(profile)){
811 radlog(L_DBG, "rlm_sql (%s): Checking profile %s",
812 inst->config->xlat_name, profile);
813 if (sql_set_user(inst, request, sqlusername, profile) < 0) {
814 return RLM_MODULE_FAIL;
816 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query,
817 request, sql_escape_func);
818 found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
819 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query,
820 request, sql_escape_func);
821 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
826 radlog(L_DBG, "rlm_sql (%s): User not found",
827 inst->config->xlat_name);
828 sql_release_socket(inst, sqlsocket);
829 /* Remove the username we (maybe) added above */
830 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
831 return RLM_MODULE_NOTFOUND;
835 * Uncomment these lines for debugging
836 * Recompile, and run 'radiusd -X'
840 DEBUG2("rlm_sql: check items");
841 vp_listdebug(check_tmp);
842 DEBUG2("rlm_sql: reply items");
843 vp_listdebug(reply_tmp);
846 if (paircmp(request, request->packet->vps, check_tmp, &reply_tmp) != 0) {
847 radlog(L_INFO, "rlm_sql (%s): No matching entry in the database for request from user [%s]",
848 inst->config->xlat_name, sqlusername);
849 /* Remove the username we (maybe) added above */
850 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
851 sql_release_socket(inst, sqlsocket);
852 pairfree(&reply_tmp);
853 pairfree(&check_tmp);
854 return RLM_MODULE_NOTFOUND;
857 pairmove(&request->reply->vps, &reply_tmp);
858 pairmove(&request->config_items, &check_tmp);
859 pairfree(&reply_tmp);
860 pairfree(&check_tmp);
862 /* Remove the username we (maybe) added above */
863 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
864 sql_release_socket(inst, sqlsocket);
866 return RLM_MODULE_OK;
870 * Accounting: save the account data to our sql table
872 static int rlm_sql_accounting(void *instance, REQUEST * request) {
874 SQLSOCK *sqlsocket = NULL;
876 SQL_INST *inst = instance;
877 int ret = RLM_MODULE_OK;
879 int acctstatustype = 0;
880 char querystr[MAX_QUERY_LEN];
881 char logstr[MAX_QUERY_LEN];
882 char sqlusername[2 * MAX_STRING_LEN + 10];
884 #ifdef CISCO_ACCOUNTING_HACK
885 int acctsessiontime = 0;
888 memset(querystr, 0, MAX_QUERY_LEN);
891 * Find the Acct Status Type
893 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
894 acctstatustype = pair->lvalue;
896 radius_xlat(logstr, sizeof(logstr), "rlm_sql: packet has no account status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, sql_escape_func);
897 radlog(L_ERR, logstr);
898 return RLM_MODULE_INVALID;
901 switch (acctstatustype) {
903 * The Terminal server informed us that it was rebooted
904 * STOP all records from this NAS
906 case PW_STATUS_ACCOUNTING_ON:
907 case PW_STATUS_ACCOUNTING_OFF:
908 radlog(L_INFO, "rlm_sql (%s): received Acct On/Off packet", inst->config->xlat_name);
909 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
910 query_log(request, inst, querystr);
912 sqlsocket = sql_get_socket(inst);
913 if (sqlsocket == NULL)
914 return(RLM_MODULE_FAIL);
915 if (*querystr) { /* non-empty query */
916 if (rlm_sql_query(sqlsocket, inst, querystr)) {
917 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting for Acct On/Off packet - %s",
918 inst->config->xlat_name,
919 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
920 ret = RLM_MODULE_FAIL;
922 (inst->module->sql_finish_query)(sqlsocket, inst->config);
928 * Got an update accounting packet
930 case PW_STATUS_ALIVE:
933 * Set, escape, and check the user attr here
935 sql_set_user(inst, request, sqlusername, NULL);
937 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
938 query_log(request, inst, querystr);
940 sqlsocket = sql_get_socket(inst);
941 if (sqlsocket == NULL)
942 return(RLM_MODULE_FAIL);
943 if (*querystr) { /* non-empty query */
944 if (rlm_sql_query(sqlsocket, inst, querystr)) {
945 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting ALIVE record - %s",
946 inst->config->xlat_name,
947 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
950 * We failed the update above. It's probably because
951 * the start record hasn't come in. We try
952 * our alternate query now (typically an INSERT)
954 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
955 query_log(request, inst, querystr);
956 if (*querystr) { /* non-empty query */
957 if (rlm_sql_query(sqlsocket, inst, querystr)) {
958 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting ALIVE record - %s",
959 inst->config->xlat_name,
960 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
961 ret = RLM_MODULE_FAIL;
963 (inst->module->sql_finish_query)(sqlsocket, inst->config);
966 (inst->module->sql_finish_query)(sqlsocket, inst->config);
972 * Got accounting start packet
974 case PW_STATUS_START:
977 * Set, escape, and check the user attr here
979 sql_set_user(inst, request, sqlusername, NULL);
981 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
982 query_log(request, inst, querystr);
984 sqlsocket = sql_get_socket(inst);
985 if (sqlsocket == NULL)
986 return(RLM_MODULE_FAIL);
987 if (*querystr) { /* non-empty query */
988 if (rlm_sql_query(sqlsocket, inst, querystr)) {
989 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting START record - %s",
990 inst->config->xlat_name,
991 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
994 * We failed the insert above. It's probably because
995 * the stop record came before the start. We try
996 * our alternate query now (typically an UPDATE)
998 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
999 query_log(request, inst, querystr);
1001 if (*querystr) { /* non-empty query */
1002 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1003 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting START record - %s",
1004 inst->config->xlat_name,
1005 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1006 ret = RLM_MODULE_FAIL;
1008 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1011 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1016 * Got accounting stop packet
1018 case PW_STATUS_STOP:
1021 * Set, escape, and check the user attr here
1023 sql_set_user(inst, request, sqlusername, NULL);
1025 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1026 query_log(request, inst, querystr);
1028 sqlsocket = sql_get_socket(inst);
1029 if (sqlsocket == NULL)
1030 return(RLM_MODULE_FAIL);
1031 if (*querystr) { /* non-empty query */
1032 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1033 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting STOP record - %s",
1034 inst->config->xlat_name,
1035 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1036 ret = RLM_MODULE_FAIL;
1039 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1040 if (numaffected < 1) {
1042 * If our update above didn't match anything
1043 * we assume it's because we haven't seen a
1044 * matching Start record. So we have to
1045 * insert this stop rather than do an update
1047 #ifdef CISCO_ACCOUNTING_HACK
1049 * If stop but zero session length AND no previous
1050 * session found, drop it as in invalid packet
1051 * This is to fix CISCO's aaa from filling our
1052 * table with bogus crap
1054 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
1055 acctsessiontime = pair->lvalue;
1057 if (acctsessiontime <= 0) {
1058 radius_xlat(logstr, sizeof(logstr), "rlm_sql: Stop packet with zero session length. (user '%{User-Name}', nas '%{NAS-IP-Address}')", request, sql_escape_func);
1059 radlog(L_ERR, logstr);
1060 sql_release_socket(inst, sqlsocket);
1061 ret = RLM_MODULE_NOOP;
1065 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1066 query_log(request, inst, querystr);
1068 if (*querystr) { /* non-empty query */
1069 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1070 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting STOP record - %s",
1071 inst->config->xlat_name,
1072 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1073 ret = RLM_MODULE_FAIL;
1075 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1079 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1084 * Anything else is ignored.
1087 radlog(L_INFO, "rlm_sql (%s): Unsupported Acct-Status-Type = %d", inst->config->xlat_name, acctstatustype);
1088 return RLM_MODULE_NOOP;
1093 sql_release_socket(inst, sqlsocket);
1100 * See if a user is already logged in. Sets request->simul_count to the
1101 * current session count for this user.
1103 * Check twice. If on the first pass the user exceeds his
1104 * max. number of logins, do a second pass and validate all
1105 * logins by querying the terminal server (using eg. SNMP).
1108 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1110 SQL_INST *inst = instance;
1112 char querystr[MAX_QUERY_LEN];
1113 char sqlusername[2*MAX_STRING_LEN+10];
1116 char *call_num = NULL;
1119 uint32_t nas_addr = 0;
1122 /* If simul_count_query is not defined, we don't do any checking */
1123 if (inst->config->simul_count_query[0] == 0) {
1124 return RLM_MODULE_NOOP;
1127 if((request->username == NULL) || (request->username->length == 0)) {
1128 radlog(L_ERR, "rlm_sql (%s): Zero Length username not permitted\n", inst->config->xlat_name);
1129 return RLM_MODULE_INVALID;
1133 if(sql_set_user(inst, request, sqlusername, 0) <0)
1134 return RLM_MODULE_FAIL;
1136 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, NULL);
1138 /* initialize the sql socket */
1139 sqlsocket = sql_get_socket(inst);
1140 if(sqlsocket == NULL)
1141 return RLM_MODULE_FAIL;
1143 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1144 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1145 sql_release_socket(inst, sqlsocket);
1146 return RLM_MODULE_FAIL;
1149 ret = rlm_sql_fetch_row(sqlsocket, inst);
1152 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1153 sql_release_socket(inst, sqlsocket);
1154 return RLM_MODULE_FAIL;
1157 row = sqlsocket->row;
1159 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1160 sql_release_socket(inst, sqlsocket);
1161 return RLM_MODULE_FAIL;
1164 request->simul_count = atoi(row[0]);
1165 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1167 if(request->simul_count < request->simul_max) {
1168 sql_release_socket(inst, sqlsocket);
1169 return RLM_MODULE_OK;
1172 /* Looks like too many sessions, so lets start verifying them */
1174 if (inst->config->simul_verify_query[0] == 0) {
1175 /* No verify query defined, so skip verify step and rely on count query only */
1176 sql_release_socket(inst, sqlsocket);
1177 return RLM_MODULE_OK;
1180 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, NULL);
1181 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1182 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Database query error", inst->config->xlat_name);
1183 sql_release_socket(inst, sqlsocket);
1184 return RLM_MODULE_FAIL;
1188 * Setup some stuff, like for MPP detection.
1190 request->simul_count = 0;
1192 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1194 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1195 call_num = vp->strvalue;
1198 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1199 row = sqlsocket->row;
1203 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1204 sql_release_socket(inst, sqlsocket);
1205 DEBUG("rlm_sql (%s): Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1206 return RLM_MODULE_FAIL;
1209 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1210 sql_release_socket(inst, sqlsocket);
1211 DEBUG("rlm_sql (%s): Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1212 return RLM_MODULE_FAIL;
1215 nas_addr = inet_addr(row[3]);
1217 nas_port = atoi(row[4]);
1219 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1222 * Failed to check the terminal server for
1223 * duplicate logins: Return an error.
1226 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1227 sql_release_socket(inst, sqlsocket);
1228 DEBUG("rlm_sql (%s) rad_check_ts() failed.",
1229 inst->config->xlat_name);
1230 return RLM_MODULE_FAIL;
1234 ++request->simul_count;
1237 * Does it look like a MPP attempt?
1239 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1240 request->simul_mpp = 2;
1241 else if (row[6] && call_num &&
1242 !strncmp(row[6],call_num,16))
1243 request->simul_mpp = 2;
1247 * Stale record - zap it.
1249 uint32_t framed_addr = 0;
1253 framed_addr = inet_addr(row[5]);
1255 if (strcmp(row[7],"SLIP") == 0)
1258 session_zap(request,
1259 nas_addr,nas_port,row[2],row[1],
1260 framed_addr, proto);
1264 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1265 sql_release_socket(inst, sqlsocket);
1267 /* The Auth module apparently looks at request->simul_count, not the return value
1268 of this module when deciding to deny a call for too many sessions */
1269 return RLM_MODULE_OK;
1274 * Execute postauth_query after authentication
1276 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1277 SQLSOCK *sqlsocket = NULL;
1278 SQL_INST *inst = instance;
1279 char querystr[MAX_QUERY_LEN];
1280 char sqlusername[2*MAX_STRING_LEN+10];
1282 DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name);
1284 if(sql_set_user(inst, request, sqlusername, 0) <0)
1285 return RLM_MODULE_FAIL;
1287 /* If postauth_query is not defined, we stop here */
1288 if (inst->config->postauth_query[0] == '\0')
1289 return RLM_MODULE_NOOP;
1291 /* Expand variables in the query */
1292 memset(querystr, 0, MAX_QUERY_LEN);
1293 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1294 request, sql_escape_func);
1295 query_log(request, inst, querystr);
1296 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1297 inst->config->xlat_name, querystr);
1299 /* Initialize the sql socket */
1300 sqlsocket = sql_get_socket(inst);
1301 if (sqlsocket == NULL)
1302 return RLM_MODULE_FAIL;
1304 /* Process the query */
1305 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1306 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1307 inst->config->xlat_name,
1308 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1309 sql_release_socket(inst, sqlsocket);
1310 return RLM_MODULE_FAIL;
1312 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1314 sql_release_socket(inst, sqlsocket);
1315 return RLM_MODULE_OK;
1318 /* globally exported name */
1319 module_t rlm_sql = {
1321 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1322 rlm_sql_init, /* initialization */
1323 rlm_sql_instantiate, /* instantiation */
1325 NULL, /* authentication */
1326 rlm_sql_authorize, /* authorization */
1327 NULL, /* preaccounting */
1328 rlm_sql_accounting, /* accounting */
1329 rlm_sql_checksimul, /* checksimul */
1330 NULL, /* pre-proxy */
1331 NULL, /* post-proxy */
1332 rlm_sql_postauth /* post-auth */
1334 rlm_sql_detach, /* detach */
1335 rlm_sql_destroy, /* destroy */