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"
45 #include "blocxx/GlobalMutex.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"
51 #include "blocxx/SignalScope.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
84 extern "C"
85 {
86 struct CRYPTO_dynlock_value
87 {
89 };
90 }
91 
92 namespace BLOCXX_NAMESPACE
93 {
94 
95 namespace
96 {
97 
98 BLOCXX_NAMESPACE::Mutex* mutex_buf = 0;
99 
100 extern "C"
101 {
102 
103 static 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"
109 static 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 
122 static void dyn_destroy_function(struct CRYPTO_dynlock_value *l,
123  const char *, int)
124 {
125  delete l;
126 }
127 
128 static unsigned long id_function()
129 {
131 }
132 
133 static 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 
146 class X509Freer
147 {
148 public:
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  }
160 private:
161  X509* m_x509;
162 };
163 
164 enum SSLLibraryInit_t {
165  BLOCXX_SSL_LIBRARY_NOT_INITIALIZED,
166  BLOCXX_SSL_LIBRARY_INITIALIZED,
167  BLOCXX_SSL_LIBRARY_INITIALIZATION_DISABLED
168 };
169 SSLLibraryInit_t m_initState = BLOCXX_SSL_LIBRARY_NOT_INITIALIZED;
170 GlobalMutex m_initStateGuard = BLOCXX_GLOBAL_MUTEX_INIT();
171 
172 
173 enum SSLLocks_t {
174  BLOCXX_SSL_LOCKS_NOT_USED,
175  BLOCXX_SSL_LOCKS_USED,
176  BLOCXX_SSL_LOCKS_DISABLED
177 };
178 SSLLocks_t m_locksState = BLOCXX_SSL_LOCKS_NOT_USED;
179 GlobalMutex m_locksStateGuard = BLOCXX_GLOBAL_MUTEX_INIT();
180 
182 struct 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 
240 struct SSLGlobalWorkFactory
241 {
242  static SSLGlobalWork* create(int /*dummy*/)
243  {
244  return new SSLGlobalWork();
245  }
246 };
247 
248 LazyGlobal<SSLGlobalWork, int, SSLGlobalWorkFactory> g_SSLGlobalWork = BLOCXX_LAZY_GLOBAL_INIT(0);
249 
250 
251 } // end unnamed namespace
252 
253 SSL_CTX* SSLCtxMgr::m_ctxClient = 0;
254 SSL_CTX* SSLCtxMgr::m_ctxServer = 0;
255 certVerifyFuncPtr_t SSLCtxMgr::m_clientCertVerifyCB = 0;
256 certVerifyFuncPtr_t SSLCtxMgr::m_serverCertVerifyCB = 0;
257 
259 // static
260 String
261 SSLCtxMgr::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
279 void 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
291 void 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
303 Bool SSLCtxMgr::getSSLInitDisabled()
304 {
305  MutexLock lock(m_initStateGuard);
306  return m_initState == BLOCXX_SSL_LIBRARY_INITIALIZATION_DISABLED;
307 }
308 
310 // static
311 Bool SSLCtxMgr::getSSLLocksDisabled()
312 {
313  MutexLock lock(m_locksStateGuard);
314  return m_locksState == BLOCXX_SSL_LOCKS_DISABLED;
315 }
316 
318 SSL_CTX*
319 SSLCtxMgr::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 
362 void
363 SSLCtxMgr::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 }
384 void
385 SSLCtxMgr::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 }
402 void
403 SSLCtxMgr::initClient(const String& certfile, const String& keyfile)
404 {
405  if (m_ctxClient)
406  {
407  uninitClient();
408  }
409  m_ctxClient = initCtx(certfile,keyfile);
410 }
412 void
413 SSLCtxMgr::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
440 int
441 SSLCtxMgr::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
453 bool
454 SSLCtxMgr::checkClientCert(SSL* ssl, const String& hostName)
455 {
456  return checkCert(ssl, hostName, m_clientCertVerifyCB);
457 }
459 // STATIC
460 bool
461 SSLCtxMgr::checkServerCert(SSL* ssl, const String& hostName)
462 {
463  return checkCert(ssl, hostName, m_serverCertVerifyCB);
464 }
466 // STATIC
467 bool
468 SSLCtxMgr::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
505 int
506 SSLCtxMgr::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
530 int
531 SSLCtxMgr::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 }
566 void
567 SSLCtxMgr::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 }
584 void
585 SSLCtxMgr::uninitClient()
586 {
587  if (m_ctxClient)
588  {
589  SSL_CTX_free(m_ctxClient);
590  m_ctxClient = NULL;
591  }
592 }
594 void
595 SSLCtxMgr::uninitServer()
596 {
597  if (m_ctxServer)
598  {
599  SSL_CTX_free(m_ctxServer);
600  m_ctxServer = NULL;
601  }
602 }
603 
604 namespace
605 {
606 
608 extern "C"
609 {
610 static 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 
677 SSLCtxBase::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 
737 SSLCtxBase::~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 
748 SSL_CTX*
749 SSLCtxBase::getSSLCtx() const
750 {
751  return m_ctx;
752 }
753 
754 SSLOpts::SSLOpts()
755  : verifyMode(MODE_DISABLED)
756  , pkey(0)
757 {
758 }
759 
760 SSLOpts::~SSLOpts()
761 {
762  if (pkey != 0)
763  {
764  EVP_PKEY_free(pkey);
765  pkey = 0;
766  }
767 }
768 
769 
770 
772 SSLServerCtx::SSLServerCtx(const SSLOpts& opts)
773  : SSLCtxBase(opts)
774 {
775 }
777 SSLClientCtx::SSLClientCtx(const SSLOpts& opts)
778  : SSLCtxBase(opts)
779 {
780 }
781 
782 static GlobalMutex m_mapGuard = BLOCXX_GLOBAL_MUTEX_INIT();
783 
785 SSLTrustStore::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 
797 bool
798 SSLTrustStore::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 
812 void
813 SSLTrustStore::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 
862 String
863 SSLTrustStore::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 
872 void
873 SSLTrustStore::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 
890 void
891 SSLTrustStore::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 
928 OWSSLContext::OWSSLContext()
929  : peerCertPassedVerify(VERIFY_NONE)
930 {
931 }
933 OWSSLContext::~OWSSLContext()
934 {
935 }
936 
937 
938 } // end namespace BLOCXX_NAMESPACE
939 
940 #endif // #ifdef BLOCXX_HAVE_OPENSSL
941