1 /*
2  Copyright (C) 2003 Justin Karneges <justin@affinix.com>
3  Copyright (C) 2006 Brad Hards <bradh@frogmouth.net>
4 
5  Permission is hereby granted, free of charge, to any person obtaining a copy
6  of this software and associated documentation files (the "Software"), to deal
7  in the Software without restriction, including without limitation the rights
8  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9  copies of the Software, and to permit persons to whom the Software is
10  furnished to do so, subject to the following conditions:
11 
12  The above copyright notice and this permission notice shall be included in
13  all copies or substantial portions of the Software.
14 
15  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22 
23 #include <QtCrypto>
24 
25 #include <QCoreApplication>
26 #include <QDebug>
27 #include <QHostAddress>
28 #include <QTcpServer>
29 #include <QTcpSocket>
30 #include <QTimer>
31 
32 #ifdef QT_STATICPLUGIN
33 #include "import_plugins.h"
34 #endif
35 
36 char pemdata_cert[] =
37     "-----BEGIN CERTIFICATE-----\n"
38     "MIICeTCCAeKgAwIBAgIRAKKKnOj6Aarmwf0phApitVAwDQYJKoZIhvcNAQEFBQAw\n"
39     "ODELMAkGA1UEBhMCVVMxFDASBgNVBAoTC0V4YW1wbGUgT3JnMRMwEQYDVQQDEwpF\n"
40     "eGFtcGxlIENBMB4XDTA2MDMxNTA3MDU1MloXDTA3MDMxNTA3MDU1MlowOjEVMBMG\n"
41     "A1UEAxMMRXhhbXBsZSBVc2VyMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRXhhbXBs\n"
42     "ZSBPcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPkKn0FfHMvRZv+3uFcw\n"
43     "VrOadJmANzLVeVW/DHZp4CXokXSksM66ZMqFuQRBk5rnIZZpZmVp1tTRDVt9sEAY\n"
44     "YNa8CRM4HXkVlU0lCKdey18CSq2VuSvNtw8dDpoBmQt3nr9tePvKHnpS3nm6YjR2\n"
45     "NEvIKt1P4mHzYXLmwoF24C1bAgMBAAGjgYAwfjAdBgNVHQ4EFgQUmQIdzyDaPYWF\n"
46     "fPJ8PPOOm1eSsucwHwYDVR0jBBgwFoAUkCglAizTO7iqwLeaO6r/8kJuqhMwDAYD\n"
47     "VR0TAQH/BAIwADAeBgNVHREEFzAVgRNleGFtcGxlQGV4YW1wbGUuY29tMA4GA1Ud\n"
48     "DwEB/wQEAwIF4DANBgkqhkiG9w0BAQUFAAOBgQAuhbiUgy2a++EUccaonID7eTJZ\n"
49     "F3D5qXMqUpQxlYxU8du+9AxDD7nFxTMkQC2pzfmEc1znRNmJ1ZeLRL72VYsVndcT\n"
50     "psyM8ABkvPp1d2jWIyccVjGpt+/RN5IPKm/YIbtIZcywvWuXrOp1lanVmppLfPnO\n"
51     "6yneBkC9iqjOv/+Q+A==\n"
52     "-----END CERTIFICATE-----\n";
53 
54 char pemdata_privkey[] =
55     "-----BEGIN PRIVATE KEY-----\n"
56     "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPkKn0FfHMvRZv+3\n"
57     "uFcwVrOadJmANzLVeVW/DHZp4CXokXSksM66ZMqFuQRBk5rnIZZpZmVp1tTRDVt9\n"
58     "sEAYYNa8CRM4HXkVlU0lCKdey18CSq2VuSvNtw8dDpoBmQt3nr9tePvKHnpS3nm6\n"
59     "YjR2NEvIKt1P4mHzYXLmwoF24C1bAgMBAAECgYEAyIjJHDaeVXDU42zovyxpZE4n\n"
60     "PcOEryY+gdFJE8DFgUD4f1huFsj4iCuNg+PaG42p+hf9IARNvSho/RcEaVg4AJrV\n"
61     "jRP8r7fSqcIGr6lGuvDFFv3SU5ddy84g5oqLYGKvuPSHMGfVsZSxAwOrzD4bH19L\n"
62     "SNqtNcpdBsBd7ZiEE4ECQQD/oJGui9D5Dx3QVcS+QV4F8wuyN9jYIANmX/17o0fl\n"
63     "BL0bwRU4RICwadrcybi5N0JQLIYSUm2HGqNvAJbtnuQxAkEA+WeYLLYPeawcy+WU\n"
64     "kGcOR7BUjHiG71+6cvU4XIDW2bezA04fqWXkZRFAwHTMpQb785/XalFftgS21kql\n"
65     "8yLDSwJAHkeT2hwftdDPlEUEmBDAJW5DvWmWGwu3u2G1cfbGZl9oUyhM7ixXHg57\n"
66     "6VlPs0jTZxHPE86FwNIr99MXDbCbkQJBAMDFOJK+ecGirXNP1P+0GA6DFSap9inJ\n"
67     "BRTbwx+EmgwX966DUOefEOSpbDIVVSPs/Qr2LgtIMEFA7Y0+j3wZD3cCQBsTwccd\n"
68     "ASQx59xakpq11eOlTYz14rjwodr4QMyj26WxEPJtz7hKokx/+EH6fWuPIUSrROM5\n"
69     "07y2gaVbYxtis0s=\n"
70     "-----END PRIVATE KEY-----\n";
71 
72 class SecureServer : public QObject
73 {
74     Q_OBJECT
75 
76 public:
77     enum
78     {
79         Idle,
80         Handshaking,
81         Active,
82         Closing
83     };
84 
SecureServer(quint16 _port)85     SecureServer(quint16 _port)
86         : port(_port)
87     {
88         server = new QTcpServer;
89         connect(server, &QTcpServer::newConnection, this, &SecureServer::server_handleConnection);
90 
91         ssl = new QCA::TLS;
92         connect(ssl, &QCA::TLS::handshaken, this, &SecureServer::ssl_handshaken);
93         connect(ssl, &QCA::TLS::readyRead, this, &SecureServer::ssl_readyRead);
94         connect(ssl, &QCA::TLS::readyReadOutgoing, this, &SecureServer::ssl_readyReadOutgoing);
95         connect(ssl, &QCA::TLS::closed, this, &SecureServer::ssl_closed);
96         connect(ssl, &QCA::TLS::error, this, &SecureServer::ssl_error);
97 
98         cert    = QCA::Certificate::fromPEM(QString::fromLatin1(pemdata_cert));
99         privkey = QCA::PrivateKey::fromPEM(QString::fromLatin1(pemdata_privkey));
100 
101         mode = Idle;
102     }
103 
~SecureServer()104     ~SecureServer() override
105     {
106         delete ssl;
107         delete server;
108     }
109 
start()110     void start()
111     {
112         if (cert.isNull()) {
113             qDebug() << "Error loading cert!";
114             QTimer::singleShot(0, this, &SecureServer::quit);
115             return;
116         }
117         if (privkey.isNull()) {
118             qDebug() << "Error loading private key!";
119             QTimer::singleShot(0, this, &SecureServer::quit);
120             return;
121         }
122         if (false == server->listen(QHostAddress::Any, port)) {
123             qDebug() << "Error binding to port " << port;
124             QTimer::singleShot(0, this, &SecureServer::quit);
125             return;
126         }
127         qDebug() << "Listening on port" << port;
128     }
129 
130 Q_SIGNALS:
131     void quit();
132 
133 private Q_SLOTS:
sock_readyRead()134     void sock_readyRead()
135     {
136         QByteArray buf(sock->bytesAvailable(), 0x00);
137 
138         int num = sock->read(buf.data(), buf.size());
139 
140         if (-1 == num)
141             qDebug() << "Error reading data from socket";
142 
143         if (num < buf.size())
144             buf.resize(num);
145 
146         ssl->writeIncoming(buf);
147     }
148 
server_handleConnection()149     void server_handleConnection()
150     {
151         // Note: only 1 connection supported at a time in this example!
152         if (mode != Idle) {
153             QTcpSocket *tmp = server->nextPendingConnection();
154             tmp->close();
155             connect(tmp, &QTcpSocket::disconnected, tmp, &QTcpSocket::deleteLater);
156             qDebug() << "throwing away extra connection";
157             return;
158         }
159         mode = Handshaking;
160         sock = server->nextPendingConnection();
161         connect(sock, &QTcpSocket::readyRead, this, &SecureServer::sock_readyRead);
162         connect(sock, &QTcpSocket::disconnected, this, &SecureServer::sock_disconnected);
163 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
164         connect(sock, &QTcpSocket::errorOccurred, this, &SecureServer::sock_error);
165 #else
166         connect(sock, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &SecureServer::sock_error);
167 #endif
168         connect(sock, &QTcpSocket::bytesWritten, this, &SecureServer::sock_bytesWritten);
169 
170         qDebug() << "Connection received!  Starting TLS handshake.";
171         ssl->setCertificate(cert, privkey);
172         ssl->startServer();
173     }
174 
sock_disconnected()175     void sock_disconnected()
176     {
177         qDebug() << "Connection closed.";
178     }
179 
sock_bytesWritten(qint64 x)180     void sock_bytesWritten(qint64 x)
181     {
182         if (mode == Active && sent) {
183             qint64 bytes = ssl->convertBytesWritten(x);
184             bytesLeft -= bytes;
185 
186             if (bytesLeft == 0) {
187                 mode = Closing;
188                 qDebug() << "Data transfer complete - SSL shutting down";
189                 ssl->close();
190             }
191         }
192     }
193 
sock_error(QAbstractSocket::SocketError error)194     void sock_error(QAbstractSocket::SocketError error)
195     {
196         qDebug() << "Socket error: " << (unsigned)error;
197     }
198 
ssl_handshaken()199     void ssl_handshaken()
200     {
201         qDebug() << "Successful SSL handshake.  Waiting for newline.";
202         bytesLeft = 0;
203         sent      = false;
204         mode      = Active;
205         ssl->continueAfterStep();
206     }
207 
ssl_readyRead()208     void ssl_readyRead()
209     {
210         ssl->read();
211         QByteArray b =
212             "<html>\n"
213             "<head><title>Test</title></head>\n"
214             "<body>this is only a test</body>\n"
215             "</html>\n";
216 
217         qDebug() << "Sending test response.";
218         sent = true;
219         ssl->write(b);
220     }
221 
ssl_readyReadOutgoing()222     void ssl_readyReadOutgoing()
223     {
224         int        plainBytes;
225         QByteArray outgoingData = ssl->readOutgoing(&plainBytes);
226         sock->write(outgoingData);
227     }
228 
ssl_closed()229     void ssl_closed()
230     {
231         qDebug() << "Closing socket.";
232         sock->close();
233         mode = Idle;
234     }
235 
ssl_error()236     void ssl_error()
237     {
238         if (ssl->errorCode() == QCA::TLS::ErrorHandshake) {
239             qDebug() << "SSL Handshake Error!  Closing.";
240             sock->close();
241         } else {
242             qDebug() << "SSL Error!  Closing.";
243             sock->close();
244         }
245         mode = Idle;
246     }
247 
248 private:
249     quint16          port;
250     QTcpServer *     server;
251     QTcpSocket *     sock;
252     QCA::TLS *       ssl;
253     QCA::Certificate cert;
254     QCA::PrivateKey  privkey;
255 
256     bool   sent;
257     int    mode;
258     qint64 bytesLeft;
259 };
260 
261 #include "sslservtest.moc"
262 
main(int argc,char ** argv)263 int main(int argc, char **argv)
264 {
265     QCA::Initializer init;
266 
267     QCoreApplication app(argc, argv);
268     int              port = argc > 1 ? QString::fromLatin1(argv[1]).toInt() : 8000;
269 
270     if (!QCA::isSupported("tls")) {
271         qDebug() << "TLS not supported!";
272         return 1;
273     }
274 
275     SecureServer *server = new SecureServer(port);
276     QObject::connect(server, &SecureServer::quit, &app, &QCoreApplication::quit);
277     server->start();
278     app.exec();
279     delete server;
280 
281     return 0;
282 }
283