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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * Copyright 2001,2006 The FreeRADIUS server project
22 * Copyright 2000 Mike Machado <mike@innercite.com>
23 * Copyright 2000 Alan DeKok <aland@ox.org>
24 * Copyright 2001 Chad Miller <cmiller@surfsouth.com>
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/rad_assert.h>
43 * Translate rlm_sql rcodes to humanly
44 * readable reason strings.
46 const FR_NAME_NUMBER sql_rcode_table[] = {
47 { "success", RLM_SQL_OK },
48 { "need alt query", RLM_SQL_ALT_QUERY },
49 { "server error", RLM_SQL_ERROR },
50 { "query invalid", RLM_SQL_QUERY_INVALID },
51 { "no connection", RLM_SQL_RECONNECT },
56 static int _mod_conn_free(rlm_sql_handle_t *conn)
58 rlm_sql_t *inst = conn->inst;
62 exec_trigger(NULL, inst->cs, "modules.sql.close", false);
67 void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
70 rlm_sql_t *inst = instance;
71 rlm_sql_handle_t *handle;
74 * Connections cannot be alloced from the inst or
75 * pool contexts due to threading issues.
77 handle = talloc_zero(ctx, rlm_sql_handle_t);
78 if (!handle) return NULL;
80 handle->log_ctx = talloc_pool(handle, 2048);
81 if (!handle->log_ctx) {
87 * Handle requires a pointer to the SQL inst so the
88 * destructor has access to the module configuration.
93 * When something frees this handle the destructor set by
94 * the driver will be called first, closing any open sockets.
95 * Then we call our destructor to trigger an modules.sql.close
96 * event, then all the memory is freed.
98 talloc_set_destructor(handle, _mod_conn_free);
100 rcode = (inst->module->sql_socket_init)(handle, inst->config);
103 exec_trigger(NULL, inst->cs, "modules.sql.fail", true);
106 * Destroy any half opened connections.
112 if (inst->config->connect_query) {
113 if (rlm_sql_select_query(inst, NULL, &handle, inst->config->connect_query) != RLM_SQL_OK) goto fail;
114 (inst->module->sql_finish_select_query)(handle, inst->config);
117 exec_trigger(NULL, inst->cs, "modules.sql.open", false);
121 /*************************************************************************
123 * Function: sql_fr_pair_list_afrom_str
125 * Purpose: Read entries from the database and fill VALUE_PAIR structures
127 *************************************************************************/
128 int sql_fr_pair_list_afrom_str(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **head, rlm_sql_row_t row)
131 char const *ptr, *value;
132 char buf[MAX_STRING_LEN];
134 FR_TOKEN token, operator = T_EOL;
137 * Verify the 'Attribute' field
139 if (!row[2] || row[2][0] == '\0') {
140 REDEBUG("The 'Attribute' field is empty or NULL, skipping the entire row");
145 * Verify the 'op' field
147 if (row[4] != NULL && row[4][0] != '\0') {
149 operator = gettoken(&ptr, buf, sizeof(buf), false);
150 if ((operator < T_OP_ADD) ||
151 (operator > T_OP_CMP_EQ)) {
152 REDEBUG("Invalid operator \"%s\" for attribute %s", row[4], row[2]);
158 * Complain about empty or invalid 'op' field
160 operator = T_OP_CMP_EQ;
161 REDEBUG("The 'op' field for attribute '%s = %s' is NULL, or non-existent.", row[2], row[3]);
162 REDEBUG("You MUST FIX THIS if you want the configuration to behave as you expect");
166 * The 'Value' field may be empty or NULL
170 * If we have a new-style quoted string, where the
171 * *entire* string is quoted, do xlat's.
173 if (row[3] != NULL &&
174 ((row[3][0] == '\'') || (row[3][0] == '`') || (row[3][0] == '"')) &&
175 (row[3][0] == row[3][strlen(row[3])-1])) {
177 token = gettoken(&value, buf, sizeof(buf), false);
180 * Take the unquoted string.
182 case T_SINGLE_QUOTED_STRING:
183 case T_DOUBLE_QUOTED_STRING:
188 * Mark the pair to be allocated later.
190 case T_BACK_QUOTED_STRING:
196 * Keep the original string.
207 vp = fr_pair_make(ctx, NULL, row[2], NULL, operator);
209 REDEBUG("Failed to create the pair: %s", fr_strerror());
214 if (fr_pair_mark_xlat(vp, value) < 0) {
215 REDEBUG("Error marking pair for xlat");
221 if (fr_pair_value_from_str(vp, value, -1) < 0) {
222 REDEBUG("Error parsing value: %s", fr_strerror());
230 * Add the pair into the packet
232 fr_pair_add(head, vp);
236 /** Call the driver's sql_fetch_row function
238 * Calls the driver's sql_fetch_row logging any errors. On success, will
239 * write row data to (*handle)->row.
241 * @param inst Instance of rlm_sql.
242 * @param request The Current request, may be NULL.
243 * @param handle Handle to retrieve errors for.
244 * @return on success RLM_SQL_OK, other sql_rcode_t constants on error.
246 sql_rcode_t rlm_sql_fetch_row(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **handle)
250 if (!*handle || !(*handle)->conn) return RLM_SQL_ERROR;
253 * We can't implement reconnect logic here, because the caller
254 * may require the original connection to free up queries or
255 * result sets associated with that connection.
257 ret = (inst->module->sql_fetch_row)(*handle, inst->config);
259 MOD_ROPTIONAL(RERROR, ERROR, "Error fetching row");
261 rlm_sql_print_error(inst, request, *handle, false);
267 /** Retrieve any errors from the SQL driver
269 * Retrieves errors from the driver from the last operation and writes them to
270 * to request/global log, in the ERROR, WARN, INFO and DEBUG categories.
272 * @param inst Instance of rlm_sql.
273 * @param request Current request, may be NULL.
274 * @param handle Handle to retrieve errors for.
275 * @param force_debug Force all errors to be logged as debug messages.
277 void rlm_sql_print_error(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t *handle, bool force_debug)
280 sql_log_entry_t log[20];
283 num = (inst->module->sql_error)(handle->log_ctx, log, (sizeof(log) / sizeof(*log)), handle, inst->config);
285 MOD_ROPTIONAL(RERROR, ERROR, "Unknown error");
289 driver = inst->config->sql_driver_name;
291 for (i = 0; i < num; i++) {
292 if (force_debug) goto debug;
294 switch (log[i].type) {
296 MOD_ROPTIONAL(RERROR, ERROR, "%s: %s", driver, log[i].msg);
300 MOD_ROPTIONAL(RWARN, WARN, "%s: %s", driver, log[i].msg);
304 MOD_ROPTIONAL(RINFO, INFO, "%s: %s", driver, log[i].msg);
310 MOD_ROPTIONAL(RDEBUG, DEBUG, "%s: %s", driver, log[i].msg);
315 talloc_free_children(handle->log_ctx);
318 /** Call the driver's sql_query method, reconnecting if necessary.
320 * @note Caller must call (inst->module->sql_finish_query)(handle, inst->config);
321 * after they're done with the result.
323 * @param handle to query the database with. *handle should not be NULL, as this indicates
324 * previous reconnection attempt has failed.
325 * @param request Current request.
326 * @param inst rlm_sql instance data.
327 * @param query to execute. Should not be zero length.
328 * @return RLM_SQL_OK on success, RLM_SQL_RECONNECT if a new handle is required
329 * (also sets *handle = NULL), RLM_SQL_QUERY_INVALID/RLM_SQL_ERROR on invalid query or
330 * connection error, RLM_SQL_ALT_QUERY on constraints violation.
332 sql_rcode_t rlm_sql_query(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **handle, char const *query)
334 int ret = RLM_SQL_ERROR;
337 /* Caller should check they have a valid handle */
340 /* There's no query to run, return an error */
341 if (query[0] == '\0') {
342 if (request) REDEBUG("Zero length query");
343 return RLM_SQL_QUERY_INVALID;
347 * inst->pool may be NULL is this function is called by mod_conn_create.
349 count = inst->pool ? fr_connection_pool_get_num(inst->pool) : 0;
352 * Here we try with each of the existing connections, then try to create
353 * a new connection, then give up.
355 for (i = 0; i < (count + 1); i++) {
356 MOD_ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query);
358 ret = (inst->module->sql_query)(*handle, inst->config, query);
364 * Run through all available sockets until we exhaust all existing
365 * sockets in the pool and fail to establish a *new* connection.
367 case RLM_SQL_RECONNECT:
368 *handle = fr_connection_reconnect(inst->pool, *handle);
369 /* Reconnection failed */
370 if (!*handle) return RLM_SQL_RECONNECT;
371 /* Reconnection succeeded, try again with the new handle */
375 * These are bad and should make rlm_sql return invalid
377 case RLM_SQL_QUERY_INVALID:
378 rlm_sql_print_error(inst, request, *handle, false);
379 (inst->module->sql_finish_query)(*handle, inst->config);
383 * Server or client errors.
385 * If the driver claims to be able to distinguish between
386 * duplicate row errors and other errors, and we hit a
387 * general error treat it as a failure.
389 * Otherwise rewrite it to RLM_SQL_ALT_QUERY.
392 if (inst->module->flags & RLM_SQL_RCODE_FLAGS_ALT_QUERY) {
393 rlm_sql_print_error(inst, request, *handle, false);
394 (inst->module->sql_finish_query)(*handle, inst->config);
397 ret = RLM_SQL_ALT_QUERY;
401 * Driver suggested using an alternative query
403 case RLM_SQL_ALT_QUERY:
404 rlm_sql_print_error(inst, request, *handle, true);
405 (inst->module->sql_finish_query)(*handle, inst->config);
413 MOD_ROPTIONAL(RERROR, ERROR, "Hit reconnection limit");
415 return RLM_SQL_ERROR;
418 /** Call the driver's sql_select_query method, reconnecting if necessary.
420 * @note Caller must call (inst->module->sql_finish_select_query)(handle, inst->config);
421 * after they're done with the result.
423 * @param inst rlm_sql instance data.
424 * @param request Current request.
425 * @param handle to query the database with. *handle should not be NULL, as this indicates
426 * previous reconnection attempt has failed.
427 * @param query to execute. Should not be zero length.
428 * @return RLM_SQL_OK on success, RLM_SQL_RECONNECT if a new handle is required (also sets *handle = NULL),
429 * RLM_SQL_QUERY_INVALID/RLM_SQL_ERROR on invalid query or connection error.
431 sql_rcode_t rlm_sql_select_query(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **handle, char const *query)
433 int ret = RLM_SQL_ERROR;
436 /* Caller should check they have a valid handle */
439 /* There's no query to run, return an error */
440 if (query[0] == '\0') {
441 if (request) REDEBUG("Zero length query");
443 return RLM_SQL_QUERY_INVALID;
447 * inst->pool may be NULL is this function is called by mod_conn_create.
449 count = inst->pool ? fr_connection_pool_get_num(inst->pool) : 0;
452 * For sanity, for when no connections are viable, and we can't make a new one
454 for (i = 0; i < (count + 1); i++) {
455 MOD_ROPTIONAL(RDEBUG2, DEBUG2, "Executing select query: %s", query);
457 ret = (inst->module->sql_select_query)(*handle, inst->config, query);
463 * Run through all available sockets until we exhaust all existing
464 * sockets in the pool and fail to establish a *new* connection.
466 case RLM_SQL_RECONNECT:
467 *handle = fr_connection_reconnect(inst->pool, *handle);
468 /* Reconnection failed */
469 if (!*handle) return RLM_SQL_RECONNECT;
470 /* Reconnection succeeded, try again with the new handle */
473 case RLM_SQL_QUERY_INVALID:
476 rlm_sql_print_error(inst, request, *handle, false);
477 (inst->module->sql_finish_select_query)(*handle, inst->config);
484 MOD_ROPTIONAL(RERROR, ERROR, "Hit reconnection limit");
486 return RLM_SQL_ERROR;
490 /*************************************************************************
492 * Function: sql_getvpdata
494 * Purpose: Get any group check or reply pairs
496 *************************************************************************/
497 int sql_getvpdata(TALLOC_CTX *ctx, rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **handle,
498 VALUE_PAIR **pair, char const *query)
506 rcode = rlm_sql_select_query(inst, request, handle, query);
507 if (rcode != RLM_SQL_OK) return -1; /* error handled by rlm_sql_select_query */
509 while (rlm_sql_fetch_row(inst, request, handle) == 0) {
510 row = (*handle)->row;
512 if (sql_fr_pair_list_afrom_str(ctx, request, pair, row) != 0) {
513 REDEBUG("Error parsing user data from database result");
515 (inst->module->sql_finish_select_query)(*handle, inst->config);
521 (inst->module->sql_finish_select_query)(*handle, inst->config);
527 * Log the query to a file.
529 void rlm_sql_query_log(rlm_sql_t *inst, REQUEST *request,
530 sql_acct_section_t *section, char const *query)
533 char const *filename = NULL;
534 char *expanded = NULL;
536 bool failed = false; /* Write the log message outside of the critical region */
539 filename = section->logfile;
541 filename = inst->config->logfile;
548 if (radius_axlat(&expanded, request, filename, NULL, NULL) < 0) {
552 fd = exfile_open(inst->ef, filename, 0640, true);
554 ERROR("rlm_sql (%s): Couldn't open logfile '%s': %s", inst->name,
555 expanded, fr_syserror(errno));
557 talloc_free(expanded);
562 if ((write(fd, query, len) < 0) || (write(fd, ";\n", 2) < 0)) {
567 ERROR("rlm_sql (%s): Failed writing to logfile '%s': %s", inst->name, expanded,
571 talloc_free(expanded);
572 exfile_close(inst->ef, fd);