/** * openssl.cpp * This file is part of the YATE Project http://YATE.null.ro * * OpenSSL based SSL/TLS socket support * * Yet Another Telephony Engine - a fully featured software PBX and IVR * Copyright (C) 2004-2006 Null Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #ifndef OPENSSL_NO_AES #include #endif #ifndef OPENSSL_NO_DES #include #endif using namespace TelEngine; namespace { // anonymous #define MAKE_ERR(x) { #x, X509_V_ERR_##x } static TokenDict s_verifyCodes[] = { MAKE_ERR(UNABLE_TO_GET_ISSUER_CERT), MAKE_ERR(UNABLE_TO_DECRYPT_CERT_SIGNATURE), MAKE_ERR(UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY), MAKE_ERR(CERT_SIGNATURE_FAILURE), MAKE_ERR(CERT_NOT_YET_VALID), MAKE_ERR(CERT_HAS_EXPIRED), MAKE_ERR(ERROR_IN_CERT_NOT_BEFORE_FIELD), MAKE_ERR(DEPTH_ZERO_SELF_SIGNED_CERT), MAKE_ERR(SELF_SIGNED_CERT_IN_CHAIN), MAKE_ERR(UNABLE_TO_GET_ISSUER_CERT_LOCALLY), MAKE_ERR(UNABLE_TO_VERIFY_LEAF_SIGNATURE), MAKE_ERR(INVALID_CA), MAKE_ERR(PATH_LENGTH_EXCEEDED), MAKE_ERR(INVALID_PURPOSE), MAKE_ERR(CERT_UNTRUSTED), MAKE_ERR(CERT_REJECTED), { 0, 0 } }; #undef MAKE_ERR static TokenDict s_verifyMode[] = { // don't ask for a certificate, don't stop if verification fails { "none", SSL_VERIFY_NONE }, // certificate is verified only if provided (a server always provides one) { "peer", SSL_VERIFY_PEER }, // server only - verify client certificate only if provided and only once { "only", SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE }, // server only - client must provide a certificate at every (re)negotiation { "must", SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT }, // server only - client must provide a certificate only at first negotiation { "once", SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE }, { 0, 0 } }; class SslContext : public String { public: // Constructor. Build the context SslContext(const char* name); // Initialize certificate, key and domains. Check the key // Return false on failure bool init(const NamedList& params); // Load a certificate and key. Check the key bool loadCertificate(const String& cert, const String& key); // Check if this context can be used for server sockets in a given domain bool hasDomain(const String& domain); // Add a comma separated list of domains to a buffer void addDomains(String& buf); // Release memory, free the context virtual void destruct(); inline operator SSL_CTX*() { return m_context; } protected: SSL_CTX* m_context; ObjList m_domains; }; class SslSocket : public Socket, public Mutex { public: SslSocket(SOCKET handle, bool server, int verify, SslContext* context = 0); virtual ~SslSocket(); virtual bool terminate(); virtual bool valid(); virtual int writeData(const void* buffer, int length); virtual int readData(void* buffer, int length); void onInfo(int where, int retVal); inline SSL* ssl() const { return m_ssl; } private: int sslError(int retcode); SSL* m_ssl; }; #ifndef OPENSSL_NO_AES // AES Counter Mode class AesCtrCipher : public Cipher { public: AesCtrCipher(); virtual ~AesCtrCipher(); virtual unsigned int blockSize() const { return AES_BLOCK_SIZE; } virtual unsigned int initVectorSize() const { return AES_BLOCK_SIZE; } virtual bool setKey(const void* key, unsigned int len, Direction dir); virtual bool initVector(const void* vect, unsigned int len, Direction dir); virtual bool encrypt(void* outData, unsigned int len, const void* inpData); virtual bool decrypt(void* outData, unsigned int len, const void* inpData); protected: AES_KEY* m_key; unsigned char m_initVector[AES_BLOCK_SIZE]; }; //AES - Cipher Feedback Mode class AesCfbCipher : public AesCtrCipher { public: AesCfbCipher(); virtual ~AesCfbCipher(); virtual bool encrypt(void* outData, unsigned int len, const void* inpData); virtual bool decrypt(void* outData, unsigned int len, const void* inpData); }; #endif #ifndef OPENSSL_NO_DES // CBC-DES Cipher - Cipher-Block Chaining Mode class DesCtrCipher : public Cipher { public: DesCtrCipher(); virtual ~DesCtrCipher(); virtual unsigned int blockSize() const { return DES_KEY_SZ; } virtual unsigned int initVectorSize() const { return DES_KEY_SZ; } virtual bool setKey(const void* key, unsigned int len, Direction dir); virtual bool initVector(const void* vect, unsigned int len, Direction dir); virtual bool encrypt(void* outData, unsigned int len, const void* inpData); virtual bool decrypt(void* outData, unsigned int len, const void* inpData); private: DES_key_schedule m_key; unsigned char m_initVector[DES_KEY_SZ]; }; #endif class CipherHandler : public MessageHandler { public: inline CipherHandler() : MessageHandler("engine.cipher") { } virtual bool received(Message& msg); }; class SslHandler; class OpenSSL : public Module { public: OpenSSL(); ~OpenSSL(); virtual void initialize(); // Find a context by name or domain // This method is not thread safe. The caller must lock the plugin // until the returned context is not used anymore SslContext* findContext(const String& token, bool byDomain = false) const; // Find a context from 'context' or 'domain' parameters // This method is not thread safe SslContext* findContext(Message& msg) const; protected: virtual void statusParams(String& str); virtual void statusDetail(String& str); SslHandler* m_handler; ObjList m_contexts; // Server contexts list String m_statusCmd; // Module status command }; static int s_index = -1; static SSL_CTX* s_context = 0; static OpenSSL __plugin; class SslHandler : public MessageHandler { public: inline SslHandler() : MessageHandler("socket.ssl",100,__plugin.name()) { } virtual bool received(Message& msg); }; // Attempt to add randomness from system time when called static void addRand(u_int64_t usec) { // a rough estimation of 2 bytes of entropy ::RAND_add(&usec,sizeof(usec),2); } // Retrieve SslSocket from SSL structure static inline SslSocket* sslSocket(const SSL* ssl) { if (ssl && s_index >= 0) return static_cast(::SSL_get_ex_data(const_cast(ssl),s_index)); return 0; } // Callback function called from OpenSSL for state changes and alerts void infoCallback(const SSL* ssl, int where, int retVal) { SslSocket* sock = sslSocket(ssl); if (sock) { if (sock->ssl() == ssl) sock->onInfo(where,retVal); else Debug(&__plugin,DebugFail,"Mismatched session %p [%p]",ssl,sock); } } // Callback function called from OpenSSL for protocol messages void msgCallback(int write, int version, int content_type, const void* buf, size_t len, SSL* ssl, void* arg) { SslSocket* sock = sslSocket(ssl); if (!sock) return; if (sock->ssl() == ssl) Debug(&__plugin,DebugAll, "%s SSL message: version=%d content_type=%d buf=%p len=%u [%p]", write ? "Sent" : "Received",version,content_type,buf,(unsigned int)len, sock); else Debug(&__plugin,DebugFail,"msgCallback: Mismatched session %p [%p]",ssl,sock); } SslContext::SslContext(const char* name) : String(name), m_context(0) { m_context = ::SSL_CTX_new(::SSLv23_method()); SSL_CTX_set_info_callback(m_context,infoCallback); #ifdef DEBUG SSL_CTX_set_msg_callback(m_context,msgCallback); #endif } // Initialize certificate, key and domains. Check the key // Return false on failure bool SslContext::init(const NamedList& params) { // Load certificate and key. Check them if (!loadCertificate(params["certificate"],params["key"])) return false; // Load domains m_domains.clear(); String* d = params.getParam("domains"); if (d) { ObjList* list = d->split(',',false); for (ObjList* o = list->skipNull(); o; o = o->skipNext()) { String* s = static_cast(o->get()); s->trimBlanks(); if (s->null()) continue; if (s->startsWith("*") && (s->length() < 3 || (*s)[1] != '.')) { Debug(&__plugin,DebugNote,"Context '%s' ignoring invalid domain='%s'", c_str(),s->c_str()); continue; } m_domains.append(new String(s->toLower())); } TelEngine::destruct(list); DDebug(&__plugin,DebugAll,"Context '%s' loaded domains=%s", c_str(),d->safe()); } return true; } // Load a certificate and key. Check the key bool SslContext::loadCertificate(const String& c, const String& k) { String cert; if (c) { cert << Engine::configPath(); if (cert && !cert.endsWith(Engine::pathSeparator())) cert << Engine::pathSeparator(); cert << c; } String key; if (k) { key << Engine::configPath(); if (key && !key.endsWith(Engine::pathSeparator())) key << Engine::pathSeparator(); key << k; } else key = cert; if (!::SSL_CTX_use_certificate_chain_file(m_context,cert)) { unsigned long err = ::ERR_get_error(); Debug(&__plugin,DebugWarn, "Context '%s' failed to load certificate from '%s' '%s'", c_str(),cert.c_str(),::ERR_error_string(err,0)); return false; } if (!::SSL_CTX_use_PrivateKey_file(m_context,key,SSL_FILETYPE_PEM)) { unsigned long err = ::ERR_get_error(); Debug(&__plugin,DebugWarn, "Context '%s' failed to load key from '%s' '%s'", c_str(),key.c_str(),::ERR_error_string(err,0)); return false; } if (!::SSL_CTX_check_private_key(m_context)) { unsigned long err = ::ERR_get_error(); Debug(&__plugin,DebugWarn, "Context '%s' certificate='%s' or key='%s' are invalid '%s'", c_str(),cert.c_str(),key.c_str(),::ERR_error_string(err,0)); return false; } DDebug(&__plugin,DebugAll,"Context '%s' loaded certificate='%s' key='%s'", c_str(),cert.c_str(),key.c_str()); return true; } // Check if this context can be used for server sockets in a given domain bool SslContext::hasDomain(const String& domain) { for (ObjList* o = m_domains.skipNull(); o; o = o->skipNext()) { String* s = static_cast(o->get()); if (*s == domain || ((*s)[0] == '*' && domain.endsWith(s->c_str() + 1))) return true; } return false; } // Add a comma separated list of domains to a buffer void SslContext::addDomains(String& buf) { bool notFirst = false; for (ObjList* o = m_domains.skipNull(); o; o = o->skipNext()) { if (notFirst) buf << ","; else notFirst = true; buf << (static_cast(o->get()))->c_str(); } } // Release memory, free the context void SslContext::destruct() { ::SSL_CTX_free(m_context); String::destruct(); } // Create a SSL socket from a regular socket handle SslSocket::SslSocket(SOCKET handle, bool server, int verify, SslContext* context) : Socket(handle), Mutex(false,"SslSocket"), m_ssl(0) { DDebug(&__plugin,DebugAll,"SslSocket::SslSocket(%d,%s,%s,%s) [%p]", handle,String::boolText(server),lookup(verify,s_verifyMode,"unknown"), context ? context->c_str() : "",this); if (Socket::valid()) { m_ssl = ::SSL_new(context ? *context : s_context); if (s_index >= 0) ::SSL_set_ex_data(m_ssl,s_index,this); ::SSL_set_verify(m_ssl,verify,0); ::SSL_set_fd(m_ssl,handle); BIO* bio = ::SSL_get_rbio(m_ssl); if (!(bio && BIO_set_close(bio,BIO_NOCLOSE))) Debug(&__plugin,DebugGoOn,"SslSocket::SslSocket(%d) no BIO or cannot set NOCLOSE [%p]", handle,this); if (server) ::SSL_set_accept_state(m_ssl); else ::SSL_set_connect_state(m_ssl); } } // Destructor, clean up early SslSocket::~SslSocket() { DDebug(&__plugin,DebugAll,"SslSocket::~SslSocket() handle=%d [%p]",handle(),this); clearFilters(); terminate(); } // Terminate the socket and the SSL session around it bool SslSocket::terminate() { lock(); if (m_ssl) { if (s_index >= 0) ::SSL_set_ex_data(m_ssl,s_index,0); ::SSL_shutdown(m_ssl); ::SSL_free(m_ssl); m_ssl = 0; // SSL_free also destroys the BIO } unlock(); return Socket::terminate(); } // Check if the socket is valid bool SslSocket::valid() { return m_ssl && Socket::valid(); } // Write unencrypted data through the SSL stream int SslSocket::writeData(const void* buffer, int length) { if (!buffer) length = 0; Lock lock(this); if (!m_ssl) { m_error = EINVAL; return socketError(); } return sslError(::SSL_write(m_ssl,buffer,length)); } // Read decrypted data from the SSL stream int SslSocket::readData(void* buffer, int length) { if (!buffer) length = 0; Lock lock(this); if (!m_ssl) { m_error = EINVAL; return socketError(); } return sslError(::SSL_read(m_ssl,buffer,length)); } // Deal with the various SSL error codes int SslSocket::sslError(int retcode) { if (retcode <= 0) { switch (::SSL_get_error(m_ssl,retcode)) { case SSL_ERROR_ZERO_RETURN: clearError(); retcode = 0; break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: m_error = EAGAIN; retcode = socketError(); break; case SSL_ERROR_SYSCALL: copyError(); break; default: m_error = EINVAL; retcode = socketError(); break; } #ifdef DEBUG if (!canRetry()) Debug(&__plugin,DebugNote,"SslSocket error='%s' state='%s' [%p]", ERR_error_string(ERR_get_error(),0),SSL_state_string_long(m_ssl),this); #endif } else clearError(); return retcode; } // Callback function called from OpenSSL for state changes and alerts void SslSocket::onInfo(int where, int retVal) { #ifdef DEBUG if (where & SSL_CB_LOOP) Debug(&__plugin,DebugAll,"State %s [%p]",SSL_state_string_long(m_ssl),this); #endif if ((where & SSL_CB_EXIT) && (retVal == 0)) Debug(&__plugin,DebugMild,"Failed %s [%p]",SSL_state_string_long(m_ssl),this); if (where & SSL_CB_ALERT) Debug(&__plugin,DebugMild,"Alert %s: %s [%p]", SSL_alert_type_string_long(retVal), SSL_alert_desc_string_long(retVal),this); if (where & SSL_CB_HANDSHAKE_DONE) { long verify = ::SSL_get_verify_result(m_ssl); if (verify != X509_V_OK) { // handshake succeeded but the certificate has problems const char* error = lookup(verify,s_verifyCodes); Debug(&__plugin,DebugWarn,"Certificate verify error %ld%s%s [%p]", verify,error ? ": " : "",c_safe(error),this); } } } // Handler for the socket.ssl message - turns regular sockets into SSL bool SslHandler::received(Message& msg) { if (msg.getBoolValue("test")) { if (!msg.getBoolValue("server")) return true; Lock lock(__plugin); return 0 != __plugin.findContext(msg); } addRand(msg.msgTime()); Socket** ppSock = static_cast(msg.userObject(YATOM("Socket*"))); if (!ppSock) { Debug(&__plugin,DebugGoOn,"SslHandler: No pointer to Socket"); return false; } Socket* pSock = *ppSock; if (!pSock) { Debug(&__plugin,DebugGoOn,"SslHandler: NULL Socket pointer"); return false; } if (!pSock->valid()) { Debug(&__plugin,DebugWarn,"SslHandler: Invalid Socket"); return false; } SslSocket* sSock = 0; if (msg.getBoolValue("server",false)) { Lock lock(&__plugin); SslContext* c = __plugin.findContext(msg); if (!c) return false; sSock = new SslSocket(pSock->handle(),true, msg.getIntValue("verify",s_verifyMode,SSL_VERIFY_NONE),c); } else { const String& cert = msg["certificate"]; SslContext* c = cert ? new SslContext(msg) : 0; if (!c || c->loadCertificate(cert,msg["key"])) sSock = new SslSocket(pSock->handle(),false, msg.getIntValue("verify",s_verifyMode,SSL_VERIFY_NONE),c); TelEngine::destruct(c); } if (!(sSock && sSock->valid())) { if (sSock) { Debug(&__plugin,DebugWarn,"SslHandler: Invalid SSL Socket"); // detach and destroy new socket, preserve old one sSock->detach(); delete sSock; } return false; } // replace socket, detach and destroy old one *ppSock = sSock; pSock->detach(); delete pSock; return true; } #ifndef OPENSSL_NO_AES AesCtrCipher::AesCtrCipher() : m_key(0) { m_key = new AES_KEY; DDebug(&__plugin,DebugAll,"AesCtrCipher::AesCtrCipher() key=%p [%p]",m_key,this); } AesCtrCipher::~AesCtrCipher() { DDebug(&__plugin,DebugAll,"AesCtrCipher::~AesCtrCipher() key=%p [%p]",m_key,this); delete m_key; } bool AesCtrCipher::setKey(const void* key, unsigned int len, Direction dir) { if (!(key && len && m_key)) return false; // AES_ctr128_encrypt is its own inverse return 0 == AES_set_encrypt_key((const unsigned char*)key,len*8,m_key); } bool AesCtrCipher::initVector(const void* vect, unsigned int len, Direction dir) { if (len && !vect) return false; if (len > AES_BLOCK_SIZE) len = AES_BLOCK_SIZE; if (len < AES_BLOCK_SIZE) ::memset(m_initVector,0,AES_BLOCK_SIZE); if (len) ::memcpy(m_initVector,vect,len); return true; } bool AesCtrCipher::encrypt(void* outData, unsigned int len, const void* inpData) { if (!(outData && len)) return false; if (!inpData) inpData = outData; unsigned int num = 0; unsigned char eCountBuf[AES_BLOCK_SIZE]; AES_ctr128_encrypt( (const unsigned char*)inpData, (unsigned char*)outData, len, m_key, m_initVector, eCountBuf, &num); return true; } bool AesCtrCipher::decrypt(void* outData, unsigned int len, const void* inpData) { // AES_ctr128_encrypt is its own inverse return encrypt(outData,len,inpData); } AesCfbCipher::AesCfbCipher() { DDebug(&__plugin,DebugAll,"AesCfbCipher::AesCfbCipher() key=%p [%p]",m_key,this); } AesCfbCipher::~AesCfbCipher() { DDebug(&__plugin,DebugAll,"AesCfbCipher::~AesCfbCipher() key=%p [%p]",m_key,this); } bool AesCfbCipher::encrypt(void* outData, unsigned int len, const void* inpData) { if (!(outData && len)) return false; if (!inpData) inpData = outData; int num = 0; AES_cfb128_encrypt( (const unsigned char*)inpData, (unsigned char*)outData, len, m_key, m_initVector, &num, AES_ENCRYPT); return true; } bool AesCfbCipher::decrypt(void* outData, unsigned int len, const void* inpData) { if (!(outData && len)) return false; if (!inpData) inpData = outData; int num = 0; AES_cfb128_encrypt( (const unsigned char*)inpData, (unsigned char*)outData, len, m_key, m_initVector, &num, AES_DECRYPT); return true; } #endif #ifndef OPENSSL_NO_DES DesCtrCipher::DesCtrCipher() { DES_cblock nativeDESKey; DES_random_key(&nativeDESKey); DES_set_key_checked(&nativeDESKey,&m_key); DDebug(&__plugin,DebugAll,"DesCtrCipher::DesCtrCipher() key=%p [%p]",&m_key,this); } DesCtrCipher::~DesCtrCipher() { DDebug(&__plugin,DebugAll,"DesCtrCipher::~DesCtrCipher() key=%p [%p]",&m_key,this); } bool DesCtrCipher::setKey(const void* key, unsigned int len, Direction dir) { if (!(key && len)) return false; DES_cblock nativeKey; ::memcpy(nativeKey,key,len); DES_set_odd_parity(&nativeKey); int r = DES_set_key_checked(&nativeKey,&m_key); return r== 0; } bool DesCtrCipher::initVector(const void* vect, unsigned int len, Direction dir) { if (len && !vect) return false; if (len > DES_KEY_SZ) len = DES_KEY_SZ; if (len < DES_KEY_SZ) ::memset(m_initVector,0,DES_KEY_SZ); if (len) ::memcpy(m_initVector,vect,len); return true; } bool DesCtrCipher::encrypt(void* outData, unsigned int len, const void* inpData) { DDebug(&__plugin,DebugAll,"DesCtrCipher::encrypt(%p, %d. %p) [%p]",outData,len,inpData,this); if (!(outData && len)) return false; if (len % 8 != 0) { Debug(&__plugin,DebugWarn,"DesCtrCipher::encrypt() - length of data block to be encrypted is not a multiple of 8, memory corruption possible - encryption aborted"); return false; } if (!inpData) inpData = outData; DES_ncbc_encrypt((const unsigned char*)inpData,(unsigned char*)outData,len,&m_key,(DES_cblock *)m_initVector,DES_ENCRYPT); return true; } bool DesCtrCipher::decrypt(void* outData, unsigned int len, const void* inpData) { DDebug(&__plugin,DebugAll,"DesCtrCipher::decrypt(%p, %d. %p) [%p]",outData,len,inpData,this); if (!(outData && len)) return false; if (len % 8 != 0) { Debug(&__plugin,DebugWarn,"DesCtrCipher::decrypt() - length of data block to be decrypted is not a multiple of 8, memory corruption possible - decryption aborted"); return false; } if (!inpData) inpData = outData; DES_ncbc_encrypt((const unsigned char*)inpData,(unsigned char*)outData,len,&m_key,(DES_cblock *)m_initVector,DES_DECRYPT); return true; } #endif // Handler for the engine.cipher message - Cipher Factory bool CipherHandler::received(Message& msg) { addRand(msg.msgTime()); const String* name = msg.getParam("cipher"); if (!name) return false; Cipher** ppCipher = static_cast(msg.userObject(YATOM("Cipher*"))); #ifndef OPENSSL_NO_AES if (*name == "aes_ctr") { if (ppCipher) *ppCipher = new AesCtrCipher(); return true; } if (*name == "aes_cfb") { if (ppCipher) *ppCipher = new AesCfbCipher(); return true; } #endif #ifndef OPENSSL_NO_DES if (*name == "des_cbc") { if (ppCipher) *ppCipher = new DesCtrCipher(); return true; } #endif return false; } OpenSSL::OpenSSL() : Module("openssl","misc",true), m_handler(0) { Output("Loaded module OpenSSL - based on " OPENSSL_VERSION_TEXT); m_statusCmd << "status " << name(); } OpenSSL::~OpenSSL() { Output("Unloading module OpenSSL"); ::SSL_CTX_free(s_context); } void OpenSSL::initialize() { Output("Initializing module OpenSSL"); Configuration cfg(Engine::configFile("openssl")); if (!m_handler) { setup(); ::SSL_load_error_strings(); ::SSL_library_init(); addRand(Time::now()); s_index = ::SSL_get_ex_new_index(0,const_cast("TelEngine::SslSocket"),0,0,0); s_context = ::SSL_CTX_new(::SSLv23_method()); SSL_CTX_set_info_callback(s_context,infoCallback); // macro - no :: m_handler = new SslHandler; Engine::install(m_handler); #if !defined(OPENSSL_NO_AES) || !defined(OPENSSL_NO_DES) Engine::install(new CipherHandler); #endif } lock(); // Load server contexts unsigned int n = cfg.sections(); for (unsigned int i = 0; i < n; i++) { NamedList* p = cfg.getSection(i); if (!p || *p == "general" || !p->c_str()) continue; SslContext* context = findContext(*p); if (!p->getBoolValue("enable",true)) { if (context) { DDebug(this,DebugAll,"Removing disabled context '%s'",context->c_str()); m_contexts.remove(context); } continue; } if (!context) context = new SslContext(*p); if (context->init(*p)) { if (!findContext(*p)) { m_contexts.append(context); DDebug(this,DebugAll,"Added context '%s'",context->c_str()); } } else { if (findContext(*p)) { DDebug(this,DebugAll,"Removing invalid context '%s'",context->c_str()); m_contexts.remove(context); } else { DDebug(this,DebugAll,"Ignoring invalid context '%s'",context->c_str()); TelEngine::destruct(context); } } } unlock(); } // Find a context by name or domain SslContext* OpenSSL::findContext(const String& token, bool byDomain) const { if (!byDomain) { ObjList* o = m_contexts.find(token); return o ? static_cast(o->get()) : 0; } for (ObjList* o = m_contexts.skipNull(); o; o = o->skipNext()) { SslContext* c = static_cast(o->get()); if (c->hasDomain(token)) return c; } return 0; } // Find a context from 'context' or 'domain' parameters SslContext* OpenSSL::findContext(Message& msg) const { SslContext* c = 0; const String& context = msg["context"]; String domain; if (context) c = __plugin.findContext(context); if (!c) { domain = msg["domain"]; if (domain) c = __plugin.findContext(domain.toLower(),true); } if (c) return c; Debug(this,DebugWarn, "SslHandler: Unable to find a server context for context=%s or domain=%s", context.safe(),domain.safe()); return 0; } void OpenSSL::statusParams(String& str) { Lock lock(this); str << "contexts=" << m_contexts.count(); } void OpenSSL::statusDetail(String& str) { Lock lock(this); for (ObjList* o = m_contexts.skipNull(); o; o = o->skipNext()) { SslContext* c = static_cast(o->get()); str.append(c->c_str(),";"); str << "="; c->addDomains(str); } } }; // anonymous namespace /* vi: set ts=8 sw=4 sts=4 noet: */