Advertisement
Guest User

Untitled

a guest
Aug 29th, 2013
116
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.59 KB | None | 0 0
  1. this patch series consists of 1 patches.
  2.  
  3. [?1034hCc:
  4. displaying [PATCH] https: support tls sni (server name indication) for https urls (issue3090) ...
  5. Content-Type: text/plain; charset="us-ascii"
  6. MIME-Version: 1.0
  7. Content-Transfer-Encoding: 7bit
  8. Subject: [PATCH] https: support tls sni (server name indication) for https
  9. urls (issue3090)
  10. X-Mercurial-Node: 1521e79acdcd4fbfea77dc4d95be2106508934d5
  11. Message-Id: <1521e79acdcd4fbfea77.1377772380@localhost>
  12. User-Agent: Mercurial-patchbomb/2.4.2
  13. Date: Thu, 29 Aug 2013 04:33:00 -0600
  14. From: Alex Orange <[email protected]>
  15.  
  16. # HG changeset patch
  17. # User Alex Orange <[email protected]>
  18. # Date 1377769698 21600
  19. # Node ID 1521e79acdcd4fbfea77dc4d95be2106508934d5
  20. # Parent d4a0055af149cdea20b3136b66cae8a24b2e2a98
  21. https: support tls sni (server name indication) for https urls (issue3090)
  22.  
  23. SNI is a common way of sharing servers across multiple domains using separate
  24. SSL certificates. Python 2.x does not, and will not, support SNI according to:
  25. http://bugs.python.org/issue5639#msg192234. In order to support SNI pyOpenSSL
  26. and pyasn1 have been used to emulate Python's SSLSocket object (the one
  27. returned by wrap_socket). Additionally, changes are made to code in httpclient,
  28. sslutil, and url to pass the server hostname to the relavent wrap_socket
  29. wrapper function.
  30.  
  31. The research I did led me to the conclusion that an OpenSSL based solution
  32. would best replicate that existing python ssl object. As opposed to a GNUTLS
  33. solution like PyGnuTLS (https://gitorious.org/pygnutls). The two packages I
  34. found that perform the basic functions of the python ssl object are pyOpenSSL
  35. (http://pythonhosted.org/pyOpenSSL/) and M2Crypto
  36. (http://www.heikkitoivonen.net/m2crypto/). M2Crypto does not support sending
  37. the host name as far as I can tell, which leaves pyOpenSSL. The shortcoming of
  38. both of these libraries is that they do not provide give the subjectAltName
  39. subobjects as separate objects. They give either the DER encoded raw data or
  40. an openssl command line style string "DNS:a.b.com, DNS:c.d.com, ...". I did not
  41. want to parse this string in case a certificate came up with a dNSName had the
  42. form 'a.b.com, DNS:c.d.com' which could conceivably lead to a security problem.
  43. In order to handle this problem I used pyasn1 to parse the subjectAltName data
  44. along with code taken from ndg-httpsclient. There appears to also be an
  45. way to do this directly through python's ssl module with an undocumented
  46. function called _test_decode_cert. However this would involve writing
  47. certificates to temporary files and then reading them back in. This seems like
  48. a bad idea both because the function is undocumented (and therefore vulnerable
  49. to going away without warning) and a possible security risk: writing to files
  50. and then reading them back in. Finally, to make the all of this as unobtrusive
  51. as possible I wrapped this functionality into a single class that emulates the
  52. python ssl class called SocketWrapper in opensslwrap.py.
  53.  
  54. Finally, imports of pyOpenSSL and pyasn1 are tried before the import of ssl in
  55. httpclient/socketutil.py and sslutil.py. This prefers the use of this scheme
  56. over the built-in ssl module for those that have the supporting packages. The
  57. imports take the form of from a import b so as to force immediate import since
  58. pyasn1 in particular is not immediately used.
  59.  
  60. diff -r d4a0055af149 -r 1521e79acdcd mercurial/sslutil.py
  61. --- a/mercurial/sslutil.py Fri Aug 23 16:16:22 2013 -0400
  62. +++ b/mercurial/sslutil.py Thu Aug 29 03:48:18 2013 -0600
  63. @@ -11,34 +11,51 @@
  64. from mercurial import util
  65. from mercurial.i18n import _
  66. try:
  67. - # avoid using deprecated/broken FakeSocket in python 2.6
  68. - import ssl
  69. - CERT_REQUIRED = ssl.CERT_REQUIRED
  70. + # Force the import
  71. + from ssl_sni import openssl
  72. +
  73. + CERT_REQUIRED = openssl.CERT_REQUIRED
  74. def ssl_wrap_socket(sock, keyfile, certfile,
  75. - cert_reqs=ssl.CERT_NONE, ca_certs=None):
  76. - sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
  77. - cert_reqs=cert_reqs, ca_certs=ca_certs,
  78. - ssl_version=ssl.PROTOCOL_SSLv3)
  79. - # check if wrap_socket failed silently because socket had been closed
  80. - # - see http://bugs.python.org/issue13721
  81. - if not sslsocket.cipher():
  82. - raise util.Abort(_('ssl connection failed'))
  83. + cert_reqs=openssl.CERT_NONE, ca_certs=None,
  84. + server_hostname=None):
  85. + sslsocket = openssl.wrap_socket(sock, keyfile, certfile,
  86. + cert_reqs=cert_reqs,
  87. + ca_certs=ca_certs,
  88. + server_hostname=server_hostname)
  89. return sslsocket
  90. except ImportError:
  91. - CERT_REQUIRED = 2
  92. + try:
  93. + # avoid using deprecated/broken FakeSocket in python 2.6
  94. + import ssl
  95. + CERT_REQUIRED = ssl.CERT_REQUIRED
  96. + def ssl_wrap_socket(sock, keyfile, certfile,
  97. + cert_reqs=ssl.CERT_NONE, ca_certs=None,
  98. + server_hostname=None):
  99. + sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
  100. + cert_reqs=cert_reqs, ca_certs=ca_certs,
  101. + ssl_version=ssl.PROTOCOL_SSLv3)
  102. + # check if wrap_socket failed silently because socket had been
  103. + # closed
  104. + # - see http://bugs.python.org/issue13721
  105. + if not sslsocket.cipher():
  106. + raise util.Abort(_('ssl connection failed'))
  107. + return sslsocket
  108. + except ImportError:
  109. + CERT_REQUIRED = 2
  110.  
  111. - import socket, httplib
  112. + import socket, httplib
  113.  
  114. - def ssl_wrap_socket(sock, key_file, cert_file,
  115. - cert_reqs=CERT_REQUIRED, ca_certs=None):
  116. - if not util.safehasattr(socket, 'ssl'):
  117. - raise util.Abort(_('Python SSL support not found'))
  118. - if ca_certs:
  119. - raise util.Abort(_(
  120. - 'certificate checking requires Python 2.6'))
  121. + def ssl_wrap_socket(sock, key_file, cert_file,
  122. + cert_reqs=CERT_REQUIRED, ca_certs=None,
  123. + server_hostname=None):
  124. + if not util.safehasattr(socket, 'ssl'):
  125. + raise util.Abort(_('Python SSL support not found'))
  126. + if ca_certs:
  127. + raise util.Abort(_(
  128. + 'certificate checking requires Python 2.6'))
  129.  
  130. - ssl = socket.ssl(sock, key_file, cert_file)
  131. - return httplib.FakeSocket(sock, ssl)
  132. + ssl = socket.ssl(sock, key_file, cert_file)
  133. + return httplib.FakeSocket(sock, ssl)
  134.  
  135. def _verifycert(cert, hostname):
  136. '''Verify that cert (in socket.getpeercert() format) matches hostname.
  137. @@ -115,9 +132,14 @@
  138. self.ui.warn(_("warning: certificate for %s can't be verified "
  139. "(Python too old)\n") % host)
  140. return
  141. + try:
  142. + # work around http://bugs.python.org/issue13721
  143. + if not sock.cipher():
  144. + raise util.Abort(_('%s ssl connection error') % host)
  145. + except AttributeError:
  146. + # This is not the ssl object you are looking for
  147. + pass
  148.  
  149. - if not sock.cipher(): # work around http://bugs.python.org/issue13721
  150. - raise util.Abort(_('%s ssl connection error') % host)
  151. try:
  152. peercert = sock.getpeercert(True)
  153. peercert2 = sock.getpeercert()
  154. diff -r d4a0055af149 -r 1521e79acdcd mercurial/url.py
  155. --- a/mercurial/url.py Fri Aug 23 16:16:22 2013 -0400
  156. +++ b/mercurial/url.py Thu Aug 29 03:48:18 2013 -0600
  157. @@ -181,7 +181,8 @@
  158. self.sock.connect((self.host, self.port))
  159. if _generic_proxytunnel(self):
  160. # we do not support client X.509 certificates
  161. - self.sock = sslutil.ssl_wrap_socket(self.sock, None, None)
  162. + self.sock = sslutil.ssl_wrap_socket(self.sock, None, None,
  163. + server_hostname=self.host)
  164. else:
  165. keepalive.HTTPConnection.connect(self)
  166.  
  167. @@ -338,7 +339,7 @@
  168. _generic_proxytunnel(self)
  169. host = self.realhostport.rsplit(':', 1)[0]
  170. self.sock = sslutil.ssl_wrap_socket(
  171. - self.sock, self.key_file, self.cert_file,
  172. + self.sock, self.key_file, self.cert_file, server_hostname=host,
  173. **sslutil.sslkwargs(self.ui, host))
  174. sslutil.validator(self.ui, host)(self.sock)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement