ICE 3.4.2
[php5-ice-freebsdport.git] / cpp / src / IceSSL / Instance.cpp
blob5eb4c58c731e9ec3ca5bbf7f8def6ef6d9992fb3
1 // **********************************************************************
2 //
3 // Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.
4 //
5 // This copy of Ice is licensed to you under the terms described in the
6 // ICE_LICENSE file included in this distribution.
7 //
8 // **********************************************************************
10 #include <IceUtil/Config.h>
11 #ifdef _WIN32
12 # include <winsock2.h>
13 #endif
15 #include <IceSSL/Instance.h>
16 #include <IceSSL/EndpointI.h>
17 #include <IceSSL/Util.h>
18 #include <IceSSL/TrustManager.h>
20 #include <Ice/Communicator.h>
21 #include <Ice/LocalException.h>
22 #include <Ice/Logger.h>
23 #include <Ice/LoggerUtil.h>
24 #include <Ice/Properties.h>
25 #include <Ice/ProtocolPluginFacade.h>
26 #include <Ice/StringConverter.h>
28 #include <IceUtil/Mutex.h>
29 #include <IceUtil/MutexPtrLock.h>
30 #include <IceUtil/StringUtil.h>
32 #include <openssl/rand.h>
33 #include <openssl/err.h>
35 #include <IceUtil/DisableWarnings.h>
37 using namespace std;
38 using namespace Ice;
39 using namespace IceSSL;
41 IceUtil::Shared* IceInternal::upCast(IceSSL::Instance* p) { return p; }
43 namespace
46 IceUtil::Mutex* staticMutex = 0;
47 int instanceCount = 0;
48 IceUtil::Mutex* locks = 0;
50 class Init
52 public:
54 Init()
56 staticMutex = new IceUtil::Mutex;
59 ~Init()
61 delete staticMutex;
62 staticMutex = 0;
64 if(locks)
66 delete[] locks;
67 locks = 0;
72 Init init;
76 extern "C"
80 // OpenSSL mutex callback.
82 void
83 IceSSL_opensslLockCallback(int mode, int n, const char* file, int line)
85 assert(locks);
86 if(mode & CRYPTO_LOCK)
88 locks[n].lock();
90 else
92 locks[n].unlock();
97 // OpenSSL thread id callback.
99 unsigned long
100 IceSSL_opensslThreadIdCallback()
102 #if defined(_WIN32)
103 return static_cast<unsigned long>(GetCurrentThreadId());
104 #elif defined(__FreeBSD__) || defined(__APPLE__) || defined(__osf1__)
106 // On some platforms, pthread_t is a pointer to a per-thread structure.
108 return reinterpret_cast<unsigned long>(pthread_self());
109 #elif (defined(__linux) || defined(__sun) || defined(__hpux)) || defined(_AIX)
111 // On Linux, Solaris, HP-UX and AIX, pthread_t is an integer.
113 return static_cast<unsigned long>(pthread_self());
114 #else
115 # error "Unknown platform"
116 #endif
120 IceSSL_opensslPasswordCallback(char* buf, int size, int flag, void* userData)
122 IceSSL::Instance* p = reinterpret_cast<IceSSL::Instance*>(userData);
123 string passwd = p->password(flag == 1);
124 int sz = static_cast<int>(passwd.size());
125 if(sz > size)
127 sz = size - 1;
129 strncpy(buf, passwd.c_str(), sz);
130 buf[sz] = '\0';
132 for(string::iterator i = passwd.begin(); i != passwd.end(); ++i)
134 *i = '\0';
137 return sz;
140 #ifndef OPENSSL_NO_DH
142 IceSSL_opensslDHCallback(SSL* ssl, int /*isExport*/, int keyLength)
144 IceSSL::Instance* p = reinterpret_cast<IceSSL::Instance*>(SSL_CTX_get_ex_data(ssl->ctx, 0));
145 return p->dhParams(keyLength);
147 #endif
150 IceSSL_opensslVerifyCallback(int ok, X509_STORE_CTX* ctx)
152 SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
153 IceSSL::Instance* p = reinterpret_cast<IceSSL::Instance*>(SSL_CTX_get_ex_data(ssl->ctx, 0));
154 return p->verifyCallback(ok, ssl, ctx);
159 static bool
160 passwordError()
162 int reason = ERR_GET_REASON(ERR_peek_error());
163 return (reason == PEM_R_BAD_BASE64_DECODE ||
164 reason == PEM_R_BAD_DECRYPT ||
165 reason == PEM_R_BAD_PASSWORD_READ ||
166 reason == PEM_R_PROBLEMS_GETTING_PASSWORD);
169 IceSSL::Instance::Instance(const CommunicatorPtr& communicator) :
170 _logger(communicator->getLogger()),
171 _initialized(false),
172 _ctx(0)
174 __setNoDelete(true);
177 // Initialize OpenSSL if necessary.
179 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(staticMutex);
180 instanceCount++;
182 if(instanceCount == 1)
184 PropertiesPtr properties = communicator->getProperties();
187 // Create the mutexes and set the callbacks.
189 if(!locks)
191 locks = new IceUtil::Mutex[CRYPTO_num_locks()];
192 CRYPTO_set_locking_callback(IceSSL_opensslLockCallback);
193 CRYPTO_set_id_callback(IceSSL_opensslThreadIdCallback);
197 // Load human-readable error messages.
199 SSL_load_error_strings();
202 // Initialize the SSL library.
204 SSL_library_init();
207 // This is necessary to allow programs that use OpenSSL 0.9.x to
208 // load private key files generated by OpenSSL 1.x.
210 OpenSSL_add_all_algorithms();
213 // Initialize the PRNG.
215 #ifdef WINDOWS
216 RAND_screen(); // Uses data from the screen if possible.
217 #endif
218 char randFile[1024];
219 if(RAND_file_name(randFile, sizeof(randFile))) // Gets the name of a default seed file.
221 RAND_load_file(randFile, 1024);
223 string randFiles = Ice::nativeToUTF8(communicator, properties->getProperty("IceSSL.Random"));
225 if(!randFiles.empty())
227 vector<string> files;
228 #ifdef _WIN32
229 const string sep = ";";
230 #else
231 const string sep = ":";
232 #endif
233 string defaultDir = Ice::nativeToUTF8(communicator, properties->getProperty("IceSSL.DefaultDir"));
235 if(!IceUtilInternal::splitString(randFiles, sep, files))
237 PluginInitializationException ex(__FILE__, __LINE__);
238 ex.reason = "IceSSL: invalid value for IceSSL.Random:\n" + randFiles;
239 throw ex;
241 for(vector<string>::iterator p = files.begin(); p != files.end(); ++p)
243 string file = *p;
244 if(!checkPath(file, defaultDir, false))
246 PluginInitializationException ex(__FILE__, __LINE__);
247 ex.reason = "IceSSL: entropy data file not found:\n" + file;
248 throw ex;
250 if(!RAND_load_file(file.c_str(), 1024))
252 PluginInitializationException ex(__FILE__, __LINE__);
253 ex.reason = "IceSSL: unable to load entropy data from " + file;
254 throw ex;
258 #ifndef _WIN32
260 // The Entropy Gathering Daemon (EGD) is not available on Windows.
261 // The file should be a Unix domain socket for the daemon.
263 string entropyDaemon = properties->getProperty("IceSSL.EntropyDaemon");
264 if(!entropyDaemon.empty())
266 if(RAND_egd(entropyDaemon.c_str()) <= 0)
268 PluginInitializationException ex(__FILE__, __LINE__);
269 ex.reason = "IceSSL: EGD failure using file " + entropyDaemon;
270 throw ex;
273 #endif
274 if(!RAND_status())
276 communicator->getLogger()->warning("IceSSL: insufficient data to initialize PRNG");
280 _facade = IceInternal::getProtocolPluginFacade(communicator);
281 _securityTraceLevel = communicator->getProperties()->getPropertyAsInt("IceSSL.Trace.Security");
282 _securityTraceCategory = "Security";
283 _trustManager = new TrustManager(communicator);
286 // Register the endpoint factory. We have to do this now, rather than
287 // in initialize, because the communicator may need to interpret
288 // proxies before the plug-in is fully initialized.
290 _facade->addEndpointFactory(new EndpointFactoryI(this));
292 __setNoDelete(false);
295 IceSSL::Instance::~Instance()
298 // Clean up OpenSSL resources.
300 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(staticMutex);
302 if(--instanceCount == 0)
305 // NOTE: We can't destroy the locks here: threads which might have called openssl methods
306 // might access openssl locks upon termination (from DllMain/THREAD_DETACHED). Instead,
307 // we release the locks in the ~Init() static destructor. See bug #4156.
309 //CRYPTO_set_locking_callback(0);
310 //CRYPTO_set_id_callback(0);
311 //delete[] locks;
312 //locks = 0;
314 CRYPTO_cleanup_all_ex_data();
315 RAND_cleanup();
316 ERR_free_strings();
317 EVP_cleanup();
321 void
322 IceSSL::Instance::initialize()
324 if(_initialized)
326 return;
331 const string propPrefix = "IceSSL.";
332 PropertiesPtr properties = communicator()->getProperties();
335 // CheckCertName determines whether we compare the name in a peer's
336 // certificate against its hostname.
338 _checkCertName = properties->getPropertyAsIntWithDefault(propPrefix + "CheckCertName", 0) > 0;
341 // VerifyDepthMax establishes the maximum length of a peer's certificate
342 // chain, including the peer's certificate. A value of 0 means there is
343 // no maximum.
345 _verifyDepthMax = properties->getPropertyAsIntWithDefault(propPrefix + "VerifyDepthMax", 2);
348 // VerifyPeer determines whether certificate validation failures abort a connection.
350 _verifyPeer = properties->getPropertyAsIntWithDefault(propPrefix + "VerifyPeer", 2);
353 // Create an SSL context if the application hasn't supplied one.
355 if(!_ctx)
357 _ctx = SSL_CTX_new(SSLv23_method());
358 if(!_ctx)
360 PluginInitializationException ex(__FILE__, __LINE__);
361 ex.reason = "IceSSL: unable to create SSL context:\n" + sslErrors();
362 throw ex;
366 // Check for a default directory. We look in this directory for
367 // files mentioned in the configuration.
369 string defaultDir = properties->getProperty(propPrefix + "DefaultDir");
372 // If the configuration defines a password, or the application has supplied
373 // a password prompt object, then register a password callback. Otherwise,
374 // let OpenSSL use its default behavior.
377 // TODO: Support quoted value?
378 string password = properties->getProperty(propPrefix + "Password");
379 if(!password.empty() || _prompt)
381 SSL_CTX_set_default_passwd_cb(_ctx, IceSSL_opensslPasswordCallback);
382 SSL_CTX_set_default_passwd_cb_userdata(_ctx, this);
383 _password = password;
387 int passwordRetryMax = properties->getPropertyAsIntWithDefault(propPrefix + "PasswordRetryMax", 3);
390 // Establish the location of CA certificates.
393 string caFile = properties->getProperty(propPrefix + "CertAuthFile");
394 string caDir = properties->getPropertyWithDefault(propPrefix + "CertAuthDir", defaultDir);
395 const char* file = 0;
396 const char* dir = 0;
397 if(!caFile.empty())
399 if(!checkPath(caFile, defaultDir, false))
401 PluginInitializationException ex(__FILE__, __LINE__);
402 ex.reason = "IceSSL: CA certificate file not found:\n" + caFile;
403 throw ex;
405 file = caFile.c_str();
407 if(!caDir.empty())
409 if(!checkPath(caDir, defaultDir, true))
411 PluginInitializationException ex(__FILE__, __LINE__);
412 ex.reason = "IceSSL: CA certificate directory not found:\n" + caDir;
413 throw ex;
415 dir = caDir.c_str();
417 if(file || dir)
420 // The certificate may be stored in an encrypted file, so handle
421 // password retries.
423 int count = 0;
424 int err = 0;
425 while(count < passwordRetryMax)
427 ERR_clear_error();
428 err = SSL_CTX_load_verify_locations(_ctx, file, dir);
429 if(err)
431 break;
433 ++count;
435 if(err == 0)
437 string msg = "IceSSL: unable to establish CA certificates";
438 if(passwordError())
440 msg += ":\ninvalid password";
442 else
444 string err = sslErrors();
445 if(!err.empty())
447 msg += ":\n" + err;
450 PluginInitializationException ex(__FILE__, __LINE__);
451 ex.reason = msg;
452 throw ex;
458 // Establish the certificate chains and private keys. One RSA certificate and
459 // one DSA certificate are allowed.
462 #ifdef _WIN32
463 const string sep = ";";
464 #else
465 const string sep = ":";
466 #endif
467 string certFile = properties->getProperty(propPrefix + "CertFile");
468 string keyFile = properties->getProperty(propPrefix + "KeyFile");
469 vector<string>::size_type numCerts = 0;
470 if(!certFile.empty())
472 vector<string> files;
473 if(!IceUtilInternal::splitString(certFile, sep, files) || files.size() > 2)
475 PluginInitializationException ex(__FILE__, __LINE__);
476 ex.reason = "IceSSL: invalid value for " + propPrefix + "CertFile:\n" + certFile;
477 throw ex;
479 numCerts = files.size();
480 for(vector<string>::iterator p = files.begin(); p != files.end(); ++p)
482 string file = *p;
483 if(!checkPath(file, defaultDir, false))
485 PluginInitializationException ex(__FILE__, __LINE__);
486 ex.reason = "IceSSL: certificate file not found:\n" + file;
487 throw ex;
490 // The certificate may be stored in an encrypted file, so handle
491 // password retries.
493 int count = 0;
494 int err = 0;
495 while(count < passwordRetryMax)
497 ERR_clear_error();
498 err = SSL_CTX_use_certificate_chain_file(_ctx, file.c_str());
499 if(err)
501 break;
503 ++count;
505 if(err == 0)
507 string msg = "IceSSL: unable to load certificate chain from file " + file;
508 if(passwordError())
510 msg += ":\ninvalid password";
512 else
514 string err = sslErrors();
515 if(!err.empty())
517 msg += ":\n" + err;
520 PluginInitializationException ex(__FILE__, __LINE__);
521 ex.reason = msg;
522 throw ex;
526 if(keyFile.empty())
528 keyFile = certFile; // Assume the certificate file also contains the private key.
530 if(!keyFile.empty())
532 vector<string> files;
533 if(!IceUtilInternal::splitString(keyFile, sep, files) || files.size() > 2)
535 PluginInitializationException ex(__FILE__, __LINE__);
536 ex.reason = "IceSSL: invalid value for " + propPrefix + "KeyFile:\n" + keyFile;
537 throw ex;
539 if(files.size() != numCerts)
541 PluginInitializationException ex(__FILE__, __LINE__);
542 ex.reason = "IceSSL: " + propPrefix + "KeyFile does not agree with " + propPrefix + "CertFile";
543 throw ex;
545 for(vector<string>::iterator p = files.begin(); p != files.end(); ++p)
547 string file = *p;
548 if(!checkPath(file, defaultDir, false))
550 PluginInitializationException ex(__FILE__, __LINE__);
551 ex.reason = "IceSSL: key file not found:\n" + file;
552 throw ex;
555 // The private key may be stored in an encrypted file, so handle
556 // password retries.
558 int count = 0;
559 int err = 0;
560 while(count < passwordRetryMax)
562 ERR_clear_error();
563 err = SSL_CTX_use_PrivateKey_file(_ctx, file.c_str(), SSL_FILETYPE_PEM);
564 if(err)
566 break;
568 ++count;
570 if(err == 0)
572 string msg = "IceSSL: unable to load private key from file " + file;
573 if(passwordError())
575 msg += ":\ninvalid password";
577 else
579 string err = sslErrors();
580 if(!err.empty())
582 msg += ":\n" + err;
585 PluginInitializationException ex(__FILE__, __LINE__);
586 ex.reason = msg;
587 throw ex;
590 if(!SSL_CTX_check_private_key(_ctx))
592 PluginInitializationException ex(__FILE__, __LINE__);
593 ex.reason = "IceSSL: unable to validate private key(s):\n" + sslErrors();
594 throw ex;
600 // Diffie Hellman configuration.
603 #ifndef OPENSSL_NO_DH
604 _dhParams = new DHParams;
605 SSL_CTX_set_options(_ctx, SSL_OP_SINGLE_DH_USE);
606 SSL_CTX_set_tmp_dh_callback(_ctx, IceSSL_opensslDHCallback);
607 #endif
609 // Properties have the following form:
611 // ...DH.<keyLength>=file
613 const string dhPrefix = propPrefix + "DH.";
614 PropertyDict d = properties->getPropertiesForPrefix(dhPrefix);
615 if(!d.empty())
617 #ifdef OPENSSL_NO_DH
618 _logger->warning("IceSSL: OpenSSL is not configured for Diffie Hellman");
619 #else
620 for(PropertyDict::iterator p = d.begin(); p != d.end(); ++p)
622 string s = p->first.substr(dhPrefix.size());
623 int keyLength = atoi(s.c_str());
624 if(keyLength > 0)
626 string file = p->second;
627 if(!checkPath(file, defaultDir, false))
629 PluginInitializationException ex(__FILE__, __LINE__);
630 ex.reason = "IceSSL: DH parameter file not found:\n" + file;
631 throw ex;
633 if(!_dhParams->add(keyLength, file))
635 PluginInitializationException ex(__FILE__, __LINE__);
636 ex.reason = "IceSSL: unable to read DH parameter file " + file;
637 throw ex;
641 #endif
647 // Store a pointer to ourself for use in OpenSSL callbacks.
649 SSL_CTX_set_ex_data(_ctx, 0, this);
652 // This is necessary for successful interop with Java. Without it, a Java
653 // client would fail to reestablish a connection: the server gets the
654 // error "session id context uninitialized" and the client receives
655 // "SSLHandshakeException: Remote host closed connection during handshake".
657 SSL_CTX_set_session_cache_mode(_ctx, SSL_SESS_CACHE_OFF);
660 // Select protocols.
662 StringSeq protocols = properties->getPropertyAsList(propPrefix + "Protocols");
663 if(!protocols.empty())
665 parseProtocols(protocols);
669 // Establish the cipher list.
671 string ciphers = properties->getProperty(propPrefix + "Ciphers");
672 if(!ciphers.empty())
674 if(!SSL_CTX_set_cipher_list(_ctx, ciphers.c_str()))
676 PluginInitializationException ex(__FILE__, __LINE__);
677 ex.reason = "IceSSL: unable to set ciphers using `" + ciphers + "':\n" + sslErrors();
678 throw ex;
683 // Determine whether a certificate is required from the peer.
686 int sslVerifyMode;
687 switch(_verifyPeer)
689 case 0:
690 sslVerifyMode = SSL_VERIFY_NONE;
691 break;
692 case 1:
693 sslVerifyMode = SSL_VERIFY_PEER;
694 break;
695 case 2:
696 sslVerifyMode = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
697 break;
698 default:
700 PluginInitializationException ex(__FILE__, __LINE__);
701 ex.reason = "IceSSL: invalid value for " + propPrefix + "VerifyPeer";
702 throw ex;
705 SSL_CTX_set_verify(_ctx, sslVerifyMode, IceSSL_opensslVerifyCallback);
708 catch(...)
711 // We free the SSL context regardless of whether the plugin created it
712 // or the application supplied it.
714 SSL_CTX_free(_ctx);
715 _ctx = 0;
716 throw;
719 _initialized = true;
722 void
723 IceSSL::Instance::context(SSL_CTX* context)
725 if(_initialized)
727 PluginInitializationException ex(__FILE__, __LINE__);
728 ex.reason = "IceSSL: plug-in is already initialized";
729 throw ex;
732 assert(!_ctx);
733 _ctx = context;
736 SSL_CTX*
737 IceSSL::Instance::context() const
739 return _ctx;
742 void
743 IceSSL::Instance::setCertificateVerifier(const CertificateVerifierPtr& verifier)
745 _verifier = verifier;
748 void
749 IceSSL::Instance::setPasswordPrompt(const PasswordPromptPtr& prompt)
751 _prompt = prompt;
754 CommunicatorPtr
755 IceSSL::Instance::communicator() const
757 return _facade->getCommunicator();
760 IceInternal::EndpointHostResolverPtr
761 IceSSL::Instance::endpointHostResolver() const
763 return _facade->getEndpointHostResolver();
766 IceInternal::ProtocolSupport
767 IceSSL::Instance::protocolSupport() const
769 return _facade->getProtocolSupport();
772 string
773 IceSSL::Instance::defaultHost() const
775 return _facade->getDefaultHost();
779 IceSSL::Instance::networkTraceLevel() const
781 return _facade->getNetworkTraceLevel();
784 string
785 IceSSL::Instance::networkTraceCategory() const
787 return _facade->getNetworkTraceCategory();
791 IceSSL::Instance::securityTraceLevel() const
793 return _securityTraceLevel;
796 string
797 IceSSL::Instance::securityTraceCategory() const
799 return _securityTraceCategory;
802 void
803 IceSSL::Instance::verifyPeer(SSL* ssl, SOCKET fd, const string& address, const NativeConnectionInfoPtr& info)
805 long result = SSL_get_verify_result(ssl);
806 if(result != X509_V_OK)
808 if(_verifyPeer == 0)
810 if(_securityTraceLevel >= 1)
812 ostringstream ostr;
813 ostr << "IceSSL: ignoring certificate verification failure:\n" << X509_verify_cert_error_string(result);
814 _logger->trace(_securityTraceCategory, ostr.str());
817 else
819 ostringstream ostr;
820 ostr << "IceSSL: certificate verification failed:\n" << X509_verify_cert_error_string(result);
821 string msg = ostr.str();
822 if(_securityTraceLevel >= 1)
824 _logger->trace(_securityTraceCategory, msg);
826 SecurityException ex(__FILE__, __LINE__);
827 ex.reason = msg;
828 throw ex;
832 X509* rawCert = SSL_get_peer_certificate(ssl);
833 CertificatePtr cert;
834 if(rawCert != 0)
836 cert = new Certificate(rawCert);
840 // For an outgoing connection, we compare the proxy address (if any) against
841 // fields in the server's certificate (if any).
843 if(cert && !address.empty())
846 // Extract the IP addresses and the DNS names from the subject
847 // alternative names.
849 vector<pair<int, string> > subjectAltNames = cert->getSubjectAlternativeNames();
850 vector<string> ipAddresses;
851 vector<string> dnsNames;
852 for(vector<pair<int, string> >::const_iterator p = subjectAltNames.begin(); p != subjectAltNames.end(); ++p)
854 if(p->first == 7)
856 ipAddresses.push_back(IceUtilInternal::toLower(p->second));
858 else if(p->first == 2)
860 dnsNames.push_back(IceUtilInternal::toLower(p->second));
865 // Compare the peer's address against the common name.
867 bool certNameOK = false;
868 string dn;
869 string addrLower = IceUtilInternal::toLower(address);
871 DistinguishedName d = cert->getSubjectDN();
872 dn = IceUtilInternal::toLower(string(d));
873 string cn = "cn=" + addrLower;
874 string::size_type pos = dn.find(cn);
875 if(pos != string::npos)
878 // Ensure we match the entire common name.
880 certNameOK = (pos + cn.size() == dn.size()) || (dn[pos + cn.size()] == ',');
885 // Compare the peer's address against the the dnsName and ipAddress
886 // values in the subject alternative name.
888 if(!certNameOK)
890 certNameOK = find(ipAddresses.begin(), ipAddresses.end(), addrLower) != ipAddresses.end();
892 if(!certNameOK)
894 certNameOK = find(dnsNames.begin(), dnsNames.end(), addrLower) != dnsNames.end();
898 // Log a message if the name comparison fails. If CheckCertName is defined,
899 // we also raise an exception to abort the connection. Don't log a message if
900 // CheckCertName is not defined and a verifier is present.
902 if(!certNameOK && (_checkCertName || (_securityTraceLevel >= 1 && !_verifier)))
904 ostringstream ostr;
905 ostr << "IceSSL: ";
906 if(!_checkCertName)
908 ostr << "ignoring ";
910 ostr << "certificate validation failure:\npeer certificate does not have `" << address
911 << "' as its commonName or in its subjectAltName extension";
912 if(!dn.empty())
914 ostr << "\nSubject DN: " << dn;
916 if(!dnsNames.empty())
918 ostr << "\nDNS names found in certificate: ";
919 for(vector<string>::const_iterator p = dnsNames.begin(); p != dnsNames.end(); ++p)
921 if(p != dnsNames.begin())
923 ostr << ", ";
925 ostr << *p;
928 if(!ipAddresses.empty())
930 ostr << "\nIP addresses found in certificate: ";
931 for(vector<string>::const_iterator p = ipAddresses.begin(); p != ipAddresses.end(); ++p)
933 if(p != ipAddresses.begin())
935 ostr << ", ";
937 ostr << *p;
940 string msg = ostr.str();
941 if(_securityTraceLevel >= 1)
943 Trace out(_logger, _securityTraceCategory);
944 out << msg;
946 if(_checkCertName)
948 SecurityException ex(__FILE__, __LINE__);
949 ex.reason = msg;
950 throw ex;
955 if(_verifyDepthMax > 0 && static_cast<int>(info->certs.size()) > _verifyDepthMax)
957 ostringstream ostr;
958 ostr << (info->incoming ? "incoming" : "outgoing") << " connection rejected:\n"
959 << "length of peer's certificate chain (" << info->certs.size() << ") exceeds maximum of "
960 << _verifyDepthMax;
961 string msg = ostr.str();
962 if(_securityTraceLevel >= 1)
964 _logger->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd));
966 SecurityException ex(__FILE__, __LINE__);
967 ex.reason = msg;
968 throw ex;
971 if(!_trustManager->verify(info))
973 string msg = string(info->incoming ? "incoming" : "outgoing") + " connection rejected by trust manager";
974 if(_securityTraceLevel >= 1)
976 _logger->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd));
978 SecurityException ex(__FILE__, __LINE__);
979 ex.reason = msg;
980 throw ex;
983 if(_verifier && !_verifier->verify(info))
985 string msg = string(info->incoming ? "incoming" : "outgoing") + " connection rejected by certificate verifier";
986 if(_securityTraceLevel >= 1)
988 _logger->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd));
990 SecurityException ex(__FILE__, __LINE__);
991 ex.reason = msg;
992 throw ex;
996 string
997 IceSSL::Instance::sslErrors() const
999 return getSslErrors(_securityTraceLevel >= 1);
1002 void
1003 IceSSL::Instance::destroy()
1005 _facade = 0;
1007 if(_ctx)
1009 SSL_CTX_free(_ctx);
1013 string
1014 IceSSL::Instance::password(bool /*encrypting*/)
1016 if(_prompt)
1020 return _prompt->getPassword();
1022 catch(...)
1025 // Don't allow exceptions to cross an OpenSSL boundary.
1027 return string();
1030 else
1032 return _password;
1037 IceSSL::Instance::verifyCallback(int ok, SSL* ssl, X509_STORE_CTX* c)
1039 if(!ok && _securityTraceLevel >= 1)
1041 X509* cert = X509_STORE_CTX_get_current_cert(c);
1042 int err = X509_STORE_CTX_get_error(c);
1043 char buf[256];
1045 Trace out(_logger, _securityTraceCategory);
1046 out << "certificate verification failure\n";
1048 X509_NAME_oneline(X509_get_issuer_name(cert), buf, static_cast<int>(sizeof(buf)));
1049 out << "issuer = " << buf << '\n';
1050 X509_NAME_oneline(X509_get_subject_name(cert), buf, static_cast<int>(sizeof(buf)));
1051 out << "subject = " << buf << '\n';
1052 out << "depth = " << X509_STORE_CTX_get_error_depth(c) << '\n';
1053 out << "error = " << X509_verify_cert_error_string(err) << '\n';
1054 out << IceInternal::fdToString(SSL_get_fd(ssl));
1056 return ok;
1059 #ifndef OPENSSL_NO_DH
1061 IceSSL::Instance::dhParams(int keyLength)
1063 return _dhParams->get(keyLength);
1065 #endif
1067 void
1068 IceSSL::Instance::traceConnection(SSL* ssl, bool incoming)
1070 Trace out(_logger, _securityTraceCategory);
1071 out << "SSL summary for " << (incoming ? "incoming" : "outgoing") << " connection\n";
1074 // The const_cast is necesary because Solaris still uses OpenSSL 0.9.7.
1076 //const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
1077 SSL_CIPHER *cipher = const_cast<SSL_CIPHER*>(SSL_get_current_cipher(ssl));
1078 if(!cipher)
1080 out << "unknown cipher\n";
1082 else
1084 out << "cipher = " << SSL_CIPHER_get_name(cipher) << "\n";
1085 out << "bits = " << SSL_CIPHER_get_bits(cipher, 0) << "\n";
1086 out << "protocol = " << SSL_get_version(ssl) << "\n";
1088 out << IceInternal::fdToString(SSL_get_fd(ssl));
1091 void
1092 IceSSL::Instance::parseProtocols(const StringSeq& protocols)
1094 bool sslv3 = false, tlsv1 = false;
1095 for(Ice::StringSeq::const_iterator p = protocols.begin(); p != protocols.end(); ++p)
1097 string prot = *p;
1099 if(prot == "ssl3" || prot == "sslv3")
1101 sslv3 = true;
1103 else if(prot == "tls" || prot == "tls1" || prot == "tlsv1")
1105 tlsv1 = true;
1107 else
1109 PluginInitializationException ex(__FILE__, __LINE__);
1110 ex.reason = "IceSSL: unrecognized protocol `" + prot + "'";
1111 throw ex;
1115 long opts = SSL_OP_NO_SSLv2; // SSLv2 is not supported.
1116 if(!sslv3)
1118 opts |= SSL_OP_NO_SSLv3;
1120 if(!tlsv1)
1122 opts |= SSL_OP_NO_TLSv1;
1124 SSL_CTX_set_options(_ctx, opts);