27RCSID(
"$Id: 3429c51635751a12bd94d9ebd86d94156d0472e0 $")
29#define LOG_PREFIX log_prefix
31#include <freeradius-devel/server/base.h>
32#include <freeradius-devel/util/debug.h>
38#ifdef HAVE_MYSQL_MYSQL_H
39# include <mysql/errmsg.h>
41# include <mysql/mysql.h>
43# include <mysql/mysqld_error.h>
44#elif defined(HAVE_MYSQL_H)
49# include <mysqld_error.h>
144 char const *log_prefix = mctx->
mi->
name;
148 ERROR(
"Invalid warnings value \"%s\", must be yes, no, or auto",
inst->warnings_str);
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;
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;
163 if (!
inst->tls_check_cert) {
164 WARN(
"Implicitly setting check_cert = yes, as check_cert_cn = yes");
165 inst->tls_check_cert =
true;
178 char const *log_prefix =
"rlm_sql_mysql";
179 if (mysql_library_init(0, NULL, NULL)) {
180 ERROR(
"libmysql initialisation failed");
185 INFO(
"libmysql version: %s", mysql_get_client_info());
195 char const *log_prefix = c->
conn->name;
197 if (c->
status == 0)
goto connected;
219 ERROR(
"MySQL error: %s", mysql_error(&c->
db));
224 DEBUG2(
"Connected to database on %s, server version %s, protocol version %i",
225 mysql_get_host_info(c->
sock),
226 mysql_get_server_info(c->
sock), mysql_get_proto_info(c->
sock));
236 char const *log_prefix = conn->name;
245 ERROR(
"Failed running \"open_query\"");
246 info = mysql_info(sql_conn->
sock);
247 if (info)
ERROR(
"%s", info);
255 result = mysql_store_result(sql_conn->
sock);
256 if (result) mysql_free_result(result);
257 while ((mysql_next_result(sql_conn->
sock) == 0) &&
258 (result = mysql_store_result(sql_conn->
sock))) {
259 mysql_free_result(result);
268 char const *log_prefix = conn->name;
272 unsigned long sql_flags;
273 enum mysql_option ssl_mysql_opt;
274 unsigned int ssl_mode = 0;
275 bool ssl_mode_isset =
false;
281 DEBUG(
"Starting connect to MySQL server");
291 if (
inst->tls_ca_file ||
inst->tls_ca_path ||
292 inst->tls_certificate_file ||
inst->tls_private_key_file) {
293 mysql_ssl_set(&(c->
db),
inst->tls_private_key_file,
inst->tls_certificate_file,
294 inst->tls_ca_file,
inst->tls_ca_path,
inst->tls_cipher);
297#ifdef MARIADB_BASE_VERSION
298 if (
inst->tls_required ||
inst->tls_check_cert ||
inst->tls_check_cert_cn) {
299 ssl_mode_isset =
true;
305 ssl_mysql_opt = MYSQL_OPT_SSL_VERIFY_SERVER_CERT;
308 ssl_mysql_opt = MYSQL_OPT_SSL_MODE;
309 if (
inst->tls_required) {
310 ssl_mode = SSL_MODE_REQUIRED;
311 ssl_mode_isset =
true;
313 if (
inst->tls_check_cert) {
314 ssl_mode = SSL_MODE_VERIFY_CA;
315 ssl_mode_isset =
true;
317 if (
inst->tls_check_cert_cn) {
318 ssl_mode = SSL_MODE_VERIFY_IDENTITY;
319 ssl_mode_isset =
true;
322 if (ssl_mode_isset) mysql_options(&(c->
db), ssl_mysql_opt, &ssl_mode);
324 if (
inst->tls_crl_file) mysql_options(&(c->
db), MYSQL_OPT_SSL_CRL,
inst->tls_crl_file);
325 if (
inst->tls_crl_path) mysql_options(&(c->
db), MYSQL_OPT_SSL_CRLPATH,
inst->tls_crl_path);
327 mysql_options(&(c->
db), MYSQL_READ_DEFAULT_GROUP,
"freeradius");
329 if (
inst->character_set) mysql_options(&(c->
db), MYSQL_SET_CHARSET_NAME,
inst->character_set);
331#if MYSQL_VERSION_ID < 80034
340 mysql_options(&(c->
db), MYSQL_OPT_RECONNECT, &reconnect);
344 sql_flags = CLIENT_MULTI_RESULTS | CLIENT_FOUND_ROWS;
346#ifdef CLIENT_MULTI_STATEMENTS
347 sql_flags |= CLIENT_MULTI_STATEMENTS;
350 mysql_options(&c->
db, MYSQL_OPT_NONBLOCK, 0);
357 config->sql_port, NULL, sql_flags);
359 c->
fd = mysql_get_socket(&c->
db);
361 ERROR(
"Could't connect to MySQL server %s@%s:%s",
config->sql_login,
363 ERROR(
"MySQL error: %s", mysql_error(&c->
db));
370 DEBUG2(
"Connected to database '%s' on %s, server version %s, protocol version %i",
372 mysql_get_server_info(c->
sock), mysql_get_proto_info(c->
sock));
380 DEBUG2(
"Connecting to database '%s' on %s:%d, fd %d",
419 if (server) sql_errno = mysql_errno(server);
420 if ((sql_errno == 0) && (client_errno != 0)) sql_errno = client_errno;
422 if (sql_errno > 0)
switch (sql_errno) {
423 case CR_SERVER_GONE_ERROR:
427 case CR_OUT_OF_MEMORY:
428 case CR_COMMANDS_OUT_OF_SYNC:
429 case CR_UNKNOWN_ERROR:
445 case ER_NO_REFERENCED_ROW:
446 case ER_ROW_IS_REFERENCED:
447#ifdef ER_FOREIGN_DUPLICATE_KEY
448 case ER_FOREIGN_DUPLICATE_KEY:
450#ifdef ER_DUP_ENTRY_WITH_KEY_NAME
451 case ER_DUP_ENTRY_WITH_KEY_NAME:
453#ifdef ER_NO_REFERENCED_ROW_2
454 case ER_NO_REFERENCED_ROW_2:
456#ifdef ER_ROW_IS_REFERENCED_2
457 case ER_ROW_IS_REFERENCED_2:
465 case ER_BAD_NULL_ERROR:
466 case ER_NON_UNIQ_ERROR:
474 case ER_SP_FETCH_NO_DATA:
488 conn->
result = mysql_store_result(conn->
sock);
492 ret = mysql_next_result(conn->
sock);
495 goto retry_store_result;
515 unsigned int fields, i;
516 MYSQL_FIELD *field_info;
524 fields = mysql_field_count(conn->
sock);
531 field_info = mysql_fetch_fields(conn->
result);
534 MEM(
names = talloc_array(query_ctx,
char const *, fields));
536 for (i = 0; i < fields; i++)
names[i] = field_info[i].
name;
548 unsigned int num_fields, i;
549 unsigned long *field_lens;
564 TALLOC_FREE(query_ctx->
row);
567 row = mysql_fetch_row(conn->
result);
572 mysql_free_result(conn->
result);
575 ret = mysql_next_result(conn->
sock);
579 goto retry_fetch_row;
581 }
else if (ret > 0) {
592 num_fields = mysql_field_count(conn->
sock);
598 field_lens = mysql_fetch_lengths(conn->
result);
600 MEM(query_ctx->
row = talloc_zero_array(query_ctx,
char *, num_fields + 1));
601 for (i = 0; i < num_fields; i++) {
602 if (!row[i])
continue;
615 mysql_free_result(conn->
result);
618 TALLOC_FREE(query_ctx->
row);
644 unsigned int num_fields;
646 char const *log_prefix = conn->
conn->name;
648 if (outlen == 0)
return 0;
654 if (mysql_query(conn->
sock,
"SHOW WARNINGS") != 0)
return -1;
655 result = mysql_store_result(conn->
sock);
656 if (!result)
return -1;
661 num_fields = mysql_field_count(conn->
sock);
662 if (num_fields < 3) {
663 WARN(
"Failed retrieving warnings, expected 3 fields got %u", num_fields);
664 mysql_free_result(result);
669 while ((row = mysql_fetch_row(result))) {
684 if (++i == outlen)
break;
687 mysql_free_result(result);
709 char const *log_prefix;
711 if (!query_ctx->
tconn)
return 0;
713 log_prefix = conn->
conn->name;
717 error = mysql_error(conn->
sock);
722 if (error && (error[0] !=
'\0')) {
724 mysql_sqlstate(conn->
sock));
737 switch (
inst->warnings) {
742 msgs = mysql_warning_count(conn->
sock);
744 DEBUG3(
"No additional diagnostic info on server");
751 if (ret > 0) i += ret;
787 if (query_ctx->
treq && !(query_ctx->
treq->state &
814 if (conn->
result == NULL) {
815 result = mysql_store_result(conn->
sock);
816 if (result) mysql_free_result(result);
835 while (((ret = mysql_next_result(conn->
sock)) == 0) &&
836 (result = mysql_store_result(conn->
sock))) {
837 mysql_free_result(result);
848 return mysql_affected_rows(conn->
sock);
856 char const *log_prefix = c->name;
867 if ((
inlen * 2 + 1) > outlen)
return 0;
871 return mysql_real_escape_string(&conn->
db,
out,
in,
inlen);
877#define LOG_PREFIX "rlm_sql_mysql"
882#define LOG_PREFIX log_prefix
889 char const *log_prefix = conn->name;
908 switch (query_ctx->
status) {
912 query_ctx->
tconn = tconn;
927 info = mysql_info(sql_conn->
sock);
929 if (info)
ERROR(
"%s", info);
930 switch (query_ctx->
rcode) {
951 sql_conn->
status = mysql_store_result_start(&sql_conn->
result, sql_conn->
sock);
986 char const *log_prefix = conn->name;
1004 switch (query_ctx->
status) {
1024 if (sql_conn->
status != 0)
return;
1028 switch (query_ctx->
status) {
1045 info = mysql_info(sql_conn->
sock);
1061 if (!query_ctx->
treq)
return;
1115 char const *log_prefix =
inst->name;
1122 inst->config.trunk_conf.conn_conf,
1126 PERROR(
"Failed allocating state handler for SQL escape connection");
1144 .name =
"sql_mysql",
1153 .sql_query_resume = sql_query_resume,
1167 .connection_alloc = sql_trunk_connection_alloc,
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
@ UNLANG_ACTION_YIELD
Temporarily pause execution until an event occurs.
#define L(_str)
Helper for initialising arrays of string literals.
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
#define CC_NO_UBSAN(_sanitize)
#define CONF_PARSER_TERMINATOR
#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
conf_parser_flags_t flags
Flags which control parsing behaviour.
#define FR_CONF_POINTER(_name, _type, _flags, _res_p)
conf_parser_t which parses a single CONF_PAIR producing a single global result
#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
@ CONF_FLAG_FILE_READABLE
File matching value must exist, and must be readable.
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Defines a CONF_PAIR to C data type mapping.
@ CONNECTION_STATE_FAILED
Connection has failed.
@ CONNECTION_STATE_HALTED
The connection is in a halted stat.
@ CONNECTION_STATE_CLOSED
Connection has been closed.
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Holds a complete set of functions for a connection.
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
#define fr_event_fd_insert(...)
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
#define unlang_function_repeat_set(_request, _repeat)
Set a new repeat function for an existing function frame.
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
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.
Stores all information relating to an event list.
@ L_DBG
Only displayed when debugging is enabled.
int strcasecmp(char *s1, char *s2)
module_instance_t * mi
Instance of the module being instantiated.
Temporary structure to hold arguments for instantiation calls.
static const conf_parser_t config[]
#define RETURN_UNLANG_FAIL
Prototypes and functions for the SQL module.
fr_sql_query_status_t status
Status of the query.
trunk_connection_t * tconn
Trunk connection this query is being run on.
rlm_sql_t const * inst
Module instance for this query.
char const * query_str
Query string to run.
request_t * request
Request this query relates to.
sql_rcode_t
Action to take at end of an SQL query.
@ RLM_SQL_QUERY_INVALID
Query syntax error.
@ RLM_SQL_ALT_QUERY
Key constraint violation, use an alternative query.
@ RLM_SQL_RECONNECT
Stale connection, should reconnect.
@ RLM_SQL_ERROR
General connection/server error.
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
#define RLM_SQL_RCODE_FLAGS_ALT_QUERY
Can distinguish between other errors and those resulting from a unique key violation.
rlm_sql_row_t row
Row data from the last query.
sql_rcode_t rcode
Result code.
trunk_request_t * treq
Trunk request for this query.
char const * connect_query
Query executed after establishing new connection.
@ SQL_QUERY_RETURNED
Query has executed.
@ SQL_QUERY_FETCHING_RESULTS
Fetching results from server.
@ SQL_QUERY_FAILED
Failed to submit.
@ SQL_QUERY_SUBMITTED
Submitted for execution.
@ SQL_QUERY_PREPARED
Ready to submit.
@ SQL_QUERY_RESULTS_FETCHED
Results fetched from the server.
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_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 *)
SQL_QUERY_FAIL static SQL_QUERY_RESUME unlang_action_t sql_select_query_resume(unlang_result_t *p_result, UNUSED request_t *request, void *uctx)
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.
static unlang_action_t sql_fetch_row(unlang_result_t *p_result, 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)
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_TRUNK_CONNECTION_ALLOC
Allocate an SQL trunk connection.
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.
void connection_signal_connected(connection_t *conn)
Asynchronously signal that the connection is open.
char const * name
Instance name e.g. user_database.
size_t inst_size
Size of the module's instance data.
void * data
Module's instance data.
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
module_t common
Common fields for all loadable modules.
module_instance_t * driver_submodule
Driver's submodule.
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
An element in a lexicographically sorted array of name to num mappings.
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
#define talloc_get_type_abort_const
static const char * names[8]
int trunk_connection_pop_cancellation(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a cancellation request off a connection's cancellation queue.
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
trunk_enqueue_t trunk_request_requeue(trunk_request_t *treq)
Re-enqueue a request on the same connection.
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
void trunk_request_signal_reapable(trunk_request_t *treq)
Signal that the request was written to a connection successfully, but no response is expected.
Associates request queues with a connection.
#define TRUNK_NOTIFY_FUNC(_name, _type)
Helper macro for building generic trunk notify callback.
trunk_cancel_reason_t
Reasons for a request being cancelled.
@ TRUNK_CANCEL_REASON_SIGNAL
Request cancelled due to a signal.
@ TRUNK_REQUEST_STATE_REAPABLE
Request has been written, needs to persist, but we are not currently waiting for any response.
@ TRUNK_REQUEST_STATE_COMPLETE
The request is complete.
@ TRUNK_REQUEST_STATE_SENT
Was written to a socket. Waiting for a response.
static fr_event_list_t * el
static size_t char fr_sbuff_t size_t inlen
static size_t char ** out