1 /***************************************************************************
2 * rlm_sql.c rlm_sql - FreeRADIUS SQL Module *
4 * Main SQL module file. Most ICRADIUS code is located in sql.c *
7 * Mike Machado <mike@innercite.com> *
8 ***************************************************************************/
9 static const char rcsid[] = "$Id$";
19 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
32 static SQL_CONFIG config = {
33 NULL, /* "localhost" */
38 NULL, /* "radcheck" */
39 NULL, /* "radreply" */
40 NULL, /* "radgroupcheck" */
41 NULL, /* "radgroupreply" */
42 NULL, /* "usergroup" */
44 NULL, /* "realmgroup" */
46 NULL, /* "dictionary" */
53 static CONF_PARSER module_config[] = {
54 { "sensitiveusername", PW_TYPE_BOOLEAN,
55 &config.sensitiveusername, "1" },
56 { "deletestalesessions", PW_TYPE_BOOLEAN,
57 &config.deletestalesessions, "0" },
58 { "sqltrace", PW_TYPE_BOOLEAN,
59 &config.sqltrace, "0" },
60 { "max_sql_socks", PW_TYPE_INTEGER,
61 &config.max_sql_socks, Stringify(MAX_SQL_SOCKS) },
62 { "server", PW_TYPE_STRING_PTR,
63 &config.sql_server, "localhost" },
64 { "login", PW_TYPE_STRING_PTR,
65 &config.sql_login, "" },
66 { "password", PW_TYPE_STRING_PTR,
67 &config.sql_password, "" },
68 { "db", PW_TYPE_STRING_PTR,
69 &config.sql_db, "radius" },
70 { "authcheck_table", PW_TYPE_STRING_PTR,
71 &config.sql_authcheck_table, "radcheck" },
72 { "authreply_table", PW_TYPE_STRING_PTR,
73 &config.sql_authreply_table, "radreply" },
74 { "groupcheck_table", PW_TYPE_STRING_PTR,
75 &config.sql_groupcheck_table, "radgroupcheck" },
76 { "groupreply_table", PW_TYPE_STRING_PTR,
77 &config.sql_groupreply_table, "radgroupreply" },
78 { "usergroup_table", PW_TYPE_STRING_PTR,
79 &config.sql_usergroup_table, "usergroup" },
80 { "realmgroup_table", PW_TYPE_STRING_PTR,
81 &config.sql_realmgroup_table, "realmgroup" },
82 { "acct_table", PW_TYPE_STRING_PTR,
83 &config.sql_acct_table, "radacct" },
84 { "nas_table", PW_TYPE_STRING_PTR,
85 &config.sql_nas_table, "nas" },
86 { "realm_table", PW_TYPE_STRING_PTR,
87 &config.sql_realm_table, "realms" },
88 { "dict_table", PW_TYPE_STRING_PTR,
89 &config.sql_dict_table, "dictionary" },
90 { NULL, -1, NULL, NULL }
94 /***********************************************************************
95 * start of main routines
96 ***********************************************************************/
98 static int rlm_sql_init(void) {
100 /* Where is the flag that tells us about a HUP?*/
103 if ((sql = malloc(sizeof(SQL))) == NULL) {
104 radlog(L_ERR|L_CONS, "no memory");
111 if ((sql->config = malloc(sizeof(SQL_CONFIG))) == NULL) {
112 radlog(L_ERR|L_CONS, "no memory");
117 sql_init(module_config, &config, reload);
122 static int rlm_sql_destroy(void) {
128 static int rlm_sql_authorize(REQUEST *request)
131 VALUE_PAIR *check_tmp = NULL;
132 VALUE_PAIR *reply_tmp = NULL;
138 name = request->username->strvalue;
141 * Check for valid input, zero length names not permitted
144 radlog(L_ERR, "zero length username not permitted\n");
148 socket = sql_get_socket();
151 * Find the NAS port ID.
153 if ((tmp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL)
154 nas_port = tmp->lvalue;
157 * Find the entry for the user.
159 if ((found = sql_getvpdata(socket, sql->config->sql_authcheck_table, &check_tmp, name, PW_VP_USERDATA)) > 0) {
160 sql_getvpdata(socket, sql->config->sql_groupcheck_table, &check_tmp, name, PW_VP_GROUPDATA);
161 sql_getvpdata(socket, sql->config->sql_authreply_table, &reply_tmp, name, PW_VP_USERDATA);
162 sql_getvpdata(socket, sql->config->sql_groupreply_table, &reply_tmp, name, PW_VP_GROUPDATA);
166 gcheck = sql_getvpdata(socket, sql->config->sql_groupcheck_table, &check_tmp, "DEFAULT", PW_VP_GROUPDATA);
167 greply = sql_getvpdata(socket, sql->config->sql_groupreply_table, &reply_tmp, "DEFAULT", PW_VP_GROUPDATA);
168 if (gcheck && greply)
171 sql_release_socket(socket);
174 DEBUG2("User %s not found and DEFAULT not found", name);
175 return RLM_MODULE_NOTFOUND;
178 if (paircmp(request->packet->vps, check_tmp, &reply_tmp) != 0) {
179 DEBUG2("Pairs do not match [%s]", name);
180 return RLM_MODULE_OK;
183 pairmove(&request->reply->vps, &reply_tmp);
184 pairmove(&request->config_items, &check_tmp);
189 return RLM_MODULE_OK;
192 static int rlm_sql_authenticate(REQUEST *request)
198 char escaped_user[AUTH_STRING_LEN*3];
200 const char query[] = "SELECT Value FROM %s WHERE UserName = '%s' AND Attribute = 'Password'";
202 user = request->username->strvalue;
205 * Ensure that a password attribute exists.
207 if ((request->password == NULL) ||
208 (request->password->length == 0) ||
209 (request->password->attribute != PW_PASSWORD)) {
210 radlog(L_AUTH, "rlm_sql: Attribute \"Password\" is required for authentication.");
211 return RLM_MODULE_INVALID;
214 sql_escape_string(escaped_user, user, strlen(user));
217 * This should really be replaced with a static buffer...
219 if ((querystr = malloc(strlen(escaped_user) +
220 strlen(sql->config->sql_authcheck_table) +
221 sizeof(query))) == NULL) {
222 radlog(L_ERR|L_CONS, "no memory");
226 sprintf(querystr, query, sql->config->sql_authcheck_table, escaped_user);
227 socket = sql_get_socket();
228 sql_select_query(socket, querystr);
229 row = sql_fetch_row(socket);
230 sql_finish_select_query(socket);
233 if (strncmp(request->password->strvalue, row[0], request->password->length) != 0)
234 return RLM_MODULE_REJECT;
236 return RLM_MODULE_OK;
240 * Accounting: does nothing for now.
242 static int rlm_sql_accounting(REQUEST *request) {
248 SQLACCTREC *sqlrecord;
253 if ((sqlrecord = malloc(sizeof(SQLACCTREC))) == NULL) {
254 radlog(L_ERR|L_CONS, "no memory");
258 pair = request->packet->vps;
259 while(pair != (VALUE_PAIR *)NULL) {
261 /* Check the pairs to see if they are anything we are interested in. */
262 switch(pair->attribute) {
263 case PW_ACCT_SESSION_ID:
264 strncpy(sqlrecord->AcctSessionId, pair->strvalue, SQLBIGREC);
268 strncpy(sqlrecord->UserName, pair->strvalue, SQLBIGREC);
271 case PW_NAS_IP_ADDRESS:
272 ip_ntoa(sqlrecord->NASIPAddress, pair->lvalue);
273 //ipaddr2str(sqlrecord->NASIPAddress, pair->lvalue);
277 sqlrecord->NASPortId = pair->lvalue;
280 case PW_NAS_PORT_TYPE:
281 dval = dict_valbyattr(PW_NAS_PORT_TYPE, pair->lvalue);
283 strncpy(sqlrecord->NASPortType, dval->attrname, SQLBIGREC);
287 case PW_ACCT_STATUS_TYPE:
288 sqlrecord->AcctStatusTypeId = pair->lvalue;
289 dval = dict_valbyattr(PW_ACCT_STATUS_TYPE, pair->lvalue);
291 strncpy(sqlrecord->AcctStatusType, dval->attrname, SQLBIGREC);
295 case PW_ACCT_SESSION_TIME:
296 sqlrecord->AcctSessionTime = pair->lvalue;
299 case PW_ACCT_AUTHENTIC:
300 dval = dict_valbyattr(PW_ACCT_AUTHENTIC, pair->lvalue);
302 strncpy(sqlrecord->AcctAuthentic, dval->attrname, SQLBIGREC);
306 case PW_CONNECT_INFO:
307 strncpy(sqlrecord->ConnectInfo, pair->strvalue, SQLBIGREC);
310 case PW_ACCT_INPUT_OCTETS:
311 sqlrecord->AcctInputOctets = pair->lvalue;
314 case PW_ACCT_OUTPUT_OCTETS:
315 sqlrecord->AcctOutputOctets = pair->lvalue;
318 case PW_CALLED_STATION_ID:
319 strncpy(sqlrecord->CalledStationId, pair->strvalue, SQLLILREC);
322 case PW_CALLING_STATION_ID:
323 strncpy(sqlrecord->CallingStationId, pair->strvalue, SQLLILREC);
326 /* case PW_ACCT_TERMINATE_CAUSE:
327 dval = dict_valbyattr(PW_ACCT_TERMINATE_CAUSE, pair->lvalue);
329 strncpy(sqlrecord->AcctTerminateCause, dval->attrname, SQLBIGREC);
335 case PW_SERVICE_TYPE:
336 dval = dict_valbyattr(PW_SERVICE_TYPE, pair->lvalue);
338 strncpy(sqlrecord->ServiceType, dval->attrname, SQLBIGREC);
342 case PW_FRAMED_PROTOCOL:
343 dval = dict_valbyattr(PW_FRAMED_PROTOCOL, pair->lvalue);
345 strncpy(sqlrecord->FramedProtocol, dval->attrname, SQLBIGREC);
349 case PW_FRAMED_IP_ADDRESS:
350 ip_ntoa(sqlrecord->FramedIPAddress, pair->lvalue);
351 //ipaddr2str(sqlrecord->FramedIPAddress, pair->lvalue);
354 case PW_ACCT_DELAY_TIME:
355 sqlrecord->AcctDelayTime = pair->lvalue;
366 nowtime = request->timestamp - sqlrecord->AcctDelayTime;
367 tim = localtime(&nowtime);
368 strftime(datebuf, sizeof(datebuf), "%Y%m%d%H%M%S", tim);
370 strncpy(sqlrecord->AcctTimeStamp, datebuf, 20);
373 socket = sql_get_socket();
374 if (sql_save_acct(socket, sqlrecord) == 0)
375 return RLM_MODULE_FAIL;
376 sql_release_socket(socket);
378 return RLM_MODULE_OK;
382 /* globally exported name */
385 0, /* type: reserved */
386 rlm_sql_init, /* initialization */
387 NULL, /* instantiation */
388 rlm_sql_authorize, /* authorization */
389 rlm_sql_authenticate, /* authentication */
390 NULL, /* preaccounting */
391 rlm_sql_accounting, /* accounting */
393 rlm_sql_destroy, /* destroy */