The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_sql_postgresql.c
Go to the documentation of this file.
1 /*
2  * sql_postgresql.c Postgresql rlm_sql driver
3  *
4  * Version: $Id: 4574f9fdf8d37e44ff50f906dbed432ddfb1656e $
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * @copyright 2000,2006 The FreeRADIUS server project
21  * @copyright 2000 Mike Machado (mike@innercite.com)
22  * @copyright 2000 Alan DeKok (aland@freeradius.org)
23  */
24 
25 /*
26  * April 2001:
27  *
28  * Use blocking queries and delete unused functions. In
29  * rlm_sql_postgresql replace all functions that are not really used
30  * with the not_implemented function.
31  *
32  * Add a new field to the rlm_sql_postgres_conn_t struct to store the
33  * number of rows affected by a query because the sql module calls
34  * finish_query before it retrieves the number of affected rows from the
35  * driver
36  *
37  * Bernhard Herzog <bh@intevation.de>
38  */
39 
40 RCSID("$Id: 4574f9fdf8d37e44ff50f906dbed432ddfb1656e $")
41 
42 #define LOG_PREFIX "sql - postgresql"
43 
44 #include <freeradius-devel/server/base.h>
45 #include <freeradius-devel/util/debug.h>
46 
47 #include <sys/stat.h>
48 
49 #include <libpq-fe.h>
50 #include <postgres_ext.h>
51 
52 #include "config.h"
53 #include "rlm_sql.h"
54 #include "rlm_sql_trunk.h"
55 
56 #ifndef NAMEDATALEN
57 # define NAMEDATALEN 64
58 #endif
59 
60 /** PostgreSQL configuration
61  *
62  */
63 typedef struct {
64  char const *db_string; //!< Text based configuration string.
65  bool send_application_name; //!< Whether we send the application name to PostgreSQL.
66  fr_trie_t *states; //!< sql state trie.
68 
69 typedef struct {
70  PGconn *db;
71  PGresult *result;
72  int cur_row;
75  char **row;
76  connection_t *conn; //!< Generic connection structure for this connection.
77  int fd; //!< fd for this connection's I/O events.
78  fr_sql_query_t *query_ctx; //!< Current query running on this connection.
80 
82  { FR_CONF_OFFSET("send_application_name", rlm_sql_postgresql_t, send_application_name), .dflt = "yes" },
84 };
85 
86 /** These are PostgreSQL specific error codes which are not covered in SQL 2011
87  *
88  */
90  { "03", "SQL statement not yet complete", RLM_SQL_OK },
91  { "0B", "Invalid transaction initiation", RLM_SQL_ERROR },
92  { "53", "Insufficient resources", RLM_SQL_ERROR },
93  /*
94  * 54000 program_limit_exceeded
95  * 54001 statement_too_complex
96  * 54011 too_many_columns
97  * 54023 too_many_arguments
98  */
99  { "54", "Program limit exceeded", RLM_SQL_QUERY_INVALID },
100 
101  { "55", "Object not in prerequisite state", RLM_SQL_ERROR },
102 
103  /*
104  * Error seen when NOWAIT is used to abort queries that involve rows
105  * which are already locked.
106  *
107  * Listed specifically for efficiency.
108  */
109  { "55P03", "Lock not available", RLM_SQL_ERROR },
110 
111  { "57", "Operator intervention", RLM_SQL_ERROR },
112 
113  /*
114  * This is really 'statement_timeout' or the error which is returned when
115  * 'statement_timeout' is hit.
116  *
117  * It's unlikely that this has been caused by a connection failure, and
118  * most likely to have been caused by a long running query.
119  *
120  * If the query is persistently long running then the database/query should
121  * be optimised, or 'statement_timeout' should be increased.
122  *
123  * Forcing a reconnect here only eats more resources on the DB so we will
124  * no longer do so as of 3.0.4.
125  */
126  { "57014", "Query cancelled", RLM_SQL_ERROR },
127  { "57P01", "Admin shutdown", RLM_SQL_RECONNECT },
128  { "57P02", "Crash shutdown", RLM_SQL_RECONNECT },
129  { "57P03", "Cannot connect now", RLM_SQL_RECONNECT },
130  { "58", "System error", RLM_SQL_RECONNECT },
131  { "72", "Snapshot failure", RLM_SQL_ERROR },
132  { "F0", "Configuration file error", RLM_SQL_ERROR },
133  { "P0", "PL/PGSQL error", RLM_SQL_ERROR },
134  { "XX", "Internal error", RLM_SQL_ERROR },
135  { NULL, NULL, RLM_SQL_ERROR } /* Default code */
136 };
137 
138 /** Return the number of affected rows of the result as an int instead of the string that postgresql provides
139  *
140  */
141 static int affected_rows(PGresult * result)
142 {
143  return atoi(PQcmdTuples(result));
144 }
145 
146 /** Free the row of the current result that's stored in the conn struct
147  *
148  */
150 {
151  TALLOC_FREE(conn->row);
152  conn->num_fields = 0;
153 }
154 
155 #if defined(PG_DIAG_SQLSTATE) && defined(PG_DIAG_MESSAGE_PRIMARY)
156 static sql_rcode_t sql_classify_error(rlm_sql_postgresql_t *inst, ExecStatusType status, PGresult const *result)
157 {
158  char const *error_code;
159  char const *error_msg;
160  sql_state_entry_t const *entry;
161 
162  error_code = PQresultErrorField(result, PG_DIAG_SQLSTATE);
163  if (!error_code) {
164  switch (status){
165  /*
166  * Successful completion of a command returning no data.
167  */
168  case PGRES_COMMAND_OK:
169  #ifdef HAVE_PGRES_SINGLE_TUPLE
170  case PGRES_SINGLE_TUPLE:
171  #endif
172  #ifdef HAVE_PGRES_TUPLES_CHUNK
173  case PGRES_TUPLES_CHUNK:
174  #endif
175  case PGRES_TUPLES_OK:
176  #ifdef HAVE_PGRES_COPY_BOTH
177  case PGRES_COPY_BOTH:
178  #endif
179  case PGRES_COPY_OUT:
180  case PGRES_COPY_IN:
181  error_code = "00000";
182  break;
183 
184  case PGRES_EMPTY_QUERY: /* Shouldn't happen */
185  error_code = "42000";
186  break;
187 
188  #ifdef HAVE_PGRES_PIPELINE_SYNC
189  case PGRES_PIPELINE_SYNC:
190  case PGRES_PIPELINE_ABORTED:
191  ERROR("libpq reported aborted pipeline");
192  return RLM_SQL_ERROR;
193  #endif
194 
195  case PGRES_BAD_RESPONSE:
196  case PGRES_NONFATAL_ERROR:
197  case PGRES_FATAL_ERROR:
198  ERROR("libpq provided no error code");
199  return RLM_SQL_ERROR;
200  }
201  }
202 
203  entry = sql_state_entry_find(inst->states, error_code);
204  if (!entry) {
205  ERROR("Can't classify: %s", error_code);
206  return RLM_SQL_ERROR;
207  }
208 
209  DEBUG3("sqlstate %s matched %s: %s (%s)", error_code,
210  entry->sql_state, entry->meaning, fr_table_str_by_value(sql_rcode_table, entry->rcode, "<DEFAULT>"));
211 
212  /*
213  * WARNING error class.
214  */
215  if ((entry->sql_state[0] == '0') && (entry->sql_state[1] == '1')) {
216  error_msg = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);
217  if (error_msg) WARN("%s", error_msg);
218  }
219 
220  return entry->rcode;
221 }
222 # else
223 static sql_rcode_t sql_classify_error(UNUSED PGresult const *result)
224 {
225  ERROR("Error occurred, no more information available, rebuild with newer libpq");
226  return RLM_SQL_ERROR;
227 }
228 #endif
229 
230 static void _sql_connect_io_notify(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
231 {
232  rlm_sql_postgres_conn_t *c = talloc_get_type_abort(uctx, rlm_sql_postgres_conn_t);
233  PostgresPollingStatusType status;
234 
236 
237  status = PQconnectPoll(c->db);
238 
239  /*
240  * Documentation says:
241  * Caution: do not assume that the socket remains the same across PQconnectPoll calls.
242  * So we get the socket again.
243  */
244  c->fd = PQsocket(c->db);
245  switch (status) {
246  case PGRES_POLLING_OK:
247  DEBUG2("Connected to database '%s' on '%s' server version %i, protocol version %i, backend PID %i ",
248  PQdb(c->db), PQhost(c->db), PQserverVersion(c->db), PQprotocolVersion(c->db),
249  PQbackendPID(c->db));
250  PQsetnonblocking(c->db, 1);
252  return;
253 
254  case PGRES_POLLING_FAILED:
255  error:
256  ERROR("Connection failed: %s", PQerrorMessage(c->db));
258  return;
259 
260  case PGRES_POLLING_READING:
261  if (fr_event_fd_insert(c, NULL, c->conn->el, c->fd, _sql_connect_io_notify, NULL, NULL, c) != 0) goto error;
262  return;
263 
264  case PGRES_POLLING_WRITING:
265  if (fr_event_fd_insert(c, NULL, c->conn->el, c->fd, NULL, _sql_connect_io_notify, NULL, c) != 0) goto error;
266  return;
267 
268  default:
269  goto error;
270 
271  }
272 }
273 
274 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
275 static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
276 {
278  rlm_sql_postgresql_t const *inst = talloc_get_type_abort(sql->driver_submodule->data, rlm_sql_postgresql_t);
280  PostgresPollingStatusType status;
281 
282  MEM(c = talloc_zero(conn, rlm_sql_postgres_conn_t));
283  c->conn = conn;
284  c->fd = -1;
285 
286  DEBUG2("Starting connection to PostgreSQL server using parameters: %s", inst->db_string);
287 
288  c->db = PQconnectStart(inst->db_string);
289  if (!c->db) {
290  ERROR("Connection failed: Out of memory");
291  talloc_free(c);
293  }
294 
295  switch (PQstatus(c->db)) {
296  case CONNECTION_OK:
297  c->fd = PQsocket(c->db);
298  DEBUG2("Connected to database '%s' on '%s' server version %i, protocol version %i, backend PID %i ",
299  PQdb(c->db), PQhost(c->db), PQserverVersion(c->db), PQprotocolVersion(c->db),
300  PQbackendPID(c->db));
301  PQsetnonblocking(c->db, 1);
304 
305  case CONNECTION_BAD:
306  ERROR("Connection failed: %s", PQerrorMessage(c->db));
307  error:
308  PQfinish(c->db);
309  talloc_free(c);
311 
312  default:
313  break;
314 
315  }
316 
317  status = PQconnectPoll(c->db);
318  c->fd = PQsocket(c->db);
319  if (fr_event_fd_insert(c, NULL, c->conn->el, c->fd,
320  status == PGRES_POLLING_READING ? _sql_connect_io_notify : NULL,
321  status == PGRES_POLLING_WRITING ? _sql_connect_io_notify : NULL, NULL, c) != 0) goto error;
322 
323  DEBUG2("Connecting to database '%s' on '%s', fd %d", PQdb(c->db), PQhost(c->db), c->fd);
324 
325  *h = c;
326 
328 }
329 
330 static void _sql_connection_close(fr_event_list_t *el, void *h, UNUSED void *uctx)
331 {
332  rlm_sql_postgres_conn_t *c = talloc_get_type_abort(h, rlm_sql_postgres_conn_t);
333 
334  if (c->fd >= 0) {
336  c->fd = -1;
337  }
338 
339  if (c->result) {
340  PQclear(c->result);
341  c->result = NULL;
342  }
343 
344  /* PQfinish also frees the memory used by the PGconn structure */
345  PQfinish(c->db);
346  c->query_ctx = NULL;
347  talloc_free(h);
348 }
349 
351 
352 TRUNK_NOTIFY_FUNC(sql_trunk_connection_notify, rlm_sql_postgres_conn_t)
353 
354 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
355 static void sql_trunk_request_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn,
356  connection_t *conn, UNUSED void *uctx)
357 {
358  rlm_sql_postgres_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_postgres_conn_t);
359  request_t *request;
360  trunk_request_t *treq;
361  fr_sql_query_t *query_ctx;
362  int err;
363 
364  if (trunk_connection_pop_request(&treq, tconn) != 0) return;
365  if (!treq) return;
366 
367  query_ctx = talloc_get_type_abort(treq->preq, fr_sql_query_t);
368  request = query_ctx->request;
369 
370  switch (query_ctx->status) {
371  case SQL_QUERY_PREPARED:
372  ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query_ctx->query_str);
373  err = PQsendQuery(sql_conn->db, query_ctx->query_str);
374  query_ctx->tconn = tconn;
375  if (!err) {
376  ROPTIONAL(RERROR, ERROR, "Failed to send query: %s", PQerrorMessage(sql_conn->db));
378  return;
379  }
380 
381  query_ctx->status = SQL_QUERY_SUBMITTED;
382  sql_conn->query_ctx = query_ctx;
384  return;
385 
386  default:
387  return;
388  }
389 }
390 
391 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
392 static void sql_trunk_request_demux(UNUSED fr_event_list_t *el, UNUSED trunk_connection_t *tconn,
393  connection_t *conn, UNUSED void *uctx)
394 {
395  rlm_sql_postgres_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_postgres_conn_t);
397  fr_sql_query_t *query_ctx;
398  request_t *request;
399  PGresult *tmp_result;
400  ExecStatusType status;
401  int numfields;
402 
403  query_ctx = sql_conn->query_ctx;
404  request = query_ctx->request;
405  inst = talloc_get_type_abort(query_ctx->inst->driver_submodule->data, rlm_sql_postgresql_t);
406 
407  switch (query_ctx->status) {
408  case SQL_QUERY_SUBMITTED:
409  if (PQconsumeInput(sql_conn->db) == 0) {
410  ROPTIONAL(RERROR, ERROR, "SQL query failed: %s", PQerrorMessage(sql_conn->db));
411  query_ctx->rcode = RLM_SQL_ERROR;
412  break;
413  }
414  if (PQisBusy(sql_conn->db)) return;
415 
416  query_ctx->status = SQL_QUERY_RETURNED;
417 
418  sql_conn->result = PQgetResult(sql_conn->db);
419 
420  /* Discard results for appended queries */
421  while ((tmp_result = PQgetResult(sql_conn->db)) != NULL)
422  PQclear(tmp_result);
423 
424  /*
425  * As this error COULD be a connection error OR an out-of-memory
426  * condition return value WILL be wrong SOME of the time
427  * regardless! Pick your poison...
428  */
429  if (!sql_conn->result) {
430  ROPTIONAL(RERROR, ERROR, "Failed getting query result: %s", PQerrorMessage(sql_conn->db));
431  query_ctx->rcode = RLM_SQL_RECONNECT;
432  break;
433  }
434 
435  status = PQresultStatus(sql_conn->result);
436  switch (status){
437  /*
438  * Successful completion of a command returning no data.
439  */
440  case PGRES_COMMAND_OK:
441  /*
442  * Affected_rows function only returns the number of affected rows of a command
443  * returning no data...
444  */
445  sql_conn->affected_rows = affected_rows(sql_conn->result);
446  ROPTIONAL(RDEBUG2, DEBUG2, "query affected rows = %i", sql_conn->affected_rows);
447  break;
448  /*
449  * Successful completion of a command returning data (such as a SELECT or SHOW).
450  */
451 #ifdef HAVE_PGRES_SINGLE_TUPLE
452  case PGRES_SINGLE_TUPLE:
453 #endif
454 #ifdef HAVE_PGRES_TUPLES_CHUNK
455  case PGRES_TUPLES_CHUNK:
456 #endif
457  case PGRES_TUPLES_OK:
458  sql_conn->cur_row = 0;
459  sql_conn->affected_rows = PQntuples(sql_conn->result);
460  numfields = PQnfields(sql_conn->result); /*Check row storing functions..*/
461  ROPTIONAL(RDEBUG2, DEBUG2, "query returned rows = %i, fields = %i", sql_conn->affected_rows, numfields);
462  break;
463 
464 #ifdef HAVE_PGRES_COPY_BOTH
465  case PGRES_COPY_BOTH:
466 #endif
467  case PGRES_COPY_OUT:
468  case PGRES_COPY_IN:
469  DEBUG2("Data transfer started");
470  break;
471 
472  /*
473  * Weird.. this shouldn't happen.
474  */
475  case PGRES_EMPTY_QUERY:
476  case PGRES_BAD_RESPONSE: /* The server's response was not understood */
477  case PGRES_NONFATAL_ERROR:
478  case PGRES_FATAL_ERROR:
479 #ifdef HAVE_PGRES_PIPELINE_SYNC
480  case PGRES_PIPELINE_SYNC:
481  case PGRES_PIPELINE_ABORTED:
482 #endif
483  break;
484  }
485 
486  query_ctx->rcode = sql_classify_error(inst, status, sql_conn->result);
487  break;
488 
489  default:
490  fr_assert(0);
491  }
492 
493  if (request) unlang_interpret_mark_runnable(request);
494 }
495 
496 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
497 static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason,
498  UNUSED void *uctx)
499 {
500  fr_sql_query_t *query_ctx = talloc_get_type_abort(preq, fr_sql_query_t);
501  rlm_sql_postgres_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_postgres_conn_t);
502 
503  if (!query_ctx->treq) return;
504  if (reason != TRUNK_CANCEL_REASON_SIGNAL) return;
505  if (sql_conn->query_ctx == query_ctx) sql_conn->query_ctx = NULL;
506 }
507 
508 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
509 static void sql_request_cancel_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn,
510  connection_t *conn, UNUSED void *uctx)
511 {
512  trunk_request_t *treq;
513  PGcancel *cancel;
514  rlm_sql_postgres_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_postgres_conn_t);
515  char errbuf[256];
516  PGresult *tmp_result;
517 
518  if ((trunk_connection_pop_cancellation(&treq, tconn)) == 0) {
519  cancel = PQgetCancel(sql_conn->db);
520  if (!cancel) goto complete;
521  if (PQcancel(cancel, errbuf, sizeof(errbuf)) == 0) {
522  ERROR("Failed to cancel query: %s", errbuf);
523  }
524  PQfreeCancel(cancel);
525 
526  /*
527  * The documentation says that regardless of the result of
528  * PQcancel, the normal processing of PQgetResult must happen.
529  */
530  while ((tmp_result = PQgetResult(sql_conn->db)) != NULL)
531  PQclear(tmp_result);
532 
533  complete:
535  }
536 }
537 
540 
541 static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
542 {
543  rlm_sql_postgres_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
544 
545  int fields, i;
546  char const **names;
547 
548  fields = PQnfields(conn->result);
549  if (fields <= 0) return RLM_SQL_ERROR;
550 
551  MEM(names = talloc_array(query_ctx, char const *, fields));
552 
553  for (i = 0; i < fields; i++) names[i] = PQfname(conn->result, i);
554  *out = names;
555 
556  return RLM_SQL_OK;
557 }
558 
559 static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
560 {
561  fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
562  int records, i, len;
563  rlm_sql_postgres_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
564 
565  query_ctx->row = NULL;
566 
567  query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
568  if (conn->cur_row >= PQntuples(conn->result)) RETURN_MODULE_OK;
569 
570  free_result_row(conn);
571 
572  records = PQnfields(conn->result);
573  conn->num_fields = records;
574 
575  if ((PQntuples(conn->result) > 0) && (records > 0)) {
576  conn->row = talloc_zero_array(conn, char *, records + 1);
577  for (i = 0; i < records; i++) {
578  if (PQgetisnull(conn->result, conn->cur_row, i)) continue;
579  len = PQgetlength(conn->result, conn->cur_row, i);
580  conn->row[i] = talloc_array(conn->row, char, len + 1);
581  strlcpy(conn->row[i], PQgetvalue(conn->result, conn->cur_row, i), len + 1);
582  }
583  conn->cur_row++;
584  query_ctx->row = conn->row;
585 
586  query_ctx->rcode = RLM_SQL_OK;
587  }
588 
590 }
591 
593 {
595 
596  if (query_ctx->treq && !(query_ctx->treq->state &
598 
599  if (!query_ctx->tconn || !query_ctx->tconn->conn || !query_ctx->tconn->conn->h) return RLM_SQL_ERROR;
600 
601  if (!(query_ctx->tconn->state & TRUNK_CONN_PROCESSING)) return RLM_SQL_ERROR;
602 
603  conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
604 
605  if (conn->result != NULL) {
606  PQclear(conn->result);
607  conn->result = NULL;
608  }
609 
610  free_result_row(conn);
611 
612  return 0;
613 }
614 
615 /** Retrieves any errors associated with the query context
616  *
617  * @note Caller will free any memory allocated in ctx.
618  *
619  * @param ctx to allocate temporary error buffers in.
620  * @param out Array of sql_log_entrys to fill.
621  * @param outlen Length of out array.
622  * @param query_ctx Query context to retrieve error for.
623  * @param config rlm_sql config.
624  * @return number of errors written to the #sql_log_entry_t array.
625  */
626 static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
627  fr_sql_query_t *query_ctx)
628 {
629  rlm_sql_postgres_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
630  char const *p, *q;
631  size_t i = 0;
632 
633  fr_assert(outlen > 0);
634 
635  p = PQerrorMessage(conn->db);
636  while ((q = strchr(p, '\n'))) {
637  out[i].type = L_ERR;
638  out[i].msg = talloc_typed_asprintf(ctx, "%.*s", (int) (q - p), p);
639  p = q + 1;
640  if (++i == outlen) return outlen;
641  }
642  if (*p != '\0') {
643  out[i].type = L_ERR;
644  out[i].msg = p;
645  i++;
646  }
647 
648  return i;
649 }
650 
652 {
653  rlm_sql_postgres_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
654 
655  return conn->affected_rows;
656 }
657 
658 static ssize_t sql_escape_func(request_t *request, char *out, size_t outlen, char const *in, void *arg)
659 {
660  size_t inlen, ret;
661  connection_t *c = talloc_get_type_abort(arg, connection_t);
663  int err;
664 
665  if ((c->state == CONNECTION_STATE_HALTED) || (c->state == CONNECTION_STATE_CLOSED)) {
666  ROPTIONAL(RERROR, ERROR, "Connection not available for escaping");
667  return -1;
668  }
669 
670  conn = talloc_get_type_abort(c->h, rlm_sql_postgres_conn_t);
671 
672  /* Check for potential buffer overflow */
673  inlen = strlen(in);
674  if ((inlen * 2 + 1) > outlen) return 0;
675  /* Prevent integer overflow */
676  if ((inlen * 2 + 1) <= inlen) return 0;
677 
678  ret = PQescapeStringConn(conn->db, out, in, inlen, &err);
679  if (err) {
680  ROPTIONAL(REDEBUG, ERROR, "Error escaping string \"%s\": %s", in, PQerrorMessage(conn->db));
681  return 0;
682  }
683 
684  return ret;
685 }
686 
687 static int mod_instantiate(module_inst_ctx_t const *mctx)
688 {
689  rlm_sql_t const *parent = talloc_get_type_abort(mctx->mi->parent->data, rlm_sql_t);
690  rlm_sql_config_t const *config = &parent->config;
691  rlm_sql_postgresql_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_postgresql_t);
692  char application_name[NAMEDATALEN];
693  char *db_string;
694 
695  /*
696  * Allow the user to set their own, or disable it
697  */
698  if (inst->send_application_name) {
699  CONF_SECTION *cs;
700  char const *name;
701 
702  cs = cf_item_to_section(cf_parent(mctx->mi->conf));
703 
704  name = cf_section_name2(cs);
705  if (!name) name = cf_section_name1(cs);
706 
707  snprintf(application_name, sizeof(application_name),
708  "FreeRADIUS " RADIUSD_VERSION_STRING " - %s (%s)", main_config->name, name);
709  }
710 
711  /*
712  * Old style database name
713  *
714  * Append options if they were set in the config
715  */
716  if (!strchr(config->sql_db, '=')) {
717  db_string = talloc_typed_asprintf(inst, "dbname='%s'", config->sql_db);
718 
719  if (config->sql_server[0] != '\0') {
720  db_string = talloc_asprintf_append(db_string, " host='%s'", config->sql_server);
721  }
722 
723  if (config->sql_port) {
724  db_string = talloc_asprintf_append(db_string, " port=%i", config->sql_port);
725  }
726 
727  if (config->sql_login[0] != '\0') {
728  db_string = talloc_asprintf_append(db_string, " user='%s'", config->sql_login);
729  }
730 
731  if (config->sql_password[0] != '\0') {
732  db_string = talloc_asprintf_append(db_string, " password='%s'", config->sql_password);
733  }
734 
735  if (fr_time_delta_ispos(config->query_timeout)) {
736  db_string = talloc_asprintf_append(db_string, " connect_timeout=%d", (int) fr_time_delta_to_sec(config->query_timeout));
737  }
738 
739  if (inst->send_application_name) {
740  db_string = talloc_asprintf_append(db_string, " application_name='%s'", application_name);
741  }
742 
743  /*
744  * New style parameter string
745  *
746  * Only append options when not already present
747  */
748  } else {
749  db_string = talloc_typed_strdup(inst, config->sql_db);
750 
751  if ((config->sql_server[0] != '\0') && !strstr(db_string, "host=")) {
752  db_string = talloc_asprintf_append(db_string, " host='%s'", config->sql_server);
753  }
754 
755  if (config->sql_port && !strstr(db_string, "port=")) {
756  db_string = talloc_asprintf_append(db_string, " port=%i", config->sql_port);
757  }
758 
759  if ((config->sql_login[0] != '\0') && !strstr(db_string, "user=")) {
760  db_string = talloc_asprintf_append(db_string, " user='%s'", config->sql_login);
761  }
762 
763  if ((config->sql_password[0] != '\0') && !strstr(db_string, "password=")) {
764  db_string = talloc_asprintf_append(db_string, " password='%s'", config->sql_password);
765  }
766 
767  if (fr_time_delta_ispos(config->query_timeout) && !strstr(db_string, "connect_timeout=")) {
768  db_string = talloc_asprintf_append(db_string, " connect_timeout=%d", (int) fr_time_delta_to_sec(config->query_timeout));
769  }
770 
771  if (inst->send_application_name && !strstr(db_string, "application_name=")) {
772  db_string = talloc_asprintf_append(db_string, " application_name='%s'", application_name);
773  }
774  }
775  inst->db_string = db_string;
776 
777  inst->states = sql_state_trie_alloc(inst);
778 
779  /*
780  * Load in the PostgreSQL specific sqlstates
781  */
782  if (sql_state_entries_from_table(inst->states, sql_state_table) < 0) return -1;
783 
784  /*
785  * Load in overrides from the driver's configuration section
786  */
787  {
788  CONF_SECTION *cs;
789 
790  cs = cf_section_find(mctx->mi->conf, "states", NULL);
791  if (cs && (sql_state_entries_from_cs(inst->states, cs) < 0)) return -1;
792  }
793 
794  return 0;
795 }
796 
797 static int mod_load(void)
798 {
799 #if defined(WITH_TLS) && (defined(HAVE_PQINITOPENSSL) || defined(HAVE_PQINITSSL))
800 # ifdef HAVE_PQINITOPENSSL
801  PQinitOpenSSL(0, 0);
802 # else
803  PQinitSSL(0);
804 # endif
805 #endif
806  return 0;
807 }
808 
809 static void *sql_escape_arg_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, void *uctx)
810 {
812  connection_t *conn;
813 
814  conn = connection_alloc(ctx, el,
816  .init = _sql_connection_init,
817  .close = _sql_connection_close,
818  },
819  inst->config.trunk_conf.conn_conf,
820  inst->name, inst);
821 
822  if (!conn) {
823  PERROR("Failed allocating state handler for SQL escape connection");
824  return NULL;
825  }
826 
828  return conn;
829 }
830 
831 static void sql_escape_arg_free(void *uctx)
832 {
833  connection_t *conn = talloc_get_type_abort(uctx, connection_t);
835 }
836 
837 /* Exported to rlm_sql */
840  .common = {
841  .magic = MODULE_MAGIC_INIT,
842  .name = "sql_postgresql",
843  .inst_size = sizeof(rlm_sql_postgresql_t),
844  .onload = mod_load,
847  },
849  .sql_query_resume = sql_query_resume,
850  .sql_select_query_resume = sql_query_resume,
851  .sql_fields = sql_fields,
852  .sql_fetch_row = sql_fetch_row,
853  .sql_error = sql_error,
854  .sql_finish_query = sql_free_result,
855  .sql_finish_select_query = sql_free_result,
856  .sql_affected_rows = sql_affected_rows,
857  .sql_escape_func = sql_escape_func,
858  .sql_escape_arg_alloc = sql_escape_arg_alloc,
859  .sql_escape_arg_free = sql_escape_arg_free,
860  .uses_trunks = true,
861  .trunk_io_funcs = {
862  .connection_alloc = sql_trunk_connection_alloc,
863  .connection_notify = sql_trunk_connection_notify,
864  .request_mux = sql_trunk_request_mux,
865  .request_demux = sql_trunk_request_demux,
866  .request_cancel = sql_request_cancel,
867  .request_cancel_mux = sql_request_cancel_mux,
868  .request_fail = sql_request_fail,
869  }
870 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
#define RCSID(id)
Definition: build.h:481
#define UNUSED
Definition: build.h:313
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:627
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:564
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:101
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition: cf_util.c:1028
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition: cf_util.c:1185
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition: cf_util.c:684
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition: cf_util.c:1171
#define cf_parent(_cf)
Definition: cf_util.h:101
connection_state_t
Definition: connection.h:45
@ CONNECTION_STATE_FAILED
Connection has failed.
Definition: connection.h:54
@ CONNECTION_STATE_HALTED
The connection is in a halted stat.
Definition: connection.h:46
@ CONNECTION_STATE_CLOSED
Connection has been closed.
Definition: connection.h:55
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Definition: connection.h:50
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition: connection.h:84
Holds a complete set of functions for a connection.
Definition: connection.h:186
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
#define RADIUSD_VERSION_STRING
Definition: dependency.h:39
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
static fr_slen_t err
Definition: dict.h:821
static fr_slen_t in
Definition: dict.h:821
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
#define fr_event_fd_insert(...)
Definition: event.h:232
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition: event.h:62
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition: interpret.c:1359
#define PERROR(_fmt,...)
Definition: log.h:228
#define DEBUG3(_fmt,...)
Definition: log.h:266
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition: log.h:528
#define RERROR(fmt,...)
Definition: log.h:298
talloc_free(reap)
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Definition: event.c:1260
Stores all information relating to an event list.
Definition: event.c:411
@ L_ERR
Error message.
Definition: log.h:56
main_config_t const * main_config
Main server configuration.
Definition: main_config.c:69
char const * name
Name of the daemon, usually 'radiusd'.
Definition: main_config.h:52
long int ssize_t
Definition: merged_model.c:24
module_instance_t * mi
Instance of the module being instantiated.
Definition: module_ctx.h:51
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:50
static const conf_parser_t config[]
Definition: base.c:183
#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 RETURN_MODULE_OK
Definition: rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
static char const * name
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1302
Prototypes and functions for the SQL module.
int sql_state_entries_from_table(fr_trie_t *states, sql_state_entry_t const table[])
Insert the contents of a state table into the state trie.
Definition: sql_state.c:124
sql_rcode_t rcode
What should happen if we receive this error.
Definition: rlm_sql.h:70
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
char const * meaning
Verbose description.
Definition: rlm_sql.h:69
fr_table_num_sorted_t const sql_rcode_table[]
Definition: sql.c:55
fr_trie_t * sql_state_trie_alloc(TALLOC_CTX *ctx)
Allocate a sql_state trie, and insert the initial set of entries.
Definition: sql_state.c:102
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
request_t * request
Request this query relates to.
Definition: rlm_sql.h:139
sql_rcode_t
Action to take at end of an SQL query.
Definition: rlm_sql.h:44
@ RLM_SQL_QUERY_INVALID
Query syntax error.
Definition: rlm_sql.h:45
@ 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
char const * sql_state
2-5 char error code.
Definition: rlm_sql.h:68
#define RLM_SQL_RCODE_FLAGS_ALT_QUERY
Can distinguish between other errors and those.
Definition: rlm_sql.h:172
sql_state_entry_t const * sql_state_entry_find(fr_trie_t const *states, char const *sql_state)
Lookup an SQL state based on an error code returned from the SQL server or client library.
Definition: sql_state.c:203
rlm_sql_row_t row
Row data from the last query.
Definition: rlm_sql.h:148
int sql_state_entries_from_cs(fr_trie_t *states, CONF_SECTION *overrides)
Insert the contents of a CONF_SECTION into the state trie.
Definition: sql_state.c:153
sql_rcode_t rcode
Result code.
Definition: rlm_sql.h:147
trunk_request_t * treq
Trunk request for this query.
Definition: rlm_sql.h:143
@ SQL_QUERY_RETURNED
Query has executed.
Definition: rlm_sql.h:131
@ SQL_QUERY_SUBMITTED
Submitted for execution.
Definition: rlm_sql.h:130
@ SQL_QUERY_PREPARED
Ready to submit.
Definition: rlm_sql.h:129
Definition: rlm_sql.h:61
Definition: rlm_sql.h:67
static void sql_request_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
Definition: rlm_sql_db2.c:301
static sql_rcode_t sql_classify_error(UNUSED PGresult const *result)
static int mod_load(void)
static void * sql_escape_arg_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, void *uctx)
static sql_state_entry_t sql_state_table[]
These are PostgreSQL specific error codes which are not covered in SQL 2011.
static void _sql_connection_close(fr_event_list_t *el, void *h, UNUSED void *uctx)
SQL_QUERY_FAIL static SQL_QUERY_RESUME sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
rlm_sql_driver_t rlm_sql_postgresql
int fd
fd for this connection's I/O events.
static void _sql_connect_io_notify(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
static ssize_t sql_escape_func(request_t *request, char *out, size_t outlen, char const *in, void *arg)
static conf_parser_t driver_config[]
connection_t * conn
Generic connection structure for this connection.
static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
static void sql_escape_arg_free(void *uctx)
#define NAMEDATALEN
fr_sql_query_t * query_ctx
Current query running on this connection.
fr_trie_t * states
sql state trie.
static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, fr_sql_query_t *query_ctx)
Retrieves any errors associated with the query context.
static sql_rcode_t sql_free_result(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
CC_NO_UBSAN(function)
static int sql_affected_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
static int affected_rows(PGresult *result)
Return the number of affected rows of the result as an int instead of the string that postgresql prov...
char const * db_string
Text based configuration string.
static int mod_instantiate(module_inst_ctx_t const *mctx)
bool send_application_name
Whether we send the application name to PostgreSQL.
static void free_result_row(rlm_sql_postgres_conn_t *conn)
Free the row of the current result that's stored in the conn struct.
PostgreSQL configuration.
Macros to reduce boilerplate in trunk SQL drivers.
#define SQL_QUERY_RESUME
Definition: rlm_sql_trunk.h:56
#define SQL_TRUNK_CONNECTION_ALLOC
Allocate an SQL trunk connection.
Definition: rlm_sql_trunk.h:35
#define SQL_QUERY_FAIL
Definition: rlm_sql_trunk.h:64
void connection_signal_halt(connection_t *conn)
Shuts down a connection ungracefully.
Definition: connection.c:1291
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
Definition: connection.c:1167
void connection_signal_init(connection_t *conn)
Asynchronously signal a halted connection to start.
Definition: connection.c:1107
connection_t * connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_funcs_t const *funcs, connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
Definition: connection.c:1512
void connection_signal_connected(connection_t *conn)
Asynchronously signal that the connection is open.
Definition: connection.c:1137
CONF_SECTION * conf
Module's instance configuration.
Definition: module.h:329
void * data
Module's instance data.
Definition: module.h:271
module_instance_t const * parent
Parent module's instance (if any).
Definition: module.h:337
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:689
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:34
module_t common
Common fields for all loadable modules.
Definition: rlm_sql.h:204
module_instance_t * driver_submodule
Driver's submodule.
Definition: rlm_sql.h:245
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition: table.h:772
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition: talloc.c:445
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition: talloc.c:492
#define talloc_get_type_abort_const
Definition: talloc.h:282
static const char * names[8]
Definition: time.c:617
#define fr_time_delta_ispos(_a)
Definition: time.h:290
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
Definition: time.h:647
int trunk_connection_pop_cancellation(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a cancellation request off a connection's cancellation queue.
Definition: trunk.c:3813
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition: trunk.c:2120
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition: trunk.c:2272
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
Definition: trunk.c:3861
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition: trunk.c:2043
Associates request queues with a connection.
Definition: trunk.c:131
Wraps a normal request.
Definition: trunk.c:97
#define TRUNK_CONN_PROCESSING
States where the connection may be processing requests.
Definition: trunk.h:138
#define TRUNK_NOTIFY_FUNC(_name, _type)
Helper macro for building generic trunk notify callback.
Definition: trunk.h:945
trunk_cancel_reason_t
Reasons for a request being cancelled.
Definition: trunk.h:55
@ TRUNK_CANCEL_REASON_SIGNAL
Request cancelled due to a signal.
Definition: trunk.h:57
@ 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_COMPLETE
The request is complete.
Definition: trunk.h:182
@ TRUNK_REQUEST_STATE_SENT
Was written to a socket. Waiting for a response.
Definition: trunk.h:172
static fr_event_list_t * el
static fr_slen_t parent
Definition: pair.h:851
static size_t char fr_sbuff_t size_t inlen
Definition: value.h:997
static size_t char ** out
Definition: value.h:997