SSLHelper.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. /*
  2. * Copyright: JessMA Open Source (ldcsaa@gmail.com)
  3. *
  4. * Version : 3.6.1
  5. * Author : Bruce Liang
  6. * Website : http://www.jessma.org
  7. * Project : https://github.com/ldcsaa
  8. * Blog : http://www.cnblogs.com/ldcsaa
  9. * Wiki : http://www.oschina.net/p/hp-socket
  10. * QQ Group : 75375912
  11. *
  12. * Licensed under the Apache License, Version 2.0 (the "License");
  13. * you may not use this file except in compliance with the License.
  14. * You may obtain a copy of the License at
  15. *
  16. * http://www.apache.org/licenses/LICENSE-2.0
  17. *
  18. * Unless required by applicable law or agreed to in writing, software
  19. * distributed under the License is distributed on an "AS IS" BASIS,
  20. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  21. * See the License for the specific language governing permissions and
  22. * limitations under the License.
  23. */
  24. #include "stdafx.h"
  25. #include "SSLHelper.h"
  26. #include "openssl/ssl.h"
  27. #include "openssl/err.h"
  28. #include "openssl/engine.h"
  29. #include "openssl/x509v3.h"
  30. #include "../../Common/Src/WaitFor.h"
  31. #include <atlpath.h>
  32. CSSLContext CSSLContext::sm_Instance;
  33. CSSLContext& g_SSL = CSSLContext::getInstance();
  34. const DWORD CSSLSessionPool::DEFAULT_ITEM_CAPACITY = CItemPool::DEFAULT_ITEM_CAPACITY;
  35. const DWORD CSSLSessionPool::DEFAULT_ITEM_POOL_SIZE = CItemPool::DEFAULT_POOL_SIZE;
  36. const DWORD CSSLSessionPool::DEFAULT_ITEM_POOL_HOLD = CItemPool::DEFAULT_POOL_HOLD;
  37. const DWORD CSSLSessionPool::DEFAULT_SESSION_LOCK_TIME = 10 * 1000;
  38. const DWORD CSSLSessionPool::DEFAULT_SESSION_POOL_SIZE = 150;
  39. const DWORD CSSLSessionPool::DEFAULT_SESSION_POOL_HOLD = 600;
  40. BOOL CSSLContext::Initialize(EnSSLSessionMode enSessionMode, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPasswod, LPCTSTR lpszCAPemCertFileOrPath)
  41. {
  42. ASSERT(!IsValid());
  43. if(IsValid())
  44. {
  45. ::SetLastError(ERROR_INVALID_STATE);
  46. return FALSE;
  47. }
  48. CreateContext();
  49. m_enSessionMode = enSessionMode;
  50. if(!LoadCertAndKey(iVerifyMode, lpszPemCertFile, lpszPemKeyFile, lpszKeyPasswod, lpszCAPemCertFileOrPath))
  51. {
  52. Cleanup();
  53. return FALSE;
  54. }
  55. return m_bValid = TRUE;
  56. }
  57. void CSSLContext::CreateContext()
  58. {
  59. m_iLockNum = ::CRYPTO_num_locks();
  60. if(m_iLockNum > 0)
  61. m_pcsLocks = new CSimpleRWLock[m_iLockNum];
  62. /*
  63. #ifdef _DEBUG
  64. CRYPTO_malloc_debug_init();
  65. CRYPTO_dbg_set_options(V_CRYPTO_MDEBUG_ALL);
  66. CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
  67. #endif
  68. */
  69. CRYPTO_set_locking_callback (&ssl_lock_callback);
  70. CRYPTO_set_dynlock_create_callback (&ssl_lock_dyn_create_callback);
  71. CRYPTO_set_dynlock_destroy_callback (&ssl_lock_dyn_destroy_callback);
  72. CRYPTO_set_dynlock_lock_callback (&ssl_lock_dyn_callback);
  73. SSL_library_init();
  74. SSL_load_error_strings();
  75. OpenSSL_add_all_algorithms();
  76. }
  77. BOOL CSSLContext::LoadCertAndKey(int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPasswod, LPCTSTR lpszCAPemCertFileOrPath)
  78. {
  79. USES_CONVERSION;
  80. m_sslCtx = SSL_CTX_new(SSLv23_method());
  81. SSL_CTX_set_verify(m_sslCtx, iVerifyMode, nullptr);
  82. if(lpszCAPemCertFileOrPath != nullptr)
  83. {
  84. LPCTSTR lpszCAPemCertFile = nullptr;
  85. LPCTSTR lpszCAPemCertPath = nullptr;
  86. if(!ATLPath::FileExists(lpszCAPemCertFileOrPath))
  87. {
  88. ::SetLastError(ERROR_FILE_NOT_FOUND);
  89. return FALSE;
  90. }
  91. if(!ATLPath::IsDirectory(lpszCAPemCertFileOrPath))
  92. lpszCAPemCertFile = lpszCAPemCertFileOrPath;
  93. else
  94. lpszCAPemCertPath = lpszCAPemCertFileOrPath;
  95. if(!SSL_CTX_load_verify_locations(m_sslCtx, T2CA(lpszCAPemCertFile), T2CA(lpszCAPemCertPath)))
  96. {
  97. ::SetLastError(ERROR_INVALID_DATA);
  98. return FALSE;
  99. }
  100. if(!SSL_CTX_set_default_verify_paths(m_sslCtx))
  101. {
  102. ::SetLastError(ERROR_FUNCTION_FAILED);
  103. return FALSE;
  104. }
  105. if(m_enSessionMode == SSL_SM_SERVER && iVerifyMode & SSL_VM_PEER)
  106. {
  107. STACK_OF(X509_NAME)* caCertNames = SSL_load_client_CA_file(T2CA(lpszCAPemCertFileOrPath));
  108. if(caCertNames == nullptr)
  109. {
  110. ::SetLastError(ERROR_EMPTY);
  111. return FALSE;
  112. }
  113. SSL_CTX_set_client_CA_list(m_sslCtx, caCertNames);
  114. }
  115. }
  116. if(lpszPemCertFile != nullptr)
  117. {
  118. if( !ATLPath::FileExists(lpszPemCertFile) ||
  119. ATLPath::IsDirectory(lpszPemCertFile) )
  120. {
  121. ::SetLastError(ERROR_FILE_NOT_FOUND);
  122. return FALSE;
  123. }
  124. if( lpszPemKeyFile == nullptr ||
  125. !ATLPath::FileExists(lpszPemKeyFile) ||
  126. ATLPath::IsDirectory(lpszPemKeyFile) )
  127. {
  128. ::SetLastError(ERROR_FILE_NOT_FOUND);
  129. return FALSE;
  130. }
  131. if(lpszKeyPasswod != nullptr)
  132. SSL_CTX_set_default_passwd_cb_userdata(m_sslCtx, (void*)T2CA(lpszKeyPasswod));
  133. if(!SSL_CTX_use_PrivateKey_file(m_sslCtx, T2CA(lpszPemKeyFile), SSL_FILETYPE_PEM))
  134. {
  135. ::SetLastError(ERROR_INVALID_PASSWORD);
  136. return FALSE;
  137. }
  138. if(!SSL_CTX_use_certificate_chain_file(m_sslCtx, T2CA(lpszPemCertFile)))
  139. {
  140. ::SetLastError(ERROR_INVALID_DATA);
  141. return FALSE;
  142. }
  143. if(!SSL_CTX_check_private_key(m_sslCtx))
  144. {
  145. ::SetLastError(ERROR_INVALID_ACCESS);
  146. return FALSE;
  147. }
  148. }
  149. return TRUE;
  150. }
  151. void CSSLContext::Cleanup()
  152. {
  153. m_bValid = FALSE;
  154. if(m_sslCtx != nullptr)
  155. {
  156. SSL_CTX_free(m_sslCtx);
  157. m_sslCtx = nullptr;
  158. }
  159. CleanupThreadState();
  160. CleanupGlobalState();
  161. CRYPTO_set_locking_callback (nullptr);
  162. CRYPTO_set_dynlock_create_callback (nullptr);
  163. CRYPTO_set_dynlock_destroy_callback (nullptr);
  164. CRYPTO_set_dynlock_lock_callback (nullptr);
  165. if(m_iLockNum > 0)
  166. {
  167. delete[] m_pcsLocks;
  168. m_pcsLocks = nullptr;
  169. m_iLockNum = 0;
  170. }
  171. }
  172. void CSSLContext::CleanupGlobalState()
  173. {
  174. CONF_modules_free();
  175. ENGINE_cleanup();
  176. EVP_cleanup();
  177. CRYPTO_cleanup_all_ex_data();
  178. ERR_free_strings();
  179. SSL_COMP_free_compression_methods();
  180. }
  181. void CSSLContext::CleanupThreadState(DWORD dwThreadID)
  182. {
  183. CRYPTO_THREADID tid = {nullptr, dwThreadID};
  184. CRYPTO_THREADID_current(&tid);
  185. ERR_remove_thread_state(&tid);
  186. }
  187. void CSSLContext::ssl_lock_callback(int mode, int n, const char *file, int line)
  188. {
  189. mode & CRYPTO_LOCK
  190. ? (mode & CRYPTO_READ
  191. ? g_SSL.m_pcsLocks[n].WaitToRead()
  192. : g_SSL.m_pcsLocks[n].WaitToWrite())
  193. : (mode & CRYPTO_READ
  194. ? g_SSL.m_pcsLocks[n].ReadDone()
  195. : g_SSL.m_pcsLocks[n].WriteDone());
  196. }
  197. CRYPTO_dynlock_value* CSSLContext::ssl_lock_dyn_create_callback(const char *file, int line)
  198. {
  199. return new DynamicLock;
  200. }
  201. void CSSLContext::ssl_lock_dyn_callback(int mode, CRYPTO_dynlock_value* l, const char *file, int line)
  202. {
  203. mode & CRYPTO_LOCK
  204. ? (mode & CRYPTO_READ
  205. ? l->cs.WaitToRead()
  206. : l->cs.WaitToWrite())
  207. : (mode & CRYPTO_READ
  208. ? l->cs.ReadDone()
  209. : l->cs.WriteDone());
  210. }
  211. void CSSLContext::ssl_lock_dyn_destroy_callback(CRYPTO_dynlock_value* l, const char *file, int line)
  212. {
  213. delete l;
  214. }
  215. BOOL CSSLSession::WriteRecvChannel(const BYTE* pData, int iLength)
  216. {
  217. ASSERT(pData && iLength > 0);
  218. BOOL isOK = TRUE;
  219. int bytes = BIO_write(m_bioRecv, pData, iLength);
  220. if(bytes > 0)
  221. ASSERT(bytes == iLength);
  222. else if(!BIO_should_retry(m_bioRecv))
  223. isOK = FALSE;
  224. return isOK;
  225. }
  226. BOOL CSSLSession::ReadRecvChannel()
  227. {
  228. BOOL isOK = TRUE;
  229. int bytes = SSL_read(m_ssl, m_bufRecv.buf, m_pitRecv->Capacity());
  230. if(bytes > 0)
  231. m_bufRecv.len = bytes;
  232. else if(!IsFatalError(bytes))
  233. m_bufRecv.len = 0;
  234. else
  235. isOK = FALSE;
  236. if(isOK && m_enStatus == SSL_HSS_PROC && SSL_is_init_finished(m_ssl))
  237. m_enStatus = SSL_HSS_SUCC;
  238. return isOK;
  239. }
  240. BOOL CSSLSession::WriteSendChannel(const BYTE* pData, int iLength)
  241. {
  242. ASSERT(IsReady());
  243. ASSERT(pData && iLength > 0);
  244. BOOL isOK = TRUE;
  245. int bytes = SSL_write(m_ssl, pData, iLength);
  246. if(bytes > 0)
  247. ASSERT(bytes == iLength);
  248. else if(IsFatalError(bytes))
  249. isOK = FALSE;
  250. return isOK;
  251. }
  252. BOOL CSSLSession::WriteSendChannel(const WSABUF pBuffers[], int iCount)
  253. {
  254. ASSERT(pBuffers && iCount > 0);
  255. BOOL isOK = TRUE;
  256. for(int i = 0; i < iCount; i++)
  257. {
  258. const WSABUF& buffer = pBuffers[i];
  259. if(buffer.len > 0)
  260. {
  261. if(!WriteSendChannel((const BYTE*)buffer.buf, buffer.len))
  262. {
  263. isOK = FALSE;
  264. break;
  265. }
  266. }
  267. }
  268. return isOK;
  269. }
  270. BOOL CSSLSession::ReadSendChannel()
  271. {
  272. if(BIO_pending(m_bioSend) == 0)
  273. {
  274. m_bufSend.len = 0;
  275. return TRUE;
  276. }
  277. BOOL isOK = TRUE;
  278. int bytes = BIO_read(m_bioSend, m_bufSend.buf, m_pitSend->Capacity());
  279. if(bytes > 0)
  280. m_bufSend.len = bytes;
  281. else if(BIO_should_retry(m_bioSend))
  282. m_bufSend.len = 0;
  283. else
  284. isOK = FALSE;
  285. return isOK;
  286. }
  287. CSSLSession* CSSLSession::Renew()
  288. {
  289. ASSERT(!IsValid());
  290. m_ssl = SSL_new(g_SSL.GetContext());
  291. m_bioSend = BIO_new(BIO_s_mem());
  292. m_bioRecv = BIO_new(BIO_s_mem());
  293. SSL_set_bio(m_ssl, m_bioRecv, m_bioSend);
  294. g_SSL.GetSessionMode() == SSL_SM_CLIENT
  295. ? SSL_connect(m_ssl) : SSL_accept(m_ssl);
  296. m_pitSend = m_itPool.PickFreeItem();
  297. m_pitRecv = m_itPool.PickFreeItem();
  298. m_bufSend.buf = (char*)m_pitSend->Ptr();
  299. m_bufRecv.buf = (char*)m_pitRecv->Ptr();
  300. m_enStatus = SSL_HSS_PROC;
  301. return this;
  302. }
  303. BOOL CSSLSession::Reset()
  304. {
  305. BOOL isOK = FALSE;
  306. if(IsValid())
  307. {
  308. CCriSecLock locallock(m_csSend);
  309. if(IsValid())
  310. {
  311. m_enStatus = SSL_HSS_INIT;
  312. SSL_shutdown(m_ssl);
  313. SSL_free(m_ssl);
  314. m_itPool.PutFreeItem(m_pitSend);
  315. m_itPool.PutFreeItem(m_pitRecv);
  316. m_pitSend = nullptr;
  317. m_pitRecv = nullptr;
  318. m_ssl = nullptr;
  319. m_bioSend = nullptr;
  320. m_bioRecv = nullptr;
  321. m_dwFreeTime= ::TimeGetTime();
  322. isOK = TRUE;
  323. }
  324. }
  325. ERR_clear_error();
  326. return isOK;
  327. }
  328. inline BOOL CSSLSession::IsFatalError(int iBytes)
  329. {
  330. int iErrorCode = SSL_get_error(m_ssl, iBytes);
  331. if( iErrorCode == SSL_ERROR_NONE ||
  332. iErrorCode == SSL_ERROR_WANT_READ ||
  333. iErrorCode == SSL_ERROR_WANT_WRITE ||
  334. iErrorCode == SSL_ERROR_WANT_CONNECT ||
  335. iErrorCode == SSL_ERROR_WANT_ACCEPT )
  336. return FALSE;
  337. #ifdef _DEBUG
  338. char szBuffer[512];
  339. #endif
  340. int i = 0;
  341. int iCode = iErrorCode;
  342. for(; iCode != SSL_ERROR_NONE; i++)
  343. {
  344. #ifdef _DEBUG
  345. ERR_error_string_n(iCode, szBuffer, sizeof(szBuffer));
  346. TRACE(" > SSL Error: %d - %s\n", iCode, szBuffer);
  347. #endif
  348. iCode = ERR_get_error();
  349. }
  350. if(iErrorCode == SSL_ERROR_SYSCALL && i == 1)
  351. {
  352. //ERR_clear_error();
  353. return FALSE;
  354. }
  355. return TRUE;
  356. }
  357. CSSLSession* CSSLSessionPool::PickFreeSession()
  358. {
  359. DWORD dwIndex;
  360. CSSLSession* pSession = nullptr;
  361. if(m_lsFreeSession.TryLock(&pSession, dwIndex))
  362. {
  363. if(::GetTimeGap32(pSession->GetFreeTime()) >= m_dwSessionLockTime)
  364. m_lsFreeSession.ReleaseLock(nullptr, dwIndex);
  365. else
  366. {
  367. m_lsFreeSession.ReleaseLock(pSession, dwIndex);
  368. pSession = nullptr;
  369. }
  370. }
  371. if(!pSession) pSession = new CSSLSession(m_itPool);
  372. ASSERT(pSession);
  373. return pSession->Renew();
  374. }
  375. void CSSLSessionPool::PutFreeSession(CSSLSession* pSession)
  376. {
  377. if(pSession->Reset())
  378. {
  379. if(!m_lsFreeSession.TryPut(pSession))
  380. {
  381. m_lsGCSession.PushBack(pSession);
  382. if(m_lsGCSession.Size() > m_dwSessionPoolSize)
  383. ReleaseGCSession();
  384. }
  385. }
  386. }
  387. void CSSLSessionPool::ReleaseGCSession(BOOL bForce)
  388. {
  389. CSSLSession* pSession = nullptr;
  390. DWORD now = ::TimeGetTime();
  391. while(m_lsGCSession.PopFront(&pSession))
  392. {
  393. if(bForce || (int)(now - pSession->GetFreeTime()) >= (int)m_dwSessionLockTime)
  394. delete pSession;
  395. else
  396. {
  397. m_lsGCSession.PushBack(pSession);
  398. break;
  399. }
  400. }
  401. }
  402. void CSSLSessionPool::Prepare()
  403. {
  404. m_itPool.Prepare();
  405. m_lsFreeSession.Reset(m_dwSessionPoolHold);
  406. }
  407. void CSSLSessionPool::Clear()
  408. {
  409. CSSLSession* pSession = nullptr;
  410. while(m_lsFreeSession.TryGet(&pSession))
  411. delete pSession;
  412. VERIFY(m_lsFreeSession.IsEmpty());
  413. m_lsFreeSession.Reset();
  414. ReleaseGCSession(TRUE);
  415. VERIFY(m_lsGCSession.IsEmpty());
  416. m_itPool.Clear();
  417. }