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 {"acct_table", PW_TYPE_STRING_PTR,
67 offsetof(SQL_CONFIG,sql_acct_table), NULL, "radacct"},
68 {"acct_table2", PW_TYPE_STRING_PTR,
69 offsetof(SQL_CONFIG,sql_acct_table2), NULL, "radacct"},
70 {"authcheck_table", PW_TYPE_STRING_PTR,
71 offsetof(SQL_CONFIG,sql_authcheck_table), NULL, "radcheck"},
72 {"authreply_table", PW_TYPE_STRING_PTR,
73 offsetof(SQL_CONFIG,sql_authreply_table), NULL, "radreply"},
74 {"groupcheck_table", PW_TYPE_STRING_PTR,
75 offsetof(SQL_CONFIG,sql_groupcheck_table), NULL, "radgroupcheck"},
76 {"groupreply_table", PW_TYPE_STRING_PTR,
77 offsetof(SQL_CONFIG,sql_groupreply_table), NULL, "radgroupreply"},
78 {"usergroup_table", PW_TYPE_STRING_PTR,
79 offsetof(SQL_CONFIG,sql_usergroup_table), NULL, "usergroup"},
80 {"nas_table", PW_TYPE_STRING_PTR,
81 offsetof(SQL_CONFIG,sql_nas_table), NULL, "nas"},
82 {"dict_table", PW_TYPE_STRING_PTR,
83 offsetof(SQL_CONFIG,sql_dict_table), NULL, "dictionary"},
84 {"sqltrace", PW_TYPE_BOOLEAN,
85 offsetof(SQL_CONFIG,sqltrace), NULL, "no"},
86 {"sqltracefile", PW_TYPE_STRING_PTR,
87 offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE},
88 {"readclients", PW_TYPE_BOOLEAN,
89 offsetof(SQL_CONFIG,do_clients), NULL, "no"},
90 {"deletestalesessions", PW_TYPE_BOOLEAN,
91 offsetof(SQL_CONFIG,deletestalesessions), NULL, "no"},
92 {"num_sql_socks", PW_TYPE_INTEGER,
93 offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"},
94 {"sql_user_name", PW_TYPE_STRING_PTR,
95 offsetof(SQL_CONFIG,query_user), NULL, ""},
96 {"default_user_profile", PW_TYPE_STRING_PTR,
97 offsetof(SQL_CONFIG,default_profile), NULL, ""},
98 {"query_on_not_found", PW_TYPE_BOOLEAN,
99 offsetof(SQL_CONFIG,query_on_not_found), NULL, "no"},
100 {"authorize_check_query", PW_TYPE_STRING_PTR,
101 offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
102 {"authorize_reply_query", PW_TYPE_STRING_PTR,
103 offsetof(SQL_CONFIG,authorize_reply_query), NULL, ""},
104 {"authorize_group_check_query", PW_TYPE_STRING_PTR,
105 offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
106 {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
107 offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
108 {"accounting_onoff_query", PW_TYPE_STRING_PTR,
109 offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
110 {"accounting_update_query", PW_TYPE_STRING_PTR,
111 offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
112 {"accounting_update_query_alt", PW_TYPE_STRING_PTR,
113 offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""},
114 {"accounting_start_query", PW_TYPE_STRING_PTR,
115 offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
116 {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
117 offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
118 {"accounting_stop_query", PW_TYPE_STRING_PTR,
119 offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
120 {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
121 offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
122 {"group_membership_query", PW_TYPE_STRING_PTR,
123 offsetof(SQL_CONFIG,groupmemb_query), NULL, ""},
124 {"connect_failure_retry_delay", PW_TYPE_INTEGER,
125 offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
126 {"simul_count_query", PW_TYPE_STRING_PTR,
127 offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
128 {"simul_verify_query", PW_TYPE_STRING_PTR,
129 offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
130 {"postauth_table", PW_TYPE_STRING_PTR,
131 offsetof(SQL_CONFIG,sql_postauth_table), NULL, "radpostauth"},
132 {"postauth_query", PW_TYPE_STRING_PTR,
133 offsetof(SQL_CONFIG,postauth_query), NULL, ""},
134 {"safe-characters", PW_TYPE_STRING_PTR,
135 offsetof(SQL_CONFIG,allowed_chars), NULL,
136 "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
138 {NULL, -1, 0, NULL, NULL}
141 /***********************************************************************
142 * start of main routines
143 ***********************************************************************/
144 static int rlm_sql_init(void) {
148 * We should put the sqlsocket array here once
149 * the module code is reworked to not unload
150 * modules on HUP. This way we can have
151 * persistant connections. -jcarneal
159 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username);
160 static int generate_sql_clients(SQL_INST *inst);
163 * sql xlat function. Right now only SELECTs are supported. Only
164 * the first element of the SELECT result will be used.
166 static int sql_xlat(void *instance, REQUEST *request,
167 char *fmt, char *out, int freespace,
168 RADIUS_ESCAPE_STRING func)
172 SQL_INST *inst = instance;
173 char querystr[MAX_QUERY_LEN];
174 char sqlusername[2 * MAX_STRING_LEN + 10];
177 DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name);
179 * Add SQL-User-Name attribute just in case it is needed
180 * We could search the string fmt for SQL-User-Name to see if this is
183 sql_set_user(inst, request, sqlusername, NULL);
185 * Do an xlat on the provided string (nice recursive operation).
187 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, func)) {
188 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
189 inst->config->xlat_name);
193 query_log(request, inst,querystr);
194 sqlsocket = sql_get_socket(inst);
195 if (sqlsocket == NULL)
197 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
198 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
199 inst->config->xlat_name,querystr,
200 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
201 sql_release_socket(inst,sqlsocket);
205 ret = rlm_sql_fetch_row(sqlsocket, inst);
208 DEBUG("rlm_sql (%s): SQL query did not succeed",
209 inst->config->xlat_name);
210 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
211 sql_release_socket(inst,sqlsocket);
215 row = sqlsocket->row;
217 DEBUG("rlm_sql (%s): SQL query did not return any results",
218 inst->config->xlat_name);
219 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
220 sql_release_socket(inst,sqlsocket);
225 DEBUG("rlm_sql (%s): row[0] returned NULL",
226 inst->config->xlat_name);
227 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
228 sql_release_socket(inst,sqlsocket);
231 ret = strlen(row[0]);
232 if (ret > freespace){
233 DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space",
234 inst->config->xlat_name);
235 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
236 sql_release_socket(inst,sqlsocket);
240 strncpy(out,row[0],ret);
242 DEBUG("rlm_sql (%s): - sql_xlat finished",
243 inst->config->xlat_name);
245 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
246 sql_release_socket(inst,sqlsocket);
250 static int generate_sql_clients(SQL_INST *inst)
254 char querystr[MAX_QUERY_LEN];
259 DEBUG("rlm_sql (%s): - generate_sql_clients",inst->config->xlat_name);
261 if (inst->config->sql_nas_table == NULL){
262 radlog(L_ERR, "rlm_sql (%s): sql_nas_table is NULL.",inst->config->xlat_name);
265 snprintf(querystr,MAX_QUERY_LEN - 1,"SELECT * FROM %s",inst->config->sql_nas_table);
267 DEBUG("rlm_sql (%s): Query: %s",inst->config->xlat_name,querystr);
268 sqlsocket = sql_get_socket(inst);
269 if (sqlsocket == NULL)
271 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
272 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
273 inst->config->xlat_name,querystr,
274 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
275 sql_release_socket(inst,sqlsocket);
279 while(rlm_sql_fetch_row(sqlsocket, inst) == 0) {
281 row = sqlsocket->row;
286 * Row1 Row2 Row3 Row4 Row5 Row6 Row7 Row8
288 * id nasname shortname type ports secret community description
293 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
297 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
300 if (strlen(row[1]) >= sizeof(c->longname)){
301 radlog(L_ERR, "rlm_sql (%s): nasname of length %d is greater than the allowed maximum of %d",
302 inst->config->xlat_name,strlen(row[1]),sizeof(c->longname) - 1);
307 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
310 if (strlen(row[2]) >= sizeof(c->shortname)){
311 radlog(L_ERR, "rlm_sql (%s): shortname of length %d is greater than the allowed maximum of %d",
312 inst->config->xlat_name,strlen(row[2]),sizeof(c->shortname) - 1);
315 if (row[3] && strlen(row[3]) >= sizeof(c->nastype)){
316 radlog(L_ERR, "rlm_sql (%s): nastype of length %d is greater than the allowed maximum of %d",
317 inst->config->xlat_name,strlen(row[3]),sizeof(c->nastype) - 1);
321 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
324 if (strlen(row[5]) >= sizeof(c->secret)){
325 radlog(L_ERR, "rlm_sql (%s): secret of length %d is greater than the allowed maximum of %d",
326 inst->config->xlat_name,strlen(row[5]),sizeof(c->secret) - 1);
330 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
331 row[1],row[2],row[5]);
333 c = rad_malloc(sizeof(RADCLIENT));
334 memset(c, 0, sizeof(RADCLIENT));
337 netmask = strchr(row[1], '/');
346 mask_length = atoi(netmask + 1);
347 if ((mask_length < 0) || (mask_length > 32)) {
348 radlog(L_ERR, "rlm_sql (%s): Invalid value '%s' for IP network mask for nasname %s.",
349 inst->config->xlat_name, netmask + 1,row[1]);
354 if (mask_length == 0) {
357 c->netmask = ~0 << (32 - mask_length);
361 c->netmask = htonl(c->netmask);
364 c->ipaddr = ip_getaddr(row[1]);
365 if (c->ipaddr == INADDR_NONE) {
366 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s",
367 inst->config->xlat_name, row[1]);
373 * Update the client name again...
377 c->ipaddr &= c->netmask;
378 strcpy(c->longname, row[1]);
380 ip_hostname(c->longname, sizeof(c->longname),
384 strcpy((char *)c->secret, row[5]);
385 strcpy(c->shortname, row[2]);
387 strcpy(c->nastype, row[3]);
389 DEBUG("rlm_sql (%s): Adding client %s (%s) to clients list",inst->config->xlat_name,
390 c->longname,c->shortname);
392 c->next = mainconfig.clients;
393 mainconfig.clients = c;
396 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
397 sql_release_socket(inst, sqlsocket);
404 * Translate the SQL queries.
406 static int sql_escape_func(char *out, int outlen, const char *in)
412 * Only one byte left.
419 * Non-printable characters get replaced with their
420 * mime-encoded equivalents.
423 strchr(allowed_chars, *in) == NULL) {
424 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
433 * Else it's a nice character.
446 * Set the SQl user name.
448 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
451 char tmpuser[MAX_STRING_LEN];
456 /* Remove any user attr we added previously */
457 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
459 if (username != NULL) {
460 strNcpy(tmpuser, username, MAX_STRING_LEN);
461 } else if (strlen(inst->config->query_user)) {
462 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
468 strNcpy(sqlusername, tmpuser, sizeof(tmpuser));
469 DEBUG2("rlm_sql (%s): sql_set_user escaped user --> '%s'",
470 inst->config->xlat_name, sqlusername);
471 vp = pairmake("SQL-User-Name", sqlusername, 0);
473 radlog(L_ERR, "%s", librad_errstr);
477 pairadd(&request->packet->vps, vp);
484 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
485 * with the group memberships reciding in sql
486 * The group membership query should only return one element which is the username. The returned
487 * username will then be checked with the passed check string.
490 static int sql_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
491 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
495 SQL_INST *inst = instance;
496 char querystr[MAX_QUERY_LEN];
497 char sqlusername[2 * MAX_STRING_LEN + 10];
499 check_pairs = check_pairs;
500 reply_pairs = reply_pairs;
502 DEBUG("rlm_sql (%s): - sql_groupcmp", inst->config->xlat_name);
503 if (!check || !check->strvalue || !check->length){
504 DEBUG("rlm_sql (%s): sql_groupcmp: Illegal group name",
505 inst->config->xlat_name);
509 DEBUG("rlm_sql (%s): sql_groupcmp: NULL request",
510 inst->config->xlat_name);
513 if (inst->config->groupmemb_query[0] == 0)
516 * Set, escape, and check the user attr here
518 if (sql_set_user(inst, req, sqlusername, 0) < 0)
520 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, req, NULL)){
521 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
522 inst->config->xlat_name);
523 /* Remove the username we (maybe) added above */
524 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
527 /* Remove the username we (maybe) added above */
528 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
530 sqlsocket = sql_get_socket(inst);
531 if (sqlsocket == NULL)
533 if ((inst->module->sql_select_query)(sqlsocket,inst->config,querystr) <0){
534 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
535 inst->config->xlat_name,querystr,
536 (char *)(inst->module->sql_error)(sqlsocket,inst->config));
537 sql_release_socket(inst,sqlsocket);
540 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
541 row = sqlsocket->row;
545 DEBUG("rlm_sql (%s): row[0] returned NULL",
546 inst->config->xlat_name);
547 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
548 sql_release_socket(inst, sqlsocket);
551 if (strcmp(row[0],check->strvalue) == 0){
552 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User belongs in group %s",
553 inst->config->xlat_name,
554 (char *)check->strvalue);
555 (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst, sqlsocket);
560 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
561 sql_release_socket(inst,sqlsocket);
563 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User does not belong in group %s",
564 inst->config->xlat_name, (char *)check->strvalue);
570 static int rlm_sql_detach(void *instance)
572 SQL_INST *inst = instance;
578 if (inst->config->xlat_name) {
579 xlat_unregister(inst->config->xlat_name,sql_xlat);
580 free(inst->config->xlat_name);
583 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
589 * Free up dynamically allocated string pointers.
591 for (i = 0; module_config[i].name != NULL; i++) {
593 if (module_config[i].type != PW_TYPE_STRING_PTR) {
598 * Treat 'config' as an opaque array of bytes,
599 * and take the offset into it. There's a
600 * (char*) pointer at that offset, and we want
603 p = (char **) (((char *)inst->config) + module_config[i].offset);
604 if (!*p) { /* nothing allocated */
617 * FIXME: Call the modules 'destroy' function?
619 lt_dlclose(inst->handle); /* ignore any errors */
626 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
631 inst = rad_malloc(sizeof(SQL_INST));
632 memset(inst, 0, sizeof(SQL_INST));
634 inst->config = rad_malloc(sizeof(SQL_CONFIG));
635 memset(inst->config, 0, sizeof(SQL_CONFIG));
638 * If the configuration parameters can't be parsed, then
641 if (cf_section_parse(conf, inst->config, module_config) < 0) {
642 rlm_sql_detach(inst);
646 xlat_name = cf_section_name2(conf);
647 if (xlat_name == NULL)
648 xlat_name = cf_section_name1(conf);
650 inst->config->xlat_name = strdup(xlat_name);
651 xlat_register(xlat_name, sql_xlat, inst);
654 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
655 radlog(L_ERR | L_CONS, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
656 inst->config->xlat_name, MAX_SQL_SOCKS);
657 rlm_sql_detach(inst);
662 * Sanity check for crazy people.
664 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
665 radlog(L_ERR, "rlm_sql (%s): \"%s\" is NOT an SQL driver!",
666 inst->config->xlat_name, inst->config->sql_driver);
667 rlm_sql_detach(inst);
671 inst->handle = lt_dlopenext(inst->config->sql_driver);
672 if (inst->handle == NULL) {
673 radlog(L_ERR, "rlm_sql (%s): Could not link driver %s: %s",
674 inst->config->xlat_name, inst->config->sql_driver,
676 radlog(L_ERR, "rlm_sql (%s): Make sure it (and all its dependent libraries!) are in the search path of your system's ld.",
677 inst->config->xlat_name);
678 rlm_sql_detach(inst);
682 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
684 radlog(L_ERR, "rlm_sql (%s): Could not link symbol %s: %s",
685 inst->config->xlat_name, inst->config->sql_driver,
687 rlm_sql_detach(inst);
691 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
692 inst->config->xlat_name, inst->config->sql_driver,
694 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
695 inst->config->xlat_name, inst->config->sql_login,
696 inst->config->sql_server, inst->config->sql_port,
697 inst->config->sql_db);
699 if (sql_init_socketpool(inst) < 0) {
700 rlm_sql_detach(inst);
703 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
705 if (inst->config->do_clients){
706 if (generate_sql_clients(inst) == -1){
707 radlog(L_ERR, "rlm_sql (%s): generate_sql_clients() returned error",inst->config->xlat_name);
708 rlm_sql_detach(inst);
712 allowed_chars = inst->config->allowed_chars;
716 return RLM_MODULE_OK;
719 static int rlm_sql_destroy(void)
725 static int rlm_sql_authorize(void *instance, REQUEST * request)
727 VALUE_PAIR *check_tmp = NULL;
728 VALUE_PAIR *reply_tmp = NULL;
729 VALUE_PAIR *user_profile = NULL;
732 SQL_INST *inst = instance;
733 char querystr[MAX_QUERY_LEN];
735 /* sqlusername holds the sql escaped username. The original
736 * username is at most MAX_STRING_LEN chars long and
737 * *sql_escape_string doubles its length in the worst case.
738 * Throw in an extra 10 to account for trailing NULs and to have
739 * a safety margin. */
740 char sqlusername[2 * MAX_STRING_LEN + 10];
743 * They MUST have a user name to do SQL authorization.
745 if ((request->username == NULL) ||
746 (request->username->length == 0)) {
747 radlog(L_ERR, "rlm_sql (%s): zero length username not permitted\n", inst->config->xlat_name);
748 return RLM_MODULE_INVALID;
753 * After this point, ALL 'return's MUST release the SQL socket!
758 * Set, escape, and check the user attr here
760 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
761 return RLM_MODULE_FAIL;
762 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func);
764 sqlsocket = sql_get_socket(inst);
765 if (sqlsocket == NULL) {
766 /* Remove the username we (maybe) added above */
767 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
768 return(RLM_MODULE_FAIL);
771 found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_USERDATA);
773 * Find the entry for the user.
776 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func);
777 sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
778 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func);
779 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_USERDATA);
780 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func);
781 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
782 } else if (found < 0) {
783 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
784 inst->config->xlat_name);
785 sql_release_socket(inst, sqlsocket);
786 /* Remove the username we (maybe) added above */
787 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
788 return RLM_MODULE_FAIL;
791 radlog(L_DBG, "rlm_sql (%s): User %s not found in radcheck",
792 inst->config->xlat_name, sqlusername);
795 * We didn't find the user in radcheck, so we try looking
796 * for radgroupcheck entry
798 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func);
799 found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
800 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func);
801 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
804 radlog(L_DBG, "rlm_sql (%s): User %s not found in radgroupcheck",
805 inst->config->xlat_name, sqlusername);
806 if (found || (!found && inst->config->query_on_not_found)){
810 * Check for a default_profile or for a User-Profile.
812 user_profile = pairfind(request->config_items, PW_USER_PROFILE);
813 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
814 char *profile = inst->config->default_profile;
816 if (user_profile != NULL)
817 profile = user_profile->strvalue;
818 if (profile && strlen(profile)){
819 radlog(L_DBG, "rlm_sql (%s): Checking profile %s",
820 inst->config->xlat_name, profile);
821 if (sql_set_user(inst, request, sqlusername, profile) < 0) {
822 return RLM_MODULE_FAIL;
824 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query,
825 request, sql_escape_func);
826 def_found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
829 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query,
830 request, sql_escape_func);
831 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
836 radlog(L_DBG, "rlm_sql (%s): User not found",
837 inst->config->xlat_name);
838 sql_release_socket(inst, sqlsocket);
839 /* Remove the username we (maybe) added above */
840 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
841 return RLM_MODULE_NOTFOUND;
845 * Uncomment these lines for debugging
846 * Recompile, and run 'radiusd -X'
850 DEBUG2("rlm_sql: check items");
851 vp_listdebug(check_tmp);
852 DEBUG2("rlm_sql: reply items");
853 vp_listdebug(reply_tmp);
856 if (paircmp(request, request->packet->vps, check_tmp, &reply_tmp) != 0) {
857 radlog(L_INFO, "rlm_sql (%s): No matching entry in the database for request from user [%s]",
858 inst->config->xlat_name, sqlusername);
859 /* Remove the username we (maybe) added above */
860 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
861 sql_release_socket(inst, sqlsocket);
862 pairfree(&reply_tmp);
863 pairfree(&check_tmp);
864 return RLM_MODULE_NOTFOUND;
867 pairxlatmove(request, &request->reply->vps, &reply_tmp);
868 pairxlatmove(request, &request->config_items, &check_tmp);
869 pairfree(&reply_tmp);
870 pairfree(&check_tmp);
872 /* Remove the username we (maybe) added above */
873 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
874 sql_release_socket(inst, sqlsocket);
876 return RLM_MODULE_OK;
880 * Accounting: save the account data to our sql table
882 static int rlm_sql_accounting(void *instance, REQUEST * request) {
884 SQLSOCK *sqlsocket = NULL;
886 SQL_INST *inst = instance;
887 int ret = RLM_MODULE_OK;
889 int acctstatustype = 0;
890 char querystr[MAX_QUERY_LEN];
891 char logstr[MAX_QUERY_LEN];
892 char sqlusername[2 * MAX_STRING_LEN + 10];
894 #ifdef CISCO_ACCOUNTING_HACK
895 int acctsessiontime = 0;
898 memset(querystr, 0, MAX_QUERY_LEN);
901 * Find the Acct Status Type
903 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
904 acctstatustype = pair->lvalue;
906 radius_xlat(logstr, sizeof(logstr), "rlm_sql: packet has no account status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, sql_escape_func);
907 radlog(L_ERR, logstr);
908 return RLM_MODULE_INVALID;
911 switch (acctstatustype) {
913 * The Terminal server informed us that it was rebooted
914 * STOP all records from this NAS
916 case PW_STATUS_ACCOUNTING_ON:
917 case PW_STATUS_ACCOUNTING_OFF:
918 radlog(L_INFO, "rlm_sql (%s): received Acct On/Off packet", inst->config->xlat_name);
919 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
920 query_log(request, inst, querystr);
922 sqlsocket = sql_get_socket(inst);
923 if (sqlsocket == NULL)
924 return(RLM_MODULE_FAIL);
925 if (*querystr) { /* non-empty query */
926 if (rlm_sql_query(sqlsocket, inst, querystr)) {
927 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting for Acct On/Off packet - %s",
928 inst->config->xlat_name,
929 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
930 ret = RLM_MODULE_FAIL;
932 (inst->module->sql_finish_query)(sqlsocket, inst->config);
938 * Got an update accounting packet
940 case PW_STATUS_ALIVE:
943 * Set, escape, and check the user attr here
945 sql_set_user(inst, request, sqlusername, NULL);
947 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
948 query_log(request, inst, querystr);
950 sqlsocket = sql_get_socket(inst);
951 if (sqlsocket == NULL)
952 return(RLM_MODULE_FAIL);
953 if (*querystr) { /* non-empty query */
954 if (rlm_sql_query(sqlsocket, inst, querystr)) {
955 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting ALIVE record - %s",
956 inst->config->xlat_name,
957 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
958 ret = RLM_MODULE_FAIL;
961 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
962 if (numaffected < 1) {
965 * If our update above didn't match anything
966 * we assume it's because we haven't seen a
967 * matching Start record. So we have to
968 * insert this update rather than do an update
970 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
971 query_log(request, inst, querystr);
972 if (*querystr) { /* non-empty query */
973 if (rlm_sql_query(sqlsocket, inst, querystr)) {
974 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting ALIVE record - %s",
975 inst->config->xlat_name,
976 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
977 ret = RLM_MODULE_FAIL;
979 (inst->module->sql_finish_query)(sqlsocket, inst->config);
983 (inst->module->sql_finish_query)(sqlsocket, inst->config);
988 * Got accounting start packet
990 case PW_STATUS_START:
993 * Set, escape, and check the user attr here
995 sql_set_user(inst, request, sqlusername, NULL);
997 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
998 query_log(request, inst, querystr);
1000 sqlsocket = sql_get_socket(inst);
1001 if (sqlsocket == NULL)
1002 return(RLM_MODULE_FAIL);
1003 if (*querystr) { /* non-empty query */
1004 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1005 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting START record - %s",
1006 inst->config->xlat_name,
1007 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1010 * We failed the insert above. It's probably because
1011 * the stop record came before the start. We try
1012 * our alternate query now (typically an UPDATE)
1014 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1015 query_log(request, inst, querystr);
1017 if (*querystr) { /* non-empty query */
1018 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1019 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting START record - %s",
1020 inst->config->xlat_name,
1021 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1022 ret = RLM_MODULE_FAIL;
1024 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1027 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1032 * Got accounting stop packet
1034 case PW_STATUS_STOP:
1037 * Set, escape, and check the user attr here
1039 sql_set_user(inst, request, sqlusername, NULL);
1041 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1042 query_log(request, inst, querystr);
1044 sqlsocket = sql_get_socket(inst);
1045 if (sqlsocket == NULL)
1046 return(RLM_MODULE_FAIL);
1047 if (*querystr) { /* non-empty query */
1048 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1049 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting STOP record - %s",
1050 inst->config->xlat_name,
1051 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1052 ret = RLM_MODULE_FAIL;
1055 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1056 if (numaffected < 1) {
1058 * If our update above didn't match anything
1059 * we assume it's because we haven't seen a
1060 * matching Start record. So we have to
1061 * insert this stop rather than do an update
1063 #ifdef CISCO_ACCOUNTING_HACK
1065 * If stop but zero session length AND no previous
1066 * session found, drop it as in invalid packet
1067 * This is to fix CISCO's aaa from filling our
1068 * table with bogus crap
1070 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
1071 acctsessiontime = pair->lvalue;
1073 if (acctsessiontime <= 0) {
1074 radius_xlat(logstr, sizeof(logstr), "rlm_sql: Stop packet with zero session length. (user '%{User-Name}', nas '%{NAS-IP-Address}')", request, sql_escape_func);
1075 radlog(L_ERR, logstr);
1076 sql_release_socket(inst, sqlsocket);
1077 ret = RLM_MODULE_NOOP;
1081 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1082 query_log(request, inst, querystr);
1084 if (*querystr) { /* non-empty query */
1085 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1086 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting STOP record - %s",
1087 inst->config->xlat_name,
1088 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1089 ret = RLM_MODULE_FAIL;
1091 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1095 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1100 * Anything else is ignored.
1103 radlog(L_INFO, "rlm_sql (%s): Unsupported Acct-Status-Type = %d", inst->config->xlat_name, acctstatustype);
1104 return RLM_MODULE_NOOP;
1109 sql_release_socket(inst, sqlsocket);
1116 * See if a user is already logged in. Sets request->simul_count to the
1117 * current session count for this user.
1119 * Check twice. If on the first pass the user exceeds his
1120 * max. number of logins, do a second pass and validate all
1121 * logins by querying the terminal server (using eg. SNMP).
1124 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1126 SQL_INST *inst = instance;
1128 char querystr[MAX_QUERY_LEN];
1129 char sqlusername[2*MAX_STRING_LEN+10];
1132 char *call_num = NULL;
1135 uint32_t nas_addr = 0;
1138 /* If simul_count_query is not defined, we don't do any checking */
1139 if (inst->config->simul_count_query[0] == 0) {
1140 return RLM_MODULE_NOOP;
1143 if((request->username == NULL) || (request->username->length == 0)) {
1144 radlog(L_ERR, "rlm_sql (%s): Zero Length username not permitted\n", inst->config->xlat_name);
1145 return RLM_MODULE_INVALID;
1149 if(sql_set_user(inst, request, sqlusername, 0) <0)
1150 return RLM_MODULE_FAIL;
1152 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, NULL);
1154 /* initialize the sql socket */
1155 sqlsocket = sql_get_socket(inst);
1156 if(sqlsocket == NULL)
1157 return RLM_MODULE_FAIL;
1159 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1160 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1161 sql_release_socket(inst, sqlsocket);
1162 return RLM_MODULE_FAIL;
1165 ret = rlm_sql_fetch_row(sqlsocket, inst);
1168 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1169 sql_release_socket(inst, sqlsocket);
1170 return RLM_MODULE_FAIL;
1173 row = sqlsocket->row;
1175 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1176 sql_release_socket(inst, sqlsocket);
1177 return RLM_MODULE_FAIL;
1180 request->simul_count = atoi(row[0]);
1181 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1183 if(request->simul_count < request->simul_max) {
1184 sql_release_socket(inst, sqlsocket);
1185 return RLM_MODULE_OK;
1188 /* Looks like too many sessions, so lets start verifying them */
1190 if (inst->config->simul_verify_query[0] == 0) {
1191 /* No verify query defined, so skip verify step and rely on count query only */
1192 sql_release_socket(inst, sqlsocket);
1193 return RLM_MODULE_OK;
1196 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, NULL);
1197 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1198 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Database query error", inst->config->xlat_name);
1199 sql_release_socket(inst, sqlsocket);
1200 return RLM_MODULE_FAIL;
1204 * Setup some stuff, like for MPP detection.
1206 request->simul_count = 0;
1208 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1210 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1211 call_num = vp->strvalue;
1214 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1215 row = sqlsocket->row;
1219 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1220 sql_release_socket(inst, sqlsocket);
1221 DEBUG("rlm_sql (%s): Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1222 return RLM_MODULE_FAIL;
1225 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1226 sql_release_socket(inst, sqlsocket);
1227 DEBUG("rlm_sql (%s): Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1228 return RLM_MODULE_FAIL;
1231 nas_addr = inet_addr(row[3]);
1233 nas_port = atoi(row[4]);
1235 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1238 * Failed to check the terminal server for
1239 * duplicate logins: Return an error.
1242 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1243 sql_release_socket(inst, sqlsocket);
1244 DEBUG("rlm_sql (%s) rad_check_ts() failed.",
1245 inst->config->xlat_name);
1246 return RLM_MODULE_FAIL;
1250 ++request->simul_count;
1253 * Does it look like a MPP attempt?
1255 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1256 request->simul_mpp = 2;
1257 else if (row[6] && call_num &&
1258 !strncmp(row[6],call_num,16))
1259 request->simul_mpp = 2;
1263 * Stale record - zap it.
1265 uint32_t framed_addr = 0;
1269 framed_addr = inet_addr(row[5]);
1271 if (strcmp(row[7],"SLIP") == 0)
1274 session_zap(request,
1275 nas_addr,nas_port,row[2],row[1],
1276 framed_addr, proto);
1280 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1281 sql_release_socket(inst, sqlsocket);
1283 /* The Auth module apparently looks at request->simul_count, not the return value
1284 of this module when deciding to deny a call for too many sessions */
1285 return RLM_MODULE_OK;
1290 * Execute postauth_query after authentication
1292 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1293 SQLSOCK *sqlsocket = NULL;
1294 SQL_INST *inst = instance;
1295 char querystr[MAX_QUERY_LEN];
1296 char sqlusername[2*MAX_STRING_LEN+10];
1298 DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name);
1300 if(sql_set_user(inst, request, sqlusername, 0) <0)
1301 return RLM_MODULE_FAIL;
1303 /* If postauth_query is not defined, we stop here */
1304 if (inst->config->postauth_query[0] == '\0')
1305 return RLM_MODULE_NOOP;
1307 /* Expand variables in the query */
1308 memset(querystr, 0, MAX_QUERY_LEN);
1309 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1310 request, sql_escape_func);
1311 query_log(request, inst, querystr);
1312 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1313 inst->config->xlat_name, querystr);
1315 /* Initialize the sql socket */
1316 sqlsocket = sql_get_socket(inst);
1317 if (sqlsocket == NULL)
1318 return RLM_MODULE_FAIL;
1320 /* Process the query */
1321 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1322 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1323 inst->config->xlat_name,
1324 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1325 sql_release_socket(inst, sqlsocket);
1326 return RLM_MODULE_FAIL;
1328 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1330 sql_release_socket(inst, sqlsocket);
1331 return RLM_MODULE_OK;
1334 /* globally exported name */
1335 module_t rlm_sql = {
1337 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1338 rlm_sql_init, /* initialization */
1339 rlm_sql_instantiate, /* instantiation */
1341 NULL, /* authentication */
1342 rlm_sql_authorize, /* authorization */
1343 NULL, /* preaccounting */
1344 rlm_sql_accounting, /* accounting */
1345 rlm_sql_checksimul, /* checksimul */
1346 NULL, /* pre-proxy */
1347 NULL, /* post-proxy */
1348 rlm_sql_postauth /* post-auth */
1350 rlm_sql_detach, /* detach */
1351 rlm_sql_destroy, /* destroy */