Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

SSecurityTLS.cxx 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /*
  2. * Copyright (C) 2004 Red Hat Inc.
  3. * Copyright (C) 2005 Martin Koegler
  4. * Copyright (C) 2010 TigerVNC Team
  5. *
  6. * This is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This software is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this software; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  19. * USA.
  20. */
  21. #ifdef HAVE_CONFIG_H
  22. #include <config.h>
  23. #endif
  24. #ifndef HAVE_GNUTLS
  25. #error "This source should not be compiled without HAVE_GNUTLS defined"
  26. #endif
  27. #include <rfb/SSecurityTLS.h>
  28. #include <rfb/SConnection.h>
  29. #include <rfb/LogWriter.h>
  30. #include <rfb/Exception.h>
  31. #include <rdr/TLSInStream.h>
  32. #include <rdr/TLSOutStream.h>
  33. #define DH_BITS 1024 /* XXX This should be configurable! */
  34. using namespace rfb;
  35. StringParameter SSecurityTLS::X509_CertFile
  36. ("x509cert", "specifies path to the x509 certificate in PEM format", "", ConfServer);
  37. StringParameter SSecurityTLS::X509_KeyFile
  38. ("x509key", "specifies path to the key of the x509 certificate in PEM format", "", ConfServer);
  39. static LogWriter vlog("TLS");
  40. static LogWriter vlog_raw("RawTLS");
  41. static void debug_log(int level, const char* str)
  42. {
  43. vlog.debug("[%d]: %s", level, str);
  44. }
  45. void SSecurityTLS::initGlobal()
  46. {
  47. static bool globalInitDone = false;
  48. if (!globalInitDone) {
  49. if (gnutls_global_init() != GNUTLS_E_SUCCESS)
  50. throw AuthFailureException("gnutls_global_init failed");
  51. /* 100 means debug log */
  52. if (vlog_raw.getLevel() >= 100) {
  53. gnutls_global_set_log_level(10);
  54. gnutls_global_set_log_function(debug_log);
  55. }
  56. globalInitDone = true;
  57. }
  58. }
  59. SSecurityTLS::SSecurityTLS(bool _anon) : session(0), dh_params(0),
  60. anon_cred(0), cert_cred(0),
  61. anon(_anon), fis(0), fos(0)
  62. {
  63. certfile = X509_CertFile.getData();
  64. keyfile = X509_KeyFile.getData();
  65. }
  66. void SSecurityTLS::shutdown()
  67. {
  68. if (session) {
  69. if (gnutls_bye(session, GNUTLS_SHUT_RDWR) != GNUTLS_E_SUCCESS) {
  70. /* FIXME: Treat as non-fatal error */
  71. vlog.error("TLS session wasn't terminated gracefully");
  72. }
  73. }
  74. if (dh_params) {
  75. gnutls_dh_params_deinit(dh_params);
  76. dh_params = 0;
  77. }
  78. if (anon_cred) {
  79. gnutls_anon_free_server_credentials(anon_cred);
  80. anon_cred = 0;
  81. }
  82. if (cert_cred) {
  83. gnutls_certificate_free_credentials(cert_cred);
  84. cert_cred = 0;
  85. }
  86. if (session) {
  87. gnutls_deinit(session);
  88. session = 0;
  89. gnutls_global_deinit();
  90. }
  91. }
  92. SSecurityTLS::~SSecurityTLS()
  93. {
  94. shutdown();
  95. if (fis)
  96. delete fis;
  97. if (fos)
  98. delete fos;
  99. delete[] keyfile;
  100. delete[] certfile;
  101. }
  102. bool SSecurityTLS::processMsg(SConnection *sc)
  103. {
  104. rdr::InStream* is = sc->getInStream();
  105. rdr::OutStream* os = sc->getOutStream();
  106. vlog.debug("Process security message (session %p)", session);
  107. if (!session) {
  108. initGlobal();
  109. if (gnutls_init(&session, GNUTLS_SERVER) != GNUTLS_E_SUCCESS)
  110. throw AuthFailureException("gnutls_init failed");
  111. if (gnutls_set_default_priority(session) != GNUTLS_E_SUCCESS)
  112. throw AuthFailureException("gnutls_set_default_priority failed");
  113. try {
  114. setParams(session);
  115. }
  116. catch(...) {
  117. os->writeU8(0);
  118. throw;
  119. }
  120. gnutls_transport_set_pull_function(session,rdr::gnutls_InStream_pull);
  121. gnutls_transport_set_push_function(session,rdr::gnutls_OutStream_push);
  122. gnutls_transport_set_ptr2(session,
  123. (gnutls_transport_ptr)is,
  124. (gnutls_transport_ptr)os);
  125. os->writeU8(1);
  126. os->flush();
  127. }
  128. int err;
  129. if ((err = gnutls_handshake(session)) != GNUTLS_E_SUCCESS) {
  130. if (!gnutls_error_is_fatal(err)) {
  131. vlog.debug("Deferring completion of TLS handshake: %s", gnutls_strerror(err));
  132. return false;
  133. }
  134. vlog.error("TLS Handshake failed: %s", gnutls_strerror (err));
  135. shutdown();
  136. throw AuthFailureException("TLS Handshake failed");
  137. }
  138. vlog.debug("Handshake completed");
  139. sc->setStreams(fis=new rdr::TLSInStream(is,session),
  140. fos=new rdr::TLSOutStream(os,session));
  141. return true;
  142. }
  143. void SSecurityTLS::setParams(gnutls_session session)
  144. {
  145. static const int kx_anon_priority[] = { GNUTLS_KX_ANON_DH, 0 };
  146. static const int kx_priority[] = { GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA,
  147. GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0 };
  148. if (gnutls_kx_set_priority(session, anon ? kx_anon_priority : kx_priority)
  149. != GNUTLS_E_SUCCESS)
  150. throw AuthFailureException("gnutls_kx_set_priority failed");
  151. if (gnutls_dh_params_init(&dh_params) != GNUTLS_E_SUCCESS)
  152. throw AuthFailureException("gnutls_dh_params_init failed");
  153. if (gnutls_dh_params_generate2(dh_params, DH_BITS) != GNUTLS_E_SUCCESS)
  154. throw AuthFailureException("gnutls_dh_params_generate2 failed");
  155. if (anon) {
  156. if (gnutls_anon_allocate_server_credentials(&anon_cred) != GNUTLS_E_SUCCESS)
  157. throw AuthFailureException("gnutls_anon_allocate_server_credentials failed");
  158. gnutls_anon_set_server_dh_params(anon_cred, dh_params);
  159. if (gnutls_credentials_set(session, GNUTLS_CRD_ANON, anon_cred)
  160. != GNUTLS_E_SUCCESS)
  161. throw AuthFailureException("gnutls_credentials_set failed");
  162. vlog.debug("Anonymous session has been set");
  163. } else {
  164. if (gnutls_certificate_allocate_credentials(&cert_cred) != GNUTLS_E_SUCCESS)
  165. throw AuthFailureException("gnutls_certificate_allocate_credentials failed");
  166. gnutls_certificate_set_dh_params(cert_cred, dh_params);
  167. if (gnutls_certificate_set_x509_key_file(cert_cred, certfile, keyfile,
  168. GNUTLS_X509_FMT_PEM) != GNUTLS_E_SUCCESS)
  169. throw AuthFailureException("load of key failed");
  170. if (gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cert_cred)
  171. != GNUTLS_E_SUCCESS)
  172. throw AuthFailureException("gnutls_credentials_set failed");
  173. vlog.debug("X509 session has been set");
  174. }
  175. }