1 /*
2 * EasySoap++ - A C++ library for SOAP (Simple Object Access Protocol)
3 * Copyright (C) 2001 David Crowley; SciTegic, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 * $Id: SOAPSecureSocketImp.cpp,v 1.42 2005/09/24 07:24:24 dcrowley Exp $
20 */
21
22
23 #ifdef _MSC_VER
24 #pragma warning (disable: 4786)
25 #endif // _MSC_VER
26
27 #include <easysoap/SOAP.h>
28 #include <easysoap/SOAPDebugger.h>
29 #include <easysoap/SOAPSSLContext.h>
30
31 #include "SOAPSecureSocketImp.h"
32
33 USING_EASYSOAP_NAMESPACE
34
35 #ifndef HAVE_LIBSSL
36
37 void
NotSupported()38 SOAPSecureSocketImp::NotSupported()
39 {
40 throw SOAPSocketException("Secure sockets not supported.");
41 }
42
SOAPSecureSocketImp()43 SOAPSecureSocketImp::SOAPSecureSocketImp()
44 {
45 NotSupported();
46 }
47
SOAPSecureSocketImp(SOAPSSLContext &,void *)48 SOAPSecureSocketImp::SOAPSecureSocketImp(SOAPSSLContext& /*ctx*/, void* /*cbdata*/)
49 {
50 NotSupported();
51 }
52
~SOAPSecureSocketImp()53 SOAPSecureSocketImp::~SOAPSecureSocketImp()
54 {
55 }
56
WaitRead(int,int)57 bool SOAPSecureSocketImp::WaitRead(int, int) {return false;}
WaitWrite(int,int)58 bool SOAPSecureSocketImp::WaitWrite(int, int) { return false; }
Close()59 void SOAPSecureSocketImp::Close() { }
IsOpen()60 bool SOAPSecureSocketImp::IsOpen() { return false; }
Connect(const char *,unsigned int,bool)61 bool SOAPSecureSocketImp::Connect(const char *, unsigned int, bool /*client*/) {return false;}
Read(char *,size_t)62 size_t SOAPSecureSocketImp::Read(char *, size_t) {return 0;}
Write(const char *,size_t)63 size_t SOAPSecureSocketImp::Write(const char *, size_t) {return 0;}
InitSSL()64 void SOAPSecureSocketImp::InitSSL() {}
VerifyCert(const char *)65 void SOAPSecureSocketImp::VerifyCert(const char*) { }
66
67 #else // HAVE_LIBSSL
68
69 #ifdef HAVE_ERRNO_H
70 #include <errno.h>
71 #endif
72
73 #ifdef HAVE_STRING_H
74 #include <string.h>
75 #endif
76
77 extern "C" {
78 #include <openssl/ssl.h>
79 #include <openssl/err.h>
80 #include <openssl/rand.h>
81 }
82
83 SOAPSecureSocketImp::SOAPSecureSocketImp()
84 : m_ssl(0)
85 , m_delctx(true)
86 , m_cbdata(0)
87 {
88 m_context = new SOAPSSLContext;
89 }
90
91 SOAPSecureSocketImp::SOAPSecureSocketImp(SOAPSSLContext& ctx, void* cbdata)
92 : m_ssl(0)
93 , m_context(&ctx)
94 , m_delctx(false)
95 , m_cbdata(cbdata)
96 {
97 }
98
99 SOAPSecureSocketImp::~SOAPSecureSocketImp()
100 {
101 if (m_delctx)
102 delete m_context;
103 Close();
104 }
105
106 bool
107 SOAPSecureSocketImp::HandleError(const char *context, int retcode, bool shouldWait)
108 {
109 // we may have an error.
110 // we need to call SSL_get_error()
111 bool retry = false;
112 char tmp[2048];
113 memset(&tmp, 0, sizeof(tmp));
114 unsigned long err = SSL_get_error(m_ssl, retcode);
115 switch (err)
116 {
117 case SSL_ERROR_NONE:
118 SOAPDebugger::Print(2, "%s: SSL_ERROR_NONE caught. retcode = %d\r\n", context, retcode);
119 break;
120
121 case SSL_ERROR_ZERO_RETURN:
122 SOAPDebugger::Print(2, "%s: SSL_ERROR_ZERO_RETURN caught\r\n", context);
123 Close();
124 break;
125
126 case SSL_ERROR_WANT_WRITE:
127 SOAPDebugger::Print(2, "%s: SSL_ERROR_WANT_WRITE caught\r\n", context);
128 if (shouldWait) {
129 WaitWrite();
130 }
131 retry = true;
132 break;
133 case SSL_ERROR_WANT_READ:
134 SOAPDebugger::Print(2, "%s: SSL_ERROR_WANT_READ caught\r\n", context);
135 if (shouldWait) {
136 WaitRead();
137 }
138 retry = true;
139 break;
140 case SSL_ERROR_WANT_X509_LOOKUP:
141 SOAPDebugger::Print(2, "%s: SSL_ERROR_WANT_X509_LOOKUP caught\r\n", context);
142 retry = true;
143 break;
144
145 case SSL_ERROR_SYSCALL:
146 SOAPDebugger::Print(2, "%s: SSL_ERROR_SYSCALL caught\r\n", context);
147 if (retcode == 0)
148 {
149 // premature EOF, but not necessarily an error
150 SOAPDebugger::Print(2, "%s: premature close on socket\r\n", context);
151 break;
152 }
153 if (retcode == -1)
154 {
155 #ifdef HAVE_STRERROR
156 sp_strncpy(tmp, strerror(errno), sizeof(tmp));
157 #else // dont HAVE_STRERROR
158 snprintf(tmp, sizeof(tmp), "socket error, errno=%d\r\n", errno);
159 tmp[sizeof(tmp) - 1] = 0;
160 #endif // HAVE_STRERROR
161 throw SOAPSSLException(context, tmp);
162 }
163 break;
164
165
166 case SSL_ERROR_SSL:
167 default:
168 SOAPString msg;
169 if (ERR_peek_error()) {
170 unsigned long sslerror = ERR_get_error();
171 msg = "An SSL error occured. Here are the contents of the SSL error queue";
172 for ( ; sslerror != 0 ; sslerror = ERR_get_error())
173 {
174
175 #if OPENSSL_VERSION_NUMBER >= 0x00906000L
176 ERR_error_string_n(sslerror, tmp, sizeof(tmp) - 1);
177 #else
178 // dangerous
179 ERR_error_string(sslerror, tmp);
180 tmp[sizeof(tmp) - 1] = 0;
181 #endif // OPENSSL_VERSION_NUMBER
182 SOAPDebugger::Print(2, "Error handled.\r\ncontext: %s\r\nMsg: %s\r\n", context, tmp);
183 msg += tmp;
184 msg += ". "; // create some space to make it readable
185 }
186 } else {
187 msg = "Unkown error";
188 }
189 throw SOAPSSLException(context, msg.Str());
190 }
191 return retry;
192
193 }
194
195 void
196 SOAPSecureSocketImp::InitSSL()
197 {
198 //
199 // set up SSL
200 //
201 m_ssl = SSL_new(m_context->GetContext());
202 if (!m_ssl)
203 throw SOAPMemoryException();
204
205 SSL_set_connect_state(m_ssl);
206 int retcode;
207
208 if ((retcode = SSL_set_fd(m_ssl, m_socket.GetRawSocketHandle())) != 1)
209 HandleError("Error attaching security layer to socket : %s\r\n", retcode, false);
210
211 bool retry = false;
212 do
213 {
214 if ((retcode = SSL_connect(m_ssl)) != 1) {
215 retry = HandleError("Error negotiating secure connection : %s\r\n", retcode, true);
216 }
217 } while(retry);
218 }
219
220 const char*
221 SOAPSecureSocketImp::CheckForCertError(int rc) {
222 const char *msg = 0;
223 if (!m_context->IgnoreCertError(rc)) {
224 switch(rc) {
225 case X509_V_OK:
226 // successful certificate verification
227 break;
228 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
229 msg = "the issuer certificate could not be found";
230 break;
231 case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
232 msg = "the certificate signature could not be decrypted.";
233 break;
234 case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
235 msg = "the public key in the certificate SubjectPublicKeyInfo could not be read.";
236 break;
237 case X509_V_ERR_CERT_SIGNATURE_FAILURE:
238 msg = "the signature of the certificate is invalid.";
239 break;
240 case X509_V_ERR_CERT_NOT_YET_VALID:
241 msg = " the certificate is not yet valid. ";
242 break;
243 case X509_V_ERR_CERT_HAS_EXPIRED:
244 msg = " the certificate has expired.";
245 break;
246 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
247 msg = "the certificate notBefore field contains an invalid time.";
248 break;
249 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
250 msg = " the certificate notAfter field contains an invalid time.";
251 break;
252 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
253 msg = "the passed certificate is self signed and the same certificate cannot be found in the list of trusted certificates.";
254 break;
255 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
256 msg = "self signed certificate in certificate chain.";
257 break;
258 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
259 msg = "unable to get local issuer certificate. ";
260 break;
261 case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
262 msg = " unable to verify the first certificate.";
263 break;
264 case X509_V_ERR_INVALID_CA:
265 msg = "Invalid CA certificate.";
266 break;
267 case X509_V_ERR_PATH_LENGTH_EXCEEDED:
268 msg = "Certificate chain too long.";
269 break;
270 case X509_V_ERR_INVALID_PURPOSE:
271 msg = "Unsupported certificate purpose.";
272 break;
273 case X509_V_ERR_CERT_UNTRUSTED:
274 msg = "the root CA is not marked as trusted for the specified purpose.";
275 break;
276 case X509_V_ERR_CERT_REJECTED:
277 msg = "the root CA is marked to reject the specified purpose.";
278 break;
279 #ifdef X509_V_ERR_KEYUSAGE_NO_CERTSIGN
280 case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
281 msg = "key usage does not include certificate signing.";
282 break;
283 #endif
284 default:
285 msg = "Server certificate verification failed due to an unknown error";
286 break;
287 }
288 }
289 return msg;
290 }
291
292 void
293 SOAPSecureSocketImp::VerifyCert(const char* host)
294 {
295 X509* server_cert = SSL_get_peer_certificate(m_ssl);
296 if (!server_cert)
297 throw SOAPSSLException("Error getting server certificate.");
298
299 try {
300 int rc = SSL_get_verify_result(m_ssl);
301
302 const char *msg = CheckForCertError(rc);
303 if (msg)
304 throw SOAPSSLException("Error verifying peer certificate: %s", msg);
305
306 SOAPSSLContext::VerifyPeerCallback cb = m_context->GetVerifyPeerCallback();
307 if (cb) {
308 if (!cb(server_cert, m_cbdata)) {
309 throw SOAPSSLException("Server certificate failed callback verification");
310 }
311 }
312 else {
313 char buf[256];
314 X509_NAME_get_text_by_NID(X509_get_subject_name(server_cert),
315 NID_commonName, buf, sizeof(buf));
316 if (sp_strcasecmp(buf, host))
317 throw SOAPSSLException("Server certificate hostname does not match (%s != %s)", buf, host);
318 }
319 }
320 catch(...) {
321 X509_free(server_cert);
322 throw;
323 }
324
325 X509_free(server_cert);
326 }
327
328 bool
329 SOAPSecureSocketImp::WaitRead(int sec, int usec)
330 {
331 if (!m_ssl)
332 return m_socket.WaitRead(sec, usec);
333
334 if (SSL_pending(m_ssl) > 0)
335 return true;
336
337 // we have to wait...
338 m_socket.Wait(sec, usec);
339 return SSL_pending(m_ssl) > 0;
340 }
341
342 bool
343 SOAPSecureSocketImp::WaitWrite(int sec, int usec)
344 {
345 // not sure if this is correct or not
346 return m_socket.WaitWrite(sec, usec);
347
348 }
349 bool
350 SOAPSecureSocketImp::IsOpen()
351 {
352 SOAPDebugger::Print(5, "SOAPSecureSocketImp::IsOpen()");
353 // more to be done?
354 return m_socket.IsOpen();
355 }
356
357 bool
358 SOAPSecureSocketImp::Connect(const char *host, unsigned int port, bool client)
359 {
360 SOAPDebugger::Print(5, "SOAPSecureSocketImp::Connect()\r\n");
361 if (!m_socket.Connect(host, port))
362 return false;
363
364 InitSSL();
365 if (m_context->VerifyServerCert())
366 VerifyCert(host);
367
368 SOAPDebugger::Print(5, "Connected to %s:%d \r\n", host, port);
369 return true;
370 }
371
372
373 size_t
374 SOAPSecureSocketImp::Read(char *buff, size_t bufflen)
375 {
376 if (!m_ssl)
377 return m_socket.Read(buff, bufflen);
378
379 int bytes = 0;
380 if (bufflen > 0)
381 {
382 bool retry = false;
383 do
384 {
385 bytes = SSL_read(m_ssl, buff, bufflen);
386 SOAPDebugger::Print(2, "SRECV: %d bytes\r\n", bytes);
387
388 if (bytes > 0)
389 {
390 // good, we read some bytes.
391 retry = false;
392 }
393 else
394 {
395 // check for an error
396 SOAPDebugger::Print(2, "About to call HandleError...\r\n");
397 retry = HandleError("Error reading from secure socket", bytes, false);
398 bytes = 0;
399 }
400 } while (retry);
401 SOAPDebugger::Write(1, buff, bytes);
402 }
403 return bytes;
404 }
405
406 size_t
407 SOAPSecureSocketImp::Write(const char *buff, size_t bufflen)
408 {
409 if (!m_ssl)
410 return m_socket.Write(buff, bufflen);
411
412 if (bufflen > 0)
413 {
414 int bytes = 0;
415 bool retry = false;
416 do
417 {
418 bytes = SSL_write(m_ssl, buff, bufflen);
419 SOAPDebugger::Print(2, "SSEND: %d bytes\n", bytes);
420
421 if (bytes > 0)
422 {
423 if ((unsigned int)bytes != bufflen)
424 throw SOAPSocketException("Error writing to secure socket, "
425 "expected to write %d bytes, wrote %d bytes",
426 bufflen, bytes);
427 retry = false;
428 }
429 else
430 {
431 // check for an error
432 retry = HandleError("Error writing to secure socket\r\n", bytes, false);
433 bytes = 0;
434 }
435 } while (retry);
436
437 SOAPDebugger::Write(1, buff, bufflen);
438 return bytes;
439 }
440 return 0;
441 }
442
443 void
444 SOAPSecureSocketImp::Close()
445 {
446 SOAPDebugger::Print(5, "SOAPSecureSocketImp::Close()\r\n");
447 if (m_ssl)
448 SSL_shutdown(m_ssl);
449
450 m_socket.Close();
451
452 if (m_ssl)
453 {
454 SSL_free(m_ssl);
455 m_ssl = 0;
456 }
457
458 }
459
460 #endif // HAVE_LIBSSL
461
462