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: 6ddc4a1a548e1ff70cb4a406e83885e7e077bce9 $
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: 6ddc4a1a548e1ff70cb4a406e83885e7e077bce9 $")
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
235 status = PQconnectPoll(c->db);
236
237 /*
238 * Documentation says:
239 * Caution: do not assume that the socket remains the same across PQconnectPoll calls.
240 * So we get the socket again.
241 */
242 c->fd = PQsocket(c->db);
243 switch (status) {
244 case PGRES_POLLING_OK:
245 DEBUG2("Connected to database '%s' on '%s' server version %i, protocol version %i, backend PID %i ",
246 PQdb(c->db), PQhost(c->db), PQserverVersion(c->db), PQprotocolVersion(c->db),
247 PQbackendPID(c->db));
249 PQsetnonblocking(c->db, 1);
251 return;
252
253 case PGRES_POLLING_FAILED:
254 error:
255 ERROR("Connection failed: %s", PQerrorMessage(c->db));
257 return;
258
259 case PGRES_POLLING_READING:
260 if (fr_event_fd_insert(c, NULL, c->conn->el, c->fd, _sql_connect_io_notify, NULL, NULL, c) != 0) goto error;
261 return;
262
263 case PGRES_POLLING_WRITING:
264 if (fr_event_fd_insert(c, NULL, c->conn->el, c->fd, NULL, _sql_connect_io_notify, NULL, c) != 0) goto error;
265 return;
266
267 default:
268 goto error;
269
270 }
271}
272
274 UNUSED connection_state_t state, void *uctx)
275{
277 rlm_sql_postgresql_t *inst = talloc_get_type_abort(sql->driver_submodule->data, rlm_sql_postgresql_t);
278 rlm_sql_postgres_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_postgres_conn_t);
279 int err;
280 PGresult *result, *tmp_result;
281 ExecStatusType status;
282 sql_rcode_t rcode;
283
284 DEBUG2("Executing \"%s\"", sql->config.connect_query);
285
286 err = PQsendQuery(sql_conn->db, sql->config.connect_query);
287 if (!err) {
288 fail:
289 ERROR("Failed running \"open_query\": %s", PQerrorMessage(sql_conn->db));
291 return;
292 }
293
294 result = PQgetResult(sql_conn->db);
295 if (!result) goto fail;
296
297 /*
298 * Clear up any additional results returned
299 */
300 while ((tmp_result = PQgetResult(sql_conn->db))) PQclear(tmp_result);
301
302 status = PQresultStatus(result);
303
304 rcode = sql_classify_error(inst, status, result);
305 PQclear(result);
306
307 if (rcode != RLM_SQL_OK) goto fail;
308}
309
310CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
311static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
312{
314 rlm_sql_postgresql_t const *inst = talloc_get_type_abort(sql->driver_submodule->data, rlm_sql_postgresql_t);
316 PostgresPollingStatusType status;
317
318 MEM(c = talloc_zero(conn, rlm_sql_postgres_conn_t));
319 c->conn = conn;
320 c->fd = -1;
321
322 DEBUG2("Starting connection to PostgreSQL server using parameters: %s", inst->db_string);
323
324 c->db = PQconnectStart(inst->db_string);
325 if (!c->db) {
326 ERROR("Connection failed: Out of memory");
327 talloc_free(c);
329 }
330
331 switch (PQstatus(c->db)) {
332 case CONNECTION_OK:
333 c->fd = PQsocket(c->db);
334 DEBUG2("Connected to database '%s' on '%s' server version %i, protocol version %i, backend PID %i ",
335 PQdb(c->db), PQhost(c->db), PQserverVersion(c->db), PQprotocolVersion(c->db),
336 PQbackendPID(c->db));
337 PQsetnonblocking(c->db, 1);
340
341 case CONNECTION_BAD:
342 ERROR("Connection failed: %s", PQerrorMessage(c->db));
343 error:
344 PQfinish(c->db);
345 talloc_free(c);
347
348 default:
349 break;
350
351 }
352
353 status = PQconnectPoll(c->db);
354 c->fd = PQsocket(c->db);
355 if (fr_event_fd_insert(c, NULL, c->conn->el, c->fd,
356 status == PGRES_POLLING_READING ? _sql_connect_io_notify : NULL,
357 status == PGRES_POLLING_WRITING ? _sql_connect_io_notify : NULL, NULL, c) != 0) goto error;
358
359 DEBUG2("Connecting to database '%s' on '%s', fd %d", PQdb(c->db), PQhost(c->db), c->fd);
360
361 *h = c;
362
364 _sql_connect_query_run, true, sql);
365
367}
368
369static void _sql_connection_close(fr_event_list_t *el, void *h, UNUSED void *uctx)
370{
371 rlm_sql_postgres_conn_t *c = talloc_get_type_abort(h, rlm_sql_postgres_conn_t);
372
373 if (c->fd >= 0) {
375 c->fd = -1;
376 }
377
378 if (c->result) {
379 PQclear(c->result);
380 c->result = NULL;
381 }
382
383 /* PQfinish also frees the memory used by the PGconn structure */
384 PQfinish(c->db);
385 c->query_ctx = NULL;
386 talloc_free(h);
387}
388
390
392
393CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
395 connection_t *conn, UNUSED void *uctx)
396{
397 rlm_sql_postgres_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_postgres_conn_t);
398 request_t *request;
399 trunk_request_t *treq;
400 fr_sql_query_t *query_ctx;
401 int err;
402
403 if (trunk_connection_pop_request(&treq, tconn) != 0) return;
404 if (!treq) return;
405
406 query_ctx = talloc_get_type_abort(treq->preq, fr_sql_query_t);
407 request = query_ctx->request;
408
409 switch (query_ctx->status) {
411 ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query_ctx->query_str);
412 err = PQsendQuery(sql_conn->db, query_ctx->query_str);
413 query_ctx->tconn = tconn;
414 if (!err) {
415 ROPTIONAL(RERROR, ERROR, "Failed to send query: %s", PQerrorMessage(sql_conn->db));
417 return;
418 }
419
420 query_ctx->status = SQL_QUERY_SUBMITTED;
421 sql_conn->query_ctx = query_ctx;
423 return;
424
425 default:
426 return;
427 }
428}
429
430CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
432 connection_t *conn, UNUSED void *uctx)
433{
434 rlm_sql_postgres_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_postgres_conn_t);
436 fr_sql_query_t *query_ctx;
437 request_t *request;
438 PGresult *tmp_result;
439 ExecStatusType status;
440 int numfields;
441
442 query_ctx = sql_conn->query_ctx;
443 request = query_ctx->request;
444 inst = talloc_get_type_abort(query_ctx->inst->driver_submodule->data, rlm_sql_postgresql_t);
445
446 switch (query_ctx->status) {
448 if (PQconsumeInput(sql_conn->db) == 0) {
449 ROPTIONAL(RERROR, ERROR, "SQL query failed: %s", PQerrorMessage(sql_conn->db));
450 query_ctx->rcode = RLM_SQL_ERROR;
451 break;
452 }
453 if (PQisBusy(sql_conn->db)) return;
454
455 query_ctx->status = SQL_QUERY_RETURNED;
456
457 sql_conn->result = PQgetResult(sql_conn->db);
458
459 /* Discard results for appended queries */
460 while ((tmp_result = PQgetResult(sql_conn->db)) != NULL)
461 PQclear(tmp_result);
462
463 /*
464 * As this error COULD be a connection error OR an out-of-memory
465 * condition return value WILL be wrong SOME of the time
466 * regardless! Pick your poison...
467 */
468 if (!sql_conn->result) {
469 ROPTIONAL(RERROR, ERROR, "Failed getting query result: %s", PQerrorMessage(sql_conn->db));
470 query_ctx->rcode = RLM_SQL_RECONNECT;
471 break;
472 }
473
474 status = PQresultStatus(sql_conn->result);
475 switch (status){
476 /*
477 * Successful completion of a command returning no data.
478 */
479 case PGRES_COMMAND_OK:
480 /*
481 * Affected_rows function only returns the number of affected rows of a command
482 * returning no data...
483 */
484 sql_conn->affected_rows = affected_rows(sql_conn->result);
485 ROPTIONAL(RDEBUG2, DEBUG2, "query affected rows = %i", sql_conn->affected_rows);
486 break;
487 /*
488 * Successful completion of a command returning data (such as a SELECT or SHOW).
489 */
490#ifdef HAVE_PGRES_SINGLE_TUPLE
491 case PGRES_SINGLE_TUPLE:
492#endif
493#ifdef HAVE_PGRES_TUPLES_CHUNK
494 case PGRES_TUPLES_CHUNK:
495#endif
496 case PGRES_TUPLES_OK:
497 sql_conn->cur_row = 0;
498 sql_conn->affected_rows = PQntuples(sql_conn->result);
499 numfields = PQnfields(sql_conn->result); /*Check row storing functions..*/
500 ROPTIONAL(RDEBUG2, DEBUG2, "query returned rows = %i, fields = %i", sql_conn->affected_rows, numfields);
501 break;
502
503#ifdef HAVE_PGRES_COPY_BOTH
504 case PGRES_COPY_BOTH:
505#endif
506 case PGRES_COPY_OUT:
507 case PGRES_COPY_IN:
508 DEBUG2("Data transfer started");
509 break;
510
511 /*
512 * Weird.. this shouldn't happen.
513 */
514 case PGRES_EMPTY_QUERY:
515 case PGRES_BAD_RESPONSE: /* The server's response was not understood */
516 case PGRES_NONFATAL_ERROR:
517 case PGRES_FATAL_ERROR:
518#ifdef HAVE_PGRES_PIPELINE_SYNC
519 case PGRES_PIPELINE_SYNC:
520 case PGRES_PIPELINE_ABORTED:
521#endif
522 break;
523 }
524
525 query_ctx->rcode = sql_classify_error(inst, status, sql_conn->result);
526 break;
527
528 default:
529 fr_assert(0);
530 }
531
532 if (request) unlang_interpret_mark_runnable(request);
533}
534
535CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
536static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason,
537 UNUSED void *uctx)
538{
539 fr_sql_query_t *query_ctx = talloc_get_type_abort(preq, fr_sql_query_t);
540 rlm_sql_postgres_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_postgres_conn_t);
541
542 if (!query_ctx->treq) return;
543 if (reason != TRUNK_CANCEL_REASON_SIGNAL) return;
544 if (sql_conn->query_ctx == query_ctx) sql_conn->query_ctx = NULL;
545}
546
547CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
549 connection_t *conn, UNUSED void *uctx)
550{
551 trunk_request_t *treq;
552 PGcancel *cancel;
553 rlm_sql_postgres_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_postgres_conn_t);
554 char errbuf[256];
555 PGresult *tmp_result;
556
557 if ((trunk_connection_pop_cancellation(&treq, tconn)) == 0) {
558 cancel = PQgetCancel(sql_conn->db);
559 if (!cancel) goto complete;
560 if (PQcancel(cancel, errbuf, sizeof(errbuf)) == 0) {
561 ERROR("Failed to cancel query: %s", errbuf);
562 }
563 PQfreeCancel(cancel);
564
565 /*
566 * The documentation says that regardless of the result of
567 * PQcancel, the normal processing of PQgetResult must happen.
568 */
569 while ((tmp_result = PQgetResult(sql_conn->db)) != NULL)
570 PQclear(tmp_result);
571
572 complete:
574 }
575}
576
579
580static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
581{
582 rlm_sql_postgres_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
583
584 int fields, i;
585 char const **names;
586
587 fields = PQnfields(conn->result);
588 if (fields <= 0) return RLM_SQL_ERROR;
589
590 MEM(names = talloc_array(query_ctx, char const *, fields));
591
592 for (i = 0; i < fields; i++) names[i] = PQfname(conn->result, i);
593 *out = names;
594
595 return RLM_SQL_OK;
596}
597
598static unlang_action_t sql_fetch_row(unlang_result_t *p_result, UNUSED request_t *request, void *uctx)
599{
600 fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
601 int records, i, len;
602 rlm_sql_postgres_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
603
604 query_ctx->row = NULL;
605
606 query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
607 if (conn->cur_row >= PQntuples(conn->result)) RETURN_UNLANG_OK;
608
609 free_result_row(conn);
610
611 records = PQnfields(conn->result);
612 conn->num_fields = records;
613
614 if ((PQntuples(conn->result) > 0) && (records > 0)) {
615 conn->row = talloc_zero_array(conn, char *, records + 1);
616 for (i = 0; i < records; i++) {
617 if (PQgetisnull(conn->result, conn->cur_row, i)) continue;
618 len = PQgetlength(conn->result, conn->cur_row, i);
619 conn->row[i] = talloc_array(conn->row, char, len + 1);
620 strlcpy(conn->row[i], PQgetvalue(conn->result, conn->cur_row, i), len + 1);
621 }
622 conn->cur_row++;
623 query_ctx->row = conn->row;
624
625 query_ctx->rcode = RLM_SQL_OK;
626 }
627
629}
630
632{
634
635 if (query_ctx->treq && !(query_ctx->treq->state &
637
638 if (!query_ctx->tconn || !query_ctx->tconn->conn || !query_ctx->tconn->conn->h) return RLM_SQL_ERROR;
639
640 if (!(query_ctx->tconn->state & TRUNK_CONN_PROCESSING)) return RLM_SQL_ERROR;
641
642 conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
643
644 if (conn->result != NULL) {
645 PQclear(conn->result);
646 conn->result = NULL;
647 }
648
649 free_result_row(conn);
650
651 return 0;
652}
653
654/** Retrieves any errors associated with the query context
655 *
656 * @note Caller will free any memory allocated in ctx.
657 *
658 * @param ctx to allocate temporary error buffers in.
659 * @param out Array of sql_log_entrys to fill.
660 * @param outlen Length of out array.
661 * @param query_ctx Query context to retrieve error for.
662 * @return number of errors written to the #sql_log_entry_t array.
663 */
664static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
665 fr_sql_query_t *query_ctx)
666{
667 rlm_sql_postgres_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
668 char const *p, *q;
669 size_t i = 0;
670
671 fr_assert(outlen > 0);
672
673 p = PQerrorMessage(conn->db);
674 while ((q = strchr(p, '\n'))) {
675 out[i].type = L_ERR;
676 out[i].msg = talloc_typed_asprintf(ctx, "%.*s", (int) (q - p), p);
677 p = q + 1;
678 if (++i == outlen) return outlen;
679 }
680 if (*p != '\0') {
681 out[i].type = L_ERR;
682 out[i].msg = p;
683 i++;
684 }
685
686 return i;
687}
688
690{
691 rlm_sql_postgres_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
692
693 return conn->affected_rows;
694}
695
696static ssize_t sql_escape_func(request_t *request, char *out, size_t outlen, char const *in, void *arg)
697{
698 size_t inlen, ret;
699 connection_t *c = talloc_get_type_abort(arg, connection_t);
701 int err;
702
703 if ((c->state == CONNECTION_STATE_HALTED) || (c->state == CONNECTION_STATE_CLOSED)) {
704 ROPTIONAL(RERROR, ERROR, "Connection not available for escaping");
705 return -1;
706 }
707
708 conn = talloc_get_type_abort(c->h, rlm_sql_postgres_conn_t);
709
710 /* Check for potential buffer overflow */
711 inlen = strlen(in);
712 if ((inlen * 2 + 1) > outlen) return 0;
713 /* Prevent integer overflow */
714 if ((inlen * 2 + 1) <= inlen) return 0;
715
716 ret = PQescapeStringConn(conn->db, out, in, inlen, &err);
717 if (err) {
718 ROPTIONAL(REDEBUG, ERROR, "Error escaping string \"%s\": %s", in, PQerrorMessage(conn->db));
719 return 0;
720 }
721
722 return ret;
723}
724
725static int mod_instantiate(module_inst_ctx_t const *mctx)
726{
727 rlm_sql_t const *parent = talloc_get_type_abort(mctx->mi->parent->data, rlm_sql_t);
728 rlm_sql_config_t const *config = &parent->config;
729 rlm_sql_postgresql_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_postgresql_t);
730 char application_name[NAMEDATALEN];
731 char *db_string;
732
733 /*
734 * Allow the user to set their own, or disable it
735 */
736 if (inst->send_application_name) {
737 CONF_SECTION *cs;
738 char const *name;
739
740 cs = cf_item_to_section(cf_parent(mctx->mi->conf));
741
743 if (!name) name = cf_section_name1(cs);
744
745 snprintf(application_name, sizeof(application_name),
746 "FreeRADIUS " RADIUSD_VERSION_STRING " - %s (%s)", main_config->name, name);
747 }
748
749 /*
750 * Old style database name
751 *
752 * Append options if they were set in the config
753 */
754 if (!strchr(config->sql_db, '=')) {
755 db_string = talloc_typed_asprintf(inst, "dbname='%s'", config->sql_db);
756
757 if (config->sql_server[0] != '\0') {
758 db_string = talloc_asprintf_append(db_string, " host='%s'", config->sql_server);
759 }
760
761 if (config->sql_port) {
762 db_string = talloc_asprintf_append(db_string, " port=%i", config->sql_port);
763 }
764
765 if (config->sql_login[0] != '\0') {
766 db_string = talloc_asprintf_append(db_string, " user='%s'", config->sql_login);
767 }
768
769 if (config->sql_password[0] != '\0') {
770 db_string = talloc_asprintf_append(db_string, " password='%s'", config->sql_password);
771 }
772
773 if (fr_time_delta_ispos(config->query_timeout)) {
774 db_string = talloc_asprintf_append(db_string, " connect_timeout=%d", (int) fr_time_delta_to_sec(config->query_timeout));
775 }
776
777 if (inst->send_application_name) {
778 db_string = talloc_asprintf_append(db_string, " application_name='%s'", application_name);
779 }
780
781 /*
782 * New style parameter string
783 *
784 * Only append options when not already present
785 */
786 } else {
787 db_string = talloc_typed_strdup(inst, config->sql_db);
788
789 if ((config->sql_server[0] != '\0') && !strstr(db_string, "host=")) {
790 db_string = talloc_asprintf_append(db_string, " host='%s'", config->sql_server);
791 }
792
793 if (config->sql_port && !strstr(db_string, "port=")) {
794 db_string = talloc_asprintf_append(db_string, " port=%i", config->sql_port);
795 }
796
797 if ((config->sql_login[0] != '\0') && !strstr(db_string, "user=")) {
798 db_string = talloc_asprintf_append(db_string, " user='%s'", config->sql_login);
799 }
800
801 if ((config->sql_password[0] != '\0') && !strstr(db_string, "password=")) {
802 db_string = talloc_asprintf_append(db_string, " password='%s'", config->sql_password);
803 }
804
805 if (fr_time_delta_ispos(config->query_timeout) && !strstr(db_string, "connect_timeout=")) {
806 db_string = talloc_asprintf_append(db_string, " connect_timeout=%d", (int) fr_time_delta_to_sec(config->query_timeout));
807 }
808
809 if (inst->send_application_name && !strstr(db_string, "application_name=")) {
810 db_string = talloc_asprintf_append(db_string, " application_name='%s'", application_name);
811 }
812 }
813 inst->db_string = db_string;
814
815 inst->states = sql_state_trie_alloc(inst);
816
817 /*
818 * Load in the PostgreSQL specific sqlstates
819 */
820 if (sql_state_entries_from_table(inst->states, sql_state_table) < 0) return -1;
821
822 /*
823 * Load in overrides from the driver's configuration section
824 */
825 {
826 CONF_SECTION *cs;
827
828 cs = cf_section_find(mctx->mi->conf, "states", NULL);
829 if (cs && (sql_state_entries_from_cs(inst->states, cs) < 0)) return -1;
830 }
831
832 return 0;
833}
834
835static int mod_load(void)
836{
837#if defined(WITH_TLS) && (defined(HAVE_PQINITOPENSSL) || defined(HAVE_PQINITSSL))
838# ifdef HAVE_PQINITOPENSSL
839 PQinitOpenSSL(0, 0);
840# else
841 PQinitSSL(0);
842# endif
843#endif
844 return 0;
845}
846
847static void *sql_escape_arg_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, void *uctx)
848{
850 connection_t *conn;
851
852 conn = connection_alloc(ctx, el,
854 .init = _sql_connection_init,
855 .close = _sql_connection_close,
856 },
857 inst->config.trunk_conf.conn_conf,
858 inst->name, inst);
859
860 if (!conn) {
861 PERROR("Failed allocating state handler for SQL escape connection");
862 return NULL;
863 }
864
866 return conn;
867}
868
869static void sql_escape_arg_free(void *uctx)
870{
871 connection_t *conn = talloc_get_type_abort(uctx, connection_t);
873}
874
875/* Exported to rlm_sql */
878 .common = {
879 .magic = MODULE_MAGIC_INIT,
880 .name = "sql_postgresql",
882 .onload = mod_load,
884 .instantiate = mod_instantiate
885 },
887 .sql_query_resume = sql_query_resume,
888 .sql_select_query_resume = sql_query_resume,
889 .sql_fields = sql_fields,
890 .sql_fetch_row = sql_fetch_row,
891 .sql_error = sql_error,
892 .sql_finish_query = sql_free_result,
893 .sql_finish_select_query = sql_free_result,
894 .sql_affected_rows = sql_affected_rows,
895 .sql_escape_func = sql_escape_func,
896 .sql_escape_arg_alloc = sql_escape_arg_alloc,
897 .sql_escape_arg_free = sql_escape_arg_free,
898 .trunk_io_funcs = {
899 .connection_alloc = sql_trunk_connection_alloc,
900 .connection_notify = sql_trunk_connection_notify,
901 .request_mux = sql_trunk_request_mux,
902 .request_demux = sql_trunk_request_demux,
903 .request_cancel = sql_request_cancel,
904 .request_cancel_mux = sql_request_cancel_mux,
905 .request_fail = sql_request_fail,
906 }
907};
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:485
#define CC_NO_UBSAN(_sanitize)
Definition build.h:428
#define UNUSED
Definition build.h:317
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:660
#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:283
conf_parser_flags_t flags
Flags which control parsing behaviour.
Definition cf_parse.h:603
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:597
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:1184
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1170
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:1027
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
#define cf_parent(_cf)
Definition cf_util.h:101
connection_state_t
Definition connection.h:47
@ CONNECTION_STATE_FAILED
Connection has failed.
Definition connection.h:56
@ CONNECTION_STATE_HALTED
The connection is in a halted stat.
Definition connection.h:48
@ CONNECTION_STATE_CLOSED
Connection has been closed.
Definition connection.h:57
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition connection.h:54
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Definition connection.h:52
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition connection.h:85
Holds a complete set of functions for a connection.
Definition connection.h:195
#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:861
static fr_slen_t in
Definition dict.h:861
#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:248
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:84
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1612
#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:1203
Stores all information relating to an event list.
Definition event.c:377
@ L_ERR
Error message.
Definition log.h:56
main_config_t const * main_config
Main server configuration.
Definition main_config.c:58
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:186
#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_UNLANG_OK
Definition rcode.h:60
static char const * name
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:144
trunk_connection_t * tconn
Trunk connection this query is being run on.
Definition rlm_sql.h:140
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:137
char const * query_str
Query string to run.
Definition rlm_sql.h:142
request_t * request
Request this query relates to.
Definition rlm_sql.h:138
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:171
rlm_sql_row_t row
Row data from the last query.
Definition rlm_sql.h:146
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:145
trunk_request_t * treq
Trunk request for this query.
Definition rlm_sql.h:141
char const * connect_query
Query executed after establishing new connection.
Definition rlm_sql.h:98
@ SQL_QUERY_RETURNED
Query has executed.
Definition rlm_sql.h:130
@ SQL_QUERY_SUBMITTED
Submitted for execution.
Definition rlm_sql.h:129
@ SQL_QUERY_PREPARED
Ready to submit.
Definition rlm_sql.h:128
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, 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
static unlang_action_t sql_fetch_row(unlang_result_t *p_result, UNUSED request_t *request, void *uctx)
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 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 void _sql_connect_query_run(connection_t *conn, UNUSED connection_state_t prev, UNUSED connection_state_t state, void *uctx)
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.
connection_watch_entry_t * connection_add_watch_post(connection_t *conn, connection_state_t state, connection_watch_t watch, bool oneshot, void const *uctx)
Add a callback to be executed after a state function has been called.
Definition connection.c:534
void connection_signal_connected(connection_t *conn)
Asynchronously signal that the connection is open.
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:349
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:291
module_instance_t const * parent
Parent module's instance (if any).
Definition module.h:357
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:201
module_instance_t * driver_submodule
Driver's submodule.
Definition rlm_sql.h:234
rlm_sql_config_t config
Definition rlm_sql.h:228
#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:514
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:467
#define talloc_get_type_abort_const
Definition talloc.h:287
static const char * names[8]
Definition time.c:584
#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:3851
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition trunk.c:2148
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition trunk.c:2300
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:3899
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition trunk.c:2066
Associates request queues with a connection.
Definition trunk.c:133
Wraps a normal request.
Definition trunk.c:99
#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:958
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:841
static size_t char fr_sbuff_t size_t inlen
Definition value.h:1023
static size_t char ** out
Definition value.h:1023