MythTV master
mythhttps.cpp
Go to the documentation of this file.
1// Qt
2#include <QFile>
3
4// MythTV
5#include "mythlogging.h"
6#include "mythcorecontext.h"
7#include "mythdirs.h"
8#include "mythhttps.h"
9
10#define LOC QString("SSL: ")
11
12#ifndef QT_NO_OPENSSL
13bool MythHTTPS::InitSSLServer(QSslConfiguration& Config)
14{
15 if (!QSslSocket::supportsSsl())
16 return false;
17
18 LOG(VB_HTTP, LOG_INFO, LOC + QSslSocket::sslLibraryVersionString());
19
20 Config = QSslConfiguration::defaultConfiguration();
21 Config.setProtocol(QSsl::SecureProtocols); // Includes SSLv3 which is insecure, but can't be helped
22 Config.setSslOption(QSsl::SslOptionDisableLegacyRenegotiation, true); // Potential DoS multiplier
23 Config.setSslOption(QSsl::SslOptionDisableCompression, true); // CRIME attack
24
25 auto availableCiphers = QSslConfiguration::supportedCiphers();
26 QList<QSslCipher> secureCiphers;
27 for (const auto & cipher : std::as_const(availableCiphers))
28 {
29 // Remove weak ciphers from the cipher list
30 if (cipher.usedBits() < 128)
31 continue;
32
33 if (cipher.name().startsWith("RC4") || // Weak cipher
34 cipher.name().startsWith("EXP") || // Weak authentication
35 cipher.name().startsWith("ADH") || // No authentication
36 cipher.name().contains("NULL")) // No encryption
37 continue;
38
39 secureCiphers.append(cipher);
40 }
41 Config.setCiphers(secureCiphers);
42
43 // Fallback to the config directory if no cert settings
44 auto configdir = GetConfDir();
45 while (configdir.endsWith("/"))
46 configdir.chop(1);
47 configdir.append(QStringLiteral("/certificates/"));
48
49 auto hostKeyPath = gCoreContext->GetSetting("hostSSLKey", "");
50 if (hostKeyPath.isEmpty())
51 hostKeyPath = configdir + "key.pem";
52
53 QFile hostKeyFile(hostKeyPath);
54 if (!hostKeyFile.exists() || !hostKeyFile.open(QIODevice::ReadOnly))
55 {
56 LOG(VB_GENERAL, LOG_WARNING, LOC +
57 QString("SSL Host key file (%1) does not exist or is not readable").arg(hostKeyPath));
58 return false;
59 }
60
61 auto rawHostKey = hostKeyFile.readAll();
62 auto hostKey = QSslKey(rawHostKey, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
63 if (!hostKey.isNull())
64 {
65 Config.setPrivateKey(hostKey);
66 }
67 else
68 {
69 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unable to load host key from file (%1)").arg(hostKeyPath));
70 return false;
71 }
72
73 auto hostCertPath = gCoreContext->GetSetting("hostSSLCertificate", "");
74 if (hostCertPath.isEmpty())
75 hostCertPath = configdir + "cert.pem";
76
77 QSslCertificate hostCert;
78 auto certList = QSslCertificate::fromPath(hostCertPath);
79 if (!certList.isEmpty())
80 hostCert = certList.first();
81
82 if (!hostCert.isNull())
83 {
84 if (hostCert.effectiveDate() > QDateTime::currentDateTime())
85 {
86 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Host certificate start date in future (%1)").arg(hostCertPath));
87 return false;
88 }
89
90 if (hostCert.expiryDate() < QDateTime::currentDateTime())
91 {
92 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Host certificate has expired (%1)").arg(hostCertPath));
93 return false;
94 }
95
96 Config.setLocalCertificate(hostCert);
97 }
98 else
99 {
100 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unable to load host cert from file (%1)").arg(hostCertPath));
101 return false;
102 }
103
104 auto caCertPath = gCoreContext->GetSetting("caSSLCertificate", "");
105 auto CACertList = QSslCertificate::fromPath(caCertPath);
106 if (!CACertList.isEmpty())
107 {
108 Config.setCaCertificates(CACertList);
109 }
110 else if (!caCertPath.isEmpty())
111 {
112 // Only warn if a path was actually configured, this isn't an error otherwise
113 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unable to load CA cert file (%1)").arg(caCertPath));
114 }
115 return true;
116}
117
118void MythHTTPS::InitSSLSocket(QSslSocket *Socket, QSslConfiguration& Config)
119{
120 if (!Socket)
121 return;
122
123 auto Encrypted = [](const QSslSocket* SslSocket)
124 {
125 LOG(VB_HTTP, LOG_INFO, LOC + "Socket encrypted");
126 if (SslSocket)
127 LOG(VB_HTTP, LOG_INFO, LOC + QString("Cypher: %1").arg(SslSocket->sessionCipher().name()));
128 };
129
130 auto SSLErrors = [](const QList<QSslError>& Errors)
131 {
132 for (const auto & error : Errors)
133 LOG(VB_GENERAL, LOG_INFO, LOC + QString("SslError: %1").arg(error.errorString()));
134 };
135
136 QObject::connect(Socket, &QSslSocket::encrypted, [Encrypted, Socket] { Encrypted(Socket); } );
137 QObject::connect(Socket, qOverload<const QList<QSslError> &>(&QSslSocket::sslErrors), SSLErrors);
138 Socket->setSslConfiguration(Config);
139 Socket->startServerEncryption();
140}
141#endif
QString GetSetting(const QString &key, const QString &defaultval="")
static void InitSSLSocket(QSslSocket *Socket, QSslConfiguration &Config)
Definition: mythhttps.cpp:118
static bool InitSSLServer(QSslConfiguration &Config)
Definition: mythhttps.cpp:13
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QString GetConfDir(void)
Definition: mythdirs.cpp:263
#define LOC
Definition: mythhttps.cpp:10
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
def error(message)
Definition: smolt.py:409