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
13 bool 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 
118 void 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
error
static void error(const char *str,...)
Definition: vbi.cpp:36
MythHTTPS::InitSSLServer
static bool InitSSLServer(QSslConfiguration &Config)
Definition: mythhttps.cpp:13
mythhttps.h
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
mythdirs.h
mythlogging.h
GetConfDir
QString GetConfDir(void)
Definition: mythdirs.cpp:256
LOC
#define LOC
Definition: mythhttps.cpp:10
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
mythcorecontext.h
MythHTTPS::InitSSLSocket
static void InitSSLSocket(QSslSocket *Socket, QSslConfiguration &Config)
Definition: mythhttps.cpp:118
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:898