2 * sql.c rlm_sql - FreeRADIUS SQL Module
3 * Main code directly taken from ICRADIUS
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>
27 #include <sys/types.h>
28 #include <sys/socket.h>
33 #include <netinet/in.h>
54 /*************************************************************************
56 * Function: sql_init_socket
58 * Purpose: Connect to the sql server
60 *************************************************************************/
61 int sql_init_socketpool(SQL_INST * inst) {
69 for (i = 0; i < inst->config->num_sql_socks; i++) {
71 sqlsocket = rad_malloc(sizeof(SQLSOCK));
72 if (sqlsocket == NULL) {
75 sqlsocket->conn = NULL;
79 sqlsocket->semaphore = (sem_t *) rad_malloc(sizeof(sem_t));
80 sem_init(sqlsocket->semaphore, 0, SQLSOCK_UNLOCKED);
82 sqlsocket->in_use = 0;
85 if ((inst->module->sql_create_socket)(sqlsocket, inst->config) < 0) {
86 radlog(L_CONS | L_ERR, "rlm_sql: Failed to connect sqlsocket %d", i);
90 /* Add this socket to the list of sockets */
91 sqlsocket->next = inst->sqlpool;
92 inst->sqlpool = sqlsocket;
98 /*************************************************************************
100 * Function: sql_poolfree
102 * Purpose: Clean up and free sql pool
104 *************************************************************************/
105 void sql_poolfree(SQL_INST * inst) {
109 for (cur = inst->sqlpool; cur; cur = cur->next) {
110 sql_close_socket(inst, cur);
113 pthread_mutex_destroy(inst->lock);
114 pthread_cond_destroy(inst->notfull);
119 /*************************************************************************
121 * Function: sql_close_socket
123 * Purpose: Close and free a sql sqlsocket
125 *************************************************************************/
126 int sql_close_socket(SQL_INST *inst, SQLSOCK * sqlsocket) {
128 radlog(L_DBG, "rlm_sql: Closing sqlsocket %d", sqlsocket->id);
129 (inst->module->sql_close)(sqlsocket, inst->config);
131 sem_destroy(sqlsocket->semaphore);
138 /*************************************************************************
140 * Function: sql_get_socket
142 * Purpose: Return a SQL sqlsocket from the connection pool
144 *************************************************************************/
145 SQLSOCK * sql_get_socket(SQL_INST * inst) {
151 pthread_mutex_lock(inst->lock);
153 while (inst->used == inst->config->num_sql_socks) {
154 radlog(L_DBG, "rlm_sql: Waiting for open sql socket");
156 pthread_cond_wait(inst->notfull, inst->lock);
159 * FIXME: Subsecond sleep needed here
165 for (cur = inst->sqlpool; cur; cur = cur->next) {
167 if (sem_trywait(cur->semaphore) == 0) {
169 if (cur->in_use == SQLSOCK_UNLOCKED) {
173 pthread_mutex_unlock(inst->lock);
175 cur->in_use = SQLSOCK_LOCKED;
177 radlog(L_DBG, "rlm_sql: Reserved sql socket id: %d", cur->id);
183 pthread_mutex_unlock(inst->lock);
187 * Should never get here, but what the hey
192 /*************************************************************************
194 * Function: sql_release_socket
196 * Purpose: Frees a SQL sqlsocket back to the connection pool
198 *************************************************************************/
199 int sql_release_socket(SQL_INST * inst, SQLSOCK * sqlsocket) {
202 pthread_mutex_lock(inst->lock);
206 sem_post(sqlsocket->semaphore);
208 sqlsocket->in_use = 0;
211 radlog(L_DBG, "rlm_sql: Released sql socket id: %d", sqlsocket->id);
214 pthread_mutex_unlock(inst->lock);
215 pthread_cond_signal(inst->notfull);
222 /*************************************************************************
224 * Function: sql_userparse
226 * Purpose: Read entries from the database and fill VALUE_PAIR structures
228 *************************************************************************/
229 int sql_userparse(VALUE_PAIR ** first_pair, SQL_ROW row, int mode, int itemtype) {
232 VALUE_PAIR *pair, *check;
234 if ((attr = dict_attrbyname(row[2])) == (DICT_ATTR *) NULL) {
235 radlog(L_ERR | L_CONS, "rlm_sql: unknown attribute %s", row[2]);
240 * If attribute is already there, skip it because we checked usercheck first
241 * and we want user settings to over ride group settings
243 if ((check = pairfind(*first_pair, attr->attr)) != NULL &&
244 #if defined( BINARY_FILTERS )
245 attr->type != PW_TYPE_ABINARY &&
247 mode == PW_VP_GROUPDATA)
250 pair = pairmake(row[2], row[3], T_OP_CMP_EQ);
251 pairadd(first_pair, pair);
253 vp_printlist(stderr, *first_pair);
259 /*************************************************************************
261 * Function: sql_getvpdata
263 * Purpose: Get any group check or reply pairs
265 *************************************************************************/
266 int sql_getvpdata(SQL_INST * inst, SQLSOCK * sqlsocket, VALUE_PAIR ** check, VALUE_PAIR ** reply, char *query, int mode) {
271 if ((inst->module->sql_select_query)(sqlsocket, inst->config, query) < 0) {
272 radlog(L_ERR, "rlm_sql_getvpdata: database query error");
275 while ((row = (inst->module->sql_fetch_row)(sqlsocket, inst->config))) {
276 if (sql_userparse(check, row, mode, PW_ITEM_CHECK) != 0) {
277 radlog(L_ERR | L_CONS, "rlm_sql: Error getting data from database");
278 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
281 if (sql_userparse(reply, row, mode, PW_ITEM_REPLY) != 0) {
282 radlog(L_ERR | L_CONS, "rlm_sql: Error getting data from database");
283 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
288 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
300 /*************************************************************************
302 * Function: sql_check_ts
304 * Purpose: Checks the terminal server for a spacific login entry
306 *************************************************************************/
307 static int sql_check_ts(SQL_ROW row) {
314 void (*handler) (int);
319 if ((nas = nas_find(ip_addr(row[4]))) == NULL) {
320 radlog(L_ERR, "rlm_sql: unknown NAS [%s]", row[4]);
327 handler = signal(SIGCHLD, SIG_DFL);
328 if ((pid = fork()) < 0) {
329 radlog(L_ERR, "rlm_sql: fork: %s", strerror(errno));
330 signal(SIGCHLD, handler);
336 * Parent - Wait for checkrad to terminate.
337 * We timeout in 10 seconds.
340 signal(SIGALRM, alrm_handler);
342 while ((e = waitpid(pid, &st, 0)) != pid)
343 if (e < 0 && (errno != EINTR || got_alrm))
346 signal(SIGCHLD, handler);
351 radlog(L_ERR, "rlm_sql: Check-TS: timeout waiting for checkrad");
355 radlog(L_ERR, "rlm_sql: Check-TS: unknown error in waitpid()");
358 return WEXITSTATUS(st);
362 * Child - exec checklogin with the right parameters.
364 for (n = 32; n >= 3; n--)
367 sprintf(session_id, "%.8s", row[1]);
370 execl(CHECKRAD2, "checkrad", nas->nastype, row[4], row[5],
371 row[2], session_id, NULL);
372 if (errno == ENOENT) {
374 execl(CHECKRAD1, "checklogin", nas->nastype, row[4], row[5],
375 row[2], session_id, NULL);
377 radlog(L_ERR, "rlm_sql: Check-TS: exec %s: %s", s, strerror(errno));
380 * Exit - 2 means "some error occured".
387 /*************************************************************************
389 * Function: sql_check_multi
391 * Purpose: Check radius accounting for duplicate logins
393 *************************************************************************/
394 int sql_check_multi(SQL_INST * inst, SQLSOCK * sqlsocket, char *name, VALUE_PAIR * request, int maxsimul) {
396 char querystr[MAX_QUERY_LEN];
404 sprintf(authstr, "UserName = '%s'", name);
405 sprintf(querystr, "SELECT COUNT(*) FROM %s WHERE %s AND AcctStopTime = 0", inst->config->sql_acct_table, authstr);
406 if ((inst->module->sql_select_query)(sqlsocket, inst->config, querystr) < 0) {
407 radlog(L_ERR, "sql_check_multi: database query error");
411 row = (inst->module->sql_fetch_row)(sqlsocket, inst->config);
412 count = atoi(row[0]);
413 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
415 if (count < maxsimul)
419 * * Setup some stuff, like for MPP detection.
421 if ((fra = pairfind(request, PW_FRAMED_IP_ADDRESS)) != NULL)
422 ipno = htonl(fra->lvalue);
425 sprintf(querystr, "SELECT * FROM %s WHERE %s AND AcctStopTime = 0", inst->config->sql_acct_table, authstr);
426 if ((inst->module->sql_select_query)(sqlsocket, inst->config, querystr) < 0) {
427 radlog(L_ERR, "sql_check_multi: database query error");
430 while ((row = (inst->module->sql_fetch_row)(sqlsocket, inst->config))) {
431 int check = sql_check_ts(row);
436 if (ipno && atoi(row[19]) == ipno)
439 } else if (check == 2)
440 radlog(L_ERR, "rlm_sql: Problem with checkrad [%s] (from nas %s)", name, row[4]);
443 * False record - zap it
446 if (inst->config->deletestalesessions) {
449 radlog(L_ERR, "rlm_sql: Deleteing stale session [%s] (from nas %s/%s)", row[2], row[4], row[5]);
450 sqlsocket1 = sql_get_socket(inst);
451 sprintf(querystr, "DELETE FROM %s WHERE RadAcctId = '%s'", inst->config->sql_acct_table, row[0]);
452 (inst->module->sql_query)(sqlsocket1, inst->config, querystr);
453 (inst->module->sql_finish_query)(sqlsocket1, inst->config);
454 sql_release_socket(inst, sqlsocket1);
458 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
460 return (count < maxsimul) ? 0 : mpp;
463 void query_log(SQL_INST * inst, char *querystr) {
466 if (inst->config->sqltrace) {
467 if ((sqlfile = fopen(inst->config->tracefile, "a")) == (FILE *) NULL) {
468 radlog(L_ERR, "rlm_sql: Couldn't open file %s",
469 inst->config->tracefile);
471 #if defined(F_LOCK) && !defined(BSD)
472 (void) lockf((int) sqlfile, (int) F_LOCK, (off_t) MAX_QUERY_LEN);
474 (void) flock(sqlfile, SQL_LOCK_EX);
476 fputs(querystr, sqlfile);
477 fputs(";\n", sqlfile);
483 VALUE_PAIR * set_userattr(SQL_INST *inst, SQLSOCK *sqlsocket, VALUE_PAIR * first, char *username, char *saveuser, int *savelen) {
485 VALUE_PAIR *uservp = NULL;
486 uint8_t escaped_user[MAX_STRING_LEN];
488 if ((uservp = pairfind(first, PW_USER_NAME)) != NULL) {
490 strNcpy(saveuser, uservp->strvalue, MAX_STRING_LEN);
492 *savelen = uservp->length;
494 (inst->module->sql_escape_string)(sqlsocket, inst->config, escaped_user, username, strlen(username));
496 (inst->module->sql_escape_string)(sqlsocket, inst->config, escaped_user, uservp->strvalue, uservp->length);
498 strNcpy(uservp->strvalue, escaped_user, MAX_STRING_LEN);
499 uservp->length = strlen(escaped_user);
505 void restore_userattr(VALUE_PAIR * uservp, char *saveuser, int savelen) {
507 strNcpy(uservp->strvalue, saveuser, MAX_STRING_LEN);
508 uservp->length = savelen;