From 31b24d9a099c7954238661431034a4f1eace381f Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Tue, 16 Apr 2019 17:48:11 +0200 Subject: [PATCH] Use GnuTLS instead --- CMakeLists.txt | 2 +- Dockerfile | 2 +- README.md | 1 + debian/control | 2 +- dist/pacman/PKGBUILD.in | 2 +- dist/rpm/strawberry.spec.in | 2 +- dist/windows/strawberry-debug-x64.nsi.in | 4 +- dist/windows/strawberry-debug-x86.nsi.in | 4 +- dist/windows/strawberry-x64.nsi.in | 4 +- dist/windows/strawberry-x86.nsi.in | 4 +- snap/snapcraft.yaml | 2 +- src/CMakeLists.txt | 4 +- src/internet/localredirectserver.cpp | 185 ++++++++++++++--------- src/internet/localredirectserver.h | 2 +- src/scrobbler/listenbrainzscrobbler.cpp | 3 +- src/scrobbler/scrobblingapi20.cpp | 18 ++- 16 files changed, 146 insertions(+), 95 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 391c6032..f8388c39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,7 @@ pkg_check_modules(GIO REQUIRED gio-2.0) pkg_check_modules(GOBJECT REQUIRED gobject-2.0) pkg_check_modules(CDIO libcdio) find_package(Threads) -find_package(OpenSSL) +find_package(GnuTLS) find_package(Boost REQUIRED) find_package(Protobuf REQUIRED) find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf) diff --git a/Dockerfile b/Dockerfile index c07fdb27..7108be87 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ run zypper --non-interactive --gpg-auto-import-keys dup -l -y run zypper --non-interactive --gpg-auto-import-keys install \ lsb-release git tar make cmake gcc gcc-c++ pkg-config gettext-tools \ - glibc-devel glib2-devel glib2-tools dbus-1-devel alsa-devel libpulse-devel libnotify-devel libopenssl-devel \ + glibc-devel glib2-devel glib2-tools dbus-1-devel alsa-devel libpulse-devel libnotify-devel libgnutls-devel \ boost-devel protobuf-devel sqlite3-devel taglib-devel \ gstreamer-devel gstreamer-plugins-base-devel libxine-devel vlc-devel \ libQt5Core-devel libQt5Gui-devel libQt5Widgets-devel libQt5Concurrent-devel libQt5Network-devel libQt5Sql-devel \ diff --git a/README.md b/README.md index f01873e3..f06f3d87 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ To build Strawberry from source you need the following installed on your system * [DBus (linux)](https://www.freedesktop.org/wiki/Software/dbus/) * [PulseAudio (linux optional)](https://www.freedesktop.org/wiki/Software/PulseAudio/?) * [GStreamer](https://gstreamer.freedesktop.org/), [Xine](https://www.xine-project.org), [VLC](https://www.videolan.org) or [Phonon](https://techbase.kde.org/Phonon) +* [GnuTLS](https://www.gnutls.org/) Optional dependencies: diff --git a/debian/control b/debian/control index 473bf5cd..74e8423c 100644 --- a/debian/control +++ b/debian/control @@ -9,7 +9,7 @@ Build-Depends: debhelper (>= 7), protobuf-compiler, libglib2.0-dev, libdbus-1-dev, - libssl-dev, + libgnutls28-dev, libprotobuf-dev, libboost-dev, libsqlite3-dev, diff --git a/dist/pacman/PKGBUILD.in b/dist/pacman/PKGBUILD.in index 4e4f7ccd..8e3ad6d6 100644 --- a/dist/pacman/PKGBUILD.in +++ b/dist/pacman/PKGBUILD.in @@ -10,7 +10,7 @@ makedepends=(git cmake make gcc boost gettext qt5-tools) depends=( desktop-file-utils hicolor-icon-theme - openssl + gnutls udisks2 protobuf qt5-base diff --git a/dist/rpm/strawberry.spec.in b/dist/rpm/strawberry.spec.in index 713874fc..5288723a 100644 --- a/dist/rpm/strawberry.spec.in +++ b/dist/rpm/strawberry.spec.in @@ -41,7 +41,7 @@ BuildRequires: pkgconfig(gio-2.0) BuildRequires: pkgconfig(gio-unix-2.0) BuildRequires: pkgconfig(gthread-2.0) BuildRequires: pkgconfig(dbus-1) -BuildRequires: pkgconfig(openssl) +BuildRequires: pkgconfig(gnutls) BuildRequires: pkgconfig(alsa) BuildRequires: pkgconfig(protobuf) BuildRequires: pkgconfig(sqlite3) diff --git a/dist/windows/strawberry-debug-x64.nsi.in b/dist/windows/strawberry-debug-x64.nsi.in index 0ac83141..864342fe 100644 --- a/dist/windows/strawberry-debug-x64.nsi.in +++ b/dist/windows/strawberry-debug-x64.nsi.in @@ -140,7 +140,7 @@ Section "Strawberry" Strawberry File "libprotobuf-18.dll" File "libspeex-1.dll" File "libsqlite3-0.dll" - File "libssl-1_1-x64.dll" + File "libssl-3-x64.dll" File "libstdc++-6.dll" File "libtag.dll" File "libvorbis-0.dll" @@ -384,7 +384,7 @@ Section "Uninstall" Delete "$INSTDIR\libprotobuf-18.dll" Delete "$INSTDIR\libspeex-1.dll" Delete "$INSTDIR\libsqlite3-0.dll" - Delete "$INSTDIR\libssl-1_1-x64.dll" + Delete "$INSTDIR\libssl-3-x64.dll" Delete "$INSTDIR\libstdc++-6.dll" Delete "$INSTDIR\libtag.dll" Delete "$INSTDIR\libvorbis-0.dll" diff --git a/dist/windows/strawberry-debug-x86.nsi.in b/dist/windows/strawberry-debug-x86.nsi.in index d05a173d..0f0e1afc 100644 --- a/dist/windows/strawberry-debug-x86.nsi.in +++ b/dist/windows/strawberry-debug-x86.nsi.in @@ -140,7 +140,7 @@ Section "Strawberry" Strawberry File "libprotobuf-18.dll" File "libspeex-1.dll" File "libsqlite3-0.dll" - File "libssl-1_1.dll" + File "libssl-3.dll" File "libstdc++-6.dll" File "libtag.dll" File "libvorbis-0.dll" @@ -384,7 +384,7 @@ Section "Uninstall" Delete "$INSTDIR\libprotobuf-18.dll" Delete "$INSTDIR\libspeex-1.dll" Delete "$INSTDIR\libsqlite3-0.dll" - Delete "$INSTDIR\libssl-1_1.dll" + Delete "$INSTDIR\libssl-3.dll" Delete "$INSTDIR\libstdc++-6.dll" Delete "$INSTDIR\libtag.dll" Delete "$INSTDIR\libvorbis-0.dll" diff --git a/dist/windows/strawberry-x64.nsi.in b/dist/windows/strawberry-x64.nsi.in index 04c35fd0..56652a58 100644 --- a/dist/windows/strawberry-x64.nsi.in +++ b/dist/windows/strawberry-x64.nsi.in @@ -140,7 +140,7 @@ Section "Strawberry" Strawberry File "libprotobuf-18.dll" File "libspeex-1.dll" File "libsqlite3-0.dll" - File "libssl-1_1-x64.dll" + File "libssl-3-x64.dll" File "libstdc++-6.dll" File "libtag.dll" File "libvorbis-0.dll" @@ -352,7 +352,7 @@ Section "Uninstall" Delete "$INSTDIR\libprotobuf-18.dll" Delete "$INSTDIR\libspeex-1.dll" Delete "$INSTDIR\libsqlite3-0.dll" - Delete "$INSTDIR\libssl-1_1-x64.dll" + Delete "$INSTDIR\libssl-3-x64.dll" Delete "$INSTDIR\libstdc++-6.dll" Delete "$INSTDIR\libtag.dll" Delete "$INSTDIR\libvorbis-0.dll" diff --git a/dist/windows/strawberry-x86.nsi.in b/dist/windows/strawberry-x86.nsi.in index c393ea30..1863f4e7 100644 --- a/dist/windows/strawberry-x86.nsi.in +++ b/dist/windows/strawberry-x86.nsi.in @@ -140,7 +140,7 @@ Section "Strawberry" Strawberry File "libprotobuf-18.dll" File "libspeex-1.dll" File "libsqlite3-0.dll" - File "libssl-1_1.dll" + File "libssl-3.dll" File "libstdc++-6.dll" File "libtag.dll" File "libvorbis-0.dll" @@ -352,7 +352,7 @@ Section "Uninstall" Delete "$INSTDIR\libprotobuf-18.dll" Delete "$INSTDIR\libspeex-1.dll" Delete "$INSTDIR\libsqlite3-0.dll" - Delete "$INSTDIR\libssl-1_1.dll" + Delete "$INSTDIR\libssl-3.dll" Delete "$INSTDIR\libstdc++-6.dll" Delete "$INSTDIR\libtag.dll" Delete "$INSTDIR\libvorbis-0.dll" diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 9fe4cf5e..d342afaf 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -84,7 +84,7 @@ parts: - g++ - protobuf-compiler - libglib2.0-dev - - libssl-dev + - libgnutls28-dev - libdbus-1-dev - libprotobuf-dev - libboost-dev diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cad1eeb0..beea5a25 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,7 +43,7 @@ include_directories(${CMAKE_BINARY_DIR}) include_directories(${GLIB_INCLUDE_DIRS}) include_directories(${GLIBCONFIG_INCLUDE_DIRS}) include_directories(${GOBJECT_INCLUDE_DIRS}) -include_directories(${OPENSSL_INCLUDE_DIR}) +include_directories(${GNUTLS_INCLUDE_DIR}) include_directories(${Boost_INCLUDE_DIRS}) include_directories(${LIBXML_INCLUDE_DIRS}) include_directories(${CHROMAPRINT_INCLUDE_DIRS}) @@ -934,7 +934,7 @@ target_link_libraries(strawberry_lib ${GLIB_LIBRARIES} ${GIO_LIBRARIES} ${GOBJECT_LIBRARIES} - ${OPENSSL_LIBRARIES} + ${GNUTLS_LIBRARIES} ${QT_LIBRARIES} ${CHROMAPRINT_LIBRARIES} ${SQLITE_LIBRARIES} diff --git a/src/internet/localredirectserver.cpp b/src/internet/localredirectserver.cpp index 781ebb93..9f49d231 100644 --- a/src/internet/localredirectserver.cpp +++ b/src/internet/localredirectserver.cpp @@ -21,10 +21,9 @@ #include "localredirectserver.h" -#include -#include -#include -#include +#include +#include +#include #include #include @@ -42,6 +41,7 @@ #include #include #include +#include #include "core/logging.h" #include "core/closure.h" @@ -56,79 +56,124 @@ LocalRedirectServer::~LocalRedirectServer() {} bool LocalRedirectServer::GenerateCertificate() { - EVP_PKEY *pkey = EVP_PKEY_new(); - q_check_ptr(pkey); + gnutls_global_init(); - RSA *rsa = RSA_generate_key(2048, RSA_F4, nullptr, nullptr); - q_check_ptr(rsa); + gnutls_x509_privkey_t key; + gnutls_x509_privkey_init(&key); - EVP_PKEY_assign_RSA(pkey, rsa); + unsigned int bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_RSA, GNUTLS_SEC_PARAM_MEDIUM); + gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, bits, 0); - X509 *x509 = X509_new(); - q_check_ptr(x509); + char buffer[4096] = ""; + size_t buffer_size = sizeof(buffer); + gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, buffer, &buffer_size); - ASN1_INTEGER_set(X509_get_serialNumber(x509), static_cast(9999999 + qrand() % 1000000)); - - X509_gmtime_adj(X509_get_notBefore(x509), 0); - X509_gmtime_adj(X509_get_notAfter(x509), 31536000L); - X509_set_pubkey(x509, pkey); - - X509_NAME *name = X509_get_subject_name(x509); - q_check_ptr(name); - - X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *) "US", -1, -1, 0); - X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *) "Strawberry Music Player", -1, -1, 0); - X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *) "localhost", -1, -1, 0); - X509_set_issuer_name(x509, name); - X509_sign(x509, pkey, EVP_sha1()); - - BIO *bp_private = BIO_new(BIO_s_mem()); - q_check_ptr(bp_private); - - if (PEM_write_bio_PrivateKey(bp_private, pkey, nullptr, nullptr, 0, nullptr, nullptr) != 1) { - EVP_PKEY_free(pkey); - X509_free(x509); - BIO_free_all(bp_private); - error_ = "PEM_write_bio_PrivateKey() failed."; - return false; - } - - BIO *bp_public = BIO_new(BIO_s_mem()); - q_check_ptr(bp_public); - - if (PEM_write_bio_X509(bp_public, x509) != 1) { - EVP_PKEY_free(pkey); - X509_free(x509); - BIO_free_all(bp_public); - BIO_free_all(bp_private); - error_ = "PEM_write_bio_X509() failed."; - return false; - } - - const char *buffer; - - long size = BIO_get_mem_data(bp_public, &buffer); - q_check_ptr(buffer); - - QSslCertificate ssl_certificate(QByteArray(buffer, size)); - if (ssl_certificate.isNull()) { - error_ = "Failed to generate a random client certificate."; - return false; - } - - size = BIO_get_mem_data(bp_private, &buffer); - q_check_ptr(buffer); - - QSslKey ssl_key(QByteArray(buffer, size), QSsl::Rsa); + QSslKey ssl_key(QByteArray(buffer, buffer_size), QSsl::Rsa); if (ssl_key.isNull()) { error_ = "Failed to generate a random private key."; + gnutls_x509_privkey_deinit(key); + gnutls_global_deinit(); return false; } - EVP_PKEY_free(pkey); - X509_free(x509); - BIO_free_all(bp_public); - BIO_free_all(bp_private); + gnutls_x509_crt_t crt; + if (gnutls_x509_crt_init(&crt) != GNUTLS_E_SUCCESS) { + error_ = "gnutls_x509_crt_init failed."; + gnutls_x509_privkey_deinit(key); + gnutls_global_deinit(); + return false; + } + if (gnutls_x509_crt_set_version(crt, 1) != GNUTLS_E_SUCCESS) { + error_ = "gnutls_x509_crt_set_version failed."; + gnutls_x509_privkey_deinit(key); + gnutls_x509_crt_deinit(crt); + gnutls_global_deinit(); + return false; + } + if (gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, "US", 2) != GNUTLS_E_SUCCESS) { + error_ = "gnutls_x509_crt_set_dn_by_oid failed."; + gnutls_x509_privkey_deinit(key); + gnutls_x509_crt_deinit(crt); + gnutls_global_deinit(); + return false; + } + if (gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, "Strawberry Music Player", strlen("Strawberry Music Player")) != GNUTLS_E_SUCCESS) { + error_ = "gnutls_x509_crt_set_dn_by_oid failed."; + gnutls_x509_privkey_deinit(key); + gnutls_x509_crt_deinit(crt); + gnutls_global_deinit(); + return false; + } + if (gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0, "localhost", strlen("localhost")) != GNUTLS_E_SUCCESS) { + error_ = "gnutls_x509_crt_set_dn_by_oid failed."; + gnutls_x509_privkey_deinit(key); + gnutls_x509_crt_deinit(crt); + gnutls_global_deinit(); + return false; + } + if (gnutls_x509_crt_set_key(crt, key) != GNUTLS_E_SUCCESS) { + error_ = "gnutls_x509_crt_set_key failed."; + gnutls_x509_privkey_deinit(key); + gnutls_x509_crt_deinit(crt); + gnutls_global_deinit(); + return false; + } + quint64 time = QDateTime::currentDateTime().toTime_t(); + gnutls_x509_crt_set_activation_time(crt, time); + if (gnutls_x509_crt_set_expiration_time(crt, time + 31536000L) != GNUTLS_E_SUCCESS) { + error_ = "gnutls_x509_crt_set_expiration_time failed."; + gnutls_x509_privkey_deinit(key); + gnutls_x509_crt_deinit(crt); + gnutls_global_deinit(); + return false; + } + + quint64 serial = (9999999 + qrand() % 1000000); + QByteArray q_serial; + q_serial.setNum(serial); + + if (gnutls_x509_crt_set_serial (crt, q_serial.constData(), sizeof(q_serial.size())) != GNUTLS_E_SUCCESS) { + error_ = "gnutls_x509_crt_set_serial failed."; + gnutls_x509_privkey_deinit(key); + gnutls_x509_crt_deinit(crt); + gnutls_global_deinit(); + return false; + } + + gnutls_privkey_t pkey; + gnutls_privkey_init(&pkey); + gnutls_privkey_import_x509(pkey, key, 0); + gnutls_x509_crt_privkey_sign(crt, crt, pkey, GNUTLS_DIG_SHA256, 0); + + if (gnutls_x509_crt_sign2(crt, crt, key, GNUTLS_DIG_SHA256, 0) != GNUTLS_E_SUCCESS) { + error_ = "gnutls_x509_crt_sign2 failed."; + gnutls_x509_privkey_deinit(key); + gnutls_x509_crt_deinit(crt); + gnutls_privkey_deinit(pkey); + gnutls_global_deinit(); + return false; + } + + if (gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, buffer, &buffer_size) != GNUTLS_E_SUCCESS) { + error_ = "gnutls_x509_crt_export failed."; + gnutls_x509_privkey_deinit(key); + gnutls_x509_crt_deinit(crt); + gnutls_privkey_deinit(pkey); + gnutls_global_deinit(); + return false; + } + gnutls_x509_crt_deinit(crt); + gnutls_x509_privkey_deinit(key); + gnutls_privkey_deinit(pkey); + + QSslCertificate ssl_certificate(QByteArray(buffer, buffer_size)); + if (ssl_certificate.isNull()) { + error_ = "Failed to generate a random client certificate."; + gnutls_global_deinit(); + return false; + } + + gnutls_global_deinit(); ssl_certificate_ = ssl_certificate; ssl_key_ = ssl_key; @@ -179,6 +224,7 @@ void LocalRedirectServer::incomingConnection(qintptr socket_descriptor) { QSslSocket *ssl_socket = new QSslSocket(this); if (!ssl_socket->setSocketDescriptor(socket_descriptor)) { delete ssl_socket; + close(); error_ = "Unable to set socket descriptor"; emit Finished(); return; @@ -190,7 +236,7 @@ void LocalRedirectServer::incomingConnection(qintptr socket_descriptor) { ssl_socket->startServerEncryption(); connect(ssl_socket, SIGNAL(sslErrors(QList)), this, SLOT(SSLErrors(QList))); - connect(ssl_socket, SIGNAL(encrypted()), this, SLOT(Encrypted(QSslSocket*))); + connect(ssl_socket, SIGNAL(encrypted()), this, SLOT(Encrypted())); socket_ = ssl_socket; } @@ -198,6 +244,7 @@ void LocalRedirectServer::incomingConnection(qintptr socket_descriptor) { QTcpSocket *tcp_socket = new QTcpSocket(this); if (!tcp_socket->setSocketDescriptor(socket_descriptor)) { delete tcp_socket; + close(); error_ = "Unable to set socket descriptor"; emit Finished(); return; diff --git a/src/internet/localredirectserver.h b/src/internet/localredirectserver.h index 4cdbc97d..237e1de2 100644 --- a/src/internet/localredirectserver.h +++ b/src/internet/localredirectserver.h @@ -46,7 +46,7 @@ class LocalRedirectServer : public QTcpServer { const QString &error() const { return error_; } signals: - void Finished(QString error = QString()); + void Finished(); public slots: void NewConnection(); diff --git a/src/scrobbler/listenbrainzscrobbler.cpp b/src/scrobbler/listenbrainzscrobbler.cpp index f5386f86..e83497f5 100644 --- a/src/scrobbler/listenbrainzscrobbler.cpp +++ b/src/scrobbler/listenbrainzscrobbler.cpp @@ -123,8 +123,6 @@ void ListenBrainzScrobbler::Logout() { void ListenBrainzScrobbler::Authenticate(const bool https) { - QUrl url(kAuthUrl); - LocalRedirectServer *server = new LocalRedirectServer(https, this); if (!server->Listen()) { AuthError(server->error()); @@ -141,6 +139,7 @@ void ListenBrainzScrobbler::Authenticate(const bool https) { url_query.addQueryItem("client_id", kClientID); url_query.addQueryItem("redirect_uri", redirect_url.toString()); url_query.addQueryItem("scope", "profile;email;tag;rating;collection;submit_isrc;submit_barcode"); + QUrl url(kAuthUrl); url.setQuery(url_query); bool result = QDesktopServices::openUrl(url); diff --git a/src/scrobbler/scrobblingapi20.cpp b/src/scrobbler/scrobblingapi20.cpp index 4ba34297..7b74a37d 100644 --- a/src/scrobbler/scrobblingapi20.cpp +++ b/src/scrobbler/scrobblingapi20.cpp @@ -116,26 +116,25 @@ void ScrobblingAPI20::Logout() { void ScrobblingAPI20::Authenticate(const bool https) { - QUrl url(auth_url_); - LocalRedirectServer *server = new LocalRedirectServer(https, this); if (!server->Listen()) { AuthError(server->error()); delete server; return; } - NewClosure(server, SIGNAL(Finished(QString)), this, &ScrobblingAPI20::RedirectArrived, server); + NewClosure(server, SIGNAL(Finished()), this, &ScrobblingAPI20::RedirectArrived, server); - QUrl redirect_url(kRedirectUrl); QUrlQuery redirect_url_query; const QString port = QString::number(server->url().port()); redirect_url_query.addQueryItem("port", port); + if (https) redirect_url_query.addQueryItem("https", QString("1")); + QUrl redirect_url(kRedirectUrl); redirect_url.setQuery(redirect_url_query); QUrlQuery url_query; url_query.addQueryItem("api_key", kApiKey); - url_query.addQueryItem("cb", QUrl::toPercentEncoding(redirect_url.toString())); - qLog(Debug) << QUrl::toPercentEncoding(redirect_url.toString()); + url_query.addQueryItem("cb", redirect_url.toString()); + QUrl url(auth_url_); url.setQuery(url_query); QMessageBox messagebox(QMessageBox::Information, tr("%1 Scrobbler Authentication").arg(name_), tr("Open URL in web browser?
%1
Press \"Save\" to copy the URL to clipboard and manually open it in a web browser.").arg(url.toString()), QMessageBox::Open|QMessageBox::Save|QMessageBox::Cancel); @@ -169,9 +168,14 @@ void ScrobblingAPI20::RedirectArrived(LocalRedirectServer *server) { server->deleteLater(); + if (!server->error().isEmpty()) { + AuthError(server->error()); + return; + } + QUrl url = server->request_url(); if (!url.isValid()) { - AuthError(tr("Invalid reply from web browser. Try using Chromium or Chrome instead.")); + AuthError(tr("Received invalid reply from web browser. Try the HTTPS option, or use another browser like Chromium or Chrome.")); return; } QUrlQuery url_query(url);