// // Copyright (c) ZeroC, Inc. All rights reserved. // #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace Ice; using namespace IceUtil; using namespace IceSSL; IceUtil::Shared* IceSSL::upCast(IceSSL::SSLEngine* p) { return p; } IceSSL::SSLEngine::SSLEngine(const Ice::CommunicatorPtr& communicator) : _initialized(false), _communicator(communicator), _logger(communicator->getLogger()), _trustManager(new TrustManager(communicator)) { } IceSSL::CertificateVerifierPtr IceSSL::SSLEngine::getCertificateVerifier() const { return _verifier; } void IceSSL::SSLEngine::setCertificateVerifier(const IceSSL::CertificateVerifierPtr& verifier) { _verifier = verifier; } IceSSL::PasswordPromptPtr IceSSL::SSLEngine::getPasswordPrompt() const { return _prompt; } void IceSSL::SSLEngine::setPasswordPrompt(const IceSSL::PasswordPromptPtr& prompt) { _prompt = prompt; } string IceSSL::SSLEngine::password(bool /*encrypting*/) { if(_prompt) { try { return _prompt->getPassword(); } catch(...) { // // Don't allow exceptions to cross an OpenSSL boundary. // return string(); } } else { return _password; } } bool IceSSL::SSLEngine::initialized() const { Mutex::Lock lock(_mutex); return _initialized; } string IceSSL::SSLEngine::getPassword() const { return _password; } void IceSSL::SSLEngine::setPassword(const std::string& password) { _password = password; } void IceSSL::SSLEngine::initialize() { const string propPrefix = "IceSSL."; const PropertiesPtr properties = communicator()->getProperties(); // // CheckCertName determines whether we compare the name in a peer's // certificate against its hostname. // _checkCertName = properties->getPropertyAsIntWithDefault(propPrefix + "CheckCertName", 0) > 0; // // CheckCertName > 1 enables SNI, the SNI extension applies to client connections, // indicating the hostname to the server (must be DNS hostname, not an IP address). // _serverNameIndication = properties->getPropertyAsIntWithDefault(propPrefix + "CheckCertName", 0) > 1; // // VerifyDepthMax establishes the maximum length of a peer's certificate // chain, including the peer's certificate. A value of 0 means there is // no maximum. // _verifyDepthMax = properties->getPropertyAsIntWithDefault(propPrefix + "VerifyDepthMax", 3); // // VerifyPeer determines whether certificate validation failures abort a connection. // _verifyPeer = properties->getPropertyAsIntWithDefault(propPrefix + "VerifyPeer", 2); if(_verifyPeer < 0 || _verifyPeer > 2) { throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: invalid value for " + propPrefix + "VerifyPeer"); } _securityTraceLevel = properties->getPropertyAsInt("IceSSL.Trace.Security"); _securityTraceCategory = "Security"; } void IceSSL::SSLEngine::verifyPeerCertName(const string& address, const ConnectionInfoPtr& info) { // // For an outgoing connection, we compare the proxy address (if any) against // fields in the server's certificate (if any). // if(_checkCertName && !info->certs.empty() && !address.empty()) { const CertificatePtr cert = info->certs[0]; // // Extract the IP addresses and the DNS names from the subject // alternative names. // vector > subjectAltNames = cert->getSubjectAlternativeNames(); vector ipAddresses; vector dnsNames; for(vector >::const_iterator p = subjectAltNames.begin(); p != subjectAltNames.end(); ++p) { if(p->first == AltNAmeIP) { ipAddresses.push_back(IceUtilInternal::toLower(p->second)); } else if(p->first == AltNameDNS) { dnsNames.push_back(IceUtilInternal::toLower(p->second)); } } bool certNameOK = false; string addrLower = IceUtilInternal::toLower(address); bool isIpAddress = IceInternal::isIpAddress(address); // // If address is an IP address, compare it to the subject alternative names IP adddress // if(isIpAddress) { certNameOK = find(ipAddresses.begin(), ipAddresses.end(), addrLower) != ipAddresses.end(); } else { // // If subjectAlt is empty compare it ot the subject CN, othewise // compare it to the to the subject alt name dnsNames // if(dnsNames.empty()) { DistinguishedName d = cert->getSubjectDN(); string dn = IceUtilInternal::toLower(string(d)); string cn = "cn=" + addrLower; string::size_type pos = dn.find(cn); if(pos != string::npos) { // // Ensure we match the entire common name. // certNameOK = (pos + cn.size() == dn.size()) || (dn[pos + cn.size()] == ','); } } else { certNameOK = find(dnsNames.begin(), dnsNames.end(), addrLower) != dnsNames.end(); } } if(!certNameOK) { ostringstream ostr; ostr << "IceSSL: "; if(_verifyPeer > 0) { ostr << "ignoring "; } ostr << "certificate verification failure " << (isIpAddress ? "IP address mismatch" : "Hostname mismatch"); string msg = ostr.str(); if(_securityTraceLevel >= 1) { Trace out(_logger, _securityTraceCategory); out << msg; } throw SecurityException(__FILE__, __LINE__, msg); } } } void IceSSL::SSLEngine::verifyPeer(const string& /*address*/, const ConnectionInfoPtr& info, const string& desc) { const CertificateVerifierPtr verifier = getCertificateVerifier(); if(_verifyDepthMax > 0 && static_cast(info->certs.size()) > _verifyDepthMax) { ostringstream ostr; ostr << (info->incoming ? "incoming" : "outgoing") << " connection rejected:\n" << "length of peer's certificate chain (" << info->certs.size() << ") exceeds maximum of " << _verifyDepthMax; string msg = ostr.str(); if(_securityTraceLevel >= 1) { _logger->trace(_securityTraceCategory, msg + "\n" + desc); } throw SecurityException(__FILE__, __LINE__, msg); } if(!_trustManager->verify(info, desc)) { string msg = string(info->incoming ? "incoming" : "outgoing") + " connection rejected by trust manager"; if(_securityTraceLevel >= 1) { _logger->trace(_securityTraceCategory, msg + "\n" + desc); } throw SecurityException(__FILE__, __LINE__, msg); } if(verifier && !verifier->verify(info)) { string msg = string(info->incoming ? "incoming" : "outgoing") + " connection rejected by certificate verifier"; if(_securityTraceLevel >= 1) { _logger->trace(_securityTraceCategory, msg + "\n" + desc); } throw SecurityException(__FILE__, __LINE__, msg); } } bool IceSSL::SSLEngine::getCheckCertName() const { return _checkCertName; } bool IceSSL::SSLEngine::getServerNameIndication() const { return _serverNameIndication; } int IceSSL::SSLEngine::getVerifyPeer() const { return _verifyPeer; } int IceSSL::SSLEngine::securityTraceLevel() const { return _securityTraceLevel; } std::string IceSSL::SSLEngine::securityTraceCategory() const { return _securityTraceCategory; }