PageRenderTime 91ms CodeModel.GetById 45ms app.highlight 40ms RepoModel.GetById 2ms app.codeStats 0ms

/mordor/streams/ssl.cpp

http://github.com/mozy/mordor
C++ | 730 lines | 637 code | 55 blank | 38 comment | 99 complexity | b93480671002c7c1e56d2e0387833173 MD5 | raw file
  1// Copyright (c) 2009 - Mozy, Inc.
  2
  3#include "ssl.h"
  4
  5#include <sstream>
  6
  7#include <openssl/err.h>
  8#include <openssl/x509v3.h>
  9
 10#include "mordor/assert.h"
 11#include "mordor/log.h"
 12#include "mordor/util.h"
 13
 14#ifdef MSVC
 15#pragma comment(lib, "libeay32")
 16#pragma comment(lib, "ssleay32")
 17#endif
 18
 19namespace Mordor {
 20
 21static Logger::ptr g_log = Log::lookup("mordor:streams:ssl");
 22
 23namespace {
 24
 25static struct SSLInitializer {
 26    SSLInitializer()
 27    {
 28        SSL_library_init();
 29        SSL_load_error_strings();
 30    }
 31    ~SSLInitializer()
 32    {
 33        ERR_free_strings();
 34        CRYPTO_cleanup_all_ex_data();
 35        EVP_cleanup();
 36    }
 37} g_init;
 38
 39}
 40
 41static bool hasOpenSSLError()
 42{
 43    unsigned long err = ERR_peek_error();
 44    if (err == SSL_ERROR_NONE)
 45        return false;
 46    switch (ERR_GET_REASON(err)) {
 47        case ERR_R_MALLOC_FAILURE:
 48            throw std::bad_alloc();
 49        case ERR_R_PASSED_NULL_PARAMETER:
 50            {
 51                char buf[120];
 52                ERR_error_string(err, buf);
 53                MORDOR_THROW_EXCEPTION(std::invalid_argument(buf));
 54            }
 55        default:
 56            return true;
 57    }
 58}
 59
 60static std::string getOpenSSLErrorMessage()
 61{
 62    std::ostringstream os;
 63    unsigned long err;
 64    char buf[120];
 65    while ( (err = ERR_get_error()) != SSL_ERROR_NONE) {
 66        if (!os.str().empty())
 67            os << "\n";
 68        os << ERR_error_string(err, buf);
 69    }
 70    return os.str();
 71}
 72
 73OpenSSLException::OpenSSLException() :
 74    std::runtime_error(getOpenSSLErrorMessage())
 75{
 76}
 77
 78std::string
 79CertificateVerificationException::constructMessage(long verifyResult)
 80{
 81    return X509_verify_cert_error_string(verifyResult);
 82}
 83
 84// Adapted from https://www.codeblog.org/viewsrc/openssl-fips-1.1.1/demos/x509/mkcert.c
 85static void add_ext(X509 *cert, int nid, const char *value);
 86
 87static void mkcert(boost::shared_ptr<X509> &cert,
 88                  boost::shared_ptr<EVP_PKEY> &pkey, int bits, int serial,
 89                  int days)
 90{
 91    RSA *rsa;
 92    X509_NAME *name=NULL;
 93
 94    pkey.reset(EVP_PKEY_new(), &EVP_PKEY_free);
 95    if (!pkey)
 96        throw std::bad_alloc();
 97    cert.reset(X509_new(), &X509_free);
 98    if (!cert)
 99        throw std::bad_alloc();
100
101    rsa = RSA_generate_key(bits,RSA_F4,NULL,NULL);
102    MORDOR_VERIFY(EVP_PKEY_assign_RSA(pkey.get(),rsa));
103
104    X509_set_version(cert.get(),2);
105    ASN1_INTEGER_set(X509_get_serialNumber(cert.get()),serial);
106    X509_gmtime_adj(X509_get_notBefore(cert.get()),0);
107    X509_gmtime_adj(X509_get_notAfter(cert.get()),(long)60*60*24*days);
108    X509_set_pubkey(cert.get(),pkey.get());
109
110    name=X509_get_subject_name(cert.get());
111
112    /* This function creates and adds the entry, working out the
113     * correct string type and performing checks on its length.
114     * Normally we'd check the return value for errors...
115     */
116    X509_NAME_add_entry_by_txt(name,"C",
117                            MBSTRING_ASC,
118                            (const unsigned char *)"United States",
119                            -1, -1, 0);
120    X509_NAME_add_entry_by_txt(name,"CN",
121                            MBSTRING_ASC,
122                            (const unsigned char *)"Mordor Default Self-signed Certificate",
123                            -1, -1, 0);
124
125    /* Its self signed so set the issuer name to be the same as the
126     * subject.
127     */
128    X509_set_issuer_name(cert.get(),name);
129
130    /* Add various extensions: standard extensions */
131    add_ext(cert.get(), NID_basic_constraints, "critical,CA:TRUE");
132    add_ext(cert.get(), NID_key_usage, "critical,keyCertSign,cRLSign");
133
134    add_ext(cert.get(), NID_subject_key_identifier, "hash");
135
136    /* Some Netscape specific extensions */
137    add_ext(cert.get(), NID_netscape_cert_type, "sslCA");
138
139    MORDOR_VERIFY(X509_sign(cert.get(),pkey.get(),EVP_md5()));
140}
141
142/* Add extension using V3 code: we can set the config file as NULL
143 * because we wont reference any other sections.
144 */
145
146void add_ext(X509 *cert, int nid, const char *value)
147{
148    X509V3_CTX ctx;
149    /* This sets the 'context' of the extensions. */
150    /* No configuration database */
151    X509V3_set_ctx_nodb(&ctx);
152    /* Issuer and subject certs: both the target since it is self signed,
153     * no request and no CRL
154     */
155    X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
156    X509_EXTENSION *ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, (char*) value);
157    MORDOR_VERIFY(ex);
158
159    X509_add_ext(cert,ex,-1);
160    X509_EXTENSION_free(ex);
161}
162
163
164SSLStream::SSLStream(Stream::ptr parent, bool client, bool own, SSL_CTX *ctx)
165: MutatingFilterStream(parent, own)
166{
167    MORDOR_ASSERT(parent);
168    clearSSLError();
169    if (ctx)
170        m_ctx.reset(ctx, &nop<SSL_CTX *>);
171    else
172        m_ctx.reset(SSL_CTX_new(client ? SSLv23_client_method() :
173            SSLv23_server_method()), &SSL_CTX_free);
174    if (!m_ctx) {
175        MORDOR_ASSERT(hasOpenSSLError());
176        MORDOR_THROW_EXCEPTION(OpenSSLException(getOpenSSLErrorMessage()))
177            << boost::errinfo_api_function("SSL_CTX_new");
178    }
179    // Auto-generate self-signed server cert
180    if (!ctx && !client) {
181        boost::shared_ptr<X509> cert;
182        boost::shared_ptr<EVP_PKEY> pkey;
183        mkcert(cert, pkey, 1024, rand(), 365);
184        SSL_CTX_use_certificate(m_ctx.get(), cert.get());
185        SSL_CTX_use_PrivateKey(m_ctx.get(), pkey.get());
186    }
187    m_ssl.reset(SSL_new(m_ctx.get()), &SSL_free);
188    if (!m_ssl) {
189        MORDOR_ASSERT(hasOpenSSLError());
190        MORDOR_THROW_EXCEPTION(OpenSSLException(getOpenSSLErrorMessage()))
191            << boost::errinfo_api_function("SSL_CTX_new");
192    }
193    m_readBio = BIO_new(BIO_s_mem());
194    m_writeBio = BIO_new(BIO_s_mem());
195    if (!m_readBio || !m_writeBio) {
196        if (m_readBio) BIO_free(m_readBio);
197        if (m_writeBio) BIO_free(m_writeBio);
198        MORDOR_ASSERT(hasOpenSSLError());
199        MORDOR_THROW_EXCEPTION(OpenSSLException(getOpenSSLErrorMessage()))
200            << boost::errinfo_api_function("BIO_new");
201    }
202    BIO_set_mem_eof_return(m_readBio, -1);
203
204    SSL_set_bio(m_ssl.get(), m_readBio, m_writeBio);
205}
206
207void
208SSLStream::clearSSLError()
209{
210    std::string msg;
211    unsigned long err = SSL_ERROR_NONE;
212    while ((err = ERR_get_error()) != SSL_ERROR_NONE) {
213        switch (ERR_GET_REASON(err)) {
214            case ERR_R_MALLOC_FAILURE:
215                msg = "bad alloc";
216                break;
217            case ERR_R_PASSED_NULL_PARAMETER:
218                msg = "invalid argument";
219                break;
220            default:
221                {
222                    char buf[120];
223                    const char * errBuf = ERR_error_string(err, buf);
224                    if (errBuf != NULL) {
225                        msg = errBuf;
226                    }
227                }
228        }
229        MORDOR_LOG_ERROR(g_log) << this
230                << " ssl: " << m_ssl.get()
231                << " ignoring error: " << err
232                << " error msg: " << msg;
233    };
234
235    // Clear it again for insurance.
236    ERR_clear_error();
237}
238
239void
240SSLStream::close(CloseType type)
241{
242    MORDOR_ASSERT(type == BOTH);
243    if (!(sslCallWithLock(boost::bind(SSL_get_shutdown, m_ssl.get()), NULL) & SSL_SENT_SHUTDOWN)) {
244        unsigned long error = SSL_ERROR_NONE;
245        const int result = sslCallWithLock(boost::bind(SSL_shutdown, m_ssl.get()), &error);
246        if (result <= 0) {
247            MORDOR_LOG_DEBUG(g_log) << this << " SSL_shutdown(" << m_ssl.get()
248                << "): " << result << " (" << error << ")";
249            switch (error) {
250                case SSL_ERROR_NONE:
251                case SSL_ERROR_ZERO_RETURN:
252                    break;
253                case SSL_ERROR_WANT_READ:
254                case SSL_ERROR_WANT_WRITE:
255                case SSL_ERROR_WANT_CONNECT:
256                case SSL_ERROR_WANT_ACCEPT:
257                case SSL_ERROR_WANT_X509_LOOKUP:
258                    MORDOR_NOTREACHED();
259                case SSL_ERROR_SYSCALL:
260                    if (hasOpenSSLError()) {
261                        std::string message = getOpenSSLErrorMessage();
262                        MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown("
263                            << m_ssl.get() << "): " << result << " (" << error
264                            << ", " << message << ")";
265                        MORDOR_THROW_EXCEPTION(OpenSSLException(message))
266                            << boost::errinfo_api_function("SSL_shutdown");
267                    }
268                    MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown("
269                        << m_ssl.get() << "): " << result << " (" << error
270                        << ")";
271                    if (result == 0)
272                        break;
273                    MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_shutdown");
274                case SSL_ERROR_SSL:
275                    {
276                        MORDOR_ASSERT(hasOpenSSLError());
277                        std::string message = getOpenSSLErrorMessage();
278                        MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown("
279                            << m_ssl.get() << "): " << result << " (" << error
280                            << ", " << message << ")";
281                        MORDOR_THROW_EXCEPTION(OpenSSLException(message))
282                            << boost::errinfo_api_function("SSL_shutdown");
283                    }
284                default:
285                    MORDOR_NOTREACHED();
286            }
287        }
288        flush(false);
289    }
290
291    // follow apache to call SSL_shutdown blindly without error handling
292    int count = 0;
293    while (!(sslCallWithLock(boost::bind(SSL_get_shutdown, m_ssl.get()), NULL) & SSL_RECEIVED_SHUTDOWN)) {
294        unsigned long error = SSL_ERROR_NONE;
295        const int result = sslCallWithLock(boost::bind(SSL_shutdown, m_ssl.get()), &error);
296        MORDOR_LOG_DEBUG(g_log) << this << " SSL_shutdown(" << m_ssl.get()
297            << "): " << result << " (" << error << "), count=" << count;
298        if (result > 0 || ++count == 3) {
299            break;
300        }
301    }
302    parent()->close();
303}
304
305size_t
306SSLStream::read(void *buffer, size_t length)
307{
308    const int toRead = (int)std::min<size_t>(0x0fffffff, length);
309    while (true) {
310        unsigned long error = SSL_ERROR_NONE;
311        const int result = sslCallWithLock(boost::bind(SSL_read, m_ssl.get(), buffer, toRead), &error);
312        if (result > 0) {
313            return result;
314        }
315        MORDOR_LOG_DEBUG(g_log) << this << " SSL_read(" << m_ssl.get() << ", "
316            << toRead << "): " << result << " (" << error << ")";
317        switch (error) {
318            case SSL_ERROR_NONE:
319                return result;
320            case SSL_ERROR_ZERO_RETURN:
321                // Received close_notify message
322                MORDOR_ASSERT(result == 0);
323                return 0;
324            case SSL_ERROR_WANT_READ:
325                wantRead();
326                continue;
327            case SSL_ERROR_WANT_WRITE:
328            case SSL_ERROR_WANT_CONNECT:
329            case SSL_ERROR_WANT_ACCEPT:
330            case SSL_ERROR_WANT_X509_LOOKUP:
331                MORDOR_NOTREACHED();
332            case SSL_ERROR_SYSCALL:
333                if (hasOpenSSLError()) {
334                    std::string message = getOpenSSLErrorMessage();
335                    MORDOR_LOG_ERROR(g_log) << this << " SSL_read("
336                        << m_ssl.get() << ", " << toRead << "): " << result
337                        << " (" << error << ", " << message << ")";
338                    MORDOR_THROW_EXCEPTION(OpenSSLException(message))
339                        << boost::errinfo_api_function("SSL_read");
340                }
341                MORDOR_LOG_WARNING(g_log) << this << " SSL_read("
342                    << m_ssl.get() << ", " << toRead << "): " << result
343                    << " (" << error << ")";
344                if (result == 0) {
345                    return 0;
346                }
347                MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_read");
348            case SSL_ERROR_SSL:
349                {
350                    MORDOR_ASSERT(hasOpenSSLError());
351                    std::string message = getOpenSSLErrorMessage();
352                    MORDOR_LOG_ERROR(g_log) << this << " SSL_read("
353                        << m_ssl.get() << ", " << toRead << "): " << result
354                        << " (" << error << ", " << message << ")";
355                    MORDOR_THROW_EXCEPTION(OpenSSLException(message))
356                        << boost::errinfo_api_function("SSL_read");
357                }
358            default:
359                MORDOR_NOTREACHED();
360        }
361    }
362}
363
364size_t
365SSLStream::write(const Buffer &buffer, size_t length)
366{
367    // SSL_write will create at least two SSL records for each call -
368    // one for data, and one tiny one for the checksum or IV or something.
369    // Dealing with lots of extra records can take some serious CPU time
370    // server-side, so we want to provide it with as much data as possible,
371    // even if that means reallocating.  That's why we use pass the flag to
372    // coalesce small segments, instead of only doing the first available
373    // segment
374    return Stream::write(buffer, length, true);
375}
376
377size_t
378SSLStream::write(const void *buffer, size_t length)
379{
380    flush(false);
381    if (length == 0)
382        return 0;
383
384    const int toWrite = (int)std::min<size_t>(0x7fffffff, length);
385    while (true) {
386        unsigned long error = SSL_ERROR_NONE;
387        const int result = sslCallWithLock(boost::bind(SSL_write, m_ssl.get(), buffer, toWrite), &error);
388        if (result > 0) {
389            return result;
390        }
391
392        MORDOR_LOG_DEBUG(g_log) << this << " SSL_write(" << m_ssl.get() << ", "
393            << toWrite << "): " << result << " (" << error << ")";
394        switch (error) {
395            case SSL_ERROR_NONE:
396                return result;
397            case SSL_ERROR_ZERO_RETURN:
398                // Received close_notify message
399                MORDOR_ASSERT(result != 0);
400                return result;
401            case SSL_ERROR_WANT_READ:
402                MORDOR_THROW_EXCEPTION(OpenSSLException("SSL_write generated SSL_ERROR_WANT_READ"));
403            case SSL_ERROR_WANT_WRITE:
404            case SSL_ERROR_WANT_CONNECT:
405            case SSL_ERROR_WANT_ACCEPT:
406            case SSL_ERROR_WANT_X509_LOOKUP:
407                MORDOR_NOTREACHED();
408            case SSL_ERROR_SYSCALL:
409                if (hasOpenSSLError()) {
410                        std::string message = getOpenSSLErrorMessage();
411                        MORDOR_LOG_ERROR(g_log) << this << " SSL_write("
412                            << m_ssl.get() << ", " << toWrite << "): "
413                            << result << " (" << error << ", " << message
414                            << ")";
415                        MORDOR_THROW_EXCEPTION(OpenSSLException(message))
416                            << boost::errinfo_api_function("SSL_write");
417                }
418                MORDOR_LOG_ERROR(g_log) << this << " SSL_write("
419                    << m_ssl.get() << ", " << toWrite << "): " << result
420                    << " (" << error << ")";
421                if (result == 0) {
422                    MORDOR_THROW_EXCEPTION(UnexpectedEofException());
423                }
424                MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_write");
425            case SSL_ERROR_SSL:
426                {
427                    MORDOR_ASSERT(hasOpenSSLError());
428                    std::string message = getOpenSSLErrorMessage();
429                    MORDOR_LOG_ERROR(g_log) << this << " SSL_write("
430                        << m_ssl.get() << ", " << toWrite << "): " << result
431                        << " (" << error << ", " << message << ")";
432                    MORDOR_THROW_EXCEPTION(OpenSSLException(message))
433                        << boost::errinfo_api_function("SSL_write");
434                }
435            default:
436                MORDOR_NOTREACHED();
437        }
438    }
439}
440
441void
442SSLStream::flush(bool flushParent)
443{
444    static const int WRITE_BUF_LENGTH = 4096;
445    char writeBuf[WRITE_BUF_LENGTH];
446    int toWrite = 0;
447    do {
448        toWrite = BIO_read(m_writeBio, (void *)writeBuf, WRITE_BUF_LENGTH);
449        if (toWrite > 0) {
450            m_writeBuffer.copyIn((const void *)writeBuf, toWrite);
451        }
452    } while (toWrite > 0);
453
454    if (m_writeBuffer.readAvailable() == 0)
455        return;
456
457    while (m_writeBuffer.readAvailable()) {
458        MORDOR_LOG_TRACE(g_log) << this << " parent()->write("
459            << m_writeBuffer.readAvailable() << ")";
460        size_t written = parent()->write(m_writeBuffer,
461            m_writeBuffer.readAvailable());
462        MORDOR_LOG_TRACE(g_log) << this << " parent()->write("
463            << m_writeBuffer.readAvailable() << "): " << written;
464        m_writeBuffer.consume(written);
465    }
466
467    if (flushParent)
468        parent()->flush(flushParent);
469}
470
471void
472SSLStream::accept()
473{
474    while (true) {
475        unsigned long error = SSL_ERROR_NONE;
476        const int result = sslCallWithLock(boost::bind(SSL_accept, m_ssl.get()), &error);
477        if (result > 0) {
478            flush(false);
479            return;
480        }
481        MORDOR_LOG_DEBUG(g_log) << this << " SSL_accept(" << m_ssl.get()
482            << "): " << result << " (" << error << ")";
483        switch (error) {
484            case SSL_ERROR_NONE:
485                flush(false);
486                return;
487            case SSL_ERROR_ZERO_RETURN:
488                // Received close_notify message
489                return;
490            case SSL_ERROR_WANT_READ:
491                flush();
492                wantRead();
493                continue;
494            case SSL_ERROR_WANT_WRITE:
495            case SSL_ERROR_WANT_CONNECT:
496            case SSL_ERROR_WANT_ACCEPT:
497            case SSL_ERROR_WANT_X509_LOOKUP:
498                MORDOR_NOTREACHED();
499            case SSL_ERROR_SYSCALL:
500                if (hasOpenSSLError()) {
501                    std::string message = getOpenSSLErrorMessage();
502                    MORDOR_LOG_ERROR(g_log) << this << " SSL_accept("
503                        << m_ssl.get() << "): " << result << " (" << error
504                        << ", " << message << ")";
505                    MORDOR_THROW_EXCEPTION(OpenSSLException(message))
506                        << boost::errinfo_api_function("SSL_accept");
507                }
508                MORDOR_LOG_ERROR(g_log) << this << " SSL_accept("
509                    << m_ssl.get() << "): " << result << " (" << error
510                    << ")";
511                if (result == 0) {
512                    MORDOR_THROW_EXCEPTION(UnexpectedEofException());
513                }
514                MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_accept");
515            case SSL_ERROR_SSL:
516                {
517                    MORDOR_ASSERT(hasOpenSSLError());
518                    std::string message = getOpenSSLErrorMessage();
519                    MORDOR_LOG_ERROR(g_log) << this << " SSL_accept("
520                        << m_ssl.get() << "): " << result << " (" << error
521                        << ", " << message << ")";
522                    MORDOR_THROW_EXCEPTION(OpenSSLException(message))
523                        << boost::errinfo_api_function("SSL_accept");
524                }
525            default:
526                MORDOR_NOTREACHED();
527        }
528    }
529}
530
531void
532SSLStream::connect()
533{
534    while (true) {
535        unsigned long error = SSL_ERROR_NONE;
536        const int result = sslCallWithLock(boost::bind(SSL_connect, m_ssl.get()), &error);
537        MORDOR_LOG_DEBUG(g_log) << this << " SSL_connect(" << m_ssl.get()
538            << "): " << result << " (" << error << ")";
539        if (result > 0) {
540            flush(false);
541            return;
542        }
543        switch (error) {
544            case SSL_ERROR_NONE:
545                flush(false);
546                return;
547            case SSL_ERROR_ZERO_RETURN:
548                // Received close_notify message
549                return;
550            case SSL_ERROR_WANT_READ:
551                flush();
552                wantRead();
553                continue;
554            case SSL_ERROR_WANT_WRITE:
555            case SSL_ERROR_WANT_CONNECT:
556            case SSL_ERROR_WANT_ACCEPT:
557            case SSL_ERROR_WANT_X509_LOOKUP:
558                MORDOR_NOTREACHED();
559            case SSL_ERROR_SYSCALL:
560                if (hasOpenSSLError()) {
561                    std::string message = getOpenSSLErrorMessage();
562                    MORDOR_LOG_ERROR(g_log) << this << " SSL_connect("
563                        << m_ssl.get() << "): " << result << " (" << error
564                        << ", " << message << ")";
565                    MORDOR_THROW_EXCEPTION(OpenSSLException(message))
566                        << boost::errinfo_api_function("SSL_connect");
567                }
568                MORDOR_LOG_ERROR(g_log) << this << " SSL_connect("
569                    << m_ssl.get() << "): " << result << " (" << error
570                    << ")";
571                if (result == 0) {
572                    MORDOR_THROW_EXCEPTION(UnexpectedEofException());
573                }
574                MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_connect");
575            case SSL_ERROR_SSL:
576                {
577                    MORDOR_ASSERT(hasOpenSSLError());
578                    std::string message = getOpenSSLErrorMessage();
579                    MORDOR_LOG_ERROR(g_log) << this << " SSL_connect("
580                        << m_ssl.get() << "): " << result << " (" << error
581                        << ", " << message << ")";
582                    MORDOR_THROW_EXCEPTION(OpenSSLException(message))
583                        << boost::errinfo_api_function("SSL_connect");
584                }
585            default:
586                MORDOR_NOTREACHED();
587        }
588    }
589}
590
591void
592SSLStream::serverNameIndication(const std::string &hostname)
593{
594    // Older versions of OpenSSL don't support this (I'm looking at you,
595    // Leopard); just ignore it then
596#ifdef SSL_set_tlsext_host_name
597    boost::mutex::scoped_lock lock(m_mutex);
598    if (!SSL_set_tlsext_host_name(m_ssl.get(), hostname.c_str())) {
599        if (!hasOpenSSLError()) return;
600        std::string message = getOpenSSLErrorMessage();
601        MORDOR_LOG_ERROR(g_log) << this << " SSL_set_tlsext_host_name("
602            << m_ssl.get() << ", " << hostname.c_str() << "): " << message;
603        MORDOR_THROW_EXCEPTION(OpenSSLException(message))
604            << boost::errinfo_api_function("SSL_set_tlsext_host_name");
605    }
606#endif
607}
608
609void
610SSLStream::verifyPeerCertificate()
611{
612    const long verifyResult = sslCallWithLock(boost::bind(SSL_get_verify_result, m_ssl.get()), NULL);
613    MORDOR_LOG_LEVEL(g_log, verifyResult ? Log::WARNING : Log::DEBUG) << this
614        << " SSL_get_verify_result(" << m_ssl.get() << "): "
615        << verifyResult;
616    if (verifyResult != X509_V_OK)
617        MORDOR_THROW_EXCEPTION(CertificateVerificationException(verifyResult));
618}
619
620void
621SSLStream::verifyPeerCertificate(const std::string &hostname)
622{
623    if (!hostname.empty()) {
624        boost::mutex::scoped_lock lock(m_mutex);
625        std::string wildcardHostname = "*";
626        size_t dot = hostname.find('.');
627        if (dot != std::string::npos)
628            wildcardHostname.append(hostname.substr(dot));
629        boost::shared_ptr<X509> cert;
630        cert.reset(SSL_get_peer_certificate(m_ssl.get()), &X509_free);
631        if (!cert)
632            MORDOR_THROW_EXCEPTION(CertificateVerificationException(
633                X509_V_ERR_APPLICATION_VERIFICATION,
634                "No Certificate Presented"));
635        int critical = -1, altNameIndex = -1;
636        GENERAL_NAMES *gens = (GENERAL_NAMES *)X509_get_ext_d2i(cert.get(),
637            NID_subject_alt_name, &critical, &altNameIndex);
638        if (gens) {
639            do {
640                try {
641                    bool success = false;
642                    for(int i = 0; i < sk_GENERAL_NAME_num(gens); i++)
643                    {
644                        GENERAL_NAME *gen = sk_GENERAL_NAME_value(gens, i);
645                        if(gen->type != GEN_DNS) continue;
646                        std::string altName((const char *)gen->d.dNSName->data,
647                            gen->d.dNSName->length);
648                        if (altName == wildcardHostname ||
649                            altName == hostname) {
650                            success = true;
651                            break;
652                        }
653                    }
654                    sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
655                    if (success)
656                        return;
657                } catch (...) {
658                    sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
659                    throw;
660                }
661                gens = (GENERAL_NAMES *)X509_get_ext_d2i(cert.get(),
662                    NID_subject_alt_name, &critical, &altNameIndex);
663            } while (gens);
664        }
665        X509_NAME *name = X509_get_subject_name(cert.get());
666        if (!name)
667            MORDOR_THROW_EXCEPTION(CertificateVerificationException(
668                X509_V_ERR_APPLICATION_VERIFICATION,
669                "No Subject Name"));
670        int len = X509_NAME_get_text_by_NID(name, NID_commonName, NULL, 0);
671        if (len == -1)
672            MORDOR_THROW_EXCEPTION(CertificateVerificationException(
673                X509_V_ERR_APPLICATION_VERIFICATION,
674                "No Common Name"));
675        std::string commonName;
676        commonName.resize(len);
677        X509_NAME_get_text_by_NID(name, NID_commonName, &commonName[0],
678            len + 1);
679        if (commonName == wildcardHostname || commonName == hostname)
680            return;
681        MORDOR_THROW_EXCEPTION(CertificateVerificationException(
682                X509_V_ERR_APPLICATION_VERIFICATION,
683                "No Matching Common Name"));
684    }
685}
686
687void
688SSLStream::wantRead()
689{
690    if (m_readBuffer.readAvailable() == 0) {
691        MORDOR_LOG_TRACE(g_log) << this << " parent()->read(32768)";
692        const size_t result = parent()->read(m_readBuffer, 32768);
693        MORDOR_LOG_TRACE(g_log) << this << " parent()->read(32768): " << result;
694        if (result == 0) {
695            BIO_set_mem_eof_return(m_readBio, 0);
696            return;
697        }
698    }
699    MORDOR_ASSERT(m_readBuffer.readAvailable());
700    const iovec iov = m_readBuffer.readBuffer(~0, false);
701    MORDOR_ASSERT(iov.iov_len > 0);
702    const int written = BIO_write(m_readBio, (char *)iov.iov_base, iov.iov_len);
703    MORDOR_ASSERT(written > 0);
704    if (written > 0) {
705        m_readBuffer.consume(written);
706    }
707    MORDOR_LOG_DEBUG(g_log) << this << " wantRead(): " << written;
708}
709
710int
711SSLStream::sslCallWithLock(boost::function<int ()> dg, unsigned long *error)
712{
713    boost::mutex::scoped_lock lock(m_mutex);
714
715    // If error is NULL, it means that sslCallWithLock is not supposed to call SSL_get_error
716    // after dg got called. If SSL_get_error is not supposed to be called, there is no need
717    // to clear current thread's error queue.
718    if (error == NULL) {
719        return dg();
720    }
721
722    clearSSLError();
723    const int result = dg();
724    if (result <= 0) {
725        *error = SSL_get_error(m_ssl.get(), result);
726    }
727    return result;
728}
729
730}