The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_sql_postgresql.c
Go to the documentation of this file.
1/*
2 * sql_postgresql.c Postgresql rlm_sql driver
3 *
4 * Version: $Id: 7a5534a5772ab818bcb58228827cbd2db0a3fc86 $
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
40RCSID("$Id: 7a5534a5772ab818bcb58228827cbd2db0a3fc86 $")
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 */
63typedef 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
69typedef struct {
70 PGconn *db;
71 PGresult *result;
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 */
141static 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)
156static 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
223static 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
230static 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
274CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
275static 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
330static 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
353
354CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
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) {
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
391CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
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) {
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
496CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
497static 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
508CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
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
541static 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
559static 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 * @return number of errors written to the #sql_log_entry_t array.
624 */
625static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
626 fr_sql_query_t *query_ctx)
627{
628 rlm_sql_postgres_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
629 char const *p, *q;
630 size_t i = 0;
631
632 fr_assert(outlen > 0);
633
634 p = PQerrorMessage(conn->db);
635 while ((q = strchr(p, '\n'))) {
636 out[i].type = L_ERR;
637 out[i].msg = talloc_typed_asprintf(ctx, "%.*s", (int) (q - p), p);
638 p = q + 1;
639 if (++i == outlen) return outlen;
640 }
641 if (*p != '\0') {
642 out[i].type = L_ERR;
643 out[i].msg = p;
644 i++;
645 }
646
647 return i;
648}
649
651{
652 rlm_sql_postgres_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
653
654 return conn->affected_rows;
655}
656
657static ssize_t sql_escape_func(request_t *request, char *out, size_t outlen, char const *in, void *arg)
658{
659 size_t inlen, ret;
660 connection_t *c = talloc_get_type_abort(arg, connection_t);
662 int err;
663
664 if ((c->state == CONNECTION_STATE_HALTED) || (c->state == CONNECTION_STATE_CLOSED)) {
665 ROPTIONAL(RERROR, ERROR, "Connection not available for escaping");
666 return -1;
667 }
668
669 conn = talloc_get_type_abort(c->h, rlm_sql_postgres_conn_t);
670
671 /* Check for potential buffer overflow */
672 inlen = strlen(in);
673 if ((inlen * 2 + 1) > outlen) return 0;
674 /* Prevent integer overflow */
675 if ((inlen * 2 + 1) <= inlen) return 0;
676
677 ret = PQescapeStringConn(conn->db, out, in, inlen, &err);
678 if (err) {
679 ROPTIONAL(REDEBUG, ERROR, "Error escaping string \"%s\": %s", in, PQerrorMessage(conn->db));
680 return 0;
681 }
682
683 return ret;
684}
685
686static int mod_instantiate(module_inst_ctx_t const *mctx)
687{
688 rlm_sql_t const *parent = talloc_get_type_abort(mctx->mi->parent->data, rlm_sql_t);
689 rlm_sql_config_t const *config = &parent->config;
690 rlm_sql_postgresql_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_postgresql_t);
691 char application_name[NAMEDATALEN];
692 char *db_string;
693
694 /*
695 * Allow the user to set their own, or disable it
696 */
697 if (inst->send_application_name) {
698 CONF_SECTION *cs;
699 char const *name;
700
701 cs = cf_item_to_section(cf_parent(mctx->mi->conf));
702
704 if (!name) name = cf_section_name1(cs);
705
706 snprintf(application_name, sizeof(application_name),
707 "FreeRADIUS " RADIUSD_VERSION_STRING " - %s (%s)", main_config->name, name);
708 }
709
710 /*
711 * Old style database name
712 *
713 * Append options if they were set in the config
714 */
715 if (!strchr(config->sql_db, '=')) {
716 db_string = talloc_typed_asprintf(inst, "dbname='%s'", config->sql_db);
717
718 if (config->sql_server[0] != '\0') {
719 db_string = talloc_asprintf_append(db_string, " host='%s'", config->sql_server);
720 }
721
722 if (config->sql_port) {
723 db_string = talloc_asprintf_append(db_string, " port=%i", config->sql_port);
724 }
725
726 if (config->sql_login[0] != '\0') {
727 db_string = talloc_asprintf_append(db_string, " user='%s'", config->sql_login);
728 }
729
730 if (config->sql_password[0] != '\0') {
731 db_string = talloc_asprintf_append(db_string, " password='%s'", config->sql_password);
732 }
733
734 if (fr_time_delta_ispos(config->query_timeout)) {
735 db_string = talloc_asprintf_append(db_string, " connect_timeout=%d", (int) fr_time_delta_to_sec(config->query_timeout));
736 }
737
738 if (inst->send_application_name) {
739 db_string = talloc_asprintf_append(db_string, " application_name='%s'", application_name);
740 }
741
742 /*
743 * New style parameter string
744 *
745 * Only append options when not already present
746 */
747 } else {
748 db_string = talloc_typed_strdup(inst, config->sql_db);
749
750 if ((config->sql_server[0] != '\0') && !strstr(db_string, "host=")) {
751 db_string = talloc_asprintf_append(db_string, " host='%s'", config->sql_server);
752 }
753
754 if (config->sql_port && !strstr(db_string, "port=")) {
755 db_string = talloc_asprintf_append(db_string, " port=%i", config->sql_port);
756 }
757
758 if ((config->sql_login[0] != '\0') && !strstr(db_string, "user=")) {
759 db_string = talloc_asprintf_append(db_string, " user='%s'", config->sql_login);
760 }
761
762 if ((config->sql_password[0] != '\0') && !strstr(db_string, "password=")) {
763 db_string = talloc_asprintf_append(db_string, " password='%s'", config->sql_password);
764 }
765
766 if (fr_time_delta_ispos(config->query_timeout) && !strstr(db_string, "connect_timeout=")) {
767 db_string = talloc_asprintf_append(db_string, " connect_timeout=%d", (int) fr_time_delta_to_sec(config->query_timeout));
768 }
769
770 if (inst->send_application_name && !strstr(db_string, "application_name=")) {
771 db_string = talloc_asprintf_append(db_string, " application_name='%s'", application_name);
772 }
773 }
774 inst->db_string = db_string;
775
776 inst->states = sql_state_trie_alloc(inst);
777
778 /*
779 * Load in the PostgreSQL specific sqlstates
780 */
781 if (sql_state_entries_from_table(inst->states, sql_state_table) < 0) return -1;
782
783 /*
784 * Load in overrides from the driver's configuration section
785 */
786 {
787 CONF_SECTION *cs;
788
789 cs = cf_section_find(mctx->mi->conf, "states", NULL);
790 if (cs && (sql_state_entries_from_cs(inst->states, cs) < 0)) return -1;
791 }
792
793 return 0;
794}
795
796static int mod_load(void)
797{
798#if defined(WITH_TLS) && (defined(HAVE_PQINITOPENSSL) || defined(HAVE_PQINITSSL))
799# ifdef HAVE_PQINITOPENSSL
800 PQinitOpenSSL(0, 0);
801# else
802 PQinitSSL(0);
803# endif
804#endif
805 return 0;
806}
807
808static void *sql_escape_arg_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, void *uctx)
809{
811 connection_t *conn;
812
813 conn = connection_alloc(ctx, el,
815 .init = _sql_connection_init,
816 .close = _sql_connection_close,
817 },
818 inst->config.trunk_conf.conn_conf,
819 inst->name, inst);
820
821 if (!conn) {
822 PERROR("Failed allocating state handler for SQL escape connection");
823 return NULL;
824 }
825
827 return conn;
828}
829
830static void sql_escape_arg_free(void *uctx)
831{
832 connection_t *conn = talloc_get_type_abort(uctx, connection_t);
834}
835
836/* Exported to rlm_sql */
839 .common = {
840 .magic = MODULE_MAGIC_INIT,
841 .name = "sql_postgresql",
843 .onload = mod_load,
846 },
848 .sql_query_resume = sql_query_resume,
849 .sql_select_query_resume = sql_query_resume,
850 .sql_fields = sql_fields,
851 .sql_fetch_row = sql_fetch_row,
852 .sql_error = sql_error,
853 .sql_finish_query = sql_free_result,
854 .sql_finish_select_query = sql_free_result,
855 .sql_affected_rows = sql_affected_rows,
856 .sql_escape_func = sql_escape_func,
857 .sql_escape_arg_alloc = sql_escape_arg_alloc,
858 .sql_escape_arg_free = sql_escape_arg_free,
859 .trunk_io_funcs = {
860 .connection_alloc = sql_trunk_connection_alloc,
861 .connection_notify = sql_trunk_connection_notify,
862 .request_mux = sql_trunk_request_mux,
863 .request_demux = sql_trunk_request_demux,
864 .request_cancel = sql_request_cancel,
865 .request_cancel_mux = sql_request_cancel_mux,
866 .request_fail = sql_request_fail,
867 }
868};
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:483
#define CC_NO_UBSAN(_sanitize)
Definition build.h:426
#define UNUSED
Definition build.h:315
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:642
#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
conf_parser_flags_t flags
Flags which control parsing behaviour.
Definition cf_parse.h:585
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:579
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1185
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1171
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
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:684
#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
#define MEM(x)
Definition debug.h:36
#define RADIUSD_VERSION_STRING
Definition dependency.h:39
#define ERROR(fmt,...)
Definition dhcpclient.c:41
static fr_slen_t err
Definition dict.h:824
static fr_slen_t in
Definition dict.h:824
#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
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 fr_assert(_expr)
Definition rad_assert.h:38
#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:1310
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:138
trunk_connection_t * tconn
Trunk connection this query is being run on.
Definition rlm_sql.h:134
char const * meaning
Verbose description.
Definition rlm_sql.h:69
fr_table_num_sorted_t const sql_rcode_table[]
Definition sql.c:55
rlm_sql_t const * inst
Module instance for this query.
Definition rlm_sql.h:131
char const * query_str
Query string to run.
Definition rlm_sql.h:136
request_t * request
Request this query relates to.
Definition rlm_sql.h:132
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
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 resulting from a unique key violation.
Definition rlm_sql.h:164
rlm_sql_row_t row
Row data from the last query.
Definition rlm_sql.h:140
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:139
trunk_request_t * treq
Trunk request for this query.
Definition rlm_sql.h:135
@ SQL_QUERY_RETURNED
Query has executed.
Definition rlm_sql.h:124
@ SQL_QUERY_SUBMITTED
Submitted for execution.
Definition rlm_sql.h:123
@ SQL_QUERY_PREPARED
Ready to submit.
Definition rlm_sql.h:122
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
Definition rlm_sql.h:61
Definition rlm_sql.h:67
static void sql_trunk_connection_notify(UNUSED trunk_connection_t *tconn, connection_t *conn, UNUSED fr_event_list_t *el, trunk_connection_event_t notify_on, UNUSED void *uctx)
static void sql_request_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
static sql_rcode_t sql_classify_error(UNUSED PGresult const *result)
static int mod_load(void)
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)
static SQL_TRUNK_CONNECTION_ALLOC void sql_trunk_request_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
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 void * sql_escape_arg_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, void *uctx)
static conf_parser_t driver_config[]
connection_t * conn
Generic connection structure for this connection.
static void sql_trunk_request_demux(UNUSED fr_event_list_t *el, UNUSED trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
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)
static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason, UNUSED void *uctx)
#define NAMEDATALEN
fr_sql_query_t * query_ctx
Current query running on this connection.
fr_trie_t * states
sql state trie.
static void sql_request_cancel_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
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)
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
#define SQL_TRUNK_CONNECTION_ALLOC
Allocate an SQL trunk connection.
#define SQL_QUERY_FAIL
void connection_signal_halt(connection_t *conn)
Shuts down a connection ungracefully.
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
void connection_signal_init(connection_t *conn)
Asynchronously signal a halted connection to start.
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.
void connection_signal_connected(connection_t *conn)
Asynchronously signal that the connection is open.
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:329
size_t inst_size
Size of the module's instance data.
Definition module.h:203
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
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:194
module_instance_t * driver_submodule
Driver's submodule.
Definition rlm_sql.h:227
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
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
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
#define talloc_get_type_abort_const
Definition talloc.h:282
static const char * names[8]
Definition time.c:621
#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:3835
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition trunk.c:2132
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition trunk.c:2284
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:3883
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition trunk.c:2050
Associates request queues with a connection.
Definition trunk.c:134
Wraps a normal request.
Definition trunk.c:100
#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:953
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