comparison ui/sslconnection.cpp @ 45:c6125d73faf4

Move SSLConnection into it's own class
author Andre Heinecke <aheinecke@intevation.de>
date Fri, 14 Mar 2014 16:40:53 +0000
parents
children d28e2624c1d5
comparison
equal deleted inserted replaced
44:b3e8e047bc2c 45:c6125d73faf4
1 /* TODO: Wrap ssl_session in a class for reuse.
2 * see programs/ssl/ssl_client2.c for example of session reuse */
3 #include "sslconnection.h"
4
5 #include <QFile>
6 #include <QUuid>
7 #include <QApplication>
8
9 #define MAX_IO_TRIES 10
10
11 #ifdef CONNECTION_DEBUG
12 static void my_debug(void *ctx, int level, const char *str)
13 {
14 fprintf((FILE *) ctx, "%s", str);
15 fflush((FILE *) ctx);
16 }
17 #endif
18
19 QString getErrorMsg(int ret)
20 {
21 char errbuf[255];
22 polarssl_strerror(ret, errbuf, 255);
23 errbuf[254] = '\0'; /* Just to be sure */
24 return QString::fromLatin1(errbuf);
25 }
26
27 SSLConnection::SSLConnection(const QString& url,
28 const QByteArray& certificate):
29 mUrl(url),
30 mPinnedCert(certificate),
31 mInitialized(false),
32 mConnected(false),
33 mServerFD(-1),
34 mErrorState(NoError)
35 {
36 int ret = -1;
37
38 memset(&mSSL, 0, sizeof(ssl_context));
39
40 if (certificate.isEmpty()) {
41 QFile certResource(":certs/kolab.org");
42 certResource.open(QFile::ReadOnly);
43 mPinnedCert = certResource.readAll();
44 certResource.close();
45 }
46
47 ret = init();
48 if (ret == 0) {
49 mInitialized = true;
50 } else {
51 qDebug() << "Initialization error: " + getErrorMsg(ret);
52 }
53 }
54
55 int SSLConnection::init()
56 {
57 int ret = -1;
58 QUuid uuid = QUuid::createUuid();
59 QString personalString = QApplication::applicationName() + uuid.toString();
60 QByteArray personalBa = personalString.toLocal8Bit();
61
62 x509_crt_init(&mX509PinnedCert);
63 entropy_init(&mEntropy);
64
65 ret = ssl_init(&mSSL);
66 if (ret != 0) {
67 /* The only documented error is malloc failed */
68 mErrorState = ErrUnknown;
69 return ret;
70 }
71
72 /*
73 * Initialize random generator.
74 * Personalisation string, does not need to be random but
75 * should be unique according to documentation.
76 *
77 * the ctr_drbg structure does not need to be freed explicitly.
78 */
79 ret = ctr_drbg_init(&mCtr_drbg, entropy_func, &mEntropy,
80 (const unsigned char*) personalBa.constData(),
81 personalBa.size());
82 if (ret != 0) {
83 ssl_free(&mSSL);
84 mErrorState = ErrUnknown;
85 return ret;
86 }
87
88 ret = x509_crt_parse(&mX509PinnedCert,
89 (const unsigned char*) mPinnedCert.constData(),
90 mPinnedCert.size());
91 if (ret != 0){
92 ssl_free(&mSSL);
93 mErrorState = InvalidPinnedCertificate;
94 return ret;
95 }
96
97 ssl_set_endpoint(&mSSL, SSL_IS_CLIENT);
98 ssl_set_authmode(&mSSL, SSL_VERIFY_OPTIONAL);
99 ssl_set_ca_chain(&mSSL, &mX509PinnedCert, NULL, NULL);
100 ssl_set_renegotiation(&mSSL, SSL_RENEGOTIATION_DISABLED);
101 ssl_set_rng(&mSSL, ctr_drbg_random, &mCtr_drbg);
102 #ifdef RELEASE_BUILD
103 ssl_set_min_version(&mSSL, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3);
104 #endif
105
106 #ifdef CONNECTION_DEBUG
107 ssl_set_dbg(&mSSL, my_debug, stdout);
108 #endif
109
110 return 0;
111 }
112
113 SSLConnection::~SSLConnection() {
114 if (mConnected) {
115 ssl_close_notify (&mSSL);
116 if (mServerFD != -1) {
117 net_close(mServerFD);
118 mServerFD = -1;
119 }
120 }
121 x509_crt_free(&mX509PinnedCert);
122 entropy_free(&mEntropy);
123 if (mInitialized) {
124 ssl_free(&mSSL);
125 }
126 }
127
128 int SSLConnection::connect() {
129 int ret = -1;
130 const x509_crt *peerCert;
131
132 if (!mInitialized) {
133 mErrorState = ErrUnknown;
134 return -1;
135 }
136
137 ret = net_connect(&mServerFD, mUrl.host().toLatin1().constData(),
138 mUrl.port(443));
139
140 if (ret != 0) {
141 mErrorState = NoConnection;
142 return ret;
143 }
144
145 ssl_set_bio(&mSSL, net_recv, &mServerFD,
146 net_send, &mServerFD);
147
148 while ((ret = ssl_handshake(&mSSL)) != 0) {
149 if (ret != POLARSSL_ERR_NET_WANT_READ &&
150 ret != POLARSSL_ERR_NET_WANT_WRITE) {
151 qDebug() << "SSL Handshake failed: "
152 << getErrorMsg(ret);
153 mErrorState = SSLHandshakeFailed;
154 return ret;
155 }
156 }
157
158 /* we might want to set the verify function
159 * with ssl_set_verify before to archive the
160 * certificate pinning. */
161
162 ret = ssl_get_verify_result(&mSSL);
163
164 if (ret != 0 ) {
165 if((ret & BADCERT_EXPIRED) != 0)
166 qDebug() << "server certificate has expired";
167 if((ret & BADCERT_REVOKED) != 0)
168 qDebug() << "server certificate has been revoked";
169 if((ret & BADCERT_CN_MISMATCH) != 0)
170 qDebug() << "CN mismatch";
171 if((ret & BADCERT_NOT_TRUSTED) != 0)
172 qDebug() << "self-signed or not signed by a trusted CA";
173 ret = -1;
174 #ifdef RELEASE_BUILD
175 mErrorState = InvalidCertificate;
176 return -1;
177 #endif
178 }
179
180 peerCert = ssl_get_peer_cert(&mSSL);
181
182 if (!peerCert) {
183 mErrorState = InvalidCertificate;
184 qDebug() << "Failed to get peer cert";
185 return -1;
186 }
187
188 if (peerCert->raw.len == 0 ||
189 peerCert->raw.len != mX509PinnedCert.raw.len) {
190 mErrorState = InvalidCertificate;
191 qDebug() << "Certificate length mismatch";
192 return -1;
193 }
194
195 /* You can never be sure what those c++ operators do..
196 if (mPinnedCert != QByteArray::fromRawData(
197 (const char*) peerCert->raw.p,
198 peerCert->raw.len)) {
199 qDebug() << "Certificate content mismatch";
200 }
201 */
202
203 for (unsigned int i = 0; i < peerCert->raw.len; i++) {
204 if (peerCert->raw.p[i] != mX509PinnedCert.raw.p[i]) {
205 qDebug() << "Certificate content mismatch";
206 mErrorState = InvalidCertificate;
207 return -1;
208 }
209 }
210 return 0;
211 }
212
213 int SSLConnection::write (const QByteArray& request)
214 {
215 unsigned int tries = 0;
216 int ret = -1;
217
218 const unsigned char *buf = (const unsigned char *) request.constData();
219 size_t len = (size_t) request.size();
220
221 qDebug() << "Seinding request: " << request;
222 /* According to doc for ssl_write:
223 *
224 * When this function returns POLARSSL_ERR_NET_WANT_WRITE,
225 * it must be called later with the same arguments,
226 * until it returns a positive value.
227 */
228 do {
229 ret = ssl_write(&mSSL, buf, len);
230 if (ret >= 0) {
231 if ((unsigned int) ret == len) {
232 return 0;
233 } else {
234 qDebug() << "Write failed to write everything";
235 return -1;
236 }
237 }
238
239 if (ret != POLARSSL_ERR_NET_WANT_WRITE) {
240 return ret;
241 }
242 tries++;
243 net_usleep(100000); /* sleep 100ms to give the socket a chance
244 to clean up. */
245 } while (tries < MAX_IO_TRIES);
246
247 return ret;
248 }
249
250 QByteArray SSLConnection::read(size_t len)
251 {
252 unsigned char buf[len];
253 QByteArray retval("");
254 int ret = -1;
255
256 do {
257 memset (buf, 0, sizeof(buf));
258 ret = ssl_read(&mSSL, buf, len);
259 if (ret == 0 ||
260 ret == POLARSSL_ERR_SSL_CONN_EOF) {
261 /* EOF */
262 return retval;
263 }
264 if (ret <= 0) {
265 qDebug() << "Read failed: " << getErrorMsg(ret);
266 return QByteArray();
267 }
268 if (len < (len - (unsigned int) ret)) {
269 /* Should never happen if ssl_read behaves */
270 qDebug() << "integer overflow in polarSSLRead";
271 return QByteArray();
272 }
273 len -= (unsigned int) ret;
274 retval.append((const char *)buf, len);
275 } while (len > 0);
276
277 return retval;
278 }
279

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