From 28ceb2ea5e79d65904d2ad83b8259e75b60acff6 Mon Sep 17 00:00:00 2001 From: Olivier Delbeke Date: Mon, 8 Jun 2015 20:28:23 +0200 Subject: Added SSL support for MySQL database connections Addition of new options SSL_KEY, SSL_CERT, SSL_CA, SSL_CAPATH and SSL_CIPHER to allow SSL-encrypted connections to MySQL databases. When needed, these options must be specified in the function call QSqlDatabase::setConnectOptions() before the call to QSqlDatabase::open(). SSL_KEY = the path name to the key file SSL_CERT = the path name to the certificate file SSL_CA = the path name to the certificate authority file SSL_CAPATH = the path name to a directory that contains trusted SSL CA certificates in PEM format. SSL_CIPHER = a list of permissible ciphers to use for SSL encryption. These options replace CLIENT_SSL (which should not be used any more). Example: db.setConnectOptions("SSL_KEY=client-key.pem;" \ "SSL_CERT=client-cert.pem;" \ "SSL_CA=server-ca.pem"); [ChangeLog][QtSql] SSL support for MySQL database connections has been added. Option CLIENT_SSL replaced by SSL_KEY, SSL_CERT, SSL_CA, SSL_CAPATH and SSL_CIPHER, so that the keys, certificates and cipher can be specified. Task-number: QtBUG-3500 Change-Id: I8197234b169a818658678d6fcc953c90e83db23e Reviewed-by: Mark Brand --- .../snippets/code/src_sql_kernel_qsqldatabase.cpp | 2 +- src/sql/drivers/mysql/qsql_mysql.cpp | 97 +++++++++++++++------- src/sql/kernel/qsqldatabase.cpp | 6 +- 3 files changed, 73 insertions(+), 32 deletions(-) (limited to 'src/sql') diff --git a/src/sql/doc/snippets/code/src_sql_kernel_qsqldatabase.cpp b/src/sql/doc/snippets/code/src_sql_kernel_qsqldatabase.cpp index c3b3b8e817..95422c9e7b 100644 --- a/src/sql/doc/snippets/code/src_sql_kernel_qsqldatabase.cpp +++ b/src/sql/doc/snippets/code/src_sql_kernel_qsqldatabase.cpp @@ -80,7 +80,7 @@ if (db.open()) { //! [4] ... // MySQL connection -db.setConnectOptions("CLIENT_SSL=1;CLIENT_IGNORE_SPACE=1"); // use an SSL connection to the server +db.setConnectOptions("SSL_KEY=client-key.pem;SSL_CERT=client-cert.pem;SSL_CA=ca-cert.pem;CLIENT_IGNORE_SPACE=1"); // use an SSL connection to the server if (!db.open()) { db.setConnectOptions(); // clears the connect option string ... diff --git a/src/sql/drivers/mysql/qsql_mysql.cpp b/src/sql/drivers/mysql/qsql_mysql.cpp index d901008e00..086a232746 100644 --- a/src/sql/drivers/mysql/qsql_mysql.cpp +++ b/src/sql/drivers/mysql/qsql_mysql.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include @@ -1211,7 +1212,7 @@ static void setOptionFlag(uint &optionFlags, const QString &opt) else if (opt == QLatin1String("CLIENT_ODBC")) optionFlags |= CLIENT_ODBC; else if (opt == QLatin1String("CLIENT_SSL")) - optionFlags |= CLIENT_SSL; + qWarning("QMYSQLDriver: SSL_KEY, SSL_CERT and SSL_CA should be used instead of CLIENT_SSL."); else qWarning("QMYSQLDriver::open: Unknown connect option '%s'", opt.toLocal8Bit().constData()); } @@ -1235,6 +1236,11 @@ bool QMYSQLDriver::open(const QString& db, unsigned int optionFlags = Q_CLIENT_MULTI_STATEMENTS; const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts)); QString unixSocket; + QString sslCert; + QString sslCA; + QString sslKey; + QString sslCAPath; + QString sslCipher; #if MYSQL_VERSION_ID >= 50000 my_bool reconnect=false; uint connectTimeout = 0; @@ -1263,6 +1269,16 @@ bool QMYSQLDriver::open(const QString& db, writeTimeout = val.toInt(); } #endif + else if (opt == QLatin1String("SSL_KEY")) + sslKey = val; + else if (opt == QLatin1String("SSL_CERT")) + sslCert = val; + else if (opt == QLatin1String("SSL_CA")) + sslCA = val; + else if (opt == QLatin1String("SSL_CAPATH")) + sslCAPath = val; + else if (opt == QLatin1String("SSL_CIPHER")) + sslCipher = val; else if (val == QLatin1String("TRUE") || val == QLatin1String("1")) setOptionFlag(optionFlags, tmp.left(idx).simplified()); else @@ -1273,39 +1289,60 @@ bool QMYSQLDriver::open(const QString& db, } } - if ((d->mysql = mysql_init((MYSQL*) 0))) { + if (!(d->mysql = mysql_init((MYSQL*) 0))) { + setLastError(qMakeError(tr("Unable to allocate a MYSQL object"), + QSqlError::ConnectionError, d)); + setOpenError(true); + return false; + } + + if (!sslKey.isNull() || !sslCert.isNull() || !sslCA.isNull() || + !sslCAPath.isNull() || !sslCipher.isNull()) { + mysql_ssl_set(d->mysql, + sslKey.isNull() ? static_cast(0) + : QFile::encodeName(sslKey).constData(), + sslCert.isNull() ? static_cast(0) + : QFile::encodeName(sslCert).constData(), + sslCA.isNull() ? static_cast(0) + : QFile::encodeName(sslCA).constData(), + sslCAPath.isNull() ? static_cast(0) + : QFile::encodeName(sslCAPath).constData(), + sslCipher.isNull() ? static_cast(0) + : sslCipher.toLocal8Bit().constData()); + } + #if MYSQL_VERSION_ID >= 50000 - if (connectTimeout != 0) - mysql_options(d->mysql, MYSQL_OPT_CONNECT_TIMEOUT, &connectTimeout); - if (readTimeout != 0) - mysql_options(d->mysql, MYSQL_OPT_READ_TIMEOUT, &readTimeout); - if (writeTimeout != 0) - mysql_options(d->mysql, MYSQL_OPT_WRITE_TIMEOUT, &writeTimeout); + if (connectTimeout != 0) + mysql_options(d->mysql, MYSQL_OPT_CONNECT_TIMEOUT, &connectTimeout); + if (readTimeout != 0) + mysql_options(d->mysql, MYSQL_OPT_READ_TIMEOUT, &readTimeout); + if (writeTimeout != 0) + mysql_options(d->mysql, MYSQL_OPT_WRITE_TIMEOUT, &writeTimeout); #endif - if (mysql_real_connect(d->mysql, - host.isNull() ? static_cast(0) - : host.toLocal8Bit().constData(), - user.isNull() ? static_cast(0) - : user.toLocal8Bit().constData(), - password.isNull() ? static_cast(0) - : password.toLocal8Bit().constData(), - db.isNull() ? static_cast(0) - : db.toLocal8Bit().constData(), - (port > -1) ? port : 0, - unixSocket.isNull() ? static_cast(0) - : unixSocket.toLocal8Bit().constData(), - optionFlags)) { - if (!db.isEmpty() && mysql_select_db(d->mysql, db.toLocal8Bit().constData())) { - setLastError(qMakeError(tr("Unable to open database '%1'").arg(db), QSqlError::ConnectionError, d)); - mysql_close(d->mysql); - setOpenError(true); - return false; - } + if (mysql_real_connect(d->mysql, + host.isNull() ? static_cast(0) + : host.toLocal8Bit().constData(), + user.isNull() ? static_cast(0) + : user.toLocal8Bit().constData(), + password.isNull() ? static_cast(0) + : password.toLocal8Bit().constData(), + db.isNull() ? static_cast(0) + : db.toLocal8Bit().constData(), + (port > -1) ? port : 0, + unixSocket.isNull() ? static_cast(0) + : unixSocket.toLocal8Bit().constData(), + optionFlags)) + { + if (!db.isEmpty() && mysql_select_db(d->mysql, db.toLocal8Bit().constData())) { + setLastError(qMakeError(tr("Unable to open database '%1'").arg(db), QSqlError::ConnectionError, d)); + mysql_close(d->mysql); + setOpenError(true); + return false; + } #if MYSQL_VERSION_ID >= 50000 - if (reconnect) - mysql_options(d->mysql, MYSQL_OPT_RECONNECT, &reconnect); + if (reconnect) + mysql_options(d->mysql, MYSQL_OPT_RECONNECT, &reconnect); #endif - } } else { setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d)); diff --git a/src/sql/kernel/qsqldatabase.cpp b/src/sql/kernel/qsqldatabase.cpp index 240e19bf20..90c64d7331 100644 --- a/src/sql/kernel/qsqldatabase.cpp +++ b/src/sql/kernel/qsqldatabase.cpp @@ -1218,7 +1218,6 @@ QSqlRecord QSqlDatabase::record(const QString& tablename) const \li CLIENT_COMPRESS \li CLIENT_FOUND_ROWS \li CLIENT_IGNORE_SPACE - \li CLIENT_SSL \li CLIENT_ODBC \li CLIENT_NO_SCHEMA \li CLIENT_INTERACTIVE @@ -1227,6 +1226,11 @@ QSqlRecord QSqlDatabase::record(const QString& tablename) const \li MYSQL_OPT_CONNECT_TIMEOUT \li MYSQL_OPT_READ_TIMEOUT \li MYSQL_OPT_WRITE_TIMEOUT + \li SSL_KEY + \li SSL_CERT + \li SSL_CA + \li SSL_CAPATH + \li SSL_CIPHER \endlist \li -- cgit v1.2.3