The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
sql.c
Go to the documentation of this file.
1 /*
2  * sql.c rlm_sql - FreeRADIUS SQL Module
3  * Main code directly taken from ICRADIUS
4  *
5  * Version: $Id: 14672d7aac77fb61ceeea1da4a20f359fa491199 $
6  *
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.
11  *
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.
16  *
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
20  *
21  * @copyright 2001,2006 The FreeRADIUS server project
22  * @copyright 2000 Mike Machado (mike@innercite.com)
23  * @copyright 2000 Alan DeKok (aland@freeradius.org)
24  * @copyright 2001 Chad Miller (cmiller@surfsouth.com)
25  */
26 
27 RCSID("$Id: 14672d7aac77fb61ceeea1da4a20f359fa491199 $")
28 
29 #define LOG_PREFIX inst->name
30 
31 #include <freeradius-devel/server/base.h>
32 #include <freeradius-devel/util/debug.h>
33 
34 #include <sys/file.h>
35 #include <sys/stat.h>
36 
37 #include <ctype.h>
38 
39 #include "rlm_sql.h"
40 
41 /*
42  * Translate rlm_sql rcodes to humanly
43  * readable reason strings.
44  */
46  { L("need alt query"), RLM_SQL_ALT_QUERY },
47  { L("no connection"), RLM_SQL_RECONNECT },
48  { L("no more rows"), RLM_SQL_NO_MORE_ROWS },
49  { L("query invalid"), RLM_SQL_QUERY_INVALID },
50  { L("server error"), RLM_SQL_ERROR },
51  { L("success"), RLM_SQL_OK }
52 };
54 
56  { L("alternate"), RLM_SQL_ALT_QUERY },
57  { L("empty"), RLM_SQL_NO_MORE_ROWS },
58  { L("error"), RLM_SQL_ERROR },
59  { L("invalid"), RLM_SQL_QUERY_INVALID },
60  { L("ok"), RLM_SQL_OK },
61  { L("reconnect"), RLM_SQL_RECONNECT }
62 };
64 
65 void *sql_mod_conn_create(TALLOC_CTX *ctx, void *instance, fr_time_delta_t timeout)
66 {
67  int rcode;
68  rlm_sql_t *inst = talloc_get_type_abort(instance, rlm_sql_t);
69  rlm_sql_handle_t *handle;
70 
71  /*
72  * Connections cannot be alloced from the inst or
73  * pool contexts due to threading issues.
74  */
75  handle = talloc_zero(ctx, rlm_sql_handle_t);
76  if (!handle) return NULL;
77 
78  handle->log_ctx = talloc_pool(handle, 2048);
79  if (!handle->log_ctx) {
80  talloc_free(handle);
81  return NULL;
82  }
83 
84  /*
85  * Handle requires a pointer to the SQL inst so the
86  * destructor has access to the module configuration.
87  */
88  handle->inst = inst;
89 
90  rcode = (inst->driver->sql_socket_init)(handle, &inst->config, timeout);
91  if (rcode != 0) {
92  fail:
93  /*
94  * Destroy any half opened connections.
95  */
96  talloc_free(handle);
97  return NULL;
98  }
99 
100  if (inst->config.connect_query) {
101  fr_sql_query_t *query_ctx;
102  rlm_rcode_t p_result;
103  MEM(query_ctx = fr_sql_query_alloc(ctx, inst, NULL, handle, NULL, inst->config.connect_query, SQL_QUERY_OTHER));
104  inst->query(&p_result, NULL, NULL, query_ctx);
105  if (query_ctx->rcode != RLM_SQL_OK) {
106  talloc_free(query_ctx);
107  goto fail;
108  }
109  talloc_free(query_ctx);
110  }
111 
112  return handle;
113 }
114 
115 #if 0
116 /*************************************************************************
117  *
118  * Function: sql_pair_afrom_row
119  *
120  * Purpose: Convert one rlm_sql_row_t to a fr_pair_t, and add it to "out"
121  *
122  *************************************************************************/
123 static int sql_pair_afrom_row(TALLOC_CTX *ctx, request_t *request, fr_pair_list_t *out, rlm_sql_row_t row, fr_pair_t **relative_vp)
124 {
125  fr_pair_t *vp;
126  char const *ptr, *value;
127  char buf[FR_MAX_STRING_LEN];
128  fr_dict_attr_t const *da;
129  fr_token_t token, op = T_EOL;
130  fr_pair_list_t *my_list;
131  TALLOC_CTX *my_ctx;
132 
133  /*
134  * Verify the 'Attribute' field
135  */
136  if (!row[2] || row[2][0] == '\0') {
137  REDEBUG("Attribute field is empty or NULL, skipping the entire row");
138  return -1;
139  }
140 
141  /*
142  * Verify the 'op' field
143  */
144  if (row[4] != NULL && row[4][0] != '\0') {
145  ptr = row[4];
146  op = gettoken(&ptr, buf, sizeof(buf), false);
147  if (!fr_assignment_op[op] && !fr_comparison_op[op]) {
148  REDEBUG("Invalid op \"%s\" for attribute %s", row[4], row[2]);
149  return -1;
150  }
151 
152  } else {
153  /*
154  * Complain about empty or invalid 'op' field
155  */
156  op = T_OP_CMP_EQ;
157  REDEBUG("The op field for attribute '%s = %s' is NULL, or non-existent.", row[2], row[3]);
158  REDEBUG("You MUST FIX THIS if you want the configuration to behave as you expect");
159  }
160 
161  /*
162  * The 'Value' field may be empty or NULL
163  */
164  if (!row[3]) {
165  REDEBUG("Value field is empty or NULL, skipping the entire row");
166  return -1;
167  }
168 
169  RDEBUG3("Found row[%s]: %s %s %s", row[0], row[2], fr_table_str_by_value(fr_tokens_table, op, "<INVALID>"), row[3]);
170 
171  value = row[3];
172 
173  /*
174  * If we have a string, where the *entire* string is
175  * quoted, do xlat's.
176  */
177  if (row[3] != NULL &&
178  ((row[3][0] == '\'') || (row[3][0] == '`') || (row[3][0] == '"')) &&
179  (row[3][0] == row[3][strlen(row[3])-1])) {
180 
181  token = gettoken(&value, buf, sizeof(buf), false);
182  switch (token) {
183  /*
184  * Take the unquoted string.
185  */
189  value = buf;
190  break;
191 
192  /*
193  * Keep the original string.
194  */
195  default:
196  value = row[3];
197  break;
198  }
199  }
200 
201  /*
202  * Check for relative attributes
203  *
204  * @todo - allow "..foo" to mean "grandparent of
205  * relative_vp", and it should also update relative_vp
206  * with the new parent. However, doing this means
207  * walking the list of the current relative_vp, finding
208  * the dlist head, and then converting that into a
209  * fr_pair_t pointer. That's complex, so we don't do it
210  * right now.
211  */
212  if (row[2][0] == '.') {
213  char const *p = row[2];
214 
215  if (!*relative_vp) {
216  REDEBUG("Relative attribute '%s' can only be used immediately after an attribute of type 'group'", row[2]);
217  return -1;
218  }
219 
220  da = fr_dict_attr_by_oid(NULL, (*relative_vp)->da, p + 1);
221  if (!da) goto unknown;
222 
223  my_list = &(*relative_vp)->vp_group;
224  my_ctx = *relative_vp;
225 
226  MEM(vp = fr_pair_afrom_da(my_ctx, da));
227  fr_pair_append(my_list, vp);
228  } else {
229  /*
230  * Search in our local dictionary
231  * falling back to internal.
232  */
233  da = fr_dict_attr_by_oid(NULL, fr_dict_root(request->dict), row[2]);
234  if (!da) {
235  da = fr_dict_attr_by_oid(NULL, fr_dict_root(fr_dict_internal()), row[2]);
236  if (!da) {
237  unknown:
238  RPEDEBUG("Unknown attribute '%s'", row[2]);
239  return -1;
240  }
241  }
242 
243  my_list = out;
244  my_ctx = ctx;
245 
246  MEM(vp = fr_pair_afrom_da_nested(my_ctx, my_list, da));
247  }
248 
249  vp->op = op;
250 
251  if ((vp->vp_type == FR_TYPE_TLV) && !*value) {
252  /*
253  * Allow empty values for TLVs: we just create the value.
254  *
255  * fr_pair_value_from_str() is not yet updated to
256  * handle TLVs. Until such time as we know what
257  * to do there, we will just do a hack here,
258  * specific to the SQL module.
259  */
260  } else {
261  if (fr_pair_value_from_str(vp, value, strlen(value), NULL, true) < 0) {
262  RPEDEBUG("Error parsing value");
263  return -1;
264  }
265  }
266 
267  /*
268  * Update the relative vp.
269  */
270  if (my_list == out) switch (da->type) {
271  case FR_TYPE_STRUCTURAL:
272  *relative_vp = vp;
273  break;
274 
275  default:
276  break;
277  }
278 
279  /*
280  * If there's a relative VP, and it's not the one
281  * we just added above, and we're not adding this
282  * VP to the relative one, then nuke the relative
283  * VP.
284  */
285  if (*relative_vp && (vp != *relative_vp) && (my_ctx != *relative_vp)) {
286  *relative_vp = NULL;
287  }
288 
289  return 0;
290 }
291 #endif
292 
293 /** Call the driver's sql_fetch_row function
294  *
295  * Calls the driver's sql_fetch_row logging any errors. On success, will
296  * write row data to ``uctx->row``.
297  *
298  * The rcode within the query context is updated to
299  * - #RLM_SQL_OK on success.
300  * - other #sql_rcode_t constants on error.
301  *
302  * @param p_result Result of current module call.
303  * @param priority Unused.
304  * @param request Current request.
305  * @param uctx query context containing query to execute.
306  * @return an unlang_action_t.
307  */
308 unlang_action_t rlm_sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
309 {
310  fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
311  rlm_sql_t const *inst = query_ctx->inst;
312 
313  if ((inst->driver->uses_trunks && !query_ctx->tconn) ||
314  (!inst->driver->uses_trunks && (!query_ctx->handle || !query_ctx->handle->conn))) {
315  ROPTIONAL(RERROR, ERROR, "Invalid connection");
316  query_ctx->rcode = RLM_SQL_ERROR;
318  }
319 
320  /*
321  * We can't implement reconnect logic here, because the caller
322  * may require the original connection to free up queries or
323  * result sets associated with that connection.
324  */
325  (inst->driver->sql_fetch_row)(p_result, NULL, request, query_ctx);
326  switch (query_ctx->rcode) {
327  case RLM_SQL_OK:
328  fr_assert(query_ctx->row != NULL);
330 
332  fr_assert(query_ctx->row == NULL);
334 
335  default:
336  ROPTIONAL(RERROR, ERROR, "Error fetching row");
337  rlm_sql_print_error(inst, request, query_ctx, false);
339  }
340 }
341 
342 /** Retrieve any errors from the SQL driver
343  *
344  * Retrieves errors from the driver from the last operation and writes them to
345  * to request/global log, in the ERROR, WARN, INFO and DEBUG categories.
346  *
347  * @param inst Instance of rlm_sql.
348  * @param request Current request, may be NULL.
349  * @param query_ctx Query context to retrieve errors for.
350  * @param force_debug Force all errors to be logged as debug messages.
351  */
352 void rlm_sql_print_error(rlm_sql_t const *inst, request_t *request, fr_sql_query_t *query_ctx, bool force_debug)
353 {
354  char const *driver = inst->driver_submodule->name;
355  sql_log_entry_t log[20];
356  size_t num, i;
357  TALLOC_CTX *log_ctx = talloc_new(NULL);
358 
359  num = (inst->driver->sql_error)(log_ctx, log, (NUM_ELEMENTS(log)), query_ctx);
360  if (num == 0) {
361  ROPTIONAL(RERROR, ERROR, "Unknown error");
362  talloc_free(log_ctx);
363  return;
364  }
365 
366  for (i = 0; i < num; i++) {
367  if (force_debug) goto debug;
368 
369  switch (log[i].type) {
370  case L_ERR:
371  ROPTIONAL(RERROR, ERROR, "%s: %s", driver, log[i].msg);
372  break;
373 
374  case L_WARN:
375  ROPTIONAL(RWARN, WARN, "%s: %s", driver, log[i].msg);
376  break;
377 
378  case L_INFO:
379  ROPTIONAL(RINFO, INFO, "%s: %s", driver, log[i].msg);
380  break;
381 
382  case L_DBG:
383  default:
384  debug:
385  ROPTIONAL(RDEBUG2, DEBUG2, "%s: %s", driver, log[i].msg);
386  break;
387  }
388  }
389 
390  talloc_free(log_ctx);
391 }
392 
393 /** Automatically run the correct `finish` function when freeing an SQL query
394  *
395  * And mark any associated trunk request as complete.
396  */
397 static int fr_sql_query_free(fr_sql_query_t *to_free)
398 {
399  if (to_free->status > 0) {
400  if (to_free->type == SQL_QUERY_SELECT) {
401  (to_free->inst->driver->sql_finish_select_query)(to_free, &to_free->inst->config);
402  } else {
403  (to_free->inst->driver->sql_finish_query)(to_free, &to_free->inst->config);
404  }
405  }
406  if (to_free->treq) trunk_request_signal_complete(to_free->treq);
407  return 0;
408 }
409 
410 /** Allocate an sql query structure
411  *
412  */
413 fr_sql_query_t *fr_sql_query_alloc(TALLOC_CTX *ctx, rlm_sql_t const *inst, request_t *request, rlm_sql_handle_t *handle,
414  trunk_t *trunk, char const *query_str, fr_sql_query_type_t type)
415 {
416  fr_sql_query_t *query;
417  MEM(query = talloc(ctx, fr_sql_query_t));
418  *query = (fr_sql_query_t) {
419  .inst = inst,
420  .handle = handle,
421  .request = request,
422  .trunk = trunk,
423  .query_str = query_str,
424  .type = type
425  };
426  talloc_set_destructor(query, fr_sql_query_free);
427  return query;
428 }
429 
430 /** Call the driver's sql_query method, reconnecting if necessary.
431  *
432  * @note Caller must call ``(inst->driver->sql_finish_query)(handle, &inst->config);``
433  * after they're done with the result.
434  *
435  * The rcode within the query context is updated to
436  * - #RLM_SQL_OK on success.
437  * - #RLM_SQL_RECONNECT if a new handle is required (also sets the handle to NULL).
438  * - #RLM_SQL_QUERY_INVALID, #RLM_SQL_ERROR on invalid query or connection error.
439  * - #RLM_SQL_ALT_QUERY on constraints violation.
440  *
441  * @param p_result Result of current module call.
442  * @param priority Unused.
443  * @param request Current request.
444  * @param uctx query context containing query to execute.
445  * @return an unlang_action_t.
446  */
447 unlang_action_t rlm_sql_query(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
448 {
449  fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
450  rlm_sql_t const *inst = query_ctx->inst;
451  int i, count;
452 
453  /* Caller should check they have a valid handle */
454  fr_assert(query_ctx->handle);
455 
456  /* There's no query to run, return an error */
457  if (query_ctx->query_str[0] == '\0') {
458  if (request) REDEBUG("Zero length query");
460  }
461 
462  /*
463  * inst->pool may be NULL is this function is called by sql_mod_conn_create.
464  */
465  count = inst->pool ? fr_pool_state(inst->pool)->num : 0;
466 
467  /*
468  * Here we try with each of the existing connections, then try to create
469  * a new connection, then give up.
470  */
471  for (i = 0; i < (count + 1); i++) {
472  ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query_ctx->query_str);
473 
474  (inst->driver->sql_query)(p_result, NULL, request, query_ctx);
475  query_ctx->status = SQL_QUERY_SUBMITTED;
476  switch (query_ctx->rcode) {
477  case RLM_SQL_OK:
479 
480  /*
481  * Run through all available sockets until we exhaust all existing
482  * sockets in the pool and fail to establish a *new* connection.
483  */
484  case RLM_SQL_RECONNECT:
485  query_ctx->handle = fr_pool_connection_reconnect(inst->pool, request, query_ctx->handle);
486  /* Reconnection failed */
487  if (!query_ctx->handle) RETURN_MODULE_FAIL;
488  /* Reconnection succeeded, try again with the new handle */
489  continue;
490 
491  /*
492  * These are bad and should make rlm_sql return invalid
493  */
495  rlm_sql_print_error(inst, request, query_ctx, false);
496  (inst->driver->sql_finish_query)(query_ctx, &inst->config);
498 
499  /*
500  * Server or client errors.
501  *
502  * If the driver claims to be able to distinguish between
503  * duplicate row errors and other errors, and we hit a
504  * general error treat it as a failure.
505  *
506  * Otherwise rewrite it to RLM_SQL_ALT_QUERY.
507  */
508  case RLM_SQL_ERROR:
509  if (inst->driver->flags & RLM_SQL_RCODE_FLAGS_ALT_QUERY) {
510  rlm_sql_print_error(inst, request, query_ctx, false);
511  (inst->driver->sql_finish_query)(query_ctx, &inst->config);
513  }
514  FALL_THROUGH;
515 
516  /*
517  * Driver suggested using an alternative query
518  */
519  case RLM_SQL_ALT_QUERY:
520  rlm_sql_print_error(inst, request, query_ctx, true);
521  (inst->driver->sql_finish_query)(query_ctx, &inst->config);
522  break;
523 
524  default:
525  break;
526  }
527 
529  }
530 
531  ROPTIONAL(RERROR, ERROR, "Hit reconnection limit");
532 
533  query_ctx->rcode = RLM_SQL_ERROR;
535 }
536 
537 /** Yield processing after submitting a trunk request.
538  */
540  UNUSED request_t *request, UNUSED void *uctx)
541 {
542  return UNLANG_ACTION_YIELD;
543 }
544 
545 /** Cancel an SQL query submitted on a trunk
546  */
547 static void sql_trunk_query_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
548 {
549  fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
550 
551  if (!query_ctx->treq) return;
552 
553  /*
554  * The query_ctx needs to be parented by the treq so that it still exists
555  * when the cancel_mux callback is run.
556  */
557  if (query_ctx->inst->driver->trunk_io_funcs.request_cancel_mux) talloc_steal(query_ctx->treq, query_ctx);
558 
559  trunk_request_signal_cancel(query_ctx->treq);
560 
561  query_ctx->treq = NULL;
562 }
563 
564 /** Submit an SQL query using a trunk connection.
565  *
566  * @param p_result Result of current module call.
567  * @param priority Unused.
568  * @param request Current request.
569  * @param uctx query context containing query to execute.
570  * @return an unlang_action_t.
571  */
572 unlang_action_t rlm_sql_trunk_query(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
573 {
574  fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
575  trunk_enqueue_t status;
576 
577  fr_assert(query_ctx->trunk);
578 
579  /* There's no query to run, return an error */
580  if (query_ctx->query_str[0] == '\0') {
581  if (request) REDEBUG("Zero length query");
583  }
584 
585  /*
586  * If the query already has a treq, and that is not in the "init" state
587  * then this is part of an ongoing transaction and needs requeueing
588  * to submit on the same connection.
589  */
590  if (query_ctx->treq && query_ctx->treq->state != TRUNK_REQUEST_STATE_INIT) {
591  status = trunk_request_requeue(query_ctx->treq);
592  } else {
593  status = trunk_request_enqueue(&query_ctx->treq, query_ctx->trunk, request, query_ctx, NULL);
594  }
595  switch (status) {
596  case TRUNK_ENQUEUE_OK:
598  /*
599  * Drivers such as SQLite which are synchronous run the query immediately
600  * on queueing. If the query fails then the trunk request will be failed
601  * in which case the query_ctx will no longer have a trunk request.
602  */
603  if (!query_ctx->treq) RETURN_MODULE_FAIL;
604 
605  /*
606  * Synchronous drivers will have processed the query and set the
607  * state of the trunk request to reapable - so in that case don't
608  * yield (in sql_trunk_query_start)
609  */
610  if (unlang_function_push(request,
611  query_ctx->treq->state == TRUNK_REQUEST_STATE_REAPABLE ?
612  NULL : sql_trunk_query_start,
613  query_ctx->type == SQL_QUERY_SELECT ?
614  query_ctx->inst->driver->sql_select_query_resume :
615  query_ctx->inst->driver->sql_query_resume,
616  sql_trunk_query_cancel, ~FR_SIGNAL_CANCEL,
617  UNLANG_SUB_FRAME, query_ctx) < 0) RETURN_MODULE_FAIL;
618  *p_result = RLM_MODULE_OK;
620 
621  default:
622  REDEBUG("Unable to enqueue SQL query");
623  query_ctx->status = SQL_QUERY_FAILED;
624  query_ctx->rcode = RLM_SQL_ERROR;
626  }
627 }
628 
629 /** Call the driver's sql_select_query method, reconnecting if necessary.
630  *
631  * @note Caller must call ``(inst->driver->sql_finish_select_query)(handle, &inst->config);``
632  * after they're done with the result.
633  *
634  * The rcode within the query context is updated to
635  * - #RLM_SQL_OK on success.
636  * - #RLM_SQL_RECONNECT if a new handle is required (also sets the handle to NULL).
637  * - #RLM_SQL_QUERY_INVALID, #RLM_SQL_ERROR on invalid query or connection error.
638  * - #RLM_SQL_ALT_QUERY on constraints violation.
639  *
640  * @param p_result Result of current module call.
641  * @param priority Unused.
642  * @param request Current request.
643  * @param uctx query context containing query to execute.
644  * @return an unlang_action_t.
645  */
646 unlang_action_t rlm_sql_select_query(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
647 {
648  fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
649  rlm_sql_t const *inst = query_ctx->inst;
650  int i, count;
651 
652  /* Caller should check they have a valid handle */
653  fr_assert(query_ctx->handle);
654 
655  /* There's no query to run, return an error */
656  if (query_ctx->query_str[0] == '\0') {
657  if (request) REDEBUG("Zero length query");
658  query_ctx->rcode = RLM_SQL_QUERY_INVALID;
660  }
661 
662  /*
663  * inst->pool may be NULL is this function is called by sql_mod_conn_create.
664  */
665  count = inst->pool ? fr_pool_state(inst->pool)->num : 0;
666 
667  /*
668  * For sanity, for when no connections are viable, and we can't make a new one
669  */
670  for (i = 0; i < (count + 1); i++) {
671  ROPTIONAL(RDEBUG2, DEBUG2, "Executing select query: %s", query_ctx->query_str);
672 
673  (inst->driver->sql_select_query)(p_result, NULL, request, query_ctx);
674  query_ctx->status = SQL_QUERY_SUBMITTED;
675  switch (query_ctx->rcode) {
676  case RLM_SQL_OK:
678 
679  /*
680  * Run through all available sockets until we exhaust all existing
681  * sockets in the pool and fail to establish a *new* connection.
682  */
683  case RLM_SQL_RECONNECT:
684  query_ctx->handle = fr_pool_connection_reconnect(inst->pool, request, query_ctx->handle);
685  /* Reconnection failed */
686  if (!query_ctx->handle) RETURN_MODULE_FAIL;
687  /* Reconnection succeeded, try again with the new handle */
688  continue;
689 
691  case RLM_SQL_ERROR:
692  default:
693  rlm_sql_print_error(inst, request, query_ctx, false);
694  (inst->driver->sql_finish_select_query)(query_ctx, &inst->config);
697  }
698  }
699 
700  ROPTIONAL(RERROR, ERROR, "Hit reconnection limit");
701 
702  query_ctx->rcode = RLM_SQL_ERROR;
704 }
705 
706 /** Process the results of an SQL query to produce a map list.
707  *
708  */
709 static unlang_action_t sql_get_map_list_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
710 {
711  fr_sql_map_ctx_t *map_ctx = talloc_get_type_abort(uctx, fr_sql_map_ctx_t);
712  tmpl_rules_t lhs_rules = (tmpl_rules_t) {
713  .attr = {
714  .dict_def = request->dict,
715  .prefix = TMPL_ATTR_REF_PREFIX_AUTO,
716  .list_def = map_ctx->list,
717  .list_presence = TMPL_ATTR_LIST_ALLOW
718  }
719  };
720  tmpl_rules_t rhs_rules = lhs_rules;
721  fr_sql_query_t *query_ctx = map_ctx->query_ctx;
722  rlm_sql_row_t row;
723  map_t *parent = NULL;
724  rlm_sql_t const *inst = map_ctx->inst;
725 
727  rhs_rules.attr.list_def = request_attr_request;
728 
729  if (query_ctx->rcode != RLM_SQL_OK) RETURN_MODULE_FAIL;
730 
731  while ((inst->fetch_row(p_result, NULL, request, query_ctx) == UNLANG_ACTION_CALCULATE_RESULT) &&
732  (query_ctx->rcode == RLM_SQL_OK)) {
733  map_t *map;
734 
735  row = query_ctx->row;
736  if (!row[2] || !row[3] || !row[4]) {
737  RPERROR("SQL query returned NULL values");
739  }
740  if (map_afrom_fields(map_ctx->ctx, &map, &parent, request, row[2], row[4], row[3], &lhs_rules, &rhs_rules) < 0) {
741  RPEDEBUG("Error parsing user data from database result");
743  }
744  if (!map->parent) map_list_insert_tail(map_ctx->out, map);
745 
746  map_ctx->rows++;
747  }
748  talloc_free(query_ctx);
749 
751 }
752 
753 /** Submit the query to get any user / group check or reply pairs
754  *
755  */
757  trunk_t *trunk)
758 {
759  rlm_sql_t const *inst = map_ctx->inst;
760 
761  fr_assert(request);
762 
763  MEM(map_ctx->query_ctx = fr_sql_query_alloc(map_ctx->ctx, inst, request, *handle, trunk,
764  map_ctx->query->vb_strvalue, SQL_QUERY_SELECT));
765 
766  if (unlang_function_push(request, NULL, sql_get_map_list_resume, NULL, 0, UNLANG_SUB_FRAME, map_ctx) < 0) return UNLANG_ACTION_FAIL;
767 
768  return unlang_function_push(request, inst->select, NULL, NULL, 0, UNLANG_SUB_FRAME, map_ctx->query_ctx);
769 }
770 
771 /*
772  * Log the query to a file.
773  */
774 void rlm_sql_query_log(rlm_sql_t const *inst, char const *filename, char const *query)
775 {
776  int fd;
777  size_t len;
778  bool failed = false; /* Write the log message outside of the critical region */
779 
780  fd = exfile_open(inst->ef, filename, 0640, NULL);
781  if (fd < 0) {
782  ERROR("Couldn't open logfile '%s': %s", filename, fr_syserror(errno));
783 
784  /* coverity[missing_unlock] */
785  return;
786  }
787 
788  len = strlen(query);
789  if ((write(fd, query, len) < 0) || (write(fd, ";\n", 2) < 0)) failed = true;
790 
791  if (failed) ERROR("Failed writing to logfile '%s': %s", filename, fr_syserror(errno));
792 
793  exfile_close(inst->ef, fd);
794 }
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition: action.h:39
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition: action.h:36
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition: action.h:37
@ UNLANG_ACTION_YIELD
Temporarily pause execution until an event occurs.
Definition: action.h:42
log_entry msg
Definition: acutest.h:794
#define RCSID(id)
Definition: build.h:481
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
static fr_time_delta_t timeout
Definition: dhcpclient.c:54
fr_dict_attr_t const * fr_dict_attr_by_oid(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *oid))
Resolve an attribute using an OID string.
Definition: dict_util.c:2373
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict_util.c:2400
fr_dict_t const * fr_dict_internal(void)
Definition: dict_util.c:4610
Test enumeration values.
Definition: dict_test.h:92
int exfile_open(exfile_t *ef, char const *filename, mode_t permissions, off_t *offset)
Open a new log file, or maybe an existing one.
Definition: exfile.c:506
int exfile_close(exfile_t *ef, int fd)
Close the log file.
Definition: exfile.c:562
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition: function.h:111
#define UNLANG_SUB_FRAME
Definition: interpret.h:36
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition: log.h:528
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RWARN(fmt,...)
Definition: log.h:297
#define RERROR(fmt,...)
Definition: log.h:298
#define RPERROR(fmt,...)
Definition: log.h:302
#define RINFO(fmt,...)
Definition: log.h:296
#define RPEDEBUG(fmt,...)
Definition: log.h:376
int map_afrom_fields(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, request_t *request, char const *lhs, char const *op_str, char const *rhs, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules)
Convert a fr_pair_t into a map.
Definition: map.c:2439
talloc_free(reap)
@ L_WARN
Warning.
Definition: log.h:57
@ L_ERR
Error message.
Definition: log.h:56
@ L_INFO
Informational message.
Definition: log.h:55
@ L_DBG
Only displayed when debugging is enabled.
Definition: log.h:59
@ TMPL_ATTR_REF_PREFIX_AUTO
Attribute refs may have a '&' prefix.
Definition: merged_model.c:231
@ TMPL_ATTR_REF_PREFIX_YES
Attribute refs must have '&' prefix.
Definition: merged_model.c:229
@ FR_TYPE_TLV
Contains nested attributes.
Definition: merged_model.c:118
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition: pair.c:283
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition: pair.c:1345
fr_pair_t * fr_pair_afrom_da_nested(TALLOC_CTX *ctx, fr_pair_list_t *list, fr_dict_attr_t const *da)
Create a pair (and all intermediate parents), and append it to the list.
Definition: pair.c:467
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, bool tainted)
Convert string value to native attribute value.
Definition: pair.c:2589
fr_pool_state_t const * fr_pool_state(fr_pool_t *pool)
Get the number of connections currently in the pool.
Definition: pool.c:1173
void * fr_pool_connection_reconnect(fr_pool_t *pool, request_t *request, void *conn)
Reconnect a suspected inviable connection.
Definition: pool.c:1500
uint32_t num
Number of connections in the pool.
Definition: pool.h:67
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define DEBUG2(fmt,...)
Definition: radclient.h:43
#define WARN(fmt,...)
Definition: radclient.h:47
#define INFO(fmt,...)
Definition: radict.c:54
#define RETURN_MODULE_INVALID
Definition: rcode.h:59
#define RETURN_MODULE_OK
Definition: rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_OK
The module is OK, continue.
Definition: rcode.h:43
fr_dict_attr_t const * request_attr_request
Definition: request.c:45
Prototypes and functions for the SQL module.
rlm_sql_t const * inst
The rlm_sql instance this connection belongs to.
Definition: rlm_sql.h:115
fr_sql_query_status_t status
Status of the query.
Definition: rlm_sql.h:146
trunk_connection_t * tconn
Trunk connection this query is being run on.
Definition: rlm_sql.h:142
TALLOC_CTX * log_ctx
Talloc pool used to avoid allocing memory when log strings need to be copied.
Definition: rlm_sql.h:116
fr_sql_query_type_t type
Type of query.
Definition: rlm_sql.h:145
void * conn
Database specific connection handle.
Definition: rlm_sql.h:114
trunk_t * trunk
Trunk this query is being run on.
Definition: rlm_sql.h:141
rlm_sql_t const * inst
Module instance for this query.
Definition: rlm_sql.h:138
char const * query_str
Query string to run.
Definition: rlm_sql.h:144
fr_sql_query_t * query_ctx
Query context.
Definition: rlm_sql.h:158
map_list_t * out
List to append entries to.
Definition: rlm_sql.h:160
@ RLM_SQL_QUERY_INVALID
Query syntax error.
Definition: rlm_sql.h:45
@ RLM_SQL_ALT_QUERY
Key constraint violation, use an alternative query.
Definition: rlm_sql.h:49
@ RLM_SQL_RECONNECT
Stale connection, should reconnect.
Definition: rlm_sql.h:48
@ RLM_SQL_ERROR
General connection/server error.
Definition: rlm_sql.h:46
@ RLM_SQL_OK
Success.
Definition: rlm_sql.h:47
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
Definition: rlm_sql.h:50
fr_sql_query_type_t
Definition: rlm_sql.h:120
@ SQL_QUERY_SELECT
Definition: rlm_sql.h:121
@ SQL_QUERY_OTHER
Definition: rlm_sql.h:122
rlm_sql_handle_t * handle
Connection handle this query is being run on.
Definition: rlm_sql.h:140
TALLOC_CTX * ctx
To allocate map entries in.
Definition: rlm_sql.h:155
#define RLM_SQL_RCODE_FLAGS_ALT_QUERY
Can distinguish between other errors and those.
Definition: rlm_sql.h:172
char ** rlm_sql_row_t
Definition: rlm_sql.h:59
rlm_sql_t const * inst
Module instance data.
Definition: rlm_sql.h:156
int rows
How many rows the query returned.
Definition: rlm_sql.h:161
rlm_sql_row_t row
Row data from the last query.
Definition: rlm_sql.h:148
sql_rcode_t rcode
Result code.
Definition: rlm_sql.h:147
fr_value_box_t * query
Query string used for fetching pairs.
Definition: rlm_sql.h:157
trunk_request_t * treq
Trunk request for this query.
Definition: rlm_sql.h:143
@ SQL_QUERY_FAILED
Failed to submit.
Definition: rlm_sql.h:128
@ SQL_QUERY_SUBMITTED
Submitted for execution.
Definition: rlm_sql.h:130
fr_dict_attr_t const * list
Default list for pair evaluation.
Definition: rlm_sql.h:159
Context used when fetching attribute value pairs as a map list.
Definition: rlm_sql.h:154
Definition: rlm_sql.h:61
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition: tmpl.h:344
@ TMPL_ATTR_LIST_ALLOW
Attribute refs are allowed to have a list.
Definition: tmpl.h:274
struct tmpl_rules_s tmpl_rules_t
Definition: tmpl.h:236
Optional arguments passed to vp_tmpl functions.
Definition: tmpl.h:341
fr_signal_t
Definition: signal.h:48
size_t sql_rcode_table_len
Definition: sql.c:63
static int fr_sql_query_free(fr_sql_query_t *to_free)
Automatically run the correct finish function when freeing an SQL query.
Definition: sql.c:397
unlang_action_t rlm_sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Call the driver's sql_fetch_row function.
Definition: sql.c:308
void rlm_sql_query_log(rlm_sql_t const *inst, char const *filename, char const *query)
Definition: sql.c:774
unlang_action_t sql_get_map_list(request_t *request, fr_sql_map_ctx_t *map_ctx, rlm_sql_handle_t **handle, trunk_t *trunk)
Submit the query to get any user / group check or reply pairs.
Definition: sql.c:756
unlang_action_t rlm_sql_select_query(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Call the driver's sql_select_query method, reconnecting if necessary.
Definition: sql.c:646
fr_table_num_sorted_t const sql_rcode_table[]
Definition: sql.c:55
unlang_action_t rlm_sql_trunk_query(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Submit an SQL query using a trunk connection.
Definition: sql.c:572
static void sql_trunk_query_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
Cancel an SQL query submitted on a trunk.
Definition: sql.c:547
static unlang_action_t sql_trunk_query_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, UNUSED void *uctx)
Yield processing after submitting a trunk request.
Definition: sql.c:539
static unlang_action_t sql_get_map_list_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Process the results of an SQL query to produce a map list.
Definition: sql.c:709
void rlm_sql_print_error(rlm_sql_t const *inst, request_t *request, fr_sql_query_t *query_ctx, bool force_debug)
Retrieve any errors from the SQL driver.
Definition: sql.c:352
void * sql_mod_conn_create(TALLOC_CTX *ctx, void *instance, fr_time_delta_t timeout)
Definition: sql.c:65
fr_sql_query_t * fr_sql_query_alloc(TALLOC_CTX *ctx, rlm_sql_t const *inst, request_t *request, rlm_sql_handle_t *handle, trunk_t *trunk, char const *query_str, fr_sql_query_type_t type)
Allocate an sql query structure.
Definition: sql.c:413
size_t sql_rcode_description_table_len
Definition: sql.c:53
fr_table_num_sorted_t const sql_rcode_description_table[]
Definition: sql.c:45
unlang_action_t rlm_sql_query(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Call the driver's sql_query method, reconnecting if necessary.
Definition: sql.c:447
return count
Definition: module.c:163
RETURN_MODULE_FAIL
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
fr_pair_t * vp
Value pair map.
Definition: map.h:77
map_t * parent
Definition: map.h:88
trunk_io_funcs_t trunk_io_funcs
Trunk callback functions for this driver.
Definition: rlm_sql.h:234
unlang_function_t sql_query_resume
Callback run after an SQL trunk query is run.
Definition: rlm_sql.h:214
sql_rcode_t(* sql_finish_query)(fr_sql_query_t *query_ctx, rlm_sql_config_t const *config)
Definition: rlm_sql.h:226
sql_rcode_t(* sql_finish_select_query)(fr_sql_query_t *query_ctx, rlm_sql_config_t const *config)
Definition: rlm_sql.h:227
unlang_function_t sql_select_query_resume
Callback run after an SQL select trunk query is run.
Definition: rlm_sql.h:215
rlm_sql_config_t config
Definition: rlm_sql.h:238
rlm_sql_driver_t const * driver
Driver's exported interface.
Definition: rlm_sql.h:246
fr_dict_attr_t const * list_def
Default list to use with unqualified attribute reference.
Definition: tmpl.h:307
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition: tmpl.h:285
tmpl_attr_prefix_t prefix
Whether the attribute reference requires a prefix.
Definition: tmpl.h:310
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition: table.h:772
An element in a lexicographically sorted array of name to num mappings.
Definition: table.h:49
A time delta, a difference in time measured in nanoseconds.
Definition: time.h:80
const bool fr_assignment_op[T_TOKEN_LAST]
Definition: token.c:168
fr_token_t gettoken(char const **ptr, char *buf, int buflen, bool unescape)
Definition: token.c:468
fr_table_num_ordered_t const fr_tokens_table[]
Definition: token.c:33
const bool fr_comparison_op[T_TOKEN_LAST]
Definition: token.c:198
enum fr_token fr_token_t
@ T_EOL
Definition: token.h:40
@ T_SINGLE_QUOTED_STRING
Definition: token.h:122
@ T_BACK_QUOTED_STRING
Definition: token.h:123
@ T_DOUBLE_QUOTED_STRING
Definition: token.h:121
@ T_OP_CMP_EQ
Definition: token.h:106
trunk_enqueue_t trunk_request_enqueue(trunk_request_t **treq_out, trunk_t *trunk, request_t *request, void *preq, void *rctx)
Enqueue a request that needs data written to the trunk.
Definition: trunk.c:2575
trunk_enqueue_t trunk_request_requeue(trunk_request_t *treq)
Re-enqueue a request on the same connection.
Definition: trunk.c:2664
void trunk_request_signal_cancel(trunk_request_t *treq)
Cancel a trunk request.
Definition: trunk.c:2140
void trunk_request_signal_complete(trunk_request_t *treq)
Signal that a trunk request is complete.
Definition: trunk.c:2087
Main trunk management handle.
Definition: trunk.c:195
trunk_enqueue_t
Definition: trunk.h:148
@ TRUNK_ENQUEUE_OK
Operation was successful.
Definition: trunk.h:150
@ TRUNK_ENQUEUE_IN_BACKLOG
Request should be enqueued in backlog.
Definition: trunk.h:149
trunk_request_cancel_mux_t request_cancel_mux
!< Read one or more requests from a connection.
Definition: trunk.h:738
@ TRUNK_REQUEST_STATE_REAPABLE
Request has been written, needs to persist, but we are not currently waiting for any response.
Definition: trunk.h:173
@ TRUNK_REQUEST_STATE_INIT
Initial state.
Definition: trunk.h:162
static fr_slen_t parent
Definition: pair.h:851
#define FR_TYPE_STRUCTURAL
Definition: types.h:296
#define FR_MAX_STRING_LEN
Definition: value.h:30
static size_t char ** out
Definition: value.h:997