All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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: 340d22351adbdd015eebfe1c32548ce3854d8c62 $
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@ox.org>
26  */
27 RCSID("$Id: 340d22351adbdd015eebfe1c32548ce3854d8c62 $")
28 
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/rad_assert.h>
31 
32 #include <sys/stat.h>
33 
34 #include "config.h"
35 
36 #ifdef HAVE_MYSQL_MYSQL_H
37 # include <mysql/mysql_version.h>
38 # include <mysql/errmsg.h>
39 # include <mysql/mysql.h>
40 # include <mysql/mysqld_error.h>
41 #elif defined(HAVE_MYSQL_H)
42 # include <mysql_version.h>
43 # include <errmsg.h>
44 # include <mysql.h>
45 # include <mysqld_error.h>
46 #endif
47 
48 #include "rlm_sql.h"
49 
50 static int mysql_instance_count = 0;
51 
52 typedef enum {
57 
59  { "auto", SERVER_WARNINGS_AUTO },
60  { "yes", SERVER_WARNINGS_YES },
61  { "no", SERVER_WARNINGS_NO },
62  { NULL, 0 }
63 };
64 
65 typedef struct rlm_sql_mysql_conn {
66  MYSQL db;
67  MYSQL *sock;
68  MYSQL_RES *result;
71 
72 typedef struct rlm_sql_mysql_config {
73  char const *tls_ca_file; //!< Path to the CA used to validate the server's certificate.
74  char const *tls_ca_path; //!< Directory containing CAs that may be used to validate the
75  //!< servers certificate.
76  char const *tls_certificate_file; //!< Public certificate we present to the server.
77  char const *tls_private_key_file; //!< Private key for the certificate we present to the server.
78  char const *tls_cipher;
79 
80  char const *warnings_str; //!< Whether we always query the server for additional warnings.
81  rlm_sql_mysql_warnings warnings; //!< mysql_warning_count() doesn't
82  //!< appear to work with NDB cluster
84 
85 static CONF_PARSER tls_config[] = {
86  { FR_CONF_OFFSET("ca_file", PW_TYPE_FILE_INPUT, rlm_sql_mysql_config_t, tls_ca_file) },
87  { FR_CONF_OFFSET("ca_path", PW_TYPE_FILE_INPUT, rlm_sql_mysql_config_t, tls_ca_path) },
88  { FR_CONF_OFFSET("certificate_file", PW_TYPE_FILE_INPUT, rlm_sql_mysql_config_t, tls_certificate_file) },
89  { FR_CONF_OFFSET("private_key_file", PW_TYPE_FILE_INPUT, rlm_sql_mysql_config_t, tls_private_key_file) },
90 
91  /*
92  * MySQL Specific TLS attributes
93  */
94  { FR_CONF_OFFSET("cipher", PW_TYPE_STRING, rlm_sql_mysql_config_t, tls_cipher) },
96 };
97 
98 static const CONF_PARSER driver_config[] = {
99  { FR_CONF_POINTER("tls", PW_TYPE_SUBSECTION, NULL), .dflt = (void const *) tls_config },
100 
101  { FR_CONF_OFFSET("warnings", PW_TYPE_STRING, rlm_sql_mysql_config_t, warnings_str), .dflt = "auto" },
103 };
104 
105 /* Prototypes */
107 
109 {
110  DEBUG2("rlm_sql_mysql: Socket destructor called, closing socket");
111 
112  if (conn->sock){
113  mysql_close(conn->sock);
114  }
115 
116  return 0;
117 }
118 
120 {
121  if (--mysql_instance_count == 0) mysql_library_end();
122 
123  return 0;
124 }
125 
127 {
128  rlm_sql_mysql_config_t *driver;
129  int warnings;
130 
131  static bool version_done = false;
132 
133  if (!version_done) {
134  version_done = true;
135 
136  INFO("rlm_sql_mysql: libmysql version: %s", mysql_get_client_info());
137  }
138 
139  if (mysql_instance_count == 0) {
140  if (mysql_library_init(0, NULL, NULL)) {
141  ERROR("rlm_sql_mysql: libmysql initialisation failed");
142 
143  return -1;
144  }
145  }
146  mysql_instance_count++;
147 
148  MEM(driver = config->driver = talloc_zero(config, rlm_sql_mysql_config_t));
149  talloc_set_destructor(driver, _mod_destructor);
150 
151  if (cf_section_parse(conf, driver, driver_config) < 0) {
152  return -1;
153  }
154 
155  warnings = fr_str2int(server_warnings_table, driver->warnings_str, -1);
156  if (warnings < 0) {
157  ERROR("rlm_sql_mysql: Invalid warnings value \"%s\", must be yes, no, or auto", driver->warnings_str);
158  return -1;
159  }
160  driver->warnings = (rlm_sql_mysql_warnings)warnings;
161 
162  return 0;
163 }
164 
165 static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config, struct timeval const *timeout)
166 {
167  rlm_sql_mysql_conn_t *conn;
168  rlm_sql_mysql_config_t *driver = config->driver;
169  unsigned int connect_timeout = timeout->tv_usec;
170  unsigned long sql_flags;
171 
172  MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_mysql_conn_t));
173  talloc_set_destructor(conn, _sql_socket_destructor);
174 
175  DEBUG("rlm_sql_mysql: Starting connect to MySQL server");
176 
177  mysql_init(&(conn->db));
178 
179  /*
180  * If any of the TLS options are set, configure TLS
181  *
182  * According to MySQL docs this function always returns 0, so we won't
183  * know if ssl setup succeeded until mysql_real_connect is called below.
184  */
185  if (driver->tls_ca_file || driver->tls_ca_path ||
186  driver->tls_certificate_file || driver->tls_private_key_file) {
187  mysql_ssl_set(&(conn->db), driver->tls_private_key_file, driver->tls_certificate_file,
188  driver->tls_ca_file, driver->tls_ca_path, driver->tls_cipher);
189  }
190 
191  mysql_options(&(conn->db), MYSQL_READ_DEFAULT_GROUP, "freeradius");
192 
193  /*
194  * We need to know about connection errors, and are capable
195  * of reconnecting automatically.
196  */
197 #ifdef MYSQL_OPT_RECONNECT
198  {
199  my_bool reconnect = 0;
200  mysql_options(&(conn->db), MYSQL_OPT_RECONNECT, &reconnect);
201  }
202 #endif
203 
204 #if (MYSQL_VERSION_ID >= 50000)
205  mysql_options(&(conn->db), MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
206 
207  if (config->query_timeout) {
208  unsigned int read_timeout = config->query_timeout;
209  unsigned int write_timeout = config->query_timeout;
210 
211  /*
212  * The timeout in seconds for each attempt to read from the server.
213  * There are retries if necessary, so the total effective timeout
214  * value is three times the option value.
215  */
216  if (config->query_timeout >= 3) read_timeout /= 3;
217 
218  /*
219  * The timeout in seconds for each attempt to write to the server.
220  * There is a retry if necessary, so the total effective timeout
221  * value is two times the option value.
222  */
223  if (config->query_timeout >= 2) write_timeout /= 2;
224 
225  /*
226  * Connect timeout is actually connect timeout (according to the
227  * docs) there are no automatic retries.
228  */
229  mysql_options(&(conn->db), MYSQL_OPT_READ_TIMEOUT, &read_timeout);
230  mysql_options(&(conn->db), MYSQL_OPT_WRITE_TIMEOUT, &write_timeout);
231  }
232 #endif
233 
234 #if (MYSQL_VERSION_ID >= 40100)
235  sql_flags = CLIENT_MULTI_RESULTS | CLIENT_FOUND_ROWS;
236 #else
237  sql_flags = CLIENT_FOUND_ROWS;
238 #endif
239 
240 #ifdef CLIENT_MULTI_STATEMENTS
241  sql_flags |= CLIENT_MULTI_STATEMENTS;
242 #endif
243  conn->sock = mysql_real_connect(&(conn->db),
244  config->sql_server,
245  config->sql_login,
246  config->sql_password,
247  config->sql_db,
248  config->sql_port,
249  NULL,
250  sql_flags);
251  if (!conn->sock) {
252  ERROR("rlm_sql_mysql: Couldn't connect to MySQL server %s@%s:%s", config->sql_login,
253  config->sql_server, config->sql_db);
254  ERROR("rlm_sql_mysql: MySQL error: %s", mysql_error(&conn->db));
255 
256  conn->sock = NULL;
257  return RLM_SQL_ERROR;
258  }
259 
260  DEBUG2("rlm_sql_mysql: Connected to database '%s' on %s, server version %s, protocol version %i",
261  config->sql_db, mysql_get_host_info(conn->sock),
262  mysql_get_server_info(conn->sock), mysql_get_proto_info(conn->sock));
263 
264  return RLM_SQL_OK;
265 }
266 
267 /** Analyse the last error that occurred on the socket, and determine an action
268  *
269  * @param server Socket from which to extract the server error. May be NULL.
270  * @param client_errno Error from the client.
271  * @return an action for #rlm_sql_t to take.
272  */
273 static sql_rcode_t sql_check_error(MYSQL *server, int client_errno)
274 {
275  int sql_errno = 0;
276 
277  /*
278  * The client and server error numbers are in the
279  * same numberspace.
280  */
281  if (server) sql_errno = mysql_errno(server);
282  if ((sql_errno == 0) && (client_errno != 0)) sql_errno = client_errno;
283 
284  if (sql_errno > 0) switch (sql_errno) {
285  case CR_SERVER_GONE_ERROR:
286  case CR_SERVER_LOST:
287  case -1:
288  return RLM_SQL_RECONNECT;
289 
290  case CR_OUT_OF_MEMORY:
291  case CR_COMMANDS_OUT_OF_SYNC:
292  case CR_UNKNOWN_ERROR:
293  default:
294  return RLM_SQL_ERROR;
295 
296  /*
297  * Constraints errors that signify a duplicate, or that we might
298  * want to try an alternative query.
299  *
300  * Error constants not found in the 3.23/4.0/4.1 manual page
301  * are checked for.
302  * Other error constants should always be available.
303  */
304  case ER_DUP_UNIQUE: /* Can't write, because of unique constraint, to table '%s'. */
305  case ER_DUP_KEY: /* Can't write; duplicate key in table '%s' */
306 
307  case ER_DUP_ENTRY: /* Duplicate entry '%s' for key %d. */
308  case ER_NO_REFERENCED_ROW: /* Cannot add or update a child row: a foreign key constraint fails */
309  case ER_ROW_IS_REFERENCED: /* Cannot delete or update a parent row: a foreign key constraint fails */
310 #ifdef ER_FOREIGN_DUPLICATE_KEY
311  case ER_FOREIGN_DUPLICATE_KEY: /* Upholding foreign key constraints for table '%s', entry '%s', key %d would lead to a duplicate entry. */
312 #endif
313 #ifdef ER_DUP_ENTRY_WITH_KEY_NAME
314  case ER_DUP_ENTRY_WITH_KEY_NAME: /* Duplicate entry '%s' for key '%s' */
315 #endif
316 #ifdef ER_NO_REFERENCED_ROW_2
317  case ER_NO_REFERENCED_ROW_2:
318 #endif
319 #ifdef ER_ROW_IS_REFERENCED_2
320  case ER_ROW_IS_REFERENCED_2:
321 #endif
322  return RLM_SQL_ALT_QUERY;
323 
324  /*
325  * Constraints errors that signify an invalid query
326  * that can never succeed.
327  */
328  case ER_BAD_NULL_ERROR: /* Column '%s' cannot be null */
329  case ER_NON_UNIQ_ERROR: /* Column '%s' in %s is ambiguous */
330  return RLM_SQL_QUERY_INVALID;
331 
332  }
333 
334  return RLM_SQL_OK;
335 }
336 
337 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query)
338 {
339  rlm_sql_mysql_conn_t *conn = handle->conn;
340  sql_rcode_t rcode;
341  char const *info;
342 
343  if (!conn->sock) {
344  ERROR("rlm_sql_mysql: Socket not connected");
345  return RLM_SQL_RECONNECT;
346  }
347 
348  mysql_query(conn->sock, query);
349  rcode = sql_check_error(conn->sock, 0);
350  if (rcode != RLM_SQL_OK) {
351  return rcode;
352  }
353 
354  /* Only returns non-null string for INSERTS */
355  info = mysql_info(conn->sock);
356  if (info) DEBUG2("rlm_sql_mysql: %s", info);
357 
358  return RLM_SQL_OK;
359 }
360 
362 {
363  rlm_sql_mysql_conn_t *conn = handle->conn;
364  sql_rcode_t rcode;
365  int ret;
366 
367  if (!conn->sock) {
368  ERROR("rlm_sql_mysql: Socket not connected");
369  return RLM_SQL_RECONNECT;
370  }
371 
372 retry_store_result:
373  if (!(conn->result = mysql_store_result(conn->sock))) {
374  rcode = sql_check_error(conn->sock, 0);
375  if (rcode != RLM_SQL_OK) return rcode;
376 #if (MYSQL_VERSION_ID >= 40100)
377  ret = mysql_next_result(conn->sock);
378  if (ret == 0) {
379  /* there are more results */
380  goto retry_store_result;
381  } else if (ret > 0) return sql_check_error(NULL, ret);
382 #endif
383  }
384  return RLM_SQL_OK;
385 }
386 
388 {
389  int num = 0;
390  rlm_sql_mysql_conn_t *conn = handle->conn;
391 
392 #if MYSQL_VERSION_ID >= 32224
393  if (!(num = mysql_field_count(conn->sock))) {
394 #else
395  if (!(num = mysql_num_fields(conn->sock))) {
396 #endif
397  return -1;
398  }
399  return num;
400 }
401 
402 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
403 {
404  sql_rcode_t rcode;
405 
406  rcode = sql_query(handle, config, query);
407  if (rcode != RLM_SQL_OK) {
408  return rcode;
409  }
410 
411  rcode = sql_store_result(handle, config);
412  if (rcode != RLM_SQL_OK) {
413  return rcode;
414  }
415 
416  /* Why? Per http://www.mysql.com/doc/n/o/node_591.html,
417  * this cannot return an error. Perhaps just to complain if no
418  * fields are found?
419  */
420  sql_num_fields(handle, config);
421 
422  return rcode;
423 }
424 
426 {
427  rlm_sql_mysql_conn_t *conn = handle->conn;
428 
429  if (conn->result) {
430  return mysql_num_rows(conn->result);
431  }
432 
433  return 0;
434 }
435 
436 static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
437 {
438  rlm_sql_mysql_conn_t *conn = handle->conn;
439 
440  unsigned int fields, i;
441  MYSQL_FIELD *field_info;
442  char const **names;
443 
444  fields = mysql_num_fields(conn->result);
445  if (fields == 0) return RLM_SQL_ERROR;
446 
447  /*
448  * https://bugs.mysql.com/bug.php?id=32318
449  * Hints that we don't have to free field_info.
450  */
451  field_info = mysql_fetch_fields(conn->result);
452  if (!field_info) return RLM_SQL_ERROR;
453 
454  MEM(names = talloc_array(handle, char const *, fields));
455 
456  for (i = 0; i < fields; i++) names[i] = field_info[i].name;
457  *out = names;
458 
459  return RLM_SQL_OK;
460 }
461 
463 {
464  rlm_sql_mysql_conn_t *conn = handle->conn;
465  sql_rcode_t rcode;
466  int ret;
467 
468  *out = NULL;
469 
470  /*
471  * Check pointer before de-referencing it.
472  */
473  if (!conn->result) return RLM_SQL_RECONNECT;
474 
475 retry_fetch_row:
476  *out = handle->row = mysql_fetch_row(conn->result);
477  if (!handle->row) {
478  rcode = sql_check_error(conn->sock, 0);
479  if (rcode != RLM_SQL_OK) return rcode;
480 
481 #if (MYSQL_VERSION_ID >= 40100)
482  sql_free_result(handle, config);
483 
484  ret = mysql_next_result(conn->sock);
485  if (ret == 0) {
486  /* there are more results */
487  if ((sql_store_result(handle, config) == 0) && (conn->result != NULL)) {
488  goto retry_fetch_row;
489  }
490  } else if (ret > 0) return sql_check_error(NULL, ret);
491 #endif
492  }
493  return RLM_SQL_OK;
494 }
495 
497 {
498  rlm_sql_mysql_conn_t *conn = handle->conn;
499 
500  if (conn->result) {
501  mysql_free_result(conn->result);
502  conn->result = NULL;
503  }
504 
505  return RLM_SQL_OK;
506 }
507 
508 /** Retrieves any warnings associated with the last query
509  *
510  * MySQL stores a limited number of warnings associated with the last query
511  * executed. These can be very useful in diagnosing issues, or in some cases
512  * working around bugs in MySQL which causes it to return the wrong error.
513  *
514  * @note Caller should free any memory allocated in ctx (talloc_free_children()).
515  *
516  * @param ctx to allocate temporary error buffers in.
517  * @param out Array of sql_log_entrys to fill.
518  * @param outlen Length of out array.
519  * @param handle rlm_sql connection handle.
520  * @param config rlm_sql config.
521  * @return
522  * - Number of errors written to the #sql_log_entry_t array.
523  * - -1 on failure.
524  */
525 static size_t sql_warnings(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
526  rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
527 {
528  rlm_sql_mysql_conn_t *conn = handle->conn;
529 
530  MYSQL_RES *result;
531  MYSQL_ROW row;
532  unsigned int num_fields;
533  size_t i = 0;
534 
535  if (outlen == 0) return 0;
536 
537  /*
538  * Retrieve any warnings associated with the previous query
539  * that were left lingering on the server.
540  */
541  if (mysql_query(conn->sock, "SHOW WARNINGS") != 0) return -1;
542  result = mysql_store_result(conn->sock);
543  if (!result) return -1;
544 
545  /*
546  * Fields should be [0] = Level, [1] = Code, [2] = Message
547  */
548  num_fields = mysql_field_count(conn->sock);
549  if (num_fields < 3) {
550  WARN("rlm_sql_mysql: Failed retrieving warnings, expected 3 fields got %u", num_fields);
551  mysql_free_result(result);
552 
553  return -1;
554  }
555 
556  while ((row = mysql_fetch_row(result))) {
557  char *msg = NULL;
558  log_type_t type;
559 
560  /*
561  * Translate the MySQL log level into our internal
562  * log levels, so they get colourised correctly.
563  */
564  if (strcasecmp(row[0], "warning") == 0) type = L_WARN;
565  else if (strcasecmp(row[0], "note") == 0) type = L_DBG;
566  else type = L_ERR;
567 
568  msg = talloc_asprintf(ctx, "%s: %s", row[1], row[2]);
569  out[i].type = type;
570  out[i].msg = msg;
571  if (++i == outlen) break;
572  }
573 
574  mysql_free_result(result);
575 
576  return i;
577 }
578 
579 /** Retrieves any errors associated with the connection handle
580  *
581  * @note Caller should free any memory allocated in ctx (talloc_free_children()).
582  *
583  * @param ctx to allocate temporary error buffers in.
584  * @param out Array of sql_log_entrys to fill.
585  * @param outlen Length of out array.
586  * @param handle rlm_sql connection handle.
587  * @param config rlm_sql config.
588  * @return number of errors written to the #sql_log_entry_t array.
589  */
590 static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
591  rlm_sql_handle_t *handle, rlm_sql_config_t *config)
592 {
593  rlm_sql_mysql_conn_t *conn = handle->conn;
594  rlm_sql_mysql_config_t *driver = config->driver;
595  char const *error;
596  size_t i = 0;
597 
598  rad_assert(conn && conn->sock);
599  rad_assert(outlen > 0);
600 
601  error = mysql_error(conn->sock);
602 
603  /*
604  * Grab the error now in case it gets cleared on the next operation.
605  */
606  if (error && (error[0] != '\0')) {
607  error = talloc_asprintf(ctx, "ERROR %u (%s): %s", mysql_errno(conn->sock), error,
608  mysql_sqlstate(conn->sock));
609  }
610 
611  /*
612  * Don't attempt to get errors from the server, if the last error
613  * was that the server was unavailable.
614  */
615  if ((outlen > 1) && (sql_check_error(conn->sock, 0) != RLM_SQL_RECONNECT)) {
616  size_t ret;
617  unsigned int msgs;
618 
619  switch (driver->warnings) {
621  /*
622  * Check to see if any warnings can be retrieved from the server.
623  */
624  msgs = mysql_warning_count(conn->sock);
625  if (msgs == 0) {
626  DEBUG3("rlm_sql_mysql: No additional diagnostic info on server");
627  break;
628  }
629 
630  /* FALL-THROUGH */
631  case SERVER_WARNINGS_YES:
632  ret = sql_warnings(ctx, out, outlen - 1, handle, config);
633  if (ret > 0) i += ret;
634  break;
635 
636  case SERVER_WARNINGS_NO:
637  break;
638 
639  default:
640  rad_assert(0);
641  }
642  }
643 
644  if (error) {
645  out[i].type = L_ERR;
646  out[i].msg = error;
647  }
648  i++;
649 
650  return i;
651 }
652 
653 /** Finish query
654  *
655  * As a single SQL statement may return multiple results
656  * sets, (for example stored procedures) it is necessary to check
657  * whether more results exist and process them in turn if so.
658  *
659  */
661 {
662 #if (MYSQL_VERSION_ID >= 40100)
663  rlm_sql_mysql_conn_t *conn = handle->conn;
664  int ret;
665  MYSQL_RES *result;
666 
667  /*
668  * If there's no result associated with the
669  * connection handle, assume the first result in the
670  * result set hasn't been retrieved.
671  *
672  * MySQL docs says there's no performance penalty for
673  * calling mysql_store_result for queries which don't
674  * return results.
675  */
676  if (conn->result == NULL) {
677  result = mysql_store_result(conn->sock);
678  if (result) mysql_free_result(result);
679  /*
680  * ...otherwise call sql_free_result to free an
681  * already stored result.
682  */
683  } else {
684  sql_free_result(handle, config); /* sql_free_result sets conn->result to NULL */
685  }
686 
687  /*
688  * Drain any other results associated with the handle
689  *
690  * mysql_next_result advances the result cursor so that
691  * the next call to mysql_store_result will retrieve
692  * the next result from the server.
693  *
694  * Unfortunately this really does appear to be the
695  * only way to return the handle to a consistent state.
696  */
697  while (((ret = mysql_next_result(conn->sock)) == 0) &&
698  (result = mysql_store_result(conn->sock))) {
699  mysql_free_result(result);
700  }
701  if (ret > 0) return sql_check_error(NULL, ret);
702 #endif
703  return RLM_SQL_OK;
704 }
705 
707 {
708  rlm_sql_mysql_conn_t *conn = handle->conn;
709 
710  return mysql_affected_rows(conn->sock);
711 }
712 
713 static size_t sql_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, void *arg)
714 {
715  size_t inlen;
716  rlm_sql_handle_t *handle = talloc_get_type_abort(arg, rlm_sql_handle_t);
717  rlm_sql_mysql_conn_t *conn = handle->conn;
718 
719  /* Check for potential buffer overflow */
720  inlen = strlen(in);
721  if ((inlen * 2 + 1) > outlen) return 0;
722  /* Prevent integer overflow */
723  if ((inlen * 2 + 1) <= inlen) return 0;
724 
725  return mysql_real_escape_string(conn->sock, out, in, inlen);
726 }
727 
728 
729 /* Exported to rlm_sql */
731 rlm_sql_module_t rlm_sql_mysql = {
732  .name = "rlm_sql_mysql",
734  .mod_instantiate = mod_instantiate,
735  .sql_socket_init = sql_socket_init,
736  .sql_query = sql_query,
737  .sql_select_query = sql_select_query,
738  .sql_store_result = sql_store_result,
739  .sql_num_fields = sql_num_fields,
740  .sql_num_rows = sql_num_rows,
741  .sql_affected_rows = sql_affected_rows,
742  .sql_fields = sql_fields,
743  .sql_fetch_row = sql_fetch_row,
744  .sql_free_result = sql_free_result,
745  .sql_error = sql_error,
746  .sql_finish_query = sql_finish_query,
747  .sql_finish_select_query = sql_finish_query,
748  .sql_escape_func = sql_escape_func
749 };
static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
#define PW_TYPE_FILE_INPUT
File matching value must exist, and must be readable.
Definition: conffile.h:204
rlm_sql_mysql_warnings warnings
mysql_warning_count() doesn't appear to work with NDB cluster
Definition: rlm_sql_mysql.c:81
General connection/server error.
Definition: rlm_sql.h:46
static int _sql_socket_destructor(rlm_sql_mysql_conn_t *conn)
#define DEBUG3(fmt,...)
Definition: log.h:177
Only displayed when debugging is enabled.
Definition: log.h:41
enum log_type log_type_t
Prototypes and functions for the SQL module.
char ** rlm_sql_row_t
Definition: rlm_sql.h:59
log_type_t type
Type of log entry L_ERR, L_WARN, L_INFO, L_DBG etc..
Definition: rlm_sql.h:62
#define RLM_SQL_RCODE_FLAGS_ALT_QUERY
Can distinguish between other errors and those.
Definition: rlm_sql.h:164
static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Finish query.
#define MEM(x)
Definition: radiusd.h:396
char const * tls_private_key_file
Private key for the certificate we present to the server.
Definition: rlm_sql_mysql.c:77
static int _mod_destructor(UNUSED rlm_sql_mysql_config_t *driver)
rlm_sql_mysql_warnings
Definition: rlm_sql_mysql.c:52
#define INFO(fmt,...)
Definition: log.h:143
static char const * name
Warning.
Definition: log.h:37
char const * msg
Log message.
Definition: rlm_sql.h:63
#define UNUSED
Definition: libradius.h:134
Error message.
Definition: log.h:36
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
char const * sql_server
Server to connect to.
Definition: rlm_sql.h:85
static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query)
static sql_rcode_t sql_free_result(rlm_sql_handle_t *, rlm_sql_config_t *)
static float timeout
Definition: radclient.c:43
char const * warnings_str
Whether we always query the server for additional warnings.
Definition: rlm_sql_mysql.c:80
#define PW_TYPE_SUBSECTION
Definition: conffile.h:188
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
Key constraint violation.
Definition: rlm_sql.h:49
static sql_rcode_t sql_store_result(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
#define rad_assert(expr)
Definition: rad_assert.h:38
Stale connection, should reconnect.
Definition: rlm_sql.h:48
char const * name
Definition: rlm_sql.h:191
int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
Definition: token.c:451
#define DEBUG(fmt,...)
Definition: log.h:175
sql_rcode_t
Definition: rlm_sql.h:44
char const * tls_cipher
Definition: rlm_sql_mysql.c:78
static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config, struct timeval const *timeout)
uint32_t sql_port
Port to connect to.
Definition: rlm_sql.h:86
#define DEBUG2(fmt,...)
Definition: log.h:176
int cf_section_parse(CONF_SECTION *, void *base, CONF_PARSER const *variables)
Parse a configuration section into user-supplied variables.
Definition: conffile.c:2234
Definition: rlm_sql.h:61
static CONF_PARSER tls_config[]
Definition: rlm_sql_mysql.c:85
static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
Success.
Definition: rlm_sql.h:47
char const * tls_ca_path
Directory containing CAs that may be used to validate the servers certificate.
Definition: rlm_sql_mysql.c:74
char const * tls_ca_file
Path to the CA used to validate the server's certificate.
Definition: rlm_sql_mysql.c:73
static sql_rcode_t sql_fetch_row(rlm_sql_row_t *out, rlm_sql_handle_t *handle, rlm_sql_config_t *config)
char const * sql_password
Login password to use.
Definition: rlm_sql.h:88
void * driver
Where drivers should write a pointer to their configurations.
Definition: rlm_sql.h:135
static int sql_num_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
static rs_t * conf
Definition: radsniff.c:46
void * conn
Database specific connection handle.
Definition: rlm_sql.h:153
int strcasecmp(char *s1, char *s2)
Definition: missing.c:73
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.
rlm_sql_row_t row
Row data from the last query.
Definition: rlm_sql.h:154
static int mysql_instance_count
Definition: rlm_sql_mysql.c:50
rlm_sql_module_t rlm_sql_mysql
static int mod_instantiate(CONF_SECTION *conf, rlm_sql_config_t *config)
static const FR_NAME_NUMBER server_warnings_table[]
Definition: rlm_sql_mysql.c:58
static const CONF_PARSER driver_config[]
Definition: rlm_sql_mysql.c:98
#define FR_CONF_OFFSET(_n, _t, _s, _f)
Definition: conffile.h:168
char const * sql_login
Login credentials to use.
Definition: rlm_sql.h:87
static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Retrieves any errors associated with the connection handle.
Query syntax error.
Definition: rlm_sql.h:45
#define WARN(fmt,...)
Definition: log.h:144
uint32_t query_timeout
How long to allow queries to run for.
Definition: rlm_sql.h:130
char const * sql_db
Database to run queries against.
Definition: rlm_sql.h:89
static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
rlm_sql_row_t row
Definition: rlm_sql_mysql.c:69
String of printable characters.
Definition: radius.h:33
#define FR_CONF_POINTER(_n, _t, _p)
Definition: conffile.h:172
static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
static size_t sql_warnings(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
Retrieves any warnings associated with the last query.
#define RCSID(id)
Definition: build.h:135
struct rlm_sql_mysql_conn rlm_sql_mysql_conn_t
#define ERROR(fmt,...)
Definition: log.h:145
MYSQL_RES * result
Definition: rlm_sql_mysql.c:68
struct rlm_sql_mysql_config rlm_sql_mysql_config_t
static size_t sql_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, void *arg)
char const * tls_certificate_file
Public certificate we present to the server.
Definition: rlm_sql_mysql.c:76