Use GnuTLS instead

This commit is contained in:
Jonas Kvinge 2019-04-16 17:48:11 +02:00
parent 8b04a3b16a
commit 31b24d9a09
16 changed files with 146 additions and 95 deletions

View File

@ -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)

View File

@ -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 \

View File

@ -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:

2
debian/control vendored
View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -84,7 +84,7 @@ parts:
- g++
- protobuf-compiler
- libglib2.0-dev
- libssl-dev
- libgnutls28-dev
- libdbus-1-dev
- libprotobuf-dev
- libboost-dev

View File

@ -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}

View File

@ -21,10 +21,9 @@
#include "localredirectserver.h"
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#include <gnutls/abstract.h>
#include <QApplication>
#include <QIODevice>
@ -42,6 +41,7 @@
#include <QByteArray>
#include <QString>
#include <QUrl>
#include <QDateTime>
#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<uint64_t>(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<QSslError>)), this, SLOT(SSLErrors(QList<QSslError>)));
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;

View File

@ -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();

View File

@ -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);

View File

@ -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?<br /><a href=\"%1\">%1</a><br />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);