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);
161 static int sql_escape_func(char *out, int outlen, const char *in);
164 * sql xlat function. Right now only SELECTs are supported. Only
165 * the first element of the SELECT result will be used.
167 static int sql_xlat(void *instance, REQUEST *request,
168 char *fmt, char *out, size_t freespace,
169 RADIUS_ESCAPE_STRING func)
173 SQL_INST *inst = instance;
174 char querystr[MAX_QUERY_LEN];
175 char sqlusername[MAX_STRING_LEN];
178 DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name);
180 * Add SQL-User-Name attribute just in case it is needed
181 * We could search the string fmt for SQL-User-Name to see if this is
184 sql_set_user(inst, request, sqlusername, NULL);
186 * Do an xlat on the provided string (nice recursive operation).
188 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
189 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
190 inst->config->xlat_name);
194 query_log(request, inst,querystr);
195 sqlsocket = sql_get_socket(inst);
196 if (sqlsocket == NULL)
198 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
199 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
200 inst->config->xlat_name,querystr,
201 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
202 sql_release_socket(inst,sqlsocket);
206 ret = rlm_sql_fetch_row(sqlsocket, inst);
209 DEBUG("rlm_sql (%s): SQL query did not succeed",
210 inst->config->xlat_name);
211 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
212 sql_release_socket(inst,sqlsocket);
216 row = sqlsocket->row;
218 DEBUG("rlm_sql (%s): SQL query did not return any results",
219 inst->config->xlat_name);
220 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
221 sql_release_socket(inst,sqlsocket);
226 DEBUG("rlm_sql (%s): row[0] returned NULL",
227 inst->config->xlat_name);
228 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
229 sql_release_socket(inst,sqlsocket);
232 ret = strlen(row[0]);
233 if (ret > freespace){
234 DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space",
235 inst->config->xlat_name);
236 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
237 sql_release_socket(inst,sqlsocket);
241 strncpy(out,row[0],ret);
243 DEBUG("rlm_sql (%s): - sql_xlat finished",
244 inst->config->xlat_name);
246 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
247 sql_release_socket(inst,sqlsocket);
251 static int generate_sql_clients(SQL_INST *inst)
255 char querystr[MAX_QUERY_LEN];
260 DEBUG("rlm_sql (%s): - generate_sql_clients",inst->config->xlat_name);
262 if (inst->config->sql_nas_table == NULL){
263 radlog(L_ERR, "rlm_sql (%s): sql_nas_table is NULL.",inst->config->xlat_name);
266 snprintf(querystr,MAX_QUERY_LEN - 1,"SELECT * FROM %s",inst->config->sql_nas_table);
268 DEBUG("rlm_sql (%s): Query: %s",inst->config->xlat_name,querystr);
269 sqlsocket = sql_get_socket(inst);
270 if (sqlsocket == NULL)
272 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
273 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
274 inst->config->xlat_name,querystr,
275 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
276 sql_release_socket(inst,sqlsocket);
280 while(rlm_sql_fetch_row(sqlsocket, inst) == 0) {
282 row = sqlsocket->row;
287 * Row1 Row2 Row3 Row4 Row5 Row6 Row7 Row8
289 * id nasname shortname type ports secret community description
294 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
298 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
301 if (strlen(row[1]) >= sizeof(c->longname)){
302 radlog(L_ERR, "rlm_sql (%s): nasname of length %d is greater than the allowed maximum of %d",
303 inst->config->xlat_name,strlen(row[1]),sizeof(c->longname) - 1);
308 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
311 if (strlen(row[2]) >= sizeof(c->shortname)){
312 radlog(L_ERR, "rlm_sql (%s): shortname of length %d is greater than the allowed maximum of %d",
313 inst->config->xlat_name,strlen(row[2]),sizeof(c->shortname) - 1);
316 if (row[3] && strlen(row[3]) >= sizeof(c->nastype)){
317 radlog(L_ERR, "rlm_sql (%s): nastype of length %d is greater than the allowed maximum of %d",
318 inst->config->xlat_name,strlen(row[3]),sizeof(c->nastype) - 1);
322 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
325 if (strlen(row[5]) >= sizeof(c->secret)){
326 radlog(L_ERR, "rlm_sql (%s): secret of length %d is greater than the allowed maximum of %d",
327 inst->config->xlat_name,strlen(row[5]),sizeof(c->secret) - 1);
331 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
332 row[1],row[2],row[5]);
334 c = rad_malloc(sizeof(RADCLIENT));
335 memset(c, 0, sizeof(RADCLIENT));
338 netmask = strchr(row[1], '/');
347 mask_length = atoi(netmask + 1);
348 if ((mask_length < 0) || (mask_length > 32)) {
349 radlog(L_ERR, "rlm_sql (%s): Invalid value '%s' for IP network mask for nasname %s.",
350 inst->config->xlat_name, netmask + 1,row[1]);
355 if (mask_length == 0) {
358 c->netmask = ~0 << (32 - mask_length);
362 c->netmask = htonl(c->netmask);
365 c->ipaddr = ip_getaddr(row[1]);
366 if (c->ipaddr == INADDR_NONE) {
367 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s",
368 inst->config->xlat_name, row[1]);
374 * Update the client name again...
378 c->ipaddr &= c->netmask;
379 strcpy(c->longname, row[1]);
381 ip_hostname(c->longname, sizeof(c->longname),
385 strcpy((char *)c->secret, row[5]);
386 strcpy(c->shortname, row[2]);
388 strcpy(c->nastype, row[3]);
390 DEBUG("rlm_sql (%s): Adding client %s (%s) to clients list",inst->config->xlat_name,
391 c->longname,c->shortname);
393 c->next = mainconfig.clients;
394 mainconfig.clients = c;
397 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
398 sql_release_socket(inst, sqlsocket);
405 * Translate the SQL queries.
407 static int sql_escape_func(char *out, int outlen, const char *in)
413 * Non-printable characters get replaced with their
414 * mime-encoded equivalents.
417 strchr(allowed_chars, *in) == NULL) {
419 * Only 3 or less bytes available.
425 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
434 * Only one byte left.
454 * Set the SQL user name.
456 * We don't call the escape function here. The resulting string
457 * will be escaped later in the queries xlat so we don't need to
458 * escape it twice. (it will make things wrong if we have an
459 * escape candidate character in the username)
461 static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
464 char tmpuser[MAX_STRING_LEN];
467 sqlusername[0]= '\0';
469 /* Remove any user attr we added previously */
470 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
472 if (username != NULL) {
473 strNcpy(tmpuser, username, MAX_STRING_LEN);
474 } else if (strlen(inst->config->query_user)) {
475 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
481 strNcpy(sqlusername, tmpuser, MAX_STRING_LEN);
482 DEBUG2("rlm_sql (%s): sql_set_user escaped user --> '%s'",
483 inst->config->xlat_name, sqlusername);
484 vp = pairmake("SQL-User-Name", sqlusername, 0);
486 radlog(L_ERR, "%s", librad_errstr);
490 pairadd(&request->packet->vps, vp);
497 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
498 * with the group memberships reciding in sql
499 * The group membership query should only return one element which is the username. The returned
500 * username will then be checked with the passed check string.
503 static int sql_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
504 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
508 SQL_INST *inst = instance;
509 char querystr[MAX_QUERY_LEN];
510 char sqlusername[MAX_STRING_LEN];
512 check_pairs = check_pairs;
513 reply_pairs = reply_pairs;
515 DEBUG("rlm_sql (%s): - sql_groupcmp", inst->config->xlat_name);
516 if (!check || !check->strvalue || !check->length){
517 DEBUG("rlm_sql (%s): sql_groupcmp: Illegal group name",
518 inst->config->xlat_name);
522 DEBUG("rlm_sql (%s): sql_groupcmp: NULL request",
523 inst->config->xlat_name);
526 if (inst->config->groupmemb_query[0] == 0)
529 * Set, escape, and check the user attr here
531 if (sql_set_user(inst, req, sqlusername, NULL) < 0)
533 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, req, sql_escape_func)){
534 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
535 inst->config->xlat_name);
536 /* Remove the username we (maybe) added above */
537 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
540 /* Remove the username we (maybe) added above */
541 pairdelete(&req->packet->vps, PW_SQL_USER_NAME);
543 sqlsocket = sql_get_socket(inst);
544 if (sqlsocket == NULL)
546 if ((inst->module->sql_select_query)(sqlsocket,inst->config,querystr) <0){
547 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
548 inst->config->xlat_name,querystr,
549 (char *)(inst->module->sql_error)(sqlsocket,inst->config));
550 sql_release_socket(inst,sqlsocket);
553 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
554 row = sqlsocket->row;
558 DEBUG("rlm_sql (%s): row[0] returned NULL",
559 inst->config->xlat_name);
560 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
561 sql_release_socket(inst, sqlsocket);
564 if (strcmp(row[0],check->strvalue) == 0){
565 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User belongs in group %s",
566 inst->config->xlat_name,
567 (char *)check->strvalue);
568 (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst, sqlsocket);
573 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
574 sql_release_socket(inst,sqlsocket);
576 DEBUG("rlm_sql (%s): - sql_groupcmp finished: User does not belong in group %s",
577 inst->config->xlat_name, (char *)check->strvalue);
583 static int rlm_sql_detach(void *instance)
585 SQL_INST *inst = instance;
591 if (inst->config->xlat_name) {
592 xlat_unregister(inst->config->xlat_name,sql_xlat);
593 free(inst->config->xlat_name);
596 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
602 * Free up dynamically allocated string pointers.
604 for (i = 0; module_config[i].name != NULL; i++) {
606 if (module_config[i].type != PW_TYPE_STRING_PTR) {
611 * Treat 'config' as an opaque array of bytes,
612 * and take the offset into it. There's a
613 * (char*) pointer at that offset, and we want
616 p = (char **) (((char *)inst->config) + module_config[i].offset);
617 if (!*p) { /* nothing allocated */
623 allowed_chars = NULL;
631 * FIXME: Call the modules 'destroy' function?
633 lt_dlclose(inst->handle); /* ignore any errors */
640 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
645 inst = rad_malloc(sizeof(SQL_INST));
646 memset(inst, 0, sizeof(SQL_INST));
648 inst->config = rad_malloc(sizeof(SQL_CONFIG));
649 memset(inst->config, 0, sizeof(SQL_CONFIG));
652 * If the configuration parameters can't be parsed, then
655 if (cf_section_parse(conf, inst->config, module_config) < 0) {
656 rlm_sql_detach(inst);
660 xlat_name = cf_section_name2(conf);
661 if (xlat_name == NULL)
662 xlat_name = cf_section_name1(conf);
664 inst->config->xlat_name = strdup(xlat_name);
665 xlat_register(xlat_name, sql_xlat, inst);
668 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
669 radlog(L_ERR | L_CONS, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
670 inst->config->xlat_name, MAX_SQL_SOCKS);
671 rlm_sql_detach(inst);
676 * Sanity check for crazy people.
678 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
679 radlog(L_ERR, "rlm_sql (%s): \"%s\" is NOT an SQL driver!",
680 inst->config->xlat_name, inst->config->sql_driver);
681 rlm_sql_detach(inst);
685 inst->handle = lt_dlopenext(inst->config->sql_driver);
686 if (inst->handle == NULL) {
687 radlog(L_ERR, "rlm_sql (%s): Could not link driver %s: %s",
688 inst->config->xlat_name, inst->config->sql_driver,
690 radlog(L_ERR, "rlm_sql (%s): Make sure it (and all its dependent libraries!) are in the search path of your system's ld.",
691 inst->config->xlat_name);
692 rlm_sql_detach(inst);
696 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
698 radlog(L_ERR, "rlm_sql (%s): Could not link symbol %s: %s",
699 inst->config->xlat_name, inst->config->sql_driver,
701 rlm_sql_detach(inst);
705 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
706 inst->config->xlat_name, inst->config->sql_driver,
708 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
709 inst->config->xlat_name, inst->config->sql_login,
710 inst->config->sql_server, inst->config->sql_port,
711 inst->config->sql_db);
713 if (sql_init_socketpool(inst) < 0) {
714 rlm_sql_detach(inst);
717 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
719 if (inst->config->do_clients){
720 if (generate_sql_clients(inst) == -1){
721 radlog(L_ERR, "rlm_sql (%s): generate_sql_clients() returned error",inst->config->xlat_name);
722 rlm_sql_detach(inst);
726 allowed_chars = inst->config->allowed_chars;
730 return RLM_MODULE_OK;
733 static int rlm_sql_destroy(void)
739 static int rlm_sql_authorize(void *instance, REQUEST * request)
741 VALUE_PAIR *check_tmp = NULL;
742 VALUE_PAIR *reply_tmp = NULL;
743 VALUE_PAIR *user_profile = NULL;
746 SQL_INST *inst = instance;
747 char querystr[MAX_QUERY_LEN];
748 char sqlusername[MAX_STRING_LEN];
751 * They MUST have a user name to do SQL authorization.
753 if ((request->username == NULL) ||
754 (request->username->length == 0)) {
755 radlog(L_ERR, "rlm_sql (%s): zero length username not permitted\n", inst->config->xlat_name);
756 return RLM_MODULE_INVALID;
760 * Set, escape, and check the user attr here.
762 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
763 return RLM_MODULE_FAIL;
764 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func);
766 sqlsocket = sql_get_socket(inst);
767 if (sqlsocket == NULL) {
768 /* Remove the username we (maybe) added above */
769 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
770 return RLM_MODULE_FAIL;
774 * After this point, ALL 'return's MUST release the SQL socket!
777 found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_USERDATA);
779 * Find the entry for the user.
782 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func);
783 sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
784 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func);
785 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_USERDATA);
786 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func);
787 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
788 } else if (found < 0) {
789 radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user",
790 inst->config->xlat_name);
791 sql_release_socket(inst, sqlsocket);
792 /* Remove the username we (maybe) added above */
793 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
794 pairfree(&check_tmp);
795 return RLM_MODULE_FAIL;
798 radlog(L_DBG, "rlm_sql (%s): User %s not found in radcheck",
799 inst->config->xlat_name, sqlusername);
802 * We didn't find the user in radcheck, so we try looking
803 * for radgroupcheck entry
805 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func);
806 found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
807 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func);
808 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
811 radlog(L_DBG, "rlm_sql (%s): User %s not found in radgroupcheck",
812 inst->config->xlat_name, sqlusername);
813 if (found || (!found && inst->config->query_on_not_found)){
817 * Check for a default_profile or for a User-Profile.
819 user_profile = pairfind(request->config_items, PW_USER_PROFILE);
820 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
821 char *profile = inst->config->default_profile;
823 if (user_profile != NULL)
824 profile = user_profile->strvalue;
825 if (profile && strlen(profile)){
826 radlog(L_DBG, "rlm_sql (%s): Checking profile %s",
827 inst->config->xlat_name, profile);
828 if (sql_set_user(inst, request, sqlusername, profile) < 0) {
829 sql_release_socket(inst, sqlsocket);
830 pairfree(&reply_tmp);
831 pairfree(&check_tmp);
832 return RLM_MODULE_FAIL;
834 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query,
835 request, sql_escape_func);
836 def_found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA);
839 radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query,
840 request, sql_escape_func);
841 sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA);
847 * We don't need the SQL socket anymore.
849 sql_release_socket(inst, sqlsocket);
852 radlog(L_DBG, "rlm_sql (%s): User not found",
853 inst->config->xlat_name);
854 /* Remove the username we (maybe) added above */
855 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
856 pairfree(&reply_tmp);
857 pairfree(&check_tmp);
858 return RLM_MODULE_NOTFOUND;
862 * Uncomment these lines for debugging
863 * Recompile, and run 'radiusd -X'
867 DEBUG2("rlm_sql: check items");
868 vp_listdebug(check_tmp);
869 DEBUG2("rlm_sql: reply items");
870 vp_listdebug(reply_tmp);
873 if (paircmp(request, request->packet->vps, check_tmp, &reply_tmp) != 0) {
874 radlog(L_INFO, "rlm_sql (%s): No matching entry in the database for request from user [%s]",
875 inst->config->xlat_name, sqlusername);
876 /* Remove the username we (maybe) added above */
877 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
878 pairfree(&reply_tmp);
879 pairfree(&check_tmp);
880 return RLM_MODULE_NOTFOUND;
883 pairxlatmove(request, &request->reply->vps, &reply_tmp);
884 pairxlatmove(request, &request->config_items, &check_tmp);
885 pairfree(&reply_tmp);
886 pairfree(&check_tmp);
888 /* Remove the username we (maybe) added above */
889 pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
891 return RLM_MODULE_OK;
895 * Accounting: save the account data to our sql table
897 static int rlm_sql_accounting(void *instance, REQUEST * request) {
899 SQLSOCK *sqlsocket = NULL;
901 SQL_INST *inst = instance;
902 int ret = RLM_MODULE_OK;
904 int acctstatustype = 0;
905 char querystr[MAX_QUERY_LEN];
906 char logstr[MAX_QUERY_LEN];
907 char sqlusername[MAX_STRING_LEN];
909 #ifdef CISCO_ACCOUNTING_HACK
910 int acctsessiontime = 0;
913 memset(querystr, 0, MAX_QUERY_LEN);
916 * Find the Acct Status Type
918 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
919 acctstatustype = pair->lvalue;
921 radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
922 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s",
923 inst->config->xlat_name, logstr);
924 return RLM_MODULE_INVALID;
927 switch (acctstatustype) {
929 * The Terminal server informed us that it was rebooted
930 * STOP all records from this NAS
932 case PW_STATUS_ACCOUNTING_ON:
933 case PW_STATUS_ACCOUNTING_OFF:
934 radlog(L_INFO, "rlm_sql (%s): received Acct On/Off packet", inst->config->xlat_name);
935 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
936 query_log(request, inst, querystr);
938 sqlsocket = sql_get_socket(inst);
939 if (sqlsocket == NULL)
940 return(RLM_MODULE_FAIL);
941 if (*querystr) { /* non-empty query */
942 if (rlm_sql_query(sqlsocket, inst, querystr)) {
943 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting for Acct On/Off packet - %s",
944 inst->config->xlat_name,
945 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
946 ret = RLM_MODULE_FAIL;
948 (inst->module->sql_finish_query)(sqlsocket, inst->config);
954 * Got an update accounting packet
956 case PW_STATUS_ALIVE:
959 * Set, escape, and check the user attr here
961 sql_set_user(inst, request, sqlusername, NULL);
963 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
964 query_log(request, inst, querystr);
966 sqlsocket = sql_get_socket(inst);
967 if (sqlsocket == NULL)
968 return(RLM_MODULE_FAIL);
969 if (*querystr) { /* non-empty query */
970 if (rlm_sql_query(sqlsocket, inst, querystr)) {
971 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting ALIVE record - %s",
972 inst->config->xlat_name,
973 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
974 ret = RLM_MODULE_FAIL;
977 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
978 if (numaffected < 1) {
981 * If our update above didn't match anything
982 * we assume it's because we haven't seen a
983 * matching Start record. So we have to
984 * insert this update rather than do an update
986 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
987 query_log(request, inst, querystr);
988 if (*querystr) { /* non-empty query */
989 if (rlm_sql_query(sqlsocket, inst, querystr)) {
990 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting ALIVE record - %s",
991 inst->config->xlat_name,
992 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
993 ret = RLM_MODULE_FAIL;
995 (inst->module->sql_finish_query)(sqlsocket, inst->config);
999 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1004 * Got accounting start packet
1006 case PW_STATUS_START:
1009 * Set, escape, and check the user attr here
1011 sql_set_user(inst, request, sqlusername, NULL);
1013 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
1014 query_log(request, inst, querystr);
1016 sqlsocket = sql_get_socket(inst);
1017 if (sqlsocket == NULL)
1018 return(RLM_MODULE_FAIL);
1019 if (*querystr) { /* non-empty query */
1020 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1021 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting START record - %s",
1022 inst->config->xlat_name,
1023 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1026 * We failed the insert above. It's probably because
1027 * the stop record came before the start. We try
1028 * our alternate query now (typically an UPDATE)
1030 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1031 query_log(request, inst, querystr);
1033 if (*querystr) { /* non-empty query */
1034 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1035 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting START record - %s",
1036 inst->config->xlat_name,
1037 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1038 ret = RLM_MODULE_FAIL;
1040 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1043 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1048 * Got accounting stop packet
1050 case PW_STATUS_STOP:
1053 * Set, escape, and check the user attr here
1055 sql_set_user(inst, request, sqlusername, NULL);
1057 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1058 query_log(request, inst, querystr);
1060 sqlsocket = sql_get_socket(inst);
1061 if (sqlsocket == NULL)
1062 return(RLM_MODULE_FAIL);
1063 if (*querystr) { /* non-empty query */
1064 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1065 radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting STOP record - %s",
1066 inst->config->xlat_name,
1067 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1068 ret = RLM_MODULE_FAIL;
1071 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1072 if (numaffected < 1) {
1074 * If our update above didn't match anything
1075 * we assume it's because we haven't seen a
1076 * matching Start record. So we have to
1077 * insert this stop rather than do an update
1079 #ifdef CISCO_ACCOUNTING_HACK
1081 * If stop but zero session length AND no previous
1082 * session found, drop it as in invalid packet
1083 * This is to fix CISCO's aaa from filling our
1084 * table with bogus crap
1086 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
1087 acctsessiontime = pair->lvalue;
1089 if (acctsessiontime <= 0) {
1090 radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1091 radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s", inst->config->xlat_name, logstr);
1092 sql_release_socket(inst, sqlsocket);
1093 ret = RLM_MODULE_NOOP;
1097 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1098 query_log(request, inst, querystr);
1100 if (*querystr) { /* non-empty query */
1101 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1102 radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting STOP record - %s",
1103 inst->config->xlat_name,
1104 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1105 ret = RLM_MODULE_FAIL;
1107 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1111 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1116 * Anything else is ignored.
1119 radlog(L_INFO, "rlm_sql (%s): Unsupported Acct-Status-Type = %d", inst->config->xlat_name, acctstatustype);
1120 return RLM_MODULE_NOOP;
1125 sql_release_socket(inst, sqlsocket);
1132 * See if a user is already logged in. Sets request->simul_count to the
1133 * current session count for this user.
1135 * Check twice. If on the first pass the user exceeds his
1136 * max. number of logins, do a second pass and validate all
1137 * logins by querying the terminal server (using eg. SNMP).
1140 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1142 SQL_INST *inst = instance;
1144 char querystr[MAX_QUERY_LEN];
1145 char sqlusername[MAX_STRING_LEN];
1148 char *call_num = NULL;
1151 uint32_t nas_addr = 0;
1154 /* If simul_count_query is not defined, we don't do any checking */
1155 if (inst->config->simul_count_query[0] == 0) {
1156 return RLM_MODULE_NOOP;
1159 if((request->username == NULL) || (request->username->length == 0)) {
1160 radlog(L_ERR, "rlm_sql (%s): Zero Length username not permitted\n", inst->config->xlat_name);
1161 return RLM_MODULE_INVALID;
1165 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1166 return RLM_MODULE_FAIL;
1168 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1170 /* initialize the sql socket */
1171 sqlsocket = sql_get_socket(inst);
1172 if(sqlsocket == NULL)
1173 return RLM_MODULE_FAIL;
1175 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1176 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1177 sql_release_socket(inst, sqlsocket);
1178 return RLM_MODULE_FAIL;
1181 ret = rlm_sql_fetch_row(sqlsocket, inst);
1184 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1185 sql_release_socket(inst, sqlsocket);
1186 return RLM_MODULE_FAIL;
1189 row = sqlsocket->row;
1191 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1192 sql_release_socket(inst, sqlsocket);
1193 return RLM_MODULE_FAIL;
1196 request->simul_count = atoi(row[0]);
1197 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1199 if(request->simul_count < request->simul_max) {
1200 sql_release_socket(inst, sqlsocket);
1201 return RLM_MODULE_OK;
1204 /* Looks like too many sessions, so lets start verifying them */
1206 if (inst->config->simul_verify_query[0] == 0) {
1207 /* No verify query defined, so skip verify step and rely on count query only */
1208 sql_release_socket(inst, sqlsocket);
1209 return RLM_MODULE_OK;
1212 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1213 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1214 radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Database query error", inst->config->xlat_name);
1215 sql_release_socket(inst, sqlsocket);
1216 return RLM_MODULE_FAIL;
1220 * Setup some stuff, like for MPP detection.
1222 request->simul_count = 0;
1224 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1226 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1227 call_num = vp->strvalue;
1230 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1231 row = sqlsocket->row;
1235 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1236 sql_release_socket(inst, sqlsocket);
1237 DEBUG("rlm_sql (%s): Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1238 return RLM_MODULE_FAIL;
1241 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1242 sql_release_socket(inst, sqlsocket);
1243 DEBUG("rlm_sql (%s): Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1244 return RLM_MODULE_FAIL;
1247 nas_addr = inet_addr(row[3]);
1249 nas_port = atoi(row[4]);
1251 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1254 * Failed to check the terminal server for
1255 * duplicate logins: Return an error.
1258 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1259 sql_release_socket(inst, sqlsocket);
1260 DEBUG("rlm_sql (%s) rad_check_ts() failed.",
1261 inst->config->xlat_name);
1262 return RLM_MODULE_FAIL;
1266 ++request->simul_count;
1269 * Does it look like a MPP attempt?
1271 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1272 request->simul_mpp = 2;
1273 else if (row[6] && call_num &&
1274 !strncmp(row[6],call_num,16))
1275 request->simul_mpp = 2;
1279 * Stale record - zap it.
1281 uint32_t framed_addr = 0;
1285 framed_addr = inet_addr(row[5]);
1287 if (strcmp(row[7],"SLIP") == 0)
1290 session_zap(request,
1291 nas_addr,nas_port,row[2],row[1],
1292 framed_addr, proto);
1296 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1297 sql_release_socket(inst, sqlsocket);
1299 /* The Auth module apparently looks at request->simul_count, not the return value
1300 of this module when deciding to deny a call for too many sessions */
1301 return RLM_MODULE_OK;
1306 * Execute postauth_query after authentication
1308 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1309 SQLSOCK *sqlsocket = NULL;
1310 SQL_INST *inst = instance;
1311 char querystr[MAX_QUERY_LEN];
1312 char sqlusername[MAX_STRING_LEN];
1314 DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name);
1316 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1317 return RLM_MODULE_FAIL;
1319 /* If postauth_query is not defined, we stop here */
1320 if (inst->config->postauth_query[0] == '\0')
1321 return RLM_MODULE_NOOP;
1323 /* Expand variables in the query */
1324 memset(querystr, 0, MAX_QUERY_LEN);
1325 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1326 request, sql_escape_func);
1327 query_log(request, inst, querystr);
1328 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1329 inst->config->xlat_name, querystr);
1331 /* Initialize the sql socket */
1332 sqlsocket = sql_get_socket(inst);
1333 if (sqlsocket == NULL)
1334 return RLM_MODULE_FAIL;
1336 /* Process the query */
1337 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1338 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1339 inst->config->xlat_name,
1340 (char *)(inst->module->sql_error)(sqlsocket, inst->config));
1341 sql_release_socket(inst, sqlsocket);
1342 return RLM_MODULE_FAIL;
1344 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1346 sql_release_socket(inst, sqlsocket);
1347 return RLM_MODULE_OK;
1350 /* globally exported name */
1351 module_t rlm_sql = {
1353 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1354 rlm_sql_init, /* initialization */
1355 rlm_sql_instantiate, /* instantiation */
1357 NULL, /* authentication */
1358 rlm_sql_authorize, /* authorization */
1359 NULL, /* preaccounting */
1360 rlm_sql_accounting, /* accounting */
1361 rlm_sql_checksimul, /* checksimul */
1362 NULL, /* pre-proxy */
1363 NULL, /* post-proxy */
1364 rlm_sql_postauth /* post-auth */
1366 rlm_sql_detach, /* detach */
1367 rlm_sql_destroy, /* destroy */