blocxx
SSLCtxMgr.cpp
Go to the documentation of this file.
1/*******************************************************************************
2* Copyright (C) 2001-2004 Vintela, Inc. All rights reserved.
3* Copyright (C) 2004 Novell, Inc. All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7*
8* - Redistributions of source code must retain the above copyright notice,
9* this list of conditions and the following disclaimer.
10*
11* - Redistributions in binary form must reproduce the above copyright notice,
12* this list of conditions and the following disclaimer in the documentation
13* and/or other materials provided with the distribution.
14*
15* - Neither the name of Vintela, Inc. nor the names of its
16* contributors may be used to endorse or promote products derived from this
17* software without specific prior written permission.
18*
19* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
20* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22* ARE DISCLAIMED. IN NO EVENT SHALL Vintela, Inc. OR THE CONTRIBUTORS
23* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29* POSSIBILITY OF SUCH DAMAGE.
30*******************************************************************************/
31
37#include "blocxx/BLOCXX_config.h"
38#ifdef BLOCXX_HAVE_OPENSSL
39#include "blocxx/SSLCtxMgr.hpp"
40#include "blocxx/GetPass.hpp"
41#include "blocxx/Format.hpp"
42#include "blocxx/FileSystem.hpp"
43#include "blocxx/ThreadImpl.hpp"
44#include "blocxx/Mutex.hpp"
46#include "blocxx/MutexLock.hpp"
47#include "blocxx/Assertion.hpp"
48#include "blocxx/MD5.hpp"
49#include "blocxx/Array.hpp"
50#include "blocxx/SecureRand.hpp"
52#include "blocxx/LazyGlobal.hpp"
53
54#include <openssl/rand.h>
55#include <openssl/err.h>
56#include <cstring>
57#include <csignal>
58#include <cerrno>
59#ifndef BLOCXX_WIN32
60#include <sys/time.h>
61#include <sys/resource.h>
62#endif
63#include <fcntl.h>
64
65#ifdef BLOCXX_HAVE_SYS_TYPES_H
66#include <sys/types.h>
67#endif
68
69#ifdef BLOCXX_HAVE_SYS_STAT_H
70#include <sys/stat.h>
71#endif
72
73#ifdef BLOCXX_HAVE_UNISTD_H
74#include <unistd.h>
75#endif
76
77#ifdef BLOCXX_DEBUG
78#include <iostream>
79#endif
80
81#include <fstream>
82
83// This struct has to be in the global namespace
84extern "C"
85{
86struct CRYPTO_dynlock_value
87{
89};
90}
91
92namespace BLOCXX_NAMESPACE
93{
94
95namespace
96{
97
98BLOCXX_NAMESPACE::Mutex* mutex_buf = 0;
99
100extern "C"
101{
102
103static struct CRYPTO_dynlock_value * dyn_create_function(const char *,int)
104{
105 return new CRYPTO_dynlock_value;
106}
107
108// these need to still be static, since they get exported because of extern "C"
109static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l,
110 const char *, int)
111{
112 if (mode & CRYPTO_LOCK)
113 {
114 l->mutex.acquire();
115 }
116 else
117 {
118 l->mutex.release();
119 }
120}
121
122static void dyn_destroy_function(struct CRYPTO_dynlock_value *l,
123 const char *, int)
124{
125 delete l;
126}
127
128static unsigned long id_function()
129{
131}
132
133static void locking_function(int mode, int n, const char*, int)
134{
135 if (mode & CRYPTO_LOCK)
136 {
137 mutex_buf[n].acquire();
138 }
139 else
140 {
141 mutex_buf[n].release();
142 }
143}
144} // end extern "C"
145
146class X509Freer
147{
148public:
149 X509Freer(X509* x509)
150 : m_x509(x509)
151 {
152 }
153 ~X509Freer()
154 {
155 if (m_x509 != 0)
156 {
157 X509_free(m_x509);
158 }
159 }
160private:
161 X509* m_x509;
162};
163
164enum SSLLibraryInit_t {
165 BLOCXX_SSL_LIBRARY_NOT_INITIALIZED,
166 BLOCXX_SSL_LIBRARY_INITIALIZED,
167 BLOCXX_SSL_LIBRARY_INITIALIZATION_DISABLED
168};
169SSLLibraryInit_t m_initState = BLOCXX_SSL_LIBRARY_NOT_INITIALIZED;
170GlobalMutex m_initStateGuard = BLOCXX_GLOBAL_MUTEX_INIT();
171
172
173enum SSLLocks_t {
174 BLOCXX_SSL_LOCKS_NOT_USED,
175 BLOCXX_SSL_LOCKS_USED,
176 BLOCXX_SSL_LOCKS_DISABLED
177};
178SSLLocks_t m_locksState = BLOCXX_SSL_LOCKS_NOT_USED;
179GlobalMutex m_locksStateGuard = BLOCXX_GLOBAL_MUTEX_INIT();
180
182struct SSLGlobalWork
183{
184 SSLGlobalWork()
185 {
186 MutexLock initLock(m_initStateGuard);
187 if (m_initState == BLOCXX_SSL_LIBRARY_NOT_INITIALIZED)
188 {
189 m_initState = BLOCXX_SSL_LIBRARY_INITIALIZED;
190
191 SSL_library_init();
192 SSL_load_error_strings();
193 }
194 initLock.release();
195
196 MutexLock locksLock(m_locksStateGuard);
197 if (m_locksState == BLOCXX_SSL_LOCKS_NOT_USED)
198 {
199 m_locksState = BLOCXX_SSL_LOCKS_USED;
200
201 if (!mutex_buf)
202 {
203 mutex_buf = new Mutex[CRYPTO_num_locks()];
204 }
205
206 CRYPTO_set_id_callback(id_function);
207 CRYPTO_set_locking_callback(locking_function);
208
209 // The following three CRYPTO_... functions are the OpenSSL functions
210 // for registering the callbacks we implemented above
211 CRYPTO_set_dynlock_create_callback(dyn_create_function);
212 CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
213 CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
214 }
215 }
216
217 ~SSLGlobalWork()
218 {
219 if (SSLCtxMgr::isClient() || SSLCtxMgr::isServer())
220 {
221 Secure::rand_save_state();
222 }
223 SSLCtxMgr::uninit();
224
225 MutexLock locksLock(m_locksStateGuard);
226 if (m_locksState == BLOCXX_SSL_LOCKS_USED)
227 {
228 CRYPTO_set_id_callback(NULL);
229 CRYPTO_set_locking_callback(NULL);
230 CRYPTO_set_dynlock_create_callback(NULL);
231 CRYPTO_set_dynlock_lock_callback(NULL);
232 CRYPTO_set_dynlock_destroy_callback(NULL);
233 delete[] mutex_buf;
234 mutex_buf = 0;
235 m_locksState = BLOCXX_SSL_LOCKS_NOT_USED;
236 }
237 }
238};
239
240struct SSLGlobalWorkFactory
241{
242 static SSLGlobalWork* create(int /*dummy*/)
243 {
244 return new SSLGlobalWork();
245 }
246};
247
248LazyGlobal<SSLGlobalWork, int, SSLGlobalWorkFactory> g_SSLGlobalWork = BLOCXX_LAZY_GLOBAL_INIT(0);
249
250
251} // end unnamed namespace
252
253SSL_CTX* SSLCtxMgr::m_ctxClient = 0;
254SSL_CTX* SSLCtxMgr::m_ctxServer = 0;
255certVerifyFuncPtr_t SSLCtxMgr::m_clientCertVerifyCB = 0;
256certVerifyFuncPtr_t SSLCtxMgr::m_serverCertVerifyCB = 0;
257
259// static
260String
261SSLCtxMgr::getOpenSSLErrorDescription()
262{
263 BIO* bio = BIO_new(BIO_s_mem());
264 if (!bio)
265 {
266 return String();
267 }
268 ERR_print_errors(bio);
269 char* p = 0;
270 long len = BIO_get_mem_data(bio, &p);
271 String rval(p, len);
272 int freerv = BIO_free(bio);
273 BLOCXX_ASSERT(freerv == 1);
274 return rval;
275}
276
278// static
279void SSLCtxMgr::disableSSLInit()
280{
281 MutexLock lock(m_initStateGuard);
282 if (m_initState == BLOCXX_SSL_LIBRARY_INITIALIZED)
283 {
284 BLOCXX_THROW(SSLException, "SSLCtxMgr::disableSSLInit(): init() cannot be disabled as it has already been called");
285 }
286 m_initState = BLOCXX_SSL_LIBRARY_INITIALIZATION_DISABLED;
287}
288
290// static
291void SSLCtxMgr::disableLocks()
292{
293 MutexLock lock(m_locksStateGuard);
294 if (m_locksState == BLOCXX_SSL_LOCKS_USED)
295 {
296 BLOCXX_THROW(SSLException, "SSLCtxMgr::disableSSLLocks(): locks cannot be disabled as they are already in use");
297 }
298 m_locksState = BLOCXX_SSL_LOCKS_DISABLED;
299}
300
302// static
303Bool SSLCtxMgr::getSSLInitDisabled()
304{
305 MutexLock lock(m_initStateGuard);
306 return m_initState == BLOCXX_SSL_LIBRARY_INITIALIZATION_DISABLED;
307}
308
310// static
311Bool SSLCtxMgr::getSSLLocksDisabled()
312{
313 MutexLock lock(m_locksStateGuard);
314 return m_locksState == BLOCXX_SSL_LOCKS_DISABLED;
315}
316
318SSL_CTX*
319SSLCtxMgr::initCtx(const String& certfile, const String& keyfile, EVP_PKEY* pkey)
320{
321 g_SSLGlobalWork.get(); // trigger initialization of lazy global stuff
322
323 ERR_clear_error();
324 SSL_CTX* ctx = SSL_CTX_new(SSLv23_method());
325 if (ctx == 0)
326 {
327 BLOCXX_THROW(SSLException, Format("SSLCtxMgr::initCtx(): SSL_CTX_new returned 0: %1", getOpenSSLErrorDescription()).c_str());
328 }
329 SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
330 if (!certfile.empty())
331 {
332 if (SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str()) != 1)
333 {
334 SSL_CTX_free(ctx);
335 BLOCXX_THROW(SSLException, Format("SSLCtxMgr::initCtx(): Couldn't read certificate from file: %1: %2",
336 certfile, getOpenSSLErrorDescription()).c_str());
337 }
338 if (pkey)
339 {
340 int rv = SSL_CTX_use_PrivateKey(ctx, pkey);
341 if (rv != 1)
342 {
343 SSL_CTX_free(ctx);
344 BLOCXX_THROW(SSLException, Format("SSLCtxMgr::initCtx(): Unable to set private key: %1",
345 getOpenSSLErrorDescription()).c_str());
346 }
347 }
348 else if (SSL_CTX_use_PrivateKey_file(ctx, keyfile.empty()?certfile.c_str():keyfile.c_str(), SSL_FILETYPE_PEM) != 1)
349 {
350 SSL_CTX_free(ctx);
351 BLOCXX_THROW(SSLException, Format("SSLCtxMgr::initCtx(): Couldn't read key from file: %1: %2",
352 keyfile.empty()?certfile:keyfile, getOpenSSLErrorDescription()).c_str());
353 }
354 }
355
356 Secure::rand_init();
357
358 return ctx;
359}
360
362void
363SSLCtxMgr::loadDHParams(SSL_CTX* ctx, const String& file)
364{
365 BLOCXX_ASSERT(ctx != 0);
366 ERR_clear_error();
367 BIO* bio = BIO_new_file(file.c_str(), "r");
368 if (bio == NULL)
369 {
370 BLOCXX_THROW(SSLException, Format("SSLCtxMgr::loadDHParams(): Couldn't open DH file %1: %2", file, getOpenSSLErrorDescription()).c_str());
371 }
372 DH* ret = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
373 BIO_free(bio);
374 if (ret == 0)
375 {
376 BLOCXX_THROW(SSLException, Format("SSLCtxMgr::loadDHParams(): PEM_read_bio_DHparams failed: %1", getOpenSSLErrorDescription()).c_str());
377 }
378 if (SSL_CTX_set_tmp_dh(ctx, ret) != 1)
379 {
380 BLOCXX_THROW(SSLException, Format("SSLCtxMgr::loadDHParams(): Couldn't set DH parameters because SSL_CTX_set_tmp_dh failed: %1", getOpenSSLErrorDescription()).c_str());
381 }
382}
384void
385SSLCtxMgr::generateEphRSAKey(SSL_CTX* ctx)
386{
387 BLOCXX_ASSERT(ctx != 0);
388 ERR_clear_error();
389 RSA* rsa = RSA_generate_key(512, RSA_F4, NULL, NULL);
390 if (rsa == 0)
391 {
392 BLOCXX_THROW(SSLException, Format("SSLCtxMgr::generateEphRSAKey(): RSA_generate_key failed: %1", getOpenSSLErrorDescription()).c_str());
393 }
394 if (SSL_CTX_set_tmp_rsa(ctx, rsa) != 1)
395 {
396 RSA_free(rsa);
397 BLOCXX_THROW(SSLException, Format("SSLCtxMgr::generateEphRSAKey(): SSL_CTX_set_tmp_rsa failed. Couldn't set RSA key: %1", getOpenSSLErrorDescription()).c_str());
398 }
399 RSA_free(rsa);
400}
402void
403SSLCtxMgr::initClient(const String& certfile, const String& keyfile)
404{
405 if (m_ctxClient)
406 {
407 uninitClient();
408 }
409 m_ctxClient = initCtx(certfile,keyfile);
410}
412void
413SSLCtxMgr::initServer(const String& certfile, const String& keyfile)
414{
415 if (certfile.empty())
416 {
417 BLOCXX_THROW(SSLException, "SSLCtxMgr::initCtx(): no certificate file specified");
418 }
419 if (m_ctxServer)
420 {
421 uninitServer();
422 }
423 m_ctxServer = initCtx(certfile,keyfile);
424 //loadDHParams(m_ctx, dhfile);
425 generateEphRSAKey(m_ctxServer);
426 String sessID("SSL_SESSION_");
427 sessID += String(Secure::rand_range<UInt16>(0, 10000));
428 int sessIDLen =
429 (SSL_MAX_SSL_SESSION_ID_LENGTH < (sessID.length())) ?
430 SSL_MAX_SSL_SESSION_ID_LENGTH : (sessID.length());
431 ERR_clear_error();
432 if (SSL_CTX_set_session_id_context(m_ctxServer, reinterpret_cast<const unsigned char*>(sessID.c_str()), sessIDLen) != 1)
433 {
434 BLOCXX_THROW(SSLException, Format("SSLCtxMgr::initServer(): SSL_CTX_set_session_id_context failed: %1", getOpenSSLErrorDescription()).c_str());
435 }
436 SSL_CTX_set_verify(m_ctxServer, SSL_VERIFY_PEER /*| SSL_VERIFY_FAIL_IF_NO_PEER_CERT*/, NULL);
437}
439// STATIC
440int
441SSLCtxMgr::pem_passwd_cb(char* buf, int size, int /*rwflag*/,
442 void* /*userData*/)
443{
444 String passwd = GetPass::getPass("Enter the password for the SSL certificate: ");
445
446 strncpy(buf, passwd.c_str(), size);
447 buf[size - 1] = '\0';
448
449 return passwd.length();
450}
452// STATIC
453bool
454SSLCtxMgr::checkClientCert(SSL* ssl, const String& hostName)
455{
456 return checkCert(ssl, hostName, m_clientCertVerifyCB);
457}
459// STATIC
460bool
461SSLCtxMgr::checkServerCert(SSL* ssl, const String& hostName)
462{
463 return checkCert(ssl, hostName, m_serverCertVerifyCB);
464}
466// STATIC
467bool
468SSLCtxMgr::checkCert(SSL* ssl, const String& hostName,
469 certVerifyFuncPtr_t certVerifyCB)
470{
471 BLOCXX_ASSERT(ssl != 0);
472
473 /* TODO this isn't working.
474 if (SSL_get_verify_result(ssl)!=X509_V_OK)
475 {
476 cout << "SSL_get_verify_results failed." << endl;
477 return false;
478 }
479 */
480 /*Check the cert chain. The chain length
481 is automatically checked by OpenSSL when we
482 set the verify depth in the ctx */
483 /*Check the common name*/
484 if (certVerifyCB)
485 {
486 X509 *peer = SSL_get_peer_certificate(ssl);
487 X509Freer x509freer(peer);
488 if (peer == 0)
489 {
490 return false;
491 }
492 if (certVerifyCB(peer, hostName) == 0)
493 {
494 return false;
495 }
496 else
497 {
498 return true;
499 }
500 }
501 return true;
502}
504// STATIC
505int
506SSLCtxMgr::sslRead(SSL* ssl, char* buf, int len)
507{
508 int cc = SSL_ERROR_WANT_READ;
509 int r = -1;
510 int retries = 0;
511 while (cc == SSL_ERROR_WANT_READ && retries < BLOCXX_SSL_RETRY_LIMIT)
512 {
513 r = SSL_read(ssl, buf, len);
514 cc = SSL_get_error(ssl, r);
515 retries++;
516 }
517
518 switch (cc)
519 {
520 case SSL_ERROR_NONE:
521 return r;
522 case SSL_ERROR_ZERO_RETURN:
523 return -1;
524 default:
525 return -1;
526 }
527}
529// STATIC
530int
531SSLCtxMgr::sslWrite(SSL* ssl, const char* buf, int len)
532{
533 int r = 0;
534 int cc;
535 int retries;
536 int myLen = len;
537 int offset = 0;
538#ifndef BLOCXX_WIN32
539 // block SIGPIPE so we don't kill the process if the socket is closed.
540 SignalScope ss(SIGPIPE, SIG_IGN);
541#endif
542 while (myLen > 0)
543 {
544 cc = SSL_ERROR_WANT_WRITE;
545 retries = 0;
546 while(cc == SSL_ERROR_WANT_WRITE && retries < BLOCXX_SSL_RETRY_LIMIT)
547 {
548 r = SSL_write(ssl, buf + offset, myLen);
549 cc = SSL_get_error(ssl, r);
550 retries++;
551 }
552
553 if (cc == SSL_ERROR_NONE)
554 {
555 myLen -= r;
556 offset += r;
557 }
558 else
559 {
560 return -1;
561 }
562 }
563 return len;
564}
566void
567SSLCtxMgr::uninit()
568{
569 uninitClient();
570 uninitServer();
571
572 MutexLock initLock(m_initStateGuard);
573 if (m_initState == BLOCXX_SSL_LIBRARY_INITIALIZED)
574 {
575 // free up memory allocated in SSL_library_init()
576 EVP_cleanup();
577 // free up memory allocated in SSL_load_error_strings()
578 ERR_free_strings();
579
580 m_initState = BLOCXX_SSL_LIBRARY_NOT_INITIALIZED;
581 }
582}
584void
585SSLCtxMgr::uninitClient()
586{
587 if (m_ctxClient)
588 {
589 SSL_CTX_free(m_ctxClient);
590 m_ctxClient = NULL;
591 }
592}
594void
595SSLCtxMgr::uninitServer()
596{
597 if (m_ctxServer)
598 {
599 SSL_CTX_free(m_ctxServer);
600 m_ctxServer = NULL;
601 }
602}
603
604namespace
605{
606
608extern "C"
609{
610static int verify_callback(int ok, X509_STORE_CTX *store)
611{
612 int index = SSL_get_ex_data_X509_STORE_CTX_idx();
613 if (index < 0)
614 {
615 return 0;
616 }
617 SSL* ssl = static_cast<SSL*>(X509_STORE_CTX_get_ex_data(store, index));
618 if (ssl == 0)
619 {
620 return 0;
621 }
622 OWSSLContext* owctx = static_cast<OWSSLContext*>(SSL_get_ex_data(ssl, SSLServerCtx::SSL_DATA_INDEX));
623 BLOCXX_ASSERT(owctx);
624 if (owctx == 0)
625 {
626 return 0;
627 }
628
639 if (!ok)
640 {
641 owctx->peerCertPassedVerify = OWSSLContext::VERIFY_FAIL;
642 }
643 else
644 {
645 // if the cert failed on a previous call, we don't want to change
646 // the status.
647 if (owctx->peerCertPassedVerify != OWSSLContext::VERIFY_FAIL)
648 {
649 owctx->peerCertPassedVerify = OWSSLContext::VERIFY_PASS;
650 }
651 }
652
653#ifdef BLOCXX_DEBUG
654 if (!ok)
655 {
656 char data[256];
657 X509 *cert = X509_STORE_CTX_get_current_cert(store);
658 int depth = X509_STORE_CTX_get_error_depth(store);
659 int err = X509_STORE_CTX_get_error(store);
660
661 fprintf(stderr, "-Error with certificate at depth: %i\n", depth);
662 X509_NAME_oneline(X509_get_issuer_name(cert), data, 256);
663 fprintf(stderr, " issuer = %s\n", data);
664 X509_NAME_oneline(X509_get_subject_name(cert), data, 256);
665 fprintf(stderr, " subject = %s\n", data);
666 fprintf(stderr, " err %i:%s\n", err, X509_verify_cert_error_string(err));
667 }
668#endif
669
670 return 1;
671}
672} // end extern "C"
673
674} // end unnamed namespace
675
677SSLCtxBase::SSLCtxBase(const SSLOpts& opts)
678 : m_ctx(0)
679{
680 m_ctx = SSLCtxMgr::initCtx(opts.certfile, opts.keyfile, opts.pkey);
681
682 SSLCtxMgr::generateEphRSAKey(m_ctx); // TODO what the heck is this?
683 String sessID("SSL_SESSION_");
684 sessID += String(Secure::rand_range<UInt16>(0, 10000));
685 int sessIDLen =
686 (SSL_MAX_SSL_SESSION_ID_LENGTH < (sessID.length())) ?
687 SSL_MAX_SSL_SESSION_ID_LENGTH : (sessID.length());
688 ERR_clear_error();
689 if (SSL_CTX_set_session_id_context(m_ctx, reinterpret_cast<const unsigned char*>(sessID.c_str()), sessIDLen) != 1)
690 {
691 SSL_CTX_free(m_ctx);
692 BLOCXX_THROW(SSLException, Format("SSLCtxMgr::initServer(): SSL_CTX_set_session_id_context failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
693 }
694
695 if (opts.verifyMode != SSLOpts::MODE_DISABLED && !opts.trustStore.empty())
696 {
697 if (!FileSystem::exists(opts.trustStore))
698 {
699 SSL_CTX_free(m_ctx);
700 BLOCXX_THROW(SSLException, Format("Error loading truststore %1",
701 opts.trustStore).c_str());
702 }
703 if (SSL_CTX_load_verify_locations(m_ctx,0,opts.trustStore.c_str()) != 1)
704 {
705 SSL_CTX_free(m_ctx);
706 BLOCXX_THROW(SSLException, Format("Error loading truststore %1: %2", opts.trustStore, SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
707 }
708 }
709 /* TODO remove.
710 if (SSL_CTX_set_default_verify_paths(m_ctx) != 1)
711 {
712 BLOCXX_THROW(SSLException, "Error loading default CA store(s)");
713 }
714 */
715 switch (opts.verifyMode)
716 {
717 case SSLOpts::MODE_DISABLED:
718 SSL_CTX_set_verify(m_ctx, SSL_VERIFY_NONE, 0);
719 break;
720 case SSLOpts::MODE_REQUIRED:
721 SSL_CTX_set_verify(m_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0);
722 break;
723 case SSLOpts::MODE_OPTIONAL:
724 case SSLOpts::MODE_AUTOUPDATE:
725 SSL_CTX_set_verify(m_ctx, SSL_VERIFY_PEER, verify_callback);
726 break;
727 default:
728 BLOCXX_ASSERTMSG(false, "Bad option, shouldn't happen");
729 break;
730 }
731
732 SSL_CTX_set_verify_depth(m_ctx, 4);
733
734}
735
737SSLCtxBase::~SSLCtxBase()
738{
739 if (m_ctx)
740 {
741 SSL_CTX_free(m_ctx);
742 }
743 ERR_clear_error();
744 ERR_remove_state(0);
745}
746
748SSL_CTX*
749SSLCtxBase::getSSLCtx() const
750{
751 return m_ctx;
752}
753
754SSLOpts::SSLOpts()
755 : verifyMode(MODE_DISABLED)
756 , pkey(0)
757{
758}
759
760SSLOpts::~SSLOpts()
761{
762 if (pkey != 0)
763 {
764 EVP_PKEY_free(pkey);
765 pkey = 0;
766 }
767}
768
769
770
772SSLServerCtx::SSLServerCtx(const SSLOpts& opts)
773 : SSLCtxBase(opts)
774{
775}
777SSLClientCtx::SSLClientCtx(const SSLOpts& opts)
778 : SSLCtxBase(opts)
779{
780}
781
782static GlobalMutex m_mapGuard = BLOCXX_GLOBAL_MUTEX_INIT();
783
785SSLTrustStore::SSLTrustStore(const String& storeLocation)
786 : m_store(storeLocation)
787{
788 m_mapfile = m_store + "/map";
789 if (FileSystem::exists(m_mapfile))
790 {
791 MutexLock mlock(m_mapGuard);
792 readMap();
793 }
794}
795
797bool
798SSLTrustStore::getUser(const String& certhash, String& user, String& uid)
799{
800 MutexLock mlock(m_mapGuard);
801 Map<String, UserInfo>::const_iterator iter = m_map.find(certhash);
802 if (iter == m_map.end())
803 {
804 return false;
805 }
806 user = iter->second.user;
807 uid = iter->second.uid;
808 return true;
809}
810
812void
813SSLTrustStore::addCertificate(X509* cert, const String& user, const String& uid)
814{
815 static const int numtries = 1000;
816 BLOCXX_ASSERT(cert);
817 OStringStream ss;
818 unsigned long hash = X509_subject_name_hash(cert);
819 ss << std::hex << hash;
820 String filename = m_store + "/" + ss.toString() + ".";
821 int i = 0;
822 for (i = 0; i < numtries; ++i)
823 {
824 String temp = filename + String(i);
825 if (FileSystem::exists(temp))
826 {
827 continue;
828 }
829 filename = temp;
830 break;
831 }
832 if (i == numtries)
833 {
834 BLOCXX_THROW(SSLException, "Unable to find a valid filename to store cert");
835 }
836 FILE* fp = fopen(filename.c_str(), "w");
837 if (!fp)
838 {
839 BLOCXX_THROW_ERRNO_MSG(SSLException, Format("Unable to open new cert file for writing: %1", filename).c_str());
840 }
841
842 ERR_clear_error();
843 // Undocumented function in OpenSSL. We assume it returns 1 on success
844 // like most OpenSSL funcs.
845 if (PEM_write_X509(fp, cert) != 1)
846 {
847 fclose(fp);
848 BLOCXX_THROW(SSLException, Format("SSL error while writing certificate to %1: %2", filename, SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
849 }
850 fclose(fp);
851
852 String digest = getCertMD5Fingerprint(cert);
853 MutexLock mlock(m_mapGuard);
854 UserInfo info;
855 info.user = user;
856 info.uid = uid;
857 m_map[digest] = info;
858 writeMap();
859}
860
862String
863SSLTrustStore::getCertMD5Fingerprint(X509* cert)
864{
865 unsigned char digest[16];
866 unsigned int len = 16;
867 X509_digest(cert, EVP_md5(), digest, &len);
868 return MD5::convertBinToHex(digest);
869}
870
872void
873SSLTrustStore::writeMap()
874{
875 std::ofstream f(m_mapfile.c_str(), std::ios::out);
876 if (!f)
877 {
878 BLOCXX_THROW_ERRNO_MSG(SSLException, Format("SSL error opening map file: %1", m_mapfile).c_str());
879 }
880 for (Map<String, UserInfo>::const_iterator iter = m_map.begin();
881 iter != m_map.end(); ++iter)
882 {
883 f << iter->first << " " << iter->second.user
884 << " " << iter->second.uid << "\n";
885 }
886 f.close();
887}
888
890void
891SSLTrustStore::readMap()
892{
893 std::ifstream f(m_mapfile.c_str(), std::ios::in);
894 if (!f)
895 {
896 BLOCXX_THROW_ERRNO_MSG(SSLException, Format("SSL error opening map file: %1", m_mapfile).c_str());
897 }
898 int lineno = 0;
899 while (f)
900 {
901 String line = String::getLine(f);
902 if (!f)
903 {
904 break;
905 }
906 ++lineno;
907 StringArray toks = line.tokenize();
908 if (toks.size() != 3 && toks.size() != 2)
909 {
910 BLOCXX_THROW(SSLException, Format("Error processing user map %1 at line %2", m_mapfile, lineno).c_str());
911 }
912 UserInfo info;
913 info.user = toks[1];
914 if (toks.size() == 3)
915 {
916 info.uid = toks[2];
917 }
918 m_map.insert(std::make_pair(toks[0], info));
919 }
920#ifdef BLOCXX_DEBUG
921 std::cerr << "cert<>user map initizialized with " << m_map.size() << " users" << std::endl;
922#endif
923 f.close();
924}
925
927
928OWSSLContext::OWSSLContext()
929 : peerCertPassedVerify(VERIFY_NONE)
930{
931}
933OWSSLContext::~OWSSLContext()
934{
935}
936
937
938} // end namespace BLOCXX_NAMESPACE
939
940#endif // #ifdef BLOCXX_HAVE_OPENSSL
941
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(),...
Definition Assertion.hpp:57
#define BLOCXX_ASSERTMSG(CON, MSG)
BLOCXX_ASSERTMSG works the same as BLOCXX_ASSERT, but with a second string parameter that will be add...
Definition Assertion.hpp:71
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
#define BLOCXX_THROW_ERRNO_MSG(exType, msg)
Throw an exception using FILE, LINE, errno and strerror(errno)
#define BLOCXX_GLOBAL_MUTEX_INIT()
#define BLOCXX_LAZY_GLOBAL_INIT(...)
Statically initialize a LazyGlobal instance.
iterator insert(iterator position, const T &x)
Insert an element in the Array before an element specified by an iterator.
bool release()
Release ownership of this Mutex object.
Definition Mutex.cpp:79
void acquire()
Acquire ownership of this Mutex object.
Definition Mutex.cpp:69
BLOCXX_COMMON_API String getPass(const String &prompt)
The getpass function displays a prompt to the standard error output, and reads in a password from /de...
Definition GetPass.cpp:87
BLOCXX_COMMON_API UInt64 thread_t_ToUInt64(Thread_t thr)
Convert a Thread_t to an UInt64.
Taken from RFC 1321.
Array< String > StringArray
Definition CommonFwd.hpp:73
LazyGlobal< Mutex, int, GlobalMutexFactory > GlobalMutex