📄 rlm_sql.c
字号:
/* * rlm_sql.c SQL Module * Main SQL module file. Most ICRADIUS code is located in sql.c * * Version: $Id: rlm_sql.c,v 1.131.2.4 2005/08/31 12:49:52 nbk Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright 2000 The FreeRADIUS server project * Copyright 2000 Mike Machado <mike@innercite.com> * Copyright 2000 Alan DeKok <aland@ox.org> */static const char rcsid[] = "$Id: rlm_sql.c,v 1.131.2.4 2005/08/31 12:49:52 nbk Exp $";#include "autoconf.h"#include <stdio.h>#include <sys/stat.h>#include <stdlib.h>#include <time.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>#include <string.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include "radiusd.h"#include "modules.h"#include "conffile.h"#include "rlm_sql.h"#include "rad_assert.h"static char *allowed_chars = NULL;static CONF_PARSER module_config[] = { {"driver",PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_driver), NULL, "mysql"}, {"server",PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_server), NULL, "localhost"}, {"port",PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_port), NULL, ""}, {"login", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_login), NULL, ""}, {"password", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_password), NULL, ""}, {"radius_db", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_db), NULL, "radius"}, {"acct_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_acct_table), NULL, "radacct"}, {"acct_table2", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_acct_table2), NULL, "radacct"}, {"authcheck_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_authcheck_table), NULL, "radcheck"}, {"authreply_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_authreply_table), NULL, "radreply"}, {"groupcheck_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_groupcheck_table), NULL, "radgroupcheck"}, {"groupreply_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_groupreply_table), NULL, "radgroupreply"}, {"usergroup_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_usergroup_table), NULL, "usergroup"}, {"nas_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_nas_table), NULL, "nas"}, {"dict_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_dict_table), NULL, "dictionary"}, {"sqltrace", PW_TYPE_BOOLEAN, offsetof(SQL_CONFIG,sqltrace), NULL, "no"}, {"sqltracefile", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE}, {"readclients", PW_TYPE_BOOLEAN, offsetof(SQL_CONFIG,do_clients), NULL, "no"}, {"deletestalesessions", PW_TYPE_BOOLEAN, offsetof(SQL_CONFIG,deletestalesessions), NULL, "no"}, {"num_sql_socks", PW_TYPE_INTEGER, offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"}, {"sql_user_name", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,query_user), NULL, ""}, {"default_user_profile", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,default_profile), NULL, ""}, {"query_on_not_found", PW_TYPE_BOOLEAN, offsetof(SQL_CONFIG,query_on_not_found), NULL, "no"}, {"authorize_check_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,authorize_check_query), NULL, ""}, {"authorize_reply_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,authorize_reply_query), NULL, ""}, {"authorize_group_check_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""}, {"authorize_group_reply_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""}, {"accounting_onoff_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""}, {"accounting_update_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,accounting_update_query), NULL, ""}, {"accounting_update_query_alt", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""}, {"accounting_start_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,accounting_start_query), NULL, ""}, {"accounting_start_query_alt", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""}, {"accounting_stop_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""}, {"accounting_stop_query_alt", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""}, {"group_membership_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,groupmemb_query), NULL, ""}, {"connect_failure_retry_delay", PW_TYPE_INTEGER, offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"}, {"simul_count_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,simul_count_query), NULL, ""}, {"simul_verify_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,simul_verify_query), NULL, ""}, {"postauth_table", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,sql_postauth_table), NULL, "radpostauth"}, {"postauth_query", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,postauth_query), NULL, ""}, {"safe-characters", PW_TYPE_STRING_PTR, offsetof(SQL_CONFIG,allowed_chars), NULL, "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"}, {NULL, -1, 0, NULL, NULL}};/*********************************************************************** * start of main routines ***********************************************************************/static int rlm_sql_init(void) { /* * FIXME: * We should put the sqlsocket array here once * the module code is reworked to not unload * modules on HUP. This way we can have * persistant connections. -jcarneal */ return 0;}/* * Yucky prototype. */static int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username);static int generate_sql_clients(SQL_INST *inst);static int sql_escape_func(char *out, int outlen, const char *in);/* * sql xlat function. Right now only SELECTs are supported. Only * the first element of the SELECT result will be used. */static int sql_xlat(void *instance, REQUEST *request, char *fmt, char *out, size_t freespace, RADIUS_ESCAPE_STRING func){ SQLSOCK *sqlsocket; SQL_ROW row; SQL_INST *inst = instance; char querystr[MAX_QUERY_LEN]; char sqlusername[MAX_STRING_LEN]; int ret = 0; DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name); /* * Add SQL-User-Name attribute just in case it is needed * We could search the string fmt for SQL-User-Name to see if this is * needed or not */ sql_set_user(inst, request, sqlusername, NULL); /* * Do an xlat on the provided string (nice recursive operation). */ if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) { radlog(L_ERR, "rlm_sql (%s): xlat failed.", inst->config->xlat_name); return 0; } query_log(request, inst,querystr); sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) return 0; if (rlm_sql_select_query(sqlsocket,inst,querystr)){ radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s", inst->config->xlat_name,querystr, (char *)(inst->module->sql_error)(sqlsocket, inst->config)); sql_release_socket(inst,sqlsocket); return 0; } ret = rlm_sql_fetch_row(sqlsocket, inst); if (ret) { DEBUG("rlm_sql (%s): SQL query did not succeed", inst->config->xlat_name); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return 0; } row = sqlsocket->row; if (row == NULL) { DEBUG("rlm_sql (%s): SQL query did not return any results", inst->config->xlat_name); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return 0; } if (row[0] == NULL){ DEBUG("rlm_sql (%s): row[0] returned NULL", inst->config->xlat_name); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return 0; } ret = strlen(row[0]); if (ret > freespace){ DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space", inst->config->xlat_name); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return 0; } strncpy(out,row[0],ret); DEBUG("rlm_sql (%s): - sql_xlat finished", inst->config->xlat_name); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return ret;}static int generate_sql_clients(SQL_INST *inst){ SQLSOCK *sqlsocket; SQL_ROW row; char querystr[MAX_QUERY_LEN]; RADCLIENT *c; char *netmask; unsigned int i = 0; DEBUG("rlm_sql (%s): - generate_sql_clients",inst->config->xlat_name); if (inst->config->sql_nas_table == NULL){ radlog(L_ERR, "rlm_sql (%s): sql_nas_table is NULL.",inst->config->xlat_name); return -1; } snprintf(querystr,MAX_QUERY_LEN - 1,"SELECT * FROM %s",inst->config->sql_nas_table); DEBUG("rlm_sql (%s): Query: %s",inst->config->xlat_name,querystr); sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) return -1; if (rlm_sql_select_query(sqlsocket,inst,querystr)){ radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s", inst->config->xlat_name,querystr, (char *)(inst->module->sql_error)(sqlsocket, inst->config)); sql_release_socket(inst,sqlsocket); return -1; } while(rlm_sql_fetch_row(sqlsocket, inst) == 0) { i++; row = sqlsocket->row; if (row == NULL) break;/* * Format: * Row1 Row2 Row3 Row4 Row5 Row6 Row7 Row8 * * id nasname shortname type ports secret community description * */ if (!row[0]){ radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i); continue; } if (!row[1]){ radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]); continue; } if (strlen(row[1]) >= sizeof(c->longname)){ radlog(L_ERR, "rlm_sql (%s): nasname of length %d is greater than the allowed maximum of %d", inst->config->xlat_name,strlen(row[1]),sizeof(c->longname) - 1); continue; } if (!row[2]){ radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]); continue; } if (strlen(row[2]) >= sizeof(c->shortname)){ radlog(L_ERR, "rlm_sql (%s): shortname of length %d is greater than the allowed maximum of %d", inst->config->xlat_name,strlen(row[2]),sizeof(c->shortname) - 1); continue; } if (row[3] && strlen(row[3]) >= sizeof(c->nastype)){ radlog(L_ERR, "rlm_sql (%s): nastype of length %d is greater than the allowed maximum of %d", inst->config->xlat_name,strlen(row[3]),sizeof(c->nastype) - 1); continue; } if (!row[5]){ radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]); continue; } if (strlen(row[5]) >= sizeof(c->secret)){ radlog(L_ERR, "rlm_sql (%s): secret of length %d is greater than the allowed maximum of %d", inst->config->xlat_name,strlen(row[5]),sizeof(c->secret) - 1); continue; } DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name, row[1],row[2],row[5]); c = rad_malloc(sizeof(RADCLIENT)); memset(c, 0, sizeof(RADCLIENT)); c->netmask = ~0; netmask = strchr(row[1], '/'); /* * Look for netmasks. */ c->netmask = ~0; if (netmask) { int mask_length; mask_length = atoi(netmask + 1); if ((mask_length < 0) || (mask_length > 32)) { radlog(L_ERR, "rlm_sql (%s): Invalid value '%s' for IP network mask for nasname %s.", inst->config->xlat_name, netmask + 1,row[1]); free(c); continue; } if (mask_length == 0) { c->netmask = 0; } else { c->netmask = ~0 << (32 - mask_length); } *netmask = '\0'; c->netmask = htonl(c->netmask); } c->ipaddr = ip_getaddr(row[1]); if (c->ipaddr == INADDR_NONE) { radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s", inst->config->xlat_name, row[1]); free(c); continue; } /* * Update the client name again... */ if (netmask) { *netmask = '/'; c->ipaddr &= c->netmask; strcpy(c->longname, row[1]); } else { ip_hostname(c->longname, sizeof(c->longname), c->ipaddr); } strcpy((char *)c->secret, row[5]); strcpy(c->shortname, row[2]); if(row[3] != NULL) strcpy(c->nastype, row[3]); DEBUG("rlm_sql (%s): Adding client %s (%s) to clients list",inst->config->xlat_name, c->longname,c->shortname); c->next = mainconfig.clients; mainconfig.clients = c; } (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst, sqlsocket); return 0;}/* * Translate the SQL queries. */static int sql_escape_func(char *out, int outlen, const char *in){ int len = 0; while (in[0]) { /* * Non-printable characters get replaced with their * mime-encoded equivalents. */ if ((in[0] < 32) || strchr(allowed_chars, *in) == NULL) { /* * Only 3 or less bytes available. */ if (outlen <= 3) { break; } snprintf(out, outlen, "=%02X", (unsigned char) in[0]); in++; out += 3; outlen -= 3; len += 3; continue; } /* * Only one byte left. */ if (outlen <= 1) { break; } /* * Allowed character. */ *out = *in; out++; in++; outlen--; len++; } *out = '\0'; return len;}/* * Set the SQL user name.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -