The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_sql_mysql.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 7e5bdc2e986ba62a4cc43632ec52e14538ed4e7c $
19 * @file rlm_sql_mysql.c
20 * @brief MySQL driver.
21 *
22 * @copyright 2014-2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 * @copyright 2000-2007,2015 The FreeRADIUS server project
24 * @copyright 2000 Mike Machado (mike@innercite.com)
25 * @copyright 2000 Alan DeKok (aland@freeradius.org)
26 */
27RCSID("$Id: 7e5bdc2e986ba62a4cc43632ec52e14538ed4e7c $")
28
29#define LOG_PREFIX log_prefix
30
31#include <freeradius-devel/server/base.h>
32#include <freeradius-devel/util/debug.h>
33
34#include <sys/stat.h>
35
36#include "config.h"
37
38#ifdef HAVE_MYSQL_MYSQL_H
39# include <mysql/errmsg.h>
40DIAG_OFF(strict-prototypes) /* Seen with homebrew mysql client 5.7.13 */
41# include <mysql/mysql.h>
42DIAG_ON(strict-prototypes)
43# include <mysql/mysqld_error.h>
44#elif defined(HAVE_MYSQL_H)
45# include <errmsg.h>
46DIAG_OFF(strict-prototypes) /* Seen with homebrew mysql client 5.7.13 */
47# include <mysql.h>
48DIAG_ON(strict-prototypes)
49# include <mysqld_error.h>
50#endif
51
52#include "rlm_sql.h"
53#include "rlm_sql_trunk.h"
54
60
62 { L("auto"), SERVER_WARNINGS_AUTO },
63 { L("no"), SERVER_WARNINGS_NO },
64 { L("yes"), SERVER_WARNINGS_YES }
65};
67
68typedef struct {
69 MYSQL db; //!< Structure representing connection details.
70 MYSQL *sock; //!< Connection details as returned by connection init functions.
71 MYSQL_RES *result; //!< Result from most recent query.
72 connection_t *conn; //!< Generic connection structure for this connection.
73 int fd; //!< fd for this connection's I/O events.
74 fr_sql_query_t *query_ctx; //!< Current query running on this connection.
75 int status; //!< returned by the most recent non-blocking function call.
77
78typedef struct {
79 char const *tls_ca_file; //!< Path to the CA used to validate the server's certificate.
80 char const *tls_ca_path; //!< Directory containing CAs that may be used to validate the
81 //!< servers certificate.
82 char const *tls_certificate_file; //!< Public certificate we present to the server.
83 char const *tls_private_key_file; //!< Private key for the certificate we present to the server.
84
85 char const *tls_crl_file; //!< Public certificate we present to the server.
86 char const *tls_crl_path; //!< Private key for the certificate we present to the server.
87
88 char const *tls_cipher; //!< Colon separated list of TLS ciphers for TLS <= 1.2.
89
90 bool tls_required; //!< Require that the connection is encrypted.
91 bool tls_check_cert; //!< Verify there's a trust relationship between the server's
92 ///< cert and one of the CAs we have configured.
93 bool tls_check_cert_cn; //!< Verify that the CN in the server cert matches the host
94 ///< we passed to mysql_real_connect().
95
96 char const *warnings_str; //!< Whether we always query the server for additional warnings.
97 rlm_sql_mysql_warnings warnings; //!< mysql_warning_count() doesn't
98 //!< appear to work with NDB cluster
99
100 char const *character_set; //!< Character set to use on connections.
102
104 { FR_CONF_OFFSET_FLAGS("ca_file", CONF_FLAG_FILE_INPUT, rlm_sql_mysql_t, tls_ca_file) },
105 { FR_CONF_OFFSET_FLAGS("ca_path", CONF_FLAG_FILE_INPUT, rlm_sql_mysql_t, tls_ca_path) },
106 { FR_CONF_OFFSET_FLAGS("certificate_file", CONF_FLAG_FILE_INPUT, rlm_sql_mysql_t, tls_certificate_file) },
107 { FR_CONF_OFFSET_FLAGS("private_key_file", CONF_FLAG_FILE_INPUT, rlm_sql_mysql_t, tls_private_key_file) },
108 { FR_CONF_OFFSET_FLAGS("crl_file", CONF_FLAG_FILE_INPUT, rlm_sql_mysql_t, tls_crl_file) },
109 { FR_CONF_OFFSET_FLAGS("crl_path", CONF_FLAG_FILE_INPUT, rlm_sql_mysql_t, tls_crl_path) },
110 /*
111 * MySQL Specific TLS attributes
112 */
113 { FR_CONF_OFFSET("cipher", rlm_sql_mysql_t, tls_cipher) },
114
115 /*
116 * The closest thing we have to these options in other modules is
117 * in rlm_rest. rlm_ldap has its own bizarre option set.
118 *
119 * There, the options can be toggled independently, here they can't
120 * but for consistency we break them out anyway, and warn if the user
121 * has provided an invalid list of flags.
122 */
123 { FR_CONF_OFFSET("tls_required", rlm_sql_mysql_t, tls_required) },
124 { FR_CONF_OFFSET("check_cert", rlm_sql_mysql_t, tls_check_cert) },
125 { FR_CONF_OFFSET("check_cert_cn", rlm_sql_mysql_t, tls_check_cert_cn) },
127};
128
129static const conf_parser_t driver_config[] = {
130 { FR_CONF_POINTER("tls", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) tls_config },
131
132 { FR_CONF_OFFSET("warnings", rlm_sql_mysql_t, warnings_str), .dflt = "auto" },
133 { FR_CONF_OFFSET("character_set", rlm_sql_mysql_t, character_set) },
135};
136
137/* Prototypes */
139
140static int mod_instantiate(module_inst_ctx_t const *mctx)
141{
142 rlm_sql_mysql_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_mysql_t);
143 int warnings;
144 char const *log_prefix = mctx->mi->name;
145
146 warnings = fr_table_value_by_str(server_warnings_table, inst->warnings_str, -1);
147 if (warnings < 0) {
148 ERROR("Invalid warnings value \"%s\", must be yes, no, or auto", inst->warnings_str);
149 return -1;
150 }
151 inst->warnings = (rlm_sql_mysql_warnings)warnings;
152
153 if (inst->tls_check_cert && !inst->tls_required) {
154 WARN("Implicitly setting tls_required = yes, as tls_check_cert = yes");
155 inst->tls_required = true;
156 }
157 if (inst->tls_check_cert_cn) {
158 if (!inst->tls_required) {
159 WARN("Implicitly setting tls_required = yes, as check_cert_cn = yes");
160 inst->tls_required = true;
161 }
162
163 if (!inst->tls_check_cert) {
164 WARN("Implicitly setting check_cert = yes, as check_cert_cn = yes");
165 inst->tls_check_cert = true;
166 }
167 }
168 return 0;
169}
170
171static void mod_unload(void)
172{
173 mysql_library_end();
174}
175
176static int mod_load(void)
177{
178 char const *log_prefix = "rlm_sql_mysql";
179 if (mysql_library_init(0, NULL, NULL)) {
180 ERROR("libmysql initialisation failed");
181
182 return -1;
183 }
184
185 INFO("libmysql version: %s", mysql_get_client_info());
186
187 return 0;
188}
189
190/** Callback for I/O events in response to mysql_real_connect_start()
191 */
192static void _sql_connect_io_notify(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
193{
194 rlm_sql_mysql_conn_t *c = talloc_get_type_abort(uctx, rlm_sql_mysql_conn_t);
195 char const *log_prefix = c->conn->name;
196
198
199 if (c->status == 0) goto connected;
200 c->status = mysql_real_connect_cont(&c->sock, &c->db, c->status);
201
202 /*
203 * If status is not zero, we're still waiting for something.
204 * The event will be fired again when that happens.
205 */
206 if (c->status != 0) {
207 (void) fr_event_fd_insert(c, NULL, c->conn->el, c->fd,
208 c->status & MYSQL_WAIT_READ ? _sql_connect_io_notify : NULL,
209 c->status & MYSQL_WAIT_WRITE ? _sql_connect_io_notify : NULL, NULL, c);
210 return;
211 }
212
213connected:
214 if (!c->sock) {
215 ERROR("MySQL error: %s", mysql_error(&c->db));
217 return;
218 }
219
220 DEBUG2("Connected to database on %s, server version %s, protocol version %i",
221 mysql_get_host_info(c->sock),
222 mysql_get_server_info(c->sock), mysql_get_proto_info(c->sock));
223
225}
226
228 UNUSED connection_state_t state, void *uctx)
229{
231 rlm_sql_mysql_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_mysql_conn_t);
232 char const *log_prefix = conn->name;
233 int ret;
234 MYSQL_RES *result;
235
236 DEBUG2("Executing \"%s\"", sql->config.connect_query);
237
238 ret = mysql_real_query(sql_conn->sock, sql->config.connect_query, strlen(sql->config.connect_query));
239 if (ret != 0) {
240 char const *info;
241 ERROR("Failed running \"open_query\"");
242 info = mysql_info(sql_conn->sock);
243 if (info) ERROR("%s", info);
245 return;
246 }
247
248 /*
249 * These queries should not return any results - but let's be safe
250 */
251 result = mysql_store_result(sql_conn->sock);
252 if (result) mysql_free_result(result);
253 while ((mysql_next_result(sql_conn->sock) == 0) &&
254 (result = mysql_store_result(sql_conn->sock))) {
255 mysql_free_result(result);
256 }
257}
258
259CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
260static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
261{
263 rlm_sql_mysql_t const *inst = talloc_get_type_abort(sql->driver_submodule->data, rlm_sql_mysql_t);
264 char const *log_prefix = conn->name;
266 rlm_sql_config_t const *config = &sql->config;
267
268 unsigned long sql_flags;
269 enum mysql_option ssl_mysql_opt;
270 unsigned int ssl_mode = 0;
271 bool ssl_mode_isset = false;
272
273 MEM(c = talloc_zero(conn, rlm_sql_mysql_conn_t));
274 c->conn = conn;
275 c->fd = -1;
276
277 DEBUG("Starting connect to MySQL server");
278
279 mysql_init(&c->db);
280
281 /*
282 * If any of the TLS options are set, configure TLS
283 *
284 * According to MySQL docs this function always returns 0, so we won't
285 * know if ssl setup succeeded until mysql_real_connect is called below.
286 */
287 if (inst->tls_ca_file || inst->tls_ca_path ||
288 inst->tls_certificate_file || inst->tls_private_key_file) {
289 mysql_ssl_set(&(c->db), inst->tls_private_key_file, inst->tls_certificate_file,
290 inst->tls_ca_file, inst->tls_ca_path, inst->tls_cipher);
291 }
292
293#ifdef MARIADB_BASE_VERSION
294 if (inst->tls_required || inst->tls_check_cert || inst->tls_check_cert_cn) {
295 ssl_mode_isset = true;
296 /**
297 * For MariaDB, It should be true as can be seen in
298 * https://github.com/MariaDB/server/blob/mariadb-5.5.68/sql-common/client.c#L4338
299 */
300 ssl_mode = true;
301 ssl_mysql_opt = MYSQL_OPT_SSL_VERIFY_SERVER_CERT;
302 }
303#else
304 ssl_mysql_opt = MYSQL_OPT_SSL_MODE;
305 if (inst->tls_required) {
306 ssl_mode = SSL_MODE_REQUIRED;
307 ssl_mode_isset = true;
308 }
309 if (inst->tls_check_cert) {
310 ssl_mode = SSL_MODE_VERIFY_CA;
311 ssl_mode_isset = true;
312 }
313 if (inst->tls_check_cert_cn) {
314 ssl_mode = SSL_MODE_VERIFY_IDENTITY;
315 ssl_mode_isset = true;
316 }
317#endif
318 if (ssl_mode_isset) mysql_options(&(c->db), ssl_mysql_opt, &ssl_mode);
319
320 if (inst->tls_crl_file) mysql_options(&(c->db), MYSQL_OPT_SSL_CRL, inst->tls_crl_file);
321 if (inst->tls_crl_path) mysql_options(&(c->db), MYSQL_OPT_SSL_CRLPATH, inst->tls_crl_path);
322
323 mysql_options(&(c->db), MYSQL_READ_DEFAULT_GROUP, "freeradius");
324
325 if (inst->character_set) mysql_options(&(c->db), MYSQL_SET_CHARSET_NAME, inst->character_set);
326
327#if MYSQL_VERSION_ID < 80034
328 /*
329 * We need to know about connection errors, and are capable
330 * of reconnecting automatically.
331 *
332 * This deprecated as of 8.0.34.
333 */
334 {
335 bool reconnect = 0;
336 mysql_options(&(c->db), MYSQL_OPT_RECONNECT, &reconnect);
337 }
338#endif
339
340 sql_flags = CLIENT_MULTI_RESULTS | CLIENT_FOUND_ROWS;
341
342#ifdef CLIENT_MULTI_STATEMENTS
343 sql_flags |= CLIENT_MULTI_STATEMENTS;
344#endif
345
346 mysql_options(&c->db, MYSQL_OPT_NONBLOCK, 0);
347
348 c->status = mysql_real_connect_start(&c->sock, &c->db,
349 config->sql_server,
350 config->sql_login,
351 config->sql_password,
352 config->sql_db,
353 config->sql_port, NULL, sql_flags);
354
355 c->fd = mysql_get_socket(&c->db);
356
357 if (c->fd <= 0) {
358 ERROR("Could't connect to MySQL server %s@%s:%s", config->sql_login,
359 config->sql_server, config->sql_db);
360 ERROR("MySQL error: %s", mysql_error(&c->db));
361 error:
362 talloc_free(c);
364 }
365
366 if (c->status == 0) {
367 DEBUG2("Connected to database '%s' on %s, server version %s, protocol version %i",
368 config->sql_db, mysql_get_host_info(c->sock),
369 mysql_get_server_info(c->sock), mysql_get_proto_info(c->sock));
370 goto finish;
371 }
372
373 if (fr_event_fd_insert(c, NULL, c->conn->el, c->fd,
374 c->status & MYSQL_WAIT_READ ? _sql_connect_io_notify : NULL,
375 c->status & MYSQL_WAIT_WRITE ? _sql_connect_io_notify : NULL, NULL, c) != 0) goto error;
376
377 DEBUG2("Connecting to database '%s' on %s:%d, fd %d",
378 config->sql_db, config->sql_server, config->sql_port, c->fd);
379
380finish:
381 *h = c;
382
384 _sql_connect_query_run, true, sql);
385
387}
388
389static void _sql_connection_close(fr_event_list_t *el, void *h, UNUSED void *uctx)
390{
391 rlm_sql_mysql_conn_t *c = talloc_get_type_abort(h, rlm_sql_mysql_conn_t);
392
393 if (c->fd >= 0) {
395 c->fd = -1;
396 }
397 mysql_close(&c->db);
398 c->query_ctx = NULL;
399 talloc_free(h);
400}
401
402/** Analyse the last error that occurred on the socket, and determine an action
403 *
404 * @param server Socket from which to extract the server error. May be NULL.
405 * @param client_errno Error from the client.
406 * @return an action for #rlm_sql_t to take.
407 */
408static sql_rcode_t sql_check_error(MYSQL *server, int client_errno)
409{
410 int sql_errno = 0;
411
412 /*
413 * The client and server error numbers are in the
414 * same numberspace.
415 */
416 if (server) sql_errno = mysql_errno(server);
417 if ((sql_errno == 0) && (client_errno != 0)) sql_errno = client_errno;
418
419 if (sql_errno > 0) switch (sql_errno) {
420 case CR_SERVER_GONE_ERROR:
421 case CR_SERVER_LOST:
422 return RLM_SQL_RECONNECT;
423
424 case CR_OUT_OF_MEMORY:
425 case CR_COMMANDS_OUT_OF_SYNC:
426 case CR_UNKNOWN_ERROR:
427 default:
428 return RLM_SQL_ERROR;
429
430 /*
431 * Constraints errors that signify a duplicate, or that we might
432 * want to try an alternative query.
433 *
434 * Error constants not found in the 3.23/4.0/4.1 manual page
435 * are checked for.
436 * Other error constants should always be available.
437 */
438 case ER_DUP_UNIQUE: /* Can't write, because of unique constraint, to table '%s'. */
439 case ER_DUP_KEY: /* Can't write; duplicate key in table '%s' */
440
441 case ER_DUP_ENTRY: /* Duplicate entry '%s' for key %d. */
442 case ER_NO_REFERENCED_ROW: /* Cannot add or update a child row: a foreign key constraint fails */
443 case ER_ROW_IS_REFERENCED: /* Cannot delete or update a parent row: a foreign key constraint fails */
444#ifdef ER_FOREIGN_DUPLICATE_KEY
445 case ER_FOREIGN_DUPLICATE_KEY: /* Upholding foreign key constraints for table '%s', entry '%s', key %d would lead to a duplicate entry. */
446#endif
447#ifdef ER_DUP_ENTRY_WITH_KEY_NAME
448 case ER_DUP_ENTRY_WITH_KEY_NAME: /* Duplicate entry '%s' for key '%s' */
449#endif
450#ifdef ER_NO_REFERENCED_ROW_2
451 case ER_NO_REFERENCED_ROW_2:
452#endif
453#ifdef ER_ROW_IS_REFERENCED_2
454 case ER_ROW_IS_REFERENCED_2:
455#endif
456 return RLM_SQL_ALT_QUERY;
457
458 /*
459 * Constraints errors that signify an invalid query
460 * that can never succeed.
461 */
462 case ER_BAD_NULL_ERROR: /* Column '%s' cannot be null */
463 case ER_NON_UNIQ_ERROR: /* Column '%s' in %s is ambiguous */
465
466 /*
467 * Constraints errors that signify no data returned.
468 *
469 * This is considered OK as the caller may look for the next result set.
470 */
471 case ER_SP_FETCH_NO_DATA:
472 return RLM_SQL_OK;
473
474 }
475
476 return RLM_SQL_OK;
477}
478
480{
481 sql_rcode_t rcode;
482 int ret;
483
484retry_store_result:
485 conn->result = mysql_store_result(conn->sock);
486 if (!conn->result) {
487 rcode = sql_check_error(conn->sock, 0);
488 if (rcode != RLM_SQL_OK) return rcode;
489 ret = mysql_next_result(conn->sock);
490 if (ret == 0) {
491 /* there are more results */
492 goto retry_store_result;
493 } else if (ret > 0) return sql_check_error(NULL, ret);
494 /* ret == -1 signals no more results */
495 }
496 return RLM_SQL_OK;
497}
498
500{
501 rlm_sql_mysql_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_mysql_conn_t);
502
503 if (conn->result) return mysql_num_rows(conn->result);
504
505 return 0;
506}
507
508static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
509{
510 rlm_sql_mysql_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_mysql_conn_t);
511
512 unsigned int fields, i;
513 MYSQL_FIELD *field_info;
514 char const **names;
515
516 /*
517 * Use our internal function to abstract out the API call.
518 * Different versions of SQL use different functions,
519 * and some don't like NULL pointers.
520 */
521 fields = mysql_field_count(conn->sock);
522 if (fields == 0) return RLM_SQL_ERROR;
523
524 /*
525 * https://bugs.mysql.com/bug.php?id=32318
526 * Hints that we don't have to free field_info.
527 */
528 field_info = mysql_fetch_fields(conn->result);
529 if (!field_info) return RLM_SQL_ERROR;
530
531 MEM(names = talloc_array(query_ctx, char const *, fields));
532
533 for (i = 0; i < fields; i++) names[i] = field_info[i].name;
534 *out = names;
535
536 return RLM_SQL_OK;
537}
538
539static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
540{
541 fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
542 rlm_sql_mysql_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_mysql_conn_t);
543 MYSQL_ROW row;
544 int ret;
545 unsigned int num_fields, i;
546 unsigned long *field_lens;
547
548 /*
549 * Check pointer before de-referencing it.
550 * Lack of conn->result is either an error, or no result returned.
551 */
552 if (!conn->result) {
553 query_ctx->rcode = sql_check_error(conn->sock, 0);
554 if (query_ctx->rcode == RLM_SQL_OK) {
555 query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
557 }
559 }
560
561 TALLOC_FREE(query_ctx->row); /* Clear previous row set */
562
563retry_fetch_row:
564 row = mysql_fetch_row(conn->result);
565 if (!row) {
566 query_ctx->rcode = sql_check_error(conn->sock, 0);
567 if (query_ctx->rcode != RLM_SQL_OK) RETURN_MODULE_FAIL;
568
569 mysql_free_result(conn->result);
570 conn->result = NULL;
571
572 ret = mysql_next_result(conn->sock);
573 if (ret == 0) {
574 /* there are more results */
575 if ((sql_store_result(conn, &query_ctx->inst->config) == 0) && (conn->result != NULL)) {
576 goto retry_fetch_row;
577 }
578 } else if (ret > 0) {
579 query_ctx->rcode = sql_check_error(NULL, ret);
580 if (query_ctx->rcode == RLM_SQL_OK) RETURN_MODULE_OK;
582 }
583 /* If ret is -1 then there are no more rows */
584
585 query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
587 }
588
589 num_fields = mysql_field_count(conn->sock);
590 if (!num_fields) {
591 query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
593 }
594
595 field_lens = mysql_fetch_lengths(conn->result);
596
597 MEM(query_ctx->row = talloc_zero_array(query_ctx, char *, num_fields + 1));
598 for (i = 0; i < num_fields; i++) {
599 if (!row[i]) continue;
600 MEM(query_ctx->row[i] = talloc_bstrndup(query_ctx->row, row[i], field_lens[i]));
601 }
602
603 query_ctx->rcode = RLM_SQL_OK;
605}
606
608{
609 rlm_sql_mysql_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_mysql_conn_t);
610
611 if (conn->result) {
612 mysql_free_result(conn->result);
613 conn->result = NULL;
614 }
615 TALLOC_FREE(query_ctx->row);
616
617 return RLM_SQL_OK;
618}
619
620/** Retrieves any warnings associated with the last query
621 *
622 * MySQL stores a limited number of warnings associated with the last query
623 * executed. These can be very useful in diagnosing issues, or in some cases
624 * working around bugs in MySQL which causes it to return the wrong error.
625 *
626 * @note Caller should free any memory allocated in ctx (talloc_free_children()).
627 *
628 * @param ctx to allocate temporary error buffers in.
629 * @param out Array of sql_log_entrys to fill.
630 * @param outlen Length of out array.
631 * @param conn MySQL connection the query was run on.
632 * @return
633 * - Number of errors written to the #sql_log_entry_t array.
634 * - -1 on failure.
635 */
636static size_t sql_warnings(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
638{
639 MYSQL_RES *result;
640 MYSQL_ROW row;
641 unsigned int num_fields;
642 size_t i = 0;
643 char const *log_prefix = conn->conn->name;
644
645 if (outlen == 0) return 0;
646
647 /*
648 * Retrieve any warnings associated with the previous query
649 * that were left lingering on the server.
650 */
651 if (mysql_query(conn->sock, "SHOW WARNINGS") != 0) return -1;
652 result = mysql_store_result(conn->sock);
653 if (!result) return -1;
654
655 /*
656 * Fields should be [0] = Level, [1] = Code, [2] = Message
657 */
658 num_fields = mysql_field_count(conn->sock);
659 if (num_fields < 3) {
660 WARN("Failed retrieving warnings, expected 3 fields got %u", num_fields);
661 mysql_free_result(result);
662
663 return -1;
664 }
665
666 while ((row = mysql_fetch_row(result))) {
667 char *msg = NULL;
669
670 /*
671 * Translate the MySQL log level into our internal
672 * log levels, so they get colourised correctly.
673 */
674 if (strcasecmp(row[0], "warning") == 0) type = L_WARN;
675 else if (strcasecmp(row[0], "note") == 0) type = L_DBG;
676 else type = L_ERR;
677
678 msg = talloc_typed_asprintf(ctx, "%s: %s", row[1], row[2]);
679 out[i].type = type;
680 out[i].msg = msg;
681 if (++i == outlen) break;
682 }
683
684 mysql_free_result(result);
685
686 return i;
687}
688
689/** Retrieves any errors associated with the query context
690 *
691 * @note Caller should free any memory allocated in ctx (talloc_free_children()).
692 *
693 * @param ctx to allocate temporary error buffers in.
694 * @param out Array of sql_log_entrys to fill.
695 * @param outlen Length of out array.
696 * @param query_ctx Query context to retrieve error for.
697 * @return number of errors written to the #sql_log_entry_t array.
698 */
699static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
700 fr_sql_query_t *query_ctx)
701{
704 char const *error;
705 size_t i = 0;
706 char const *log_prefix;
707
708 if (!query_ctx->tconn) return 0;
709 conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_mysql_conn_t);
710 log_prefix = conn->conn->name;
711
712 fr_assert(outlen > 0);
713
714 error = mysql_error(conn->sock);
715
716 /*
717 * Grab the error now in case it gets cleared on the next operation.
718 */
719 if (error && (error[0] != '\0')) {
720 error = talloc_typed_asprintf(ctx, "ERROR %u (%s): %s", mysql_errno(conn->sock), error,
721 mysql_sqlstate(conn->sock));
722 } else {
723 error = NULL;
724 }
725
726 /*
727 * Don't attempt to get errors from the server, if the last error
728 * was that the server was unavailable.
729 */
730 if ((outlen > 1) && (sql_check_error(conn->sock, 0) != RLM_SQL_RECONNECT)) {
731 size_t ret;
732 unsigned int msgs;
733
734 switch (inst->warnings) {
736 /*
737 * Check to see if any warnings can be retrieved from the server.
738 */
739 msgs = mysql_warning_count(conn->sock);
740 if (msgs == 0) {
741 DEBUG3("No additional diagnostic info on server");
742 break;
743 }
744
747 ret = sql_warnings(ctx, out, outlen - 1, conn);
748 if (ret > 0) i += ret;
749 break;
750
752 break;
753
754 default:
755 fr_assert(0);
756 }
757 }
758
759 if (error) {
760 out[i].type = L_ERR;
761 out[i].msg = error;
762 i++;
763 }
764
765 return i;
766}
767
768/** Finish query
769 *
770 * As a single SQL statement may return multiple results
771 * sets, (for example stored procedures) it is necessary to check
772 * whether more results exist and process them in turn if so.
773 *
774 */
776{
778 int ret;
779 MYSQL_RES *result;
780
781 /*
782 * If the query is not in a state which would return results, then do nothing.
783 */
784 if (query_ctx->treq && !(query_ctx->treq->state &
786
787 /*
788 * If the connection doesn't exist there's nothing to do
789 */
790 if (!query_ctx->tconn || !query_ctx->tconn->conn || !query_ctx->tconn->conn->h) return RLM_SQL_ERROR;
791
792 conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_mysql_conn_t);
793
794 /*
795 * If the connection is not active, then all that we can do is free any stored results
796 */
797 if (query_ctx->tconn->conn->state != CONNECTION_STATE_CONNECTED) {
798 sql_free_result(query_ctx, config);
799 return RLM_SQL_OK;
800 }
801
802 /*
803 * If there's no result associated with the
804 * connection handle, assume the first result in the
805 * result set hasn't been retrieved.
806 *
807 * MySQL docs says there's no performance penalty for
808 * calling mysql_store_result for queries which don't
809 * return results.
810 */
811 if (conn->result == NULL) {
812 result = mysql_store_result(conn->sock);
813 if (result) mysql_free_result(result);
814 /*
815 * ...otherwise call sql_free_result to free an
816 * already stored result.
817 */
818 } else {
819 sql_free_result(query_ctx, config); /* sql_free_result sets conn->result to NULL */
820 }
821
822 /*
823 * Drain any other results associated with the handle
824 *
825 * mysql_next_result advances the result cursor so that
826 * the next call to mysql_store_result will retrieve
827 * the next result from the server.
828 *
829 * Unfortunately this really does appear to be the
830 * only way to return the handle to a consistent state.
831 */
832 while (((ret = mysql_next_result(conn->sock)) == 0) &&
833 (result = mysql_store_result(conn->sock))) {
834 mysql_free_result(result);
835 }
836 if (ret > 0) return sql_check_error(NULL, ret);
837
838 return RLM_SQL_OK;
839}
840
842{
843 rlm_sql_mysql_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_mysql_conn_t);
844
845 return mysql_affected_rows(conn->sock);
846}
847
848static ssize_t sql_escape_func(request_t *request, char *out, size_t outlen, char const *in, void *arg)
849{
850 size_t inlen;
851 connection_t *c = talloc_get_type_abort(arg, connection_t);
853 char const *log_prefix = c->name;
854
855 if ((c->state == CONNECTION_STATE_HALTED) || (c->state == CONNECTION_STATE_CLOSED)) {
856 ROPTIONAL(RERROR, ERROR, "Connection not available for escaping");
857 return -1;
858 }
859
860 conn = talloc_get_type_abort(c->h, rlm_sql_mysql_conn_t);
861
862 /* Check for potential buffer overflow */
863 inlen = strlen(in);
864 if ((inlen * 2 + 1) > outlen) return 0;
865 /* Prevent integer overflow */
866 if ((inlen * 2 + 1) <= inlen) return 0;
867
868 return mysql_real_escape_string(&conn->db, out, in, inlen);
869}
870
872
873#undef LOG_PREFIX
874#define LOG_PREFIX "rlm_sql_mysql"
875
877
878#undef LOG_PREFIX
879#define LOG_PREFIX log_prefix
880
881CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
883 connection_t *conn, UNUSED void *uctx)
884{
885 rlm_sql_mysql_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_mysql_conn_t);
886 char const *log_prefix = conn->name;
887 request_t *request;
888 trunk_request_t *treq;
889 fr_sql_query_t *query_ctx;
890 char const *info;
891 int err;
892
893 if (trunk_connection_pop_request(&treq, tconn) != 0) return;
894 if (!treq) return;
895
896 query_ctx = talloc_get_type_abort(treq->preq, fr_sql_query_t);
897 request = query_ctx->request;
898
899 /*
900 * Each of the MariaDB async "start" calls returns a non-zero value
901 * if they are waiting on I/O.
902 * A return value of zero means that the operation completed.
903 */
904
905 switch (query_ctx->status) {
907 ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query_ctx->query_str);
908 sql_conn->status = mysql_real_query_start(&err, sql_conn->sock, query_ctx->query_str, strlen(query_ctx->query_str));
909 query_ctx->tconn = tconn;
910
911 if (sql_conn->status) {
912 ROPTIONAL(RDEBUG3, DEBUG3, "Waiting for IO");
913 query_ctx->status = SQL_QUERY_SUBMITTED;
914 sql_conn->query_ctx = query_ctx;
916 return;
917 }
918
919 if (err) {
920 /*
921 * Need to check what kind of error this is - it may
922 * be a unique key conflict, we run the next query.
923 */
924 info = mysql_info(sql_conn->sock);
925 query_ctx->rcode = sql_check_error(sql_conn->sock, 0);
926 if (info) ERROR("%s", info);
927 switch (query_ctx->rcode) {
928 case RLM_SQL_OK:
930 break;
931
932 default:
933 query_ctx->status = SQL_QUERY_FAILED;
935 if (request) unlang_interpret_mark_runnable(request);
936 return;
937 }
938 } else {
939 query_ctx->rcode = RLM_SQL_OK;
940 }
941 query_ctx->status = SQL_QUERY_RETURNED;
942
943 break;
944
946 ROPTIONAL(RDEBUG2, DEBUG2, "Fetching results");
947 fr_assert(query_ctx->tconn == tconn);
948 sql_conn->status = mysql_store_result_start(&sql_conn->result, sql_conn->sock);
949
950 if (sql_conn->status) {
951 ROPTIONAL(RDEBUG3, DEBUG3, "Waiting for IO");
953 sql_conn->query_ctx = query_ctx;
955 return;
956 }
958 query_ctx->rcode = RLM_SQL_OK;
959
960 break;
961
962 default:
963 /*
964 * The request outstanding on this connection returned
965 * immediately, so we are not actually waiting for I/O.
966 */
967 return;
968 }
969
970 /*
971 * The current request is not waiting for I/O so the request can run
972 */
973 ROPTIONAL(RDEBUG3, DEBUG3, "Got immediate response");
975 if (request) unlang_interpret_mark_runnable(request);
976}
977
978CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
980 connection_t *conn, UNUSED void *uctx)
981{
982 rlm_sql_mysql_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_mysql_conn_t);
983 char const *log_prefix = conn->name;
984 fr_sql_query_t *query_ctx;
985 char const *info;
986 int err = 0;
987 request_t *request;
988
989 /*
990 * Lookup the outstanding SQL query for this connection.
991 * There will only ever be one per tconn.
992 */
993 query_ctx = sql_conn->query_ctx;
994
995 /*
996 * No outstanding query on this connection.
997 * Should not happen, but added for safety.
998 */
999 if (unlikely(!query_ctx)) return;
1000
1001 switch (query_ctx->status) {
1003 sql_conn->status = mysql_real_query_cont(&err, sql_conn->sock, sql_conn->status);
1004 break;
1005
1007 sql_conn->status = mysql_store_result_cont(&sql_conn->result, sql_conn->sock, sql_conn->status);
1008 break;
1009
1010 default:
1011 /*
1012 * The request outstanding on this connection returned
1013 * immediately, so we are not actually waiting for I/O.
1014 */
1015 return;
1016 }
1017
1018 /*
1019 * Are we still waiting for any further I/O?
1020 */
1021 if (sql_conn->status != 0) return;
1022
1023 sql_conn->query_ctx = NULL;
1024
1025 switch (query_ctx->status) {
1027 query_ctx->status = SQL_QUERY_RETURNED;
1028 break;
1029
1031 query_ctx->status = SQL_QUERY_RESULTS_FETCHED;
1032 break;
1033
1034 default:
1035 fr_assert(0);
1036 }
1037
1038 request = query_ctx->request;
1039 if (request) unlang_interpret_mark_runnable(request);
1040
1041 if (err) {
1042 info = mysql_info(sql_conn->sock);
1043 query_ctx->rcode = sql_check_error(sql_conn->sock, 0);
1044 if (info) ROPTIONAL(RERROR, ERROR, "%s", info);
1045 return;
1046 }
1047
1048 query_ctx->rcode = RLM_SQL_OK;
1049}
1050
1051CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
1052static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason,
1053 UNUSED void *uctx)
1054{
1055 fr_sql_query_t *query_ctx = talloc_get_type_abort(preq, fr_sql_query_t);
1056 rlm_sql_mysql_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_mysql_conn_t);
1057
1058 if (!query_ctx->treq) return;
1059 if (reason != TRUNK_CANCEL_REASON_SIGNAL) return;
1060 if (sql_conn->query_ctx == query_ctx) sql_conn->query_ctx = NULL;
1061}
1062
1063CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
1065 connection_t *conn, UNUSED void *uctx)
1066{
1067 trunk_request_t *treq;
1068
1069 /*
1070 * The MariaDB non-blocking API doesn't have any cancellation functions -
1071 * rather you are expected to close the connection.
1072 */
1073 if ((trunk_connection_pop_cancellation(&treq, tconn)) == 0) {
1076 }
1077}
1078
1081
1082static unlang_action_t sql_select_query_resume(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
1083{
1084 fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
1085
1086 if (query_ctx->rcode != RLM_SQL_OK) RETURN_MODULE_FAIL;
1087
1088 if (query_ctx->status == SQL_QUERY_RETURNED) {
1089 trunk_request_requeue(query_ctx->treq);
1090
1092 query_ctx->rcode = RLM_SQL_ERROR;
1094 }
1095
1096 return UNLANG_ACTION_YIELD;
1097 }
1098
1100}
1101
1102/** Allocate the argument used for the SQL escape function
1103 *
1104 * In this case, a dedicated connection to allow the escape
1105 * function to have access to server side parameters, though
1106 * no packets ever flow after the connection is made.
1107 */
1108static void *sql_escape_arg_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, void *uctx)
1109{
1110 rlm_sql_t const *inst = talloc_get_type_abort(uctx, rlm_sql_t);
1111 connection_t *conn;
1112 char const *log_prefix = inst->name;
1113
1114 conn = connection_alloc(ctx, el,
1116 .init = _sql_connection_init,
1117 .close = _sql_connection_close,
1118 },
1119 inst->config.trunk_conf.conn_conf,
1120 inst->name, inst);
1121
1122 if (!conn) {
1123 PERROR("Failed allocating state handler for SQL escape connection");
1124 return NULL;
1125 }
1126
1128 return conn;
1129}
1130
1131static void sql_escape_arg_free(void *uctx)
1132{
1133 connection_t *conn = talloc_get_type_abort(uctx, connection_t);
1135}
1136
1137/* Exported to rlm_sql */
1140 .common = {
1141 .name = "sql_mysql",
1142 .magic = MODULE_MAGIC_INIT,
1143 .inst_size = sizeof(rlm_sql_mysql_t),
1144 .onload = mod_load,
1145 .unload = mod_unload,
1148 },
1150 .sql_query_resume = sql_query_resume,
1151 .sql_select_query_resume = sql_select_query_resume,
1152 .sql_num_rows = sql_num_rows,
1153 .sql_affected_rows = sql_affected_rows,
1154 .sql_fields = sql_fields,
1155 .sql_fetch_row = sql_fetch_row,
1156 .sql_free_result = sql_free_result,
1157 .sql_error = sql_error,
1158 .sql_finish_query = sql_finish_query,
1159 .sql_finish_select_query = sql_finish_query,
1160 .sql_escape_func = sql_escape_func,
1161 .sql_escape_arg_alloc = sql_escape_arg_alloc,
1162 .sql_escape_arg_free = sql_escape_arg_free,
1163 .trunk_io_funcs = {
1164 .connection_alloc = sql_trunk_connection_alloc,
1165 .connection_notify = sql_trunk_connection_notify,
1166 .request_mux = sql_trunk_request_mux,
1167 .request_demux = sql_trunk_request_demux,
1168 .request_cancel_mux = sql_request_cancel_mux,
1169 .request_cancel = sql_request_cancel,
1170 .request_fail = sql_request_fail,
1171 }
1172};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_YIELD
Temporarily pause execution until an event occurs.
Definition action.h:42
log_entry msg
Definition acutest.h:794
#define RCSID(id)
Definition build.h:483
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:322
#define DIAG_ON(_x)
Definition build.h:458
#define CC_NO_UBSAN(_sanitize)
Definition build.h:426
#define unlikely(_x)
Definition build.h:381
#define UNUSED
Definition build.h:315
#define NUM_ELEMENTS(_t)
Definition build.h:337
#define DIAG_OFF(_x)
Definition build.h:457
#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
#define FR_CONF_POINTER(_name, _type, _flags, _res_p)
conf_parser_t which parses a single CONF_PAIR producing a single global result
Definition cf_parse.h:323
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:256
@ CONF_FLAG_FILE_INPUT
File matching value must exist, and must be readable.
Definition cf_parse.h:424
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Definition cf_parse.h:412
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:579
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_CONNECTED
File descriptor is open (ready for writing).
Definition connection.h:52
@ 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 ERROR(fmt,...)
Definition dhcpclient.c:41
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
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
#define unlang_function_repeat_set(_request, _repeat)
Set a new repeat function for an existing function frame.
Definition function.h:89
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 RDEBUG3(fmt,...)
Definition log.h:343
#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
fr_log_type_t
Definition log.h:54
@ L_WARN
Warning.
Definition log.h:57
@ L_ERR
Error message.
Definition log.h:56
@ L_DBG
Only displayed when debugging is enabled.
Definition log.h:59
long int ssize_t
int strcasecmp(char *s1, char *s2)
Definition missing.c:66
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 RDEBUG2(fmt,...)
Definition radclient.h:54
#define DEBUG2(fmt,...)
Definition radclient.h:43
#define WARN(fmt,...)
Definition radclient.h:47
#define INFO(fmt,...)
Definition radict.c:54
#define RETURN_MODULE_OK
Definition rcode.h:57
#define RETURN_MODULE_FAIL
Definition rcode.h:56
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.
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
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
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_ALT_QUERY
Key constraint violation, use an alternative query.
Definition rlm_sql.h:49
@ RLM_SQL_RECONNECT
Stale connection, should reconnect.
Definition rlm_sql.h:48
@ RLM_SQL_ERROR
General connection/server error.
Definition rlm_sql.h:46
@ RLM_SQL_OK
Success.
Definition rlm_sql.h:47
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
Definition rlm_sql.h:50
#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
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
char const * connect_query
Query executed after establishing new connection.
Definition rlm_sql.h:96
@ SQL_QUERY_RETURNED
Query has executed.
Definition rlm_sql.h:124
@ SQL_QUERY_FETCHING_RESULTS
Fetching results from server.
Definition rlm_sql.h:125
@ SQL_QUERY_FAILED
Failed to submit.
Definition rlm_sql.h:121
@ SQL_QUERY_SUBMITTED
Submitted for execution.
Definition rlm_sql.h:123
@ SQL_QUERY_PREPARED
Ready to submit.
Definition rlm_sql.h:122
@ SQL_QUERY_RESULTS_FETCHED
Results fetched from the server.
Definition rlm_sql.h:126
Definition rlm_sql.h:61
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_check_error(MYSQL *server, int client_errno)
Analyse the last error that occurred on the socket, and determine an action.
static int mod_load(void)
static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
bool tls_check_cert
Verify there's a trust relationship between the server's cert and one of the CAs we have configured.
char const * tls_certificate_file
Public certificate we present to the server.
fr_sql_query_t * query_ctx
Current query running on this connection.
static sql_rcode_t sql_free_result(fr_sql_query_t *, rlm_sql_config_t const *)
static void _sql_connection_close(fr_event_list_t *el, void *h, UNUSED void *uctx)
static fr_table_num_sorted_t const server_warnings_table[]
char const * tls_private_key_file
Private key for the certificate we present to the server.
char const * tls_crl_path
Private key for the certificate we present to the server.
char const * tls_crl_file
Public certificate we present to the server.
SQL_QUERY_FAIL static SQL_QUERY_RESUME unlang_action_t sql_select_query_resume(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
int status
returned by the most recent non-blocking function call.
char const * tls_cipher
Colon separated list of TLS ciphers for TLS <= 1.2.
static void _sql_connect_io_notify(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Callback for I/O events in response to mysql_real_connect_start()
static ssize_t sql_escape_func(request_t *request, char *out, size_t outlen, char const *in, void *arg)
char const * tls_ca_path
Directory containing CAs that may be used to validate the servers certificate.
static void * sql_escape_arg_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, void *uctx)
Allocate the argument used for the SQL escape function.
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_trunk_request_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
rlm_sql_mysql_warnings
@ SERVER_WARNINGS_AUTO
@ SERVER_WARNINGS_NO
@ SERVER_WARNINGS_YES
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)
int fd
fd for this connection's I/O events.
static conf_parser_t tls_config[]
static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason, UNUSED void *uctx)
rlm_sql_driver_t rlm_sql_mysql
MYSQL * sock
Connection details as returned by connection init functions.
static int sql_num_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
MYSQL db
Structure representing connection details.
static size_t sql_warnings(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, rlm_sql_mysql_conn_t *conn)
Retrieves any warnings associated with the last query.
static void mod_unload(void)
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.
char const * character_set
Character set to use on connections.
char const * warnings_str
Whether we always query the server for additional warnings.
static const conf_parser_t driver_config[]
static sql_rcode_t sql_store_result(rlm_sql_mysql_conn_t *conn, UNUSED rlm_sql_config_t const *config)
static size_t server_warnings_table_len
static sql_rcode_t sql_finish_query(fr_sql_query_t *query_ctx, rlm_sql_config_t const *config)
Finish query.
static void _sql_connect_query_run(connection_t *conn, UNUSED connection_state_t prev, UNUSED connection_state_t state, void *uctx)
connection_t * conn
Generic connection structure for this connection.
bool tls_check_cert_cn
Verify that the CN in the server cert matches the host we passed to mysql_real_connect().
static int sql_affected_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
char const * tls_ca_file
Path to the CA used to validate the server's certificate.
bool tls_required
Require that the connection is encrypted.
MYSQL_RES * result
Result from most recent query.
static int mod_instantiate(module_inst_ctx_t const *mctx)
rlm_sql_mysql_warnings warnings
mysql_warning_count() doesn't appear to work with NDB cluster
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:535
void connection_signal_connected(connection_t *conn)
Asynchronously signal that the connection is open.
char const * name
Instance name e.g. user_database.
Definition module.h:335
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
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
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
rlm_sql_config_t config
Definition rlm_sql.h:221
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:653
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
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_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:564
#define talloc_get_type_abort_const
Definition talloc.h:282
static const char * names[8]
Definition time.c:621
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
trunk_enqueue_t trunk_request_requeue(trunk_request_t *treq)
Re-enqueue a request on the same connection.
Definition trunk.c:2676
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
void trunk_request_signal_reapable(trunk_request_t *treq)
Signal that the request was written to a connection successfully, but no response is expected.
Definition trunk.c:2072
Associates request queues with a connection.
Definition trunk.c:134
Wraps a normal request.
Definition trunk.c:100
#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 size_t char fr_sbuff_t size_t inlen
Definition value.h:997
static size_t char ** out
Definition value.h:997