Hello,
The following patch adds support for using SSL when connecting to a
MySQL server and support for reading my.cnf files which can set other
connection related options.
New configuration parameters for SSL connections are -
tls_cert_file - File containing the client's public key.
tls_key_file - File containing the client's private key.
tls_CAfile - File containing the server's public key.
tls_CApath - Directory containing the server's public key.
tls_ciphers - A list of permissible ciphers to use for encryption.
tls_verify_cert - Verify that the name of the server matches the
common name in the certificate.
New configuration parameters for reading my.cnf files are -
option_file - Name of the file to read from.
option_group - Name of the group to read from.
Both are empty by default so no option file will be read from.
diff -urN postfix-2.11-20131105.orig/man/man5/mysql_table.5 postfix-2.11-20131105/man/man5/mysql_table.5
--- postfix-2.11-20131105.orig/man/man5/mysql_table.5 2013-11-12 14:35:57.237763765 +1300
+++ postfix-2.11-20131105/man/man5/mysql_table.5 2013-11-12 14:36:05.961595123 +1300
@@ -256,6 +256,25 @@
temporary error if the limit is exceeded. Setting the
limit to 1 ensures that lookups do not return multiple
values.
+.IP "\fBtls_cert_file\fR"
+File containing client's X509 certificate.
+.IP "\fBtls_key_file\fR"
+File containing the private key corresponding to \fBtls_cert_file\fR.
+.IP "\fBtls_CAfile\fR"
+File containing certificates for all of the X509 Certificate
+Authorities the client will recognize. Takes precedence over
+\fBtls_CApath\fR.
+.IP "\fBtls_CApath\fR"
+Directory containing X509 Certificate Authority certificates
+in separate individual files.
+.IP "\fBtls_verify_cert (default: no)\fR"
+Verify that the server's name matches the common name in the
+certficate.
+.IP "\fBoption_file\fR"
+Read options from the given file instead of the default my.cnf
+location.
+.IP "\fBoption_group\fR"
+Read options from the given group.
.SH "OBSOLETE QUERY INTERFACE"
.na
.nf
diff -urN postfix-2.11-20131105.orig/src/global/dict_mysql.c postfix-2.11-20131105/src/global/dict_mysql.c
--- postfix-2.11-20131105.orig/src/global/dict_mysql.c 2013-11-12 14:35:57.229763899 +1300
+++ postfix-2.11-20131105/src/global/dict_mysql.c 2013-11-13 17:10:13.590798887 +1300
@@ -91,6 +91,23 @@
/* releases.
/* .IP hosts
/* List of hosts to connect to.
+/* .IP tls_cert_file
+/* File containing client's X509 certificate.
+/* .IP tls_key_file
+/* File containing the private key corresponding to \fItls_cert_file\fR.
+/* .IP tls_CAfile
+/* File containing certificates for all of the X509 Certificate
+/* Authorities the client will recognize. Takes precedence over
+/* \fItls_CApath\fR.
+/* .IP tls_CApath
+/* Directory containing X509 Certificate Authority certificates
+/* in separate individual files.
+/* .IP tls_verify_cert
+/* Verify that the server's name matches the common name of the certficate.
+/* .IP option_file
+/* Read options from the given file instead of the default my.cnf location.
+/* .IP option_group
+/* Read options from the given group.
/* .PP
/* For example, if you want the map to reference databases of
/* the name "your_db" and execute a query like this: select
@@ -217,6 +234,8 @@
CFG_PARSER *parser;
char *query;
char *result_format;
+ char *option_file;
+ char *option_group;
void *ctx;
int expansion_limit;
char *username;
@@ -226,6 +245,14 @@
PLMYSQL *pldb;
#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
HOST *active_host;
+ char *tls_cert_file;
+ char *tls_key_file;
+ char *tls_CAfile;
+ char *tls_CApath;
+ char *tls_ciphers;
+#if MYSQL_VERSION_ID >= 50023
+ int tls_verify_cert;
+#endif
#endif
} DICT_MYSQL;
@@ -242,12 +269,11 @@
/* internal function declarations */
static PLMYSQL *plmysql_init(ARGV *);
-static MYSQL_RES *plmysql_query(DICT_MYSQL *, const char *, VSTRING *, char *,
- char *, char *);
+static MYSQL_RES *plmysql_query(DICT_MYSQL *, const char *, VSTRING *);
static void plmysql_dealloc(PLMYSQL *);
static void plmysql_close_host(HOST *);
static void plmysql_down_host(HOST *);
-static void plmysql_connect_single(HOST *, char *, char *, char *);
+static void plmysql_connect_single(DICT_MYSQL *, HOST *);
static const char *dict_mysql_lookup(DICT *, const char *);
DICT *dict_mysql_open(const char *, int, int);
static void dict_mysql_close(DICT *);
@@ -349,10 +375,7 @@
return (0);
/* do the query - set dict->error & cleanup if there's an error */
- if ((query_res = plmysql_query(dict_mysql, name, query,
- dict_mysql->dbname,
- dict_mysql->username,
- dict_mysql->password)) == 0) {
+ if ((query_res = plmysql_query(dict_mysql, name, query)) == 0) {
dict->error = DICT_ERR_RETRY;
return (0);
}
@@ -428,10 +451,10 @@
/* dict_mysql_get_active - get an active connection */
-static HOST *dict_mysql_get_active(PLMYSQL *PLDB, char *dbname,
- char *username, char *password)
+static HOST *dict_mysql_get_active(DICT_MYSQL *dict_mysql)
{
const char *myname = "dict_mysql_get_active";
+ PLMYSQL *PLDB = dict_mysql->pldb;
HOST *host;
int count = RETRY_CONN_MAX;
@@ -457,7 +480,7 @@
if (msg_verbose)
msg_info("%s: attempting to connect to host %s", myname,
host->hostname);
- plmysql_connect_single(host, dbname, username, password);
+ plmysql_connect_single(dict_mysql, host);
if (host->stat == STATACTIVE)
return host;
}
@@ -485,17 +508,12 @@
static MYSQL_RES *plmysql_query(DICT_MYSQL *dict_mysql,
const char *name,
- VSTRING *query,
- char *dbname,
- char *username,
- char *password)
+ VSTRING *query)
{
- PLMYSQL *PLDB = dict_mysql->pldb;
HOST *host;
MYSQL_RES *res = 0;
- while ((host = dict_mysql_get_active(PLDB, dbname, username, password)) != NULL) {
-
+ while ((host = dict_mysql_get_active(dict_mysql)) != NULL) {
#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
/*
@@ -534,15 +552,30 @@
* used to reconnect to a single database when one is down or none is
* connected yet. Log all errors and set the stat field of host accordingly
*/
-static void plmysql_connect_single(HOST *host, char *dbname, char *username, char *password)
+static void plmysql_connect_single(DICT_MYSQL *dict_mysql, HOST *host)
{
if ((host->db = mysql_init(NULL)) == NULL)
msg_fatal("dict_mysql: insufficient memory");
+ mysql_options(host->db, MYSQL_READ_DEFAULT_FILE, dict_mysql->option_file);
+ mysql_options(host->db, MYSQL_READ_DEFAULT_GROUP, dict_mysql->option_group);
+#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
+ if (dict_mysql->tls_key_file || dict_mysql->tls_cert_file ||
+ dict_mysql->tls_CAfile || dict_mysql->tls_CApath || dict_mysql->tls_ciphers)
+ mysql_ssl_set(host->db,
+ dict_mysql->tls_key_file, dict_mysql->tls_cert_file,
+ dict_mysql->tls_CAfile, dict_mysql->tls_CApath,
+ dict_mysql->tls_ciphers);
+#if MYSQL_VERSION_ID >= 50023
+ if (dict_mysql->tls_verify_cert != -1)
+ mysql_options(host->db, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
+ &dict_mysql->tls_verify_cert);
+#endif
+#endif
if (mysql_real_connect(host->db,
(host->type == TYPEINET ? host->name : 0),
- username,
- password,
- dbname,
+ dict_mysql->username,
+ dict_mysql->password,
+ dict_mysql->dbname,
host->port,
(host->type == TYPEUNIX ? host->name : 0),
0)) {
@@ -582,7 +615,7 @@
static void mysql_parse_config(DICT_MYSQL *dict_mysql, const char *mysqlcf)
{
- const char *myname = "mysqlname_parse";
+ const char *myname = "mysql_parse_config";
CFG_PARSER *p = dict_mysql->parser;
VSTRING *buf;
char *hosts;
@@ -591,6 +624,18 @@
dict_mysql->password = cfg_get_str(p, "password", "", 0, 0);
dict_mysql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
dict_mysql->result_format = cfg_get_str(p, "result_format", "%s", 1, 0);
+ dict_mysql->option_file = cfg_get_str(p, "option_file", NULL, 0, 0);
+ dict_mysql->option_group = cfg_get_str(p, "option_group", NULL, 0, 0);
+#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
+ dict_mysql->tls_key_file = cfg_get_str(p, "tls_key_file", NULL, 0, 0);
+ dict_mysql->tls_cert_file = cfg_get_str(p, "tls_cert_file", NULL, 0, 0);
+ dict_mysql->tls_CAfile = cfg_get_str(p, "tls_CAfile", NULL, 0, 0);
+ dict_mysql->tls_CApath = cfg_get_str(p, "tls_CApath", NULL, 0, 0);
+ dict_mysql->tls_ciphers = cfg_get_str(p, "tls_ciphers", NULL, 0, 0);
+#if MYSQL_VERSION_ID >= 50023
+ dict_mysql->tls_verify_cert = cfg_get_bool(p, "tls_verify_cert", -1);
+#endif
+#endif
/*
* XXX: The default should be non-zero for safety, but that is not
@@ -759,6 +804,15 @@
myfree(dict_mysql->dbname);
myfree(dict_mysql->query);
myfree(dict_mysql->result_format);
+ myfree(dict_mysql->option_file);
+ myfree(dict_mysql->option_group);
+#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
+ myfree(dict_mysql->tls_key_file);
+ myfree(dict_mysql->tls_cert_file);
+ myfree(dict_mysql->tls_CAfile);
+ myfree(dict_mysql->tls_CApath);
+ myfree(dict_mysql->tls_ciphers);
+#endif
if (dict_mysql->hosts)
argv_free(dict_mysql->hosts);
if (dict_mysql->ctx)