27 RCSID(
"$Id: 3920a7af012a0bf141e30ff305369e4b7aaf7b6b $")
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;
199 if (c->
status == 0)
goto connected;
215 ERROR(
"MySQL error: %s", mysql_error(&c->
db));
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));
232 char const *log_prefix = conn->name;
241 ERROR(
"Failed running \"open_query\"");
242 info = mysql_info(sql_conn->
sock);
243 if (info)
ERROR(
"%s", info);
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);
264 char const *log_prefix = conn->name;
268 unsigned long sql_flags;
269 enum mysql_option ssl_mysql_opt;
270 unsigned int ssl_mode = 0;
271 bool ssl_mode_isset =
false;
277 DEBUG(
"Starting connect to MySQL server");
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);
293 #ifdef MARIADB_BASE_VERSION
294 if (
inst->tls_required ||
inst->tls_check_cert ||
inst->tls_check_cert_cn) {
295 ssl_mode_isset =
true;
301 ssl_mysql_opt = MYSQL_OPT_SSL_VERIFY_SERVER_CERT;
304 ssl_mysql_opt = MYSQL_OPT_SSL_MODE;
305 if (
inst->tls_required) {
306 ssl_mode = SSL_MODE_REQUIRED;
307 ssl_mode_isset =
true;
309 if (
inst->tls_check_cert) {
310 ssl_mode = SSL_MODE_VERIFY_CA;
311 ssl_mode_isset =
true;
313 if (
inst->tls_check_cert_cn) {
314 ssl_mode = SSL_MODE_VERIFY_IDENTITY;
315 ssl_mode_isset =
true;
318 if (ssl_mode_isset) mysql_options(&(c->
db), ssl_mysql_opt, &ssl_mode);
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);
323 mysql_options(&(c->
db), MYSQL_READ_DEFAULT_GROUP,
"freeradius");
325 if (
inst->character_set) mysql_options(&(c->
db), MYSQL_SET_CHARSET_NAME,
inst->character_set);
327 #if MYSQL_VERSION_ID < 80034
336 mysql_options(&(c->
db), MYSQL_OPT_RECONNECT, &reconnect);
340 sql_flags = CLIENT_MULTI_RESULTS | CLIENT_FOUND_ROWS;
342 #ifdef CLIENT_MULTI_STATEMENTS
343 sql_flags |= CLIENT_MULTI_STATEMENTS;
346 mysql_options(&c->
db, MYSQL_OPT_NONBLOCK, 0);
353 config->sql_port, NULL, sql_flags);
355 c->
fd = mysql_get_socket(&c->
db);
358 ERROR(
"Could't connect to MySQL server %s@%s:%s",
config->sql_login,
360 ERROR(
"MySQL error: %s", mysql_error(&c->
db));
367 DEBUG2(
"Connected to database '%s' on %s, server version %s, protocol version %i",
369 mysql_get_server_info(c->
sock), mysql_get_proto_info(c->
sock));
377 DEBUG2(
"Connecting to database '%s' on %s:%d, fd %d",
416 if (server) sql_errno = mysql_errno(server);
417 if ((sql_errno == 0) && (client_errno != 0)) sql_errno = client_errno;
419 if (sql_errno > 0)
switch (sql_errno) {
420 case CR_SERVER_GONE_ERROR:
424 case CR_OUT_OF_MEMORY:
425 case CR_COMMANDS_OUT_OF_SYNC:
426 case CR_UNKNOWN_ERROR:
442 case ER_NO_REFERENCED_ROW:
443 case ER_ROW_IS_REFERENCED:
444 #ifdef ER_FOREIGN_DUPLICATE_KEY
445 case ER_FOREIGN_DUPLICATE_KEY:
447 #ifdef ER_DUP_ENTRY_WITH_KEY_NAME
448 case ER_DUP_ENTRY_WITH_KEY_NAME:
450 #ifdef ER_NO_REFERENCED_ROW_2
451 case ER_NO_REFERENCED_ROW_2:
453 #ifdef ER_ROW_IS_REFERENCED_2
454 case ER_ROW_IS_REFERENCED_2:
462 case ER_BAD_NULL_ERROR:
463 case ER_NON_UNIQ_ERROR:
471 case ER_SP_FETCH_NO_DATA:
485 conn->
result = mysql_store_result(conn->
sock);
489 ret = mysql_next_result(conn->
sock);
492 goto retry_store_result;
512 unsigned int fields, i;
513 MYSQL_FIELD *field_info;
521 fields = mysql_field_count(conn->
sock);
528 field_info = mysql_fetch_fields(conn->
result);
531 MEM(
names = talloc_array(query_ctx,
char const *, fields));
533 for (i = 0; i < fields; i++)
names[i] = field_info[i].
name;
545 unsigned int num_fields, i;
546 unsigned long *field_lens;
561 TALLOC_FREE(query_ctx->
row);
564 row = mysql_fetch_row(conn->
result);
569 mysql_free_result(conn->
result);
572 ret = mysql_next_result(conn->
sock);
576 goto retry_fetch_row;
578 }
else if (ret > 0) {
589 num_fields = mysql_field_count(conn->
sock);
595 field_lens = mysql_fetch_lengths(conn->
result);
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;
612 mysql_free_result(conn->
result);
615 TALLOC_FREE(query_ctx->
row);
642 unsigned int num_fields;
644 char const *log_prefix = conn->
conn->name;
646 if (outlen == 0)
return 0;
652 if (mysql_query(conn->
sock,
"SHOW WARNINGS") != 0)
return -1;
653 result = mysql_store_result(conn->
sock);
654 if (!result)
return -1;
659 num_fields = mysql_field_count(conn->
sock);
660 if (num_fields < 3) {
661 WARN(
"Failed retrieving warnings, expected 3 fields got %u", num_fields);
662 mysql_free_result(result);
667 while ((row = mysql_fetch_row(result))) {
682 if (++i == outlen)
break;
685 mysql_free_result(result);
708 char const *log_prefix;
710 if (!query_ctx->
tconn)
return 0;
712 log_prefix = conn->
conn->name;
716 error = mysql_error(conn->
sock);
721 if (error && (error[0] !=
'\0')) {
723 mysql_sqlstate(conn->
sock));
736 switch (
inst->warnings) {
741 msgs = mysql_warning_count(conn->
sock);
743 DEBUG3(
"No additional diagnostic info on server");
750 if (ret > 0) i += ret;
786 if (query_ctx->
treq && !(query_ctx->
treq->state &
813 if (conn->
result == NULL) {
814 result = mysql_store_result(conn->
sock);
815 if (result) mysql_free_result(result);
834 while (((ret = mysql_next_result(conn->
sock)) == 0) &&
835 (result = mysql_store_result(conn->
sock))) {
836 mysql_free_result(result);
847 return mysql_affected_rows(conn->
sock);
855 char const *log_prefix = c->name;
866 if ((
inlen * 2 + 1) > outlen)
return 0;
870 return mysql_real_escape_string(&conn->
db,
out,
in,
inlen);
876 #define LOG_PREFIX "rlm_sql_mysql"
881 #define LOG_PREFIX log_prefix
888 char const *log_prefix = conn->name;
907 switch (query_ctx->
status) {
911 query_ctx->
tconn = tconn;
926 info = mysql_info(sql_conn->
sock);
928 if (info)
ERROR(
"%s", info);
929 switch (query_ctx->
rcode) {
950 sql_conn->
status = mysql_store_result_start(&sql_conn->
result, sql_conn->
sock);
985 char const *log_prefix = conn->name;
1003 switch (query_ctx->
status) {
1023 if (sql_conn->
status != 0)
return;
1027 switch (query_ctx->
status) {
1044 info = mysql_info(sql_conn->
sock);
1060 if (!query_ctx->
treq)
return;
1114 char const *log_prefix =
inst->name;
1118 .init = _sql_connection_init,
1121 inst->config.trunk_conf.conn_conf,
1125 PERROR(
"Failed allocating state handler for SQL escape connection");
1143 .name =
"sql_mysql",
1152 .sql_query_resume = sql_query_resume,
1165 .uses_trunks =
true,
1167 .connection_alloc = sql_trunk_connection_alloc,
1168 .connection_notify = sql_trunk_connection_notify,
1169 .request_mux = sql_trunk_request_mux,
1170 .request_demux = sql_trunk_request_demux,
1171 .request_cancel_mux = sql_request_cancel_mux,
1172 .request_cancel = sql_request_cancel,
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 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
#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_INPUT
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.
fr_dcursor_eval_t void const * uctx
#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[]
rlm_rcode_t
Return codes indicating the result of the module call.
static int instantiate(module_inst_ctx_t const *mctx)
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.
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_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.
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.
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 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[]
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 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_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.
connection_t * connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_funcs_t const *funcs, connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
void connection_signal_connected(connection_t *conn)
Asynchronously signal that the connection is open.
char const * name
Instance name e.g. user_database.
void * data
Module's instance data.
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
#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