Mercurial > trustbridge
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 |