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