20131116 Feature: MySQL client support for option_file, option_group, tls_cert_file, tls_key_file, tls_CAfile, tls_CApath, tls_verify_cert. See mysql_table(5). Code by Gareth Palmer. Files: proto/mysql_table, global/dict_mysql.c. diff -cr /var/tmp/postfix-2.11-20131114/proto/mysql_table ./proto/mysql_table *** /var/tmp/postfix-2.11-20131114/proto/mysql_table Sat Feb 19 15:46:08 2011 --- ./proto/mysql_table Sat Nov 16 20:16:37 2013 *************** *** 244,249 **** --- 244,282 ---- # temporary error if the limit is exceeded. Setting the # limit to 1 ensures that lookups do not return multiple # values. + # .IP "\fBoption_file\fR" + # Read options from the given file instead of the default my.cnf + # location. + # .sp + # This parameter is available with Postfix 2.11 and later. + # .IP "\fBoption_group\fR" + # Read options from the given group. + # .sp + # This parameter is available with Postfix 2.11 and later. + # .IP "\fBtls_cert_file\fR" + # File containing client's X509 certificate. + # .sp + # This parameter is available with Postfix 2.11 and later. + # .IP "\fBtls_key_file\fR" + # File containing the private key corresponding to \fBtls_cert_file\fR. + # .sp + # This parameter is available with Postfix 2.11 and later. + # .IP "\fBtls_CAfile\fR" + # File containing certificates for all of the X509 Certificate + # Authorities the client will recognize. Takes precedence over + # \fBtls_CApath\fR. + # .sp + # This parameter is available with Postfix 2.11 and later. + # .IP "\fBtls_CApath\fR" + # Directory containing X509 Certificate Authority certificates + # in separate individual files. + # .sp + # This parameter is available with Postfix 2.11 and later. + # .IP "\fBtls_verify_cert (default: no)\fR" + # Verify that the server's name matches the common name in the + # certficate. + # .sp + # This parameter is available with Postfix 2.11 and later. # OBSOLETE QUERY INTERFACE # .ad # .fi diff -cr /var/tmp/postfix-2.11-20131114/src/global/dict_mysql.c ./src/global/dict_mysql.c *** /var/tmp/postfix-2.11-20131114/src/global/dict_mysql.c Fri Jan 13 17:24:39 2012 --- ./src/global/dict_mysql.c Sun Nov 17 09:04:47 2013 *************** *** 91,96 **** --- 91,113 ---- /* releases. /* .IP hosts /* List of hosts to connect to. + /* .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. + /* .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. /* .PP /* For example, if you want the map to reference databases of /* the name "your_db" and execute a query like this: select *************** *** 217,222 **** --- 234,241 ---- CFG_PARSER *parser; char *query; char *result_format; + char *option_file; + char *option_group; void *ctx; int expansion_limit; char *username; *************** *** 226,231 **** --- 245,258 ---- 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,253 **** /* internal function declarations */ static PLMYSQL *plmysql_init(ARGV *); ! static MYSQL_RES *plmysql_query(DICT_MYSQL *, const char *, VSTRING *, char *, ! char *, char *); 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 const char *dict_mysql_lookup(DICT *, const char *); DICT *dict_mysql_open(const char *, int, int); static void dict_mysql_close(DICT *); --- 269,279 ---- /* internal function declarations */ static PLMYSQL *plmysql_init(ARGV *); ! 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(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,358 **** 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) { dict->error = DICT_ERR_RETRY; return (0); } --- 375,381 ---- return (0); /* do the query - set dict->error & cleanup if there's an error */ ! if ((query_res = plmysql_query(dict_mysql, name, query)) == 0) { dict->error = DICT_ERR_RETRY; return (0); } *************** *** 428,437 **** /* dict_mysql_get_active - get an active connection */ ! static HOST *dict_mysql_get_active(PLMYSQL *PLDB, char *dbname, ! char *username, char *password) { const char *myname = "dict_mysql_get_active"; HOST *host; int count = RETRY_CONN_MAX; --- 451,460 ---- /* dict_mysql_get_active - get an active connection */ ! 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,463 **** if (msg_verbose) msg_info("%s: attempting to connect to host %s", myname, host->hostname); ! plmysql_connect_single(host, dbname, username, password); if (host->stat == STATACTIVE) return host; } --- 480,486 ---- if (msg_verbose) msg_info("%s: attempting to connect to host %s", myname, host->hostname); ! plmysql_connect_single(dict_mysql, host); if (host->stat == STATACTIVE) return host; } *************** *** 485,501 **** static MYSQL_RES *plmysql_query(DICT_MYSQL *dict_mysql, const char *name, ! VSTRING *query, ! char *dbname, ! char *username, ! char *password) { - PLMYSQL *PLDB = dict_mysql->pldb; HOST *host; MYSQL_RES *res = 0; ! while ((host = dict_mysql_get_active(PLDB, dbname, username, password)) != NULL) { ! #if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000 /* --- 508,519 ---- static MYSQL_RES *plmysql_query(DICT_MYSQL *dict_mysql, const char *name, ! VSTRING *query) { HOST *host; MYSQL_RES *res = 0; ! while ((host = dict_mysql_get_active(dict_mysql)) != NULL) { #if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000 /* *************** *** 534,548 **** * 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) { if ((host->db = mysql_init(NULL)) == NULL) msg_fatal("dict_mysql: insufficient memory"); if (mysql_real_connect(host->db, (host->type == TYPEINET ? host->name : 0), ! username, ! password, ! dbname, host->port, (host->type == TYPEUNIX ? host->name : 0), 0)) { --- 552,583 ---- * 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(DICT_MYSQL *dict_mysql, HOST *host) { if ((host->db = mysql_init(NULL)) == NULL) msg_fatal("dict_mysql: insufficient memory"); + if (dict_mysql->option_file) + mysql_options(host->db, MYSQL_READ_DEFAULT_FILE, dict_mysql->option_file); + if (dict_mysql->option_group) + 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), ! dict_mysql->username, ! dict_mysql->password, ! dict_mysql->dbname, host->port, (host->type == TYPEUNIX ? host->name : 0), 0)) { *************** *** 582,588 **** static void mysql_parse_config(DICT_MYSQL *dict_mysql, const char *mysqlcf) { ! const char *myname = "mysqlname_parse"; CFG_PARSER *p = dict_mysql->parser; VSTRING *buf; char *hosts; --- 617,623 ---- static void mysql_parse_config(DICT_MYSQL *dict_mysql, const char *mysqlcf) { ! const char *myname = "mysql_parse_config"; CFG_PARSER *p = dict_mysql->parser; VSTRING *buf; char *hosts; *************** *** 591,596 **** --- 626,643 ---- 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,764 **** --- 806,827 ---- myfree(dict_mysql->dbname); myfree(dict_mysql->query); myfree(dict_mysql->result_format); + if (dict_mysql->option_file) + myfree(dict_mysql->option_file); + if (dict_mysql->option_group) + myfree(dict_mysql->option_group); + #if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000 + if (dict_mysql->tls_key_file) + myfree(dict_mysql->tls_key_file); + if (dict_mysql->tls_cert_file) + myfree(dict_mysql->tls_cert_file); + if (dict_mysql->tls_CAfile) + myfree(dict_mysql->tls_CAfile); + if (dict_mysql->tls_CApath) + myfree(dict_mysql->tls_CApath); + if (dict_mysql->tls_ciphers) + myfree(dict_mysql->tls_ciphers); + #endif if (dict_mysql->hosts) argv_free(dict_mysql->hosts); if (dict_mysql->ctx)