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: c6610a44a96c44142ba97195a349bbcc1c9896d8 $
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: c6610a44a96c44142ba97195a349bbcc1c9896d8 $")
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
275 UNUSED connection_state_t state, void *uctx)
276{
278 rlm_sql_postgresql_t *inst = talloc_get_type_abort(sql->driver_submodule->data, rlm_sql_postgresql_t);
279 rlm_sql_postgres_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_postgres_conn_t);
280 int err;
281 PGresult *result, *tmp_result;
282 ExecStatusType status;
283 sql_rcode_t rcode;
284
285 DEBUG2("Executing \"%s\"", sql->config.connect_query);
286
287 err = PQsendQuery(sql_conn->db, sql->config.connect_query);
288 if (!err) {
289 fail:
290 ERROR("Failed running \"open_query\": %s", PQerrorMessage(sql_conn->db));
292 return;
293 }
294
295 result = PQgetResult(sql_conn->db);
296 if (!result) goto fail;
297
298 /*
299 * Clear up any additional results returned
300 */
301 while ((tmp_result = PQgetResult(sql_conn->db))) PQclear(tmp_result);
302
303 status = PQresultStatus(result);
304
305 rcode = sql_classify_error(inst, status, result);
306 PQclear(result);
307
308 if (rcode != RLM_SQL_OK) goto fail;
309}
310
311CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
312static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
313{
315 rlm_sql_postgresql_t const *inst = talloc_get_type_abort(sql->driver_submodule->data, rlm_sql_postgresql_t);
317 PostgresPollingStatusType status;
318
319 MEM(c = talloc_zero(conn, rlm_sql_postgres_conn_t));
320 c->conn = conn;
321 c->fd = -1;
322
323 DEBUG2("Starting connection to PostgreSQL server using parameters: %s", inst->db_string);
324
325 c->db = PQconnectStart(inst->db_string);
326 if (!c->db) {
327 ERROR("Connection failed: Out of memory");
328 talloc_free(c);
330 }
331
332 switch (PQstatus(c->db)) {
333 case CONNECTION_OK:
334 c->fd = PQsocket(c->db);
335 DEBUG2("Connected to database '%s' on '%s' server version %i, protocol version %i, backend PID %i ",
336 PQdb(c->db), PQhost(c->db), PQserverVersion(c->db), PQprotocolVersion(c->db),
337 PQbackendPID(c->db));
338 PQsetnonblocking(c->db, 1);
341
342 case CONNECTION_BAD:
343 ERROR("Connection failed: %s", PQerrorMessage(c->db));
344 error:
345 PQfinish(c->db);
346 talloc_free(c);
348
349 default:
350 break;
351
352 }
353
354 status = PQconnectPoll(c->db);
355 c->fd = PQsocket(c->db);
356 if (fr_event_fd_insert(c, NULL, c->conn->el, c->fd,
357 status == PGRES_POLLING_READING ? _sql_connect_io_notify : NULL,
358 status == PGRES_POLLING_WRITING ? _sql_connect_io_notify : NULL, NULL, c) != 0) goto error;
359
360 DEBUG2("Connecting to database '%s' on '%s', fd %d", PQdb(c->db), PQhost(c->db), c->fd);
361
362 *h = c;
363
365 _sql_connect_query_run, true, sql);
366
368}
369
370static void _sql_connection_close(fr_event_list_t *el, void *h, UNUSED void *uctx)
371{
372 rlm_sql_postgres_conn_t *c = talloc_get_type_abort(h, rlm_sql_postgres_conn_t);
373
374 if (c->fd >= 0) {
376 c->fd = -1;
377 }
378
379 if (c->result) {
380 PQclear(c->result);
381 c->result = NULL;
382 }
383
384 /* PQfinish also frees the memory used by the PGconn structure */
385 PQfinish(c->db);
386 c->query_ctx = NULL;
387 talloc_free(h);
388}
389
391
393
394CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
396 connection_t *conn, UNUSED void *uctx)
397{
398 rlm_sql_postgres_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_postgres_conn_t);
399 request_t *request;
400 trunk_request_t *treq;
401 fr_sql_query_t *query_ctx;
402 int err;
403
404 if (trunk_connection_pop_request(&treq, tconn) != 0) return;
405 if (!treq) return;
406
407 query_ctx = talloc_get_type_abort(treq->preq, fr_sql_query_t);
408 request = query_ctx->request;
409
410 switch (query_ctx->status) {
412 ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query_ctx->query_str);
413 err = PQsendQuery(sql_conn->db, query_ctx->query_str);
414 query_ctx->tconn = tconn;
415 if (!err) {
416 ROPTIONAL(RERROR, ERROR, "Failed to send query: %s", PQerrorMessage(sql_conn->db));
418 return;
419 }
420
421 query_ctx->status = SQL_QUERY_SUBMITTED;
422 sql_conn->query_ctx = query_ctx;
424 return;
425
426 default:
427 return;
428 }
429}
430
431CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
433 connection_t *conn, UNUSED void *uctx)
434{
435 rlm_sql_postgres_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_postgres_conn_t);
437 fr_sql_query_t *query_ctx;
438 request_t *request;
439 PGresult *tmp_result;
440 ExecStatusType status;
441 int numfields;
442
443 query_ctx = sql_conn->query_ctx;
444 request = query_ctx->request;
445 inst = talloc_get_type_abort(query_ctx->inst->driver_submodule->data, rlm_sql_postgresql_t);
446
447 switch (query_ctx->status) {
449 if (PQconsumeInput(sql_conn->db) == 0) {
450 ROPTIONAL(RERROR, ERROR, "SQL query failed: %s", PQerrorMessage(sql_conn->db));
451 query_ctx->rcode = RLM_SQL_ERROR;
452 break;
453 }
454 if (PQisBusy(sql_conn->db)) return;
455
456 query_ctx->status = SQL_QUERY_RETURNED;
457
458 sql_conn->result = PQgetResult(sql_conn->db);
459
460 /* Discard results for appended queries */
461 while ((tmp_result = PQgetResult(sql_conn->db)) != NULL)
462 PQclear(tmp_result);
463
464 /*
465 * As this error COULD be a connection error OR an out-of-memory
466 * condition return value WILL be wrong SOME of the time
467 * regardless! Pick your poison...
468 */
469 if (!sql_conn->result) {
470 ROPTIONAL(RERROR, ERROR, "Failed getting query result: %s", PQerrorMessage(sql_conn->db));
471 query_ctx->rcode = RLM_SQL_RECONNECT;
472 break;
473 }
474
475 status = PQresultStatus(sql_conn->result);
476 switch (status){
477 /*
478 * Successful completion of a command returning no data.
479 */
480 case PGRES_COMMAND_OK:
481 /*
482 * Affected_rows function only returns the number of affected rows of a command
483 * returning no data...
484 */
485 sql_conn->affected_rows = affected_rows(sql_conn->result);
486 ROPTIONAL(RDEBUG2, DEBUG2, "query affected rows = %i", sql_conn->affected_rows);
487 break;
488 /*
489 * Successful completion of a command returning data (such as a SELECT or SHOW).
490 */
491#ifdef HAVE_PGRES_SINGLE_TUPLE
492 case PGRES_SINGLE_TUPLE:
493#endif
494#ifdef HAVE_PGRES_TUPLES_CHUNK
495 case PGRES_TUPLES_CHUNK:
496#endif
497 case PGRES_TUPLES_OK:
498 sql_conn->cur_row = 0;
499 sql_conn->affected_rows = PQntuples(sql_conn->result);
500 numfields = PQnfields(sql_conn->result); /*Check row storing functions..*/
501 ROPTIONAL(RDEBUG2, DEBUG2, "query returned rows = %i, fields = %i", sql_conn->affected_rows, numfields);
502 break;
503
504#ifdef HAVE_PGRES_COPY_BOTH
505 case PGRES_COPY_BOTH:
506#endif
507 case PGRES_COPY_OUT:
508 case PGRES_COPY_IN:
509 DEBUG2("Data transfer started");
510 break;
511
512 /*
513 * Weird.. this shouldn't happen.
514 */
515 case PGRES_EMPTY_QUERY:
516 case PGRES_BAD_RESPONSE: /* The server's response was not understood */
517 case PGRES_NONFATAL_ERROR:
518 case PGRES_FATAL_ERROR:
519#ifdef HAVE_PGRES_PIPELINE_SYNC
520 case PGRES_PIPELINE_SYNC:
521 case PGRES_PIPELINE_ABORTED:
522#endif
523 break;
524 }
525
526 query_ctx->rcode = sql_classify_error(inst, status, sql_conn->result);
527 break;
528
529 default:
530 fr_assert(0);
531 }
532
533 if (request) unlang_interpret_mark_runnable(request);
534}
535
536CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
537static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason,
538 UNUSED void *uctx)
539{
540 fr_sql_query_t *query_ctx = talloc_get_type_abort(preq, fr_sql_query_t);
541 rlm_sql_postgres_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_postgres_conn_t);
542
543 if (!query_ctx->treq) return;
544 if (reason != TRUNK_CANCEL_REASON_SIGNAL) return;
545 if (sql_conn->query_ctx == query_ctx) sql_conn->query_ctx = NULL;
546}
547
548CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
550 connection_t *conn, UNUSED void *uctx)
551{
552 trunk_request_t *treq;
553 PGcancel *cancel;
554 rlm_sql_postgres_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_postgres_conn_t);
555 char errbuf[256];
556 PGresult *tmp_result;
557
558 if ((trunk_connection_pop_cancellation(&treq, tconn)) == 0) {
559 cancel = PQgetCancel(sql_conn->db);
560 if (!cancel) goto complete;
561 if (PQcancel(cancel, errbuf, sizeof(errbuf)) == 0) {
562 ERROR("Failed to cancel query: %s", errbuf);
563 }
564 PQfreeCancel(cancel);
565
566 /*
567 * The documentation says that regardless of the result of
568 * PQcancel, the normal processing of PQgetResult must happen.
569 */
570 while ((tmp_result = PQgetResult(sql_conn->db)) != NULL)
571 PQclear(tmp_result);
572
573 complete:
575 }
576}
577
580
581static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
582{
583 rlm_sql_postgres_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
584
585 int fields, i;
586 char const **names;
587
588 fields = PQnfields(conn->result);
589 if (fields <= 0) return RLM_SQL_ERROR;
590
591 MEM(names = talloc_array(query_ctx, char const *, fields));
592
593 for (i = 0; i < fields; i++) names[i] = PQfname(conn->result, i);
594 *out = names;
595
596 return RLM_SQL_OK;
597}
598
599static unlang_action_t sql_fetch_row(unlang_result_t *p_result, UNUSED request_t *request, void *uctx)
600{
601 fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
602 int records, i, len;
603 rlm_sql_postgres_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
604
605 query_ctx->row = NULL;
606
607 query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
608 if (conn->cur_row >= PQntuples(conn->result)) RETURN_UNLANG_OK;
609
610 free_result_row(conn);
611
612 records = PQnfields(conn->result);
613 conn->num_fields = records;
614
615 if ((PQntuples(conn->result) > 0) && (records > 0)) {
616 conn->row = talloc_zero_array(conn, char *, records + 1);
617 for (i = 0; i < records; i++) {
618 if (PQgetisnull(conn->result, conn->cur_row, i)) continue;
619 len = PQgetlength(conn->result, conn->cur_row, i);
620 conn->row[i] = talloc_array(conn->row, char, len + 1);
621 strlcpy(conn->row[i], PQgetvalue(conn->result, conn->cur_row, i), len + 1);
622 }
623 conn->cur_row++;
624 query_ctx->row = conn->row;
625
626 query_ctx->rcode = RLM_SQL_OK;
627 }
628
630}
631
633{
635
636 if (query_ctx->treq && !(query_ctx->treq->state &
638
639 if (!query_ctx->tconn || !query_ctx->tconn->conn || !query_ctx->tconn->conn->h) return RLM_SQL_ERROR;
640
641 if (!(query_ctx->tconn->state & TRUNK_CONN_PROCESSING)) return RLM_SQL_ERROR;
642
643 conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
644
645 if (conn->result != NULL) {
646 PQclear(conn->result);
647 conn->result = NULL;
648 }
649
650 free_result_row(conn);
651
652 return 0;
653}
654
655/** Retrieves any errors associated with the query context
656 *
657 * @note Caller will free any memory allocated in ctx.
658 *
659 * @param ctx to allocate temporary error buffers in.
660 * @param out Array of sql_log_entrys to fill.
661 * @param outlen Length of out array.
662 * @param query_ctx Query context to retrieve error for.
663 * @return number of errors written to the #sql_log_entry_t array.
664 */
665static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
666 fr_sql_query_t *query_ctx)
667{
668 rlm_sql_postgres_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
669 char const *p, *q;
670 size_t i = 0;
671
672 fr_assert(outlen > 0);
673
674 p = PQerrorMessage(conn->db);
675 while ((q = strchr(p, '\n'))) {
676 out[i].type = L_ERR;
677 out[i].msg = talloc_typed_asprintf(ctx, "%.*s", (int) (q - p), p);
678 p = q + 1;
679 if (++i == outlen) return outlen;
680 }
681 if (*p != '\0') {
682 out[i].type = L_ERR;
683 out[i].msg = p;
684 i++;
685 }
686
687 return i;
688}
689
691{
692 rlm_sql_postgres_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_postgres_conn_t);
693
694 return conn->affected_rows;
695}
696
697static ssize_t sql_escape_func(request_t *request, char *out, size_t outlen, char const *in, void *arg)
698{
699 size_t inlen, ret;
700 connection_t *c = talloc_get_type_abort(arg, connection_t);
702 int err;
703
704 if ((c->state == CONNECTION_STATE_HALTED) || (c->state == CONNECTION_STATE_CLOSED)) {
705 ROPTIONAL(RERROR, ERROR, "Connection not available for escaping");
706 return -1;
707 }
708
709 conn = talloc_get_type_abort(c->h, rlm_sql_postgres_conn_t);
710
711 /* Check for potential buffer overflow */
712 inlen = strlen(in);
713 if ((inlen * 2 + 1) > outlen) return 0;
714 /* Prevent integer overflow */
715 if ((inlen * 2 + 1) <= inlen) return 0;
716
717 ret = PQescapeStringConn(conn->db, out, in, inlen, &err);
718 if (err) {
719 ROPTIONAL(REDEBUG, ERROR, "Error escaping string \"%s\": %s", in, PQerrorMessage(conn->db));
720 return 0;
721 }
722
723 return ret;
724}
725
726static int mod_instantiate(module_inst_ctx_t const *mctx)
727{
728 rlm_sql_t const *parent = talloc_get_type_abort(mctx->mi->parent->data, rlm_sql_t);
729 rlm_sql_config_t const *config = &parent->config;
730 rlm_sql_postgresql_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_postgresql_t);
731 char application_name[NAMEDATALEN];
732 char *db_string;
733
734 /*
735 * Allow the user to set their own, or disable it
736 */
737 if (inst->send_application_name) {
738 CONF_SECTION *cs;
739 char const *name;
740
741 cs = cf_item_to_section(cf_parent(mctx->mi->conf));
742
744 if (!name) name = cf_section_name1(cs);
745
746 snprintf(application_name, sizeof(application_name),
747 "FreeRADIUS " RADIUSD_VERSION_STRING " - %s (%s)", main_config->name, name);
748 }
749
750 /*
751 * Old style database name
752 *
753 * Append options if they were set in the config
754 */
755 if (!strchr(config->sql_db, '=')) {
756 db_string = talloc_typed_asprintf(inst, "dbname='%s'", config->sql_db);
757
758 if (config->sql_server[0] != '\0') {
759 db_string = talloc_asprintf_append(db_string, " host='%s'", config->sql_server);
760 }
761
762 if (config->sql_port) {
763 db_string = talloc_asprintf_append(db_string, " port=%i", config->sql_port);
764 }
765
766 if (config->sql_login[0] != '\0') {
767 db_string = talloc_asprintf_append(db_string, " user='%s'", config->sql_login);
768 }
769
770 if (config->sql_password[0] != '\0') {
771 db_string = talloc_asprintf_append(db_string, " password='%s'", config->sql_password);
772 }
773
774 if (fr_time_delta_ispos(config->query_timeout)) {
775 db_string = talloc_asprintf_append(db_string, " connect_timeout=%d", (int) fr_time_delta_to_sec(config->query_timeout));
776 }
777
778 if (inst->send_application_name) {
779 db_string = talloc_asprintf_append(db_string, " application_name='%s'", application_name);
780 }
781
782 /*
783 * New style parameter string
784 *
785 * Only append options when not already present
786 */
787 } else {
788 db_string = talloc_typed_strdup(inst, config->sql_db);
789
790 if ((config->sql_server[0] != '\0') && !strstr(db_string, "host=")) {
791 db_string = talloc_asprintf_append(db_string, " host='%s'", config->sql_server);
792 }
793
794 if (config->sql_port && !strstr(db_string, "port=")) {
795 db_string = talloc_asprintf_append(db_string, " port=%i", config->sql_port);
796 }
797
798 if ((config->sql_login[0] != '\0') && !strstr(db_string, "user=")) {
799 db_string = talloc_asprintf_append(db_string, " user='%s'", config->sql_login);
800 }
801
802 if ((config->sql_password[0] != '\0') && !strstr(db_string, "password=")) {
803 db_string = talloc_asprintf_append(db_string, " password='%s'", config->sql_password);
804 }
805
806 if (fr_time_delta_ispos(config->query_timeout) && !strstr(db_string, "connect_timeout=")) {
807 db_string = talloc_asprintf_append(db_string, " connect_timeout=%d", (int) fr_time_delta_to_sec(config->query_timeout));
808 }
809
810 if (inst->send_application_name && !strstr(db_string, "application_name=")) {
811 db_string = talloc_asprintf_append(db_string, " application_name='%s'", application_name);
812 }
813 }
814 inst->db_string = db_string;
815
816 inst->states = sql_state_trie_alloc(inst);
817
818 /*
819 * Load in the PostgreSQL specific sqlstates
820 */
821 if (sql_state_entries_from_table(inst->states, sql_state_table) < 0) return -1;
822
823 /*
824 * Load in overrides from the driver's configuration section
825 */
826 {
827 CONF_SECTION *cs;
828
829 cs = cf_section_find(mctx->mi->conf, "states", NULL);
830 if (cs && (sql_state_entries_from_cs(inst->states, cs) < 0)) return -1;
831 }
832
833 return 0;
834}
835
836static int mod_load(void)
837{
838#if defined(WITH_TLS) && (defined(HAVE_PQINITOPENSSL) || defined(HAVE_PQINITSSL))
839# ifdef HAVE_PQINITOPENSSL
840 PQinitOpenSSL(0, 0);
841# else
842 PQinitSSL(0);
843# endif
844#endif
845 return 0;
846}
847
848static void *sql_escape_arg_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, void *uctx)
849{
851 connection_t *conn;
852
853 conn = connection_alloc(ctx, el,
855 .init = _sql_connection_init,
856 .failed = connection_failed_reinit,
857 .close = _sql_connection_close,
858 },
859 inst->config.trunk_conf.conn_conf,
860 inst->name, inst);
861
862 if (!conn) {
863 PERROR("Failed allocating state handler for SQL escape connection");
864 return NULL;
865 }
866
868 return conn;
869}
870
871static void sql_escape_arg_free(void *uctx)
872{
873 connection_t *conn = talloc_get_type_abort(uctx, connection_t);
875}
876
877/* Exported to rlm_sql */
880 .common = {
881 .magic = MODULE_MAGIC_INIT,
882 .name = "sql_postgresql",
884 .onload = mod_load,
886 .instantiate = mod_instantiate
887 },
889 .sql_query_resume = sql_query_resume,
890 .sql_select_query_resume = sql_query_resume,
891 .sql_fields = sql_fields,
892 .sql_fetch_row = sql_fetch_row,
893 .sql_error = sql_error,
894 .sql_finish_query = sql_free_result,
895 .sql_finish_select_query = sql_free_result,
896 .sql_affected_rows = sql_affected_rows,
897 .sql_escape_func = sql_escape_func,
898 .sql_escape_arg_alloc = sql_escape_arg_alloc,
899 .sql_escape_arg_free = sql_escape_arg_free,
900 .trunk_io_funcs = {
901 .connection_alloc = sql_trunk_connection_alloc,
902 .connection_notify = sql_trunk_connection_notify,
903 .request_mux = sql_trunk_request_mux,
904 .request_demux = sql_trunk_request_demux,
905 .request_cancel = sql_request_cancel,
906 .request_cancel_mux = sql_request_cancel_mux,
907 .request_fail = sql_request_fail,
908 }
909};
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:662
#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:284
conf_parser_flags_t flags
Flags which control parsing behaviour.
Definition cf_parse.h:605
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:599
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:1620
#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.
connection_state_t connection_failed_reinit(UNUSED void *handle, UNUSED connection_state_t state, UNUSED void *uctx)
Tell a failed connection to move to CONNECTION_STATE_INIT.
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:3849
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition trunk.c:2146
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition trunk.c:2298
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:3897
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition trunk.c:2064
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:957
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