comparison ui/sslconnection_bare.cpp @ 908:d1c951b3012d

Curl based implementation of sslconnection
author Andre Heinecke <andre.heinecke@intevation.de>
date Wed, 13 Aug 2014 19:35:08 +0200
parents
children eaed02defe6a
comparison
equal deleted inserted replaced
907:7bd75417e14e 908:d1c951b3012d
1 /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
2 * Software engineering by Intevation GmbH
3 *
4 * This file is Free Software under the GNU GPL (v>=2)
5 * and comes with ABSOLUTELY NO WARRANTY!
6 * See LICENSE.txt for details.
7 */
8 /* TODO: Wrap ssl_session in a class for reuse.
9 * see programs/ssl/ssl_client2.c for example of session reuse */
10 #include "sslconnection_bare.h"
11 #include "sslhelp.h"
12
13 #include <QFile>
14 #include <QUuid>
15 #include <QApplication>
16
17 #define MAX_IO_TRIES 10
18 #define MAX_RESETS 10
19
20 #ifdef CONNECTION_DEBUG
21 static void my_debug(void *ctx, int level, const char *str)
22 {
23 fprintf((FILE *) ctx, "%s", str);
24 fflush((FILE *) ctx);
25 }
26 #endif
27
28 SSLConnectionBare::SSLConnectionBare(const QString& url,
29 const QByteArray& certificate):
30 SSLConnection (url, certificate)
31 {
32 int ret = -1;
33
34 memset(&mSSL, 0, sizeof(ssl_context));
35 memset(&mSavedSession, 0, sizeof( ssl_session ) );
36
37 if (certificate.isEmpty()) {
38 QFile certResource(":certs/intevation.de");
39 certResource.open(QFile::ReadOnly);
40 mPinnedCert = certResource.readAll();
41 certResource.close();
42 }
43
44 ret = init();
45 if (ret == 0) {
46 mInitialized = true;
47 } else {
48 qDebug() << "Initialization error: " + getPolarSSLErrorMsg(ret);
49 }
50 }
51
52 int SSLConnectionBare::init()
53 {
54 int ret = -1;
55 QUuid uuid = QUuid::createUuid();
56 QString personalString = QApplication::applicationName() + uuid.toString();
57 QByteArray personalBa = personalString.toLocal8Bit();
58
59 x509_crt_init(&mX509PinnedCert);
60 entropy_init(&mEntropy);
61
62 ret = ssl_init(&mSSL);
63 if (ret != 0) {
64 /* The only documented error is malloc failed */
65 mErrorState = ErrUnknown;
66 return ret;
67 }
68
69 /*
70 * Initialize random generator.
71 * Personalisation string, does not need to be random but
72 * should be unique according to documentation.
73 *
74 * the ctr_drbg structure does not need to be freed explicitly.
75 */
76 ret = ctr_drbg_init(&mCtr_drbg, entropy_func, &mEntropy,
77 (const unsigned char*) personalBa.constData(),
78 personalBa.size());
79 if (ret != 0) {
80 ssl_free(&mSSL);
81 mErrorState = ErrUnknown;
82 return ret;
83 }
84
85 ret = x509_crt_parse(&mX509PinnedCert,
86 (const unsigned char*) mPinnedCert.constData(),
87 mPinnedCert.size());
88 if (ret != 0){
89 ssl_free(&mSSL);
90 mErrorState = InvalidPinnedCertificate;
91 return ret;
92 }
93
94 ssl_set_endpoint(&mSSL, SSL_IS_CLIENT);
95 ssl_set_authmode(&mSSL, SSL_VERIFY_OPTIONAL);
96 ssl_set_ca_chain(&mSSL, &mX509PinnedCert, NULL, NULL);
97 ssl_set_renegotiation(&mSSL, SSL_RENEGOTIATION_DISABLED);
98 ssl_set_rng(&mSSL, ctr_drbg_random, &mCtr_drbg);
99 #ifdef RELEASE_BUILD
100 ssl_set_min_version(&mSSL, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3);
101 #endif
102
103 #ifdef CONNECTION_DEBUG
104 ssl_set_dbg(&mSSL, my_debug, stdout);
105 #endif
106
107 return 0;
108 }
109
110 SSLConnectionBare::~SSLConnectionBare() {
111 disconnect();
112 x509_crt_free(&mX509PinnedCert);
113 entropy_free(&mEntropy);
114 if (mInitialized) {
115 ssl_free(&mSSL);
116 }
117 }
118
119 void SSLConnectionBare::disconnect() {
120 if (mConnected) {
121 ssl_close_notify(&mSSL);
122 if (mServerFD != -1) {
123 net_close(mServerFD);
124 mServerFD = -1;
125 }
126 ssl_session_free(&mSavedSession);
127 mConnected = false;
128 }
129 }
130
131 int SSLConnectionBare::connect() {
132 int ret = -1;
133
134 if (!mInitialized) {
135 mErrorState = ErrUnknown;
136 return -1;
137 }
138
139 ret = net_connect(&mServerFD, mUrl.host().toLatin1().constData(),
140 mUrl.port(443));
141
142 if (ret != 0) {
143 qDebug() << "Connect failed: " << getPolarSSLErrorMsg(ret);
144 mErrorState = NoConnection;
145 return ret;
146 }
147
148 ssl_set_bio(&mSSL, net_recv, &mServerFD,
149 net_send, &mServerFD);
150
151 while ((ret = ssl_handshake(&mSSL)) != 0) {
152 if (ret != POLARSSL_ERR_NET_WANT_READ &&
153 ret != POLARSSL_ERR_NET_WANT_WRITE) {
154 qDebug() << "SSL Handshake failed: " << getPolarSSLErrorMsg(ret);
155 mErrorState = SSLHandshakeFailed;
156 return ret;
157 }
158 }
159
160 ret = ssl_get_session(&mSSL, &mSavedSession);
161 if (ret != 0) {
162 qDebug() << "SSL get session failed: " << getPolarSSLErrorMsg(ret);
163
164 mErrorState = NoConnection;
165 return ret;
166 }
167 printf( " ok\n [ Ciphersuite is %s ]\n",
168 ssl_get_ciphersuite( &mSSL) );
169 ret = validateCertificate();
170
171 if (ret == 0) {
172 mConnected = true;
173 }
174 return ret;
175 }
176
177 int SSLConnectionBare::validateCertificate()
178 {
179 int ret = -1;
180 const x509_crt *peerCert = NULL;
181
182 /* we might want to set the verify function
183 * with ssl_set_verify before to archive the
184 * certificate pinning. */
185
186 ret = ssl_get_verify_result(&mSSL);
187
188 if (ret != 0 ) {
189 if((ret & BADCERT_EXPIRED) != 0)
190 qDebug() << "server certificate has expired";
191 if((ret & BADCERT_REVOKED) != 0)
192 qDebug() << "server certificate has been revoked";
193 if((ret & BADCERT_CN_MISMATCH) != 0)
194 qDebug() << "CN mismatch";
195 if((ret & BADCERT_NOT_TRUSTED) != 0)
196 qDebug() << "self-signed or not signed by a trusted CA";
197 ret = -1;
198 #ifdef RELEASE_BUILD
199 mErrorState = InvalidCertificate;
200 return -1;
201 #endif
202 }
203
204 peerCert = ssl_get_peer_cert(&mSSL);
205
206 if (!peerCert) {
207 mErrorState = InvalidCertificate;
208 qDebug() << "Failed to get peer cert";
209 return -1;
210 }
211
212 if (peerCert->raw.len == 0 ||
213 peerCert->raw.len != mX509PinnedCert.raw.len) {
214 mErrorState = InvalidCertificate;
215 qDebug() << "Certificate length mismatch";
216 return -1;
217 }
218
219 /* You can never be sure what those c++ operators do..
220 if (mPinnedCert != QByteArray::fromRawData(
221 (const char*) peerCert->raw.p,
222 peerCert->raw.len)) {
223 qDebug() << "Certificate content mismatch";
224 }
225 */
226
227 for (unsigned int i = 0; i < peerCert->raw.len; i++) {
228 if (peerCert->raw.p[i] != mX509PinnedCert.raw.p[i]) {
229 qDebug() << "Certificate content mismatch";
230 mErrorState = InvalidCertificate;
231 return -1;
232 }
233 }
234 return 0;
235 }
236
237 int SSLConnectionBare::write (const QByteArray& request)
238 {
239 unsigned int tries = 0;
240 int ret = -1;
241
242 const unsigned char *buf = (const unsigned char *) request.constData();
243 size_t len = (size_t) request.size();
244
245 if (mNeedsReset) {
246 ret = reset();
247 if (ret != 0) {
248 qDebug() << "Reset failed: " << getPolarSSLErrorMsg(ret);
249 return ret;
250 }
251 }
252
253 qDebug() << "Sending request: " << request;
254 /* According to doc for ssl_write:
255 *
256 * When this function returns POLARSSL_ERR_NET_WANT_WRITE,
257 * it must be called later with the same arguments,
258 * until it returns a positive value.
259 */
260 do {
261 ret = ssl_write(&mSSL, buf, len);
262 if (ret >= 0) {
263 if ((unsigned int) ret == len) {
264 return 0;
265 } else {
266 qDebug() << "Write failed to write everything";
267 return -1;
268 }
269 }
270 if (ret != POLARSSL_ERR_NET_WANT_WRITE &&
271 ret != POLARSSL_ERR_NET_WANT_READ) {
272 return ret;
273 }
274 tries++;
275 net_usleep(100000); /* sleep 100ms to give the socket a chance
276 to clean up. */
277 } while (tries < MAX_IO_TRIES);
278
279 return ret;
280 }
281
282
283 int SSLConnectionBare::reset()
284 {
285 int ret = -1;
286 ssl_close_notify(&mSSL);
287
288 ret = ssl_session_reset(&mSSL);
289 if (ret != 0)
290 {
291 qDebug() << "SSL Connection reset failed: "
292 << getPolarSSLErrorMsg(ret);
293 return ret;
294 }
295
296 ssl_set_session(&mSSL, &mSavedSession);
297
298 ret = net_connect(&mServerFD, mUrl.host().toLatin1().constData(),
299 mUrl.port(443));
300
301 if (ret != 0) {
302 mErrorState = NoConnection;
303 qDebug() << "Connection failed." << getPolarSSLErrorMsg(ret);
304 return ret;
305 }
306
307 while ((ret = ssl_handshake(&mSSL)) != 0) {
308 if (ret != POLARSSL_ERR_NET_WANT_READ &&
309 ret != POLARSSL_ERR_NET_WANT_WRITE) {
310 qDebug() << "SSL Handshake failed: "
311 << getPolarSSLErrorMsg(ret);
312 mErrorState = SSLHandshakeFailed;
313 return ret;
314 }
315 }
316
317 qDebug() << "Reset connection. ";
318 /* Validation should not be necessary as we reused a saved
319 * session. But just to be sure. */
320 return validateCertificate();
321 }
322
323 QByteArray SSLConnectionBare::read(size_t len)
324 {
325 unsigned char buf[len];
326 QByteArray retval("");
327 int ret = -1;
328 unsigned int tries = 0;
329
330 mNeedsReset = true;
331 do {
332 memset (buf, 0, sizeof(buf));
333 ret = ssl_read(&mSSL, buf, len);
334 if (ret == 0 ||
335 ret == POLARSSL_ERR_SSL_CONN_EOF ||
336 ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY) {
337 /* EOF */
338 return retval;
339 }
340 if (ret == POLARSSL_ERR_NET_WANT_WRITE ||
341 ret == POLARSSL_ERR_NET_WANT_READ) {
342 net_usleep(100000); /* sleep 100ms to give the socket a chance
343 to recover */
344 tries++;
345 }
346 if (ret <= 0) {
347 qDebug() << "Read failed: " << getPolarSSLErrorMsg(ret);
348 return QByteArray();
349 }
350 if (len < (len - (unsigned int) ret)) {
351 /* Should never happen if ssl_read behaves */
352 qDebug() << "integer overflow in polarSSLRead";
353 return QByteArray();
354 }
355 len -= (unsigned int) ret;
356 retval.append((const char *)buf, ret);
357 } while (len > 0 && tries < MAX_IO_TRIES);
358
359 return retval;
360 }
361
362

http://wald.intevation.org/projects/trustbridge/