Advertisement
Guest User

Untitled

a guest
Aug 26th, 2013
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 20.91 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: bd1dc245a27e4a723f820c6261186022c28f74ac
  11. Message-Id: <bd1dc245a27e4a723f82.1377549437@localhost>
  12. User-Agent: Mercurial-patchbomb/2.4.2
  13. Date: Mon, 26 Aug 2013 14:37:17 -0600
  14. From: Alex Orange <crazycasta@gmail.com>
  15. To: mercurial-devel@selenic.com
  16. Cc: q
  17.  
  18. # HG changeset patch
  19. # User Alex Orange <crazycasta@gmail.com>
  20. # Date 1377425168 21600
  21. # Node ID bd1dc245a27e4a723f820c6261186022c28f74ac
  22. # Parent d4a0055af149cdea20b3136b66cae8a24b2e2a98
  23. https: support tls sni (server name indication) for https urls (issue3090)
  24.  
  25. SNI is a common way of sharing servers across multiple domains using separate
  26. SSL certificates. Python 2.x does not, and will not, support SNI according to:
  27. http://bugs.python.org/issue5639#msg192234. In order to support SNI pyOpenSSL
  28. and pyasn1 have been used to emulate Python's SSLSocket object (the one
  29. returned by wrap_socket). Additionally, changes are made to code in httpclient,
  30. sslutil, and url to pass the server hostname to the relavent wrap_socket
  31. wrapper function.
  32.  
  33. The research I did led me to the conclusion that an OpenSSL based solution
  34. would best replicate that existing python ssl object. As opposed to a GNUTLS
  35. solution like PyGnuTLS (https://gitorious.org/pygnutls). The two packages I
  36. found that perform the basic functions of the python ssl object are pyOpenSSL
  37. (http://pythonhosted.org/pyOpenSSL/) and M2Crypto
  38. (http://www.heikkitoivonen.net/m2crypto/). M2Crypto does not support sending
  39. the host name as far as I can tell, which leaves pyOpenSSL. The shortcoming of
  40. both of these libraries is that they do not provide give the subjectAltName
  41. subobjects as separate objects. They give either the DER encoded raw data or
  42. an openssl command line style string "DNS:a.b.com, DNS:c.d.com, ...". I did not
  43. want to parse this string in case a certificate came up with a dNSName had the
  44. form 'a.b.com, DNS:c.d.com' which could conceivably lead to a security problem.
  45. In order to handle this problem I used pyasn1 to parse the subjectAltName data
  46. along with code taken from ndg-httpsclient. There appears to also be an
  47. way to do this directly through python's ssl module with an undocumented
  48. function called _test_decode_cert. However this would involve writing
  49. certificates to temporary files and then reading them back in. This seems like
  50. a bad idea both because the function is undocumented (and therefore vulnerable
  51. to going away without warning) and a possible security risk: writing to files
  52. and then reading them back in. Finally, to make the all of this as unobtrusive
  53. as possible I wrapped this functionality into a single class that emulates the
  54. python ssl class called SocketWrapper in opensslwrap.py.
  55.  
  56. Finally, imports of pyOpenSSL and pyasn1 are tried before the import of ssl in
  57. httpclient/socketutil.py and sslutil.py. This prefers the use of this scheme
  58. over the built-in ssl module for those that have the supporting packages.
  59.  
  60. diff -r d4a0055af149 -r bd1dc245a27e mercurial/httpclient/__init__.py
  61. --- a/mercurial/httpclient/__init__.py Fri Aug 23 16:16:22 2013 -0400
  62. +++ b/mercurial/httpclient/__init__.py Sun Aug 25 04:06:08 2013 -0600
  63. @@ -325,6 +325,8 @@
  64. raise Exception('ssl requested but unavailable on this Python')
  65. self.ssl = use_ssl
  66. self.ssl_opts = ssl_opts
  67. + if 'server_hostname' not in self.ssl_opts:
  68. + self.ssl_opts['server_hostname'] = self.host
  69. self._ssl_validator = ssl_validator
  70. self.host = host
  71. self.sock = None
  72. diff -r d4a0055af149 -r bd1dc245a27e mercurial/httpclient/opensslwrap.py
  73. --- /dev/null Thu Jan 01 00:00:00 1970 +0000
  74. +++ b/mercurial/httpclient/opensslwrap.py Sun Aug 25 04:06:08 2013 -0600
  75. @@ -0,0 +1,123 @@
  76. +import OpenSSL
  77. +import pyasn1.codec.der.decoder
  78. +import subjaltname
  79. +
  80. +import socket
  81. +
  82. +# Try to use python's builtin SSLError if we can, imitate it as best as we can
  83. +# if the import fails
  84. +try:
  85. + import ssl
  86. +except ImportError:
  87. + class ssl(object):
  88. + class SSLError(socket.error):
  89. + pass
  90. +
  91. +# Based heavily on code from:
  92. +# https://github.com/t-8ch/requests/blob/d7908a9fdef7bca16e384ca42478d69d1894c8b6/requests/packages/urllib3/contrib/pyopenssl.py check-code-ignore
  93. +CERT_NONE = OpenSSL.SSL.VERIFY_NONE
  94. +CERT_OPTIONAL = OpenSSL.SSL.VERIFY_PEER
  95. +CERT_REQUIRED = OpenSSL.SSL.VERIFY_PEER | \
  96. + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT
  97. +
  98. +_PROTOCOL_SSLv23 = 2
  99. +_PROTOCOL_SSLv3 = 1
  100. +_PROTOCOL_TLSv1 = 3
  101. +_openssl_versions = {
  102. + _PROTOCOL_SSLv23: OpenSSL.SSL.SSLv23_METHOD,
  103. + _PROTOCOL_SSLv3: OpenSSL.SSL.SSLv3_METHOD,
  104. + _PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD,
  105. +}
  106. +
  107. +class socketwrapper(object):
  108. + def __init__(self, connection, sock):
  109. + self.connection = connection
  110. + self.sock = sock
  111. +
  112. + def makefile(self, mode, bufsize=-1):
  113. + return socket._fileobject(self.connection, mode, bufsize)
  114. +
  115. + def getpeercert(self, binary_form=False):
  116. + x509 = self.connection.get_peer_certificate()
  117. + if not x509:
  118. + raise ssl.SSLError('')
  119. +
  120. + if binary_form:
  121. + return OpenSSL.crypto.dump_certificate(
  122. + OpenSSL.crypto.FILETYPE_ASN1,
  123. + x509)
  124. +
  125. + dns_name = []
  126. + general_names = subjaltname.SubjectAltName()
  127. +
  128. + for i in range(x509.get_extension_count()):
  129. + ext = x509.get_extension(i)
  130. + ext_name = ext.get_short_name()
  131. + if ext_name != 'subjectAltName':
  132. + continue
  133. +
  134. + ext_dat = ext.get_data()
  135. + der_decoder = pyasn1.codec.der.decoder
  136. + decoded_dat = der_decoder.decode(ext_dat,
  137. + asn1Spec=general_names)
  138. +
  139. + for name in decoded_dat:
  140. + if not isinstance(name, subjaltname.SubjectAltName):
  141. + continue
  142. + for entry in range(len(name)):
  143. + component = name.getComponentByPosition(entry)
  144. + if component.getName() != 'dNSName':
  145. + continue
  146. + dns_name.append(('DNS', str(component.getComponent())))
  147. +
  148. + return {
  149. + 'subject': (
  150. + (('commonName', x509.get_subject().CN),),
  151. + ),
  152. + 'subjectAltName': dns_name
  153. + }
  154. +
  155. + # Pass any unhandle function calls on to connection
  156. + def __getattr__(self, name):
  157. + try:
  158. + return getattr(self.connection, name)
  159. + except AttributeError:
  160. + return getattr(self.sock, name)
  161. +
  162. +class OpenSSLReformattedError(Exception):
  163. + def __init__(self, e):
  164. + self.e = e
  165. +
  166. + def __str__(self):
  167. + return '*:%s:%s (glob)'%(self.e.args[0][0][1], self.e.args[0][0][2])
  168. +
  169. +
  170. +def wrap_socket(sock, keyfile=None, certfile=None, server_side=False,
  171. + cert_reqs=CERT_NONE, ssl_version=_PROTOCOL_TLSv1,
  172. + ca_certs=None, do_handshake_on_connect=True,
  173. + suppress_ragged_eofs=True, server_hostname=None):
  174. + ctx = OpenSSL.SSL.Context(_openssl_versions[ssl_version])
  175. + if certfile:
  176. + ctx.use_certificate_file(certfile)
  177. + if keyfile:
  178. + ctx.use_privatekey_file(keyfile)
  179. + if cert_reqs != CERT_NONE:
  180. + ctx.set_verify(cert_reqs, lambda a, b, err_no, c, d: err_no == 0)
  181. + if ca_certs:
  182. + try:
  183. + ctx.load_verify_locations(ca_certs, None)
  184. + except OpenSSL.SSL.Error, e:
  185. + raise ssl.SSLError('bad ca_certs: %r' % ca_certs,
  186. + OpenSSLReformattedError(e))
  187. +
  188. + cnx = OpenSSL.SSL.Connection(ctx, sock)
  189. + if server_hostname is not None:
  190. + cnx.set_tlsext_host_name(server_hostname)
  191. + cnx.set_connect_state()
  192. + try:
  193. + cnx.do_handshake()
  194. + except OpenSSL.SSL.Error, e:
  195. + raise ssl.SSLError('bad handshake',
  196. + OpenSSLReformattedError(e))
  197. +
  198. + return socketwrapper(cnx, sock)
  199. diff -r d4a0055af149 -r bd1dc245a27e mercurial/httpclient/socketutil.py
  200. --- a/mercurial/httpclient/socketutil.py Fri Aug 23 16:16:22 2013 -0400
  201. +++ b/mercurial/httpclient/socketutil.py Sun Aug 25 04:06:08 2013 -0600
  202. @@ -38,15 +38,27 @@
  203. logger = logging.getLogger(__name__)
  204.  
  205. try:
  206. - import ssl
  207. - # make demandimporters load the module
  208. - ssl.wrap_socket # pylint: disable=W0104
  209. + # Required to successfully import opensslwrap
  210. + from OpenSSL import SSL
  211. + from pyasn1 import types
  212. +
  213. + import opensslwrap
  214. have_ssl = True
  215. + have_sni = True
  216. except ImportError:
  217. - import httplib
  218. - import urllib2
  219. - have_ssl = getattr(urllib2, 'HTTPSHandler', False)
  220. - ssl = False
  221. + opensslwrap = False
  222. + try:
  223. + import ssl
  224. + # make demandimporters load the module
  225. + ssl.wrap_socket # pylint: disable=W0104
  226. + have_ssl = True
  227. + have_sni = False
  228. + except ImportError:
  229. + import httplib
  230. + import urllib2
  231. + have_ssl = getattr(urllib2, 'HTTPSHandler', False)
  232. + have_sni = False
  233. + ssl = False
  234.  
  235.  
  236. try:
  237. @@ -75,8 +87,18 @@
  238. raise socket.error(msg)
  239. return sock
  240.  
  241. -if ssl:
  242. - wrap_socket = ssl.wrap_socket
  243. +if opensslwrap:
  244. + CERT_NONE = opensslwrap.CERT_NONE
  245. + CERT_OPTIONAL = opensslwrap.CERT_OPTIONAL
  246. + CERT_REQUIRED = opensslwrap.CERT_REQUIRED
  247. +
  248. + wrap_socket = opensslwrap.wrap_socket
  249. +
  250. +elif ssl:
  251. + # Throw away the server hostname since Python 2.7 hasn't a clue about SNI
  252. + def wrap_socket(server_hostname=None, *args, **kwargs):
  253. + return ssl.wrap_socket(*args, **kwargs)
  254. +
  255. CERT_NONE = ssl.CERT_NONE
  256. CERT_OPTIONAL = ssl.CERT_OPTIONAL
  257. CERT_REQUIRED = ssl.CERT_REQUIRED
  258. @@ -120,7 +142,7 @@
  259. server_side=False, cert_reqs=CERT_NONE,
  260. ssl_version=_PROTOCOL_SSLv3, ca_certs=None,
  261. do_handshake_on_connect=True,
  262. - suppress_ragged_eofs=True):
  263. + suppress_ragged_eofs=True, server_hostname=None):
  264. """Backport of ssl.wrap_socket from Python 2.6."""
  265. if cert_reqs != CERT_NONE and ca_certs:
  266. raise CertificateValidationUnsupported(
  267. diff -r d4a0055af149 -r bd1dc245a27e mercurial/httpclient/subjaltname.py
  268. --- /dev/null Thu Jan 01 00:00:00 1970 +0000
  269. +++ b/mercurial/httpclient/subjaltname.py Sun Aug 25 04:06:08 2013 -0600
  270. @@ -0,0 +1,124 @@
  271. +"""NDG HTTPS Client package
  272. +
  273. +Use pyasn1 to provide support for parsing ASN.1 formatted subjectAltName
  274. +content for SSL peer verification. Code based on:
  275. +
  276. +http://stackoverflow.com/questions/5519958/how-do-i-parse-subjectaltname-extension-data-using-pyasn1
  277. +"""
  278. +__author__ = "P J Kershaw"
  279. +__date__ = "01/02/12"
  280. +__copyright__ = "(C) 2012 Science and Technology Facilities Council"
  281. +__license__ = "BSD - see LICENSE file in top-level directory"
  282. +__contact__ = "Philip.Kershaw@stfc.ac.uk"
  283. +__revision__ = '$Id$'
  284. +
  285. +from pyasn1.type import univ, constraint, char, namedtype, tag
  286. +
  287. +
  288. +class DirectoryString(univ.Choice):
  289. + """ASN.1 Directory string class"""
  290. + componentType = namedtype.NamedTypes(
  291. + namedtype.NamedType(
  292. + 'teletexString', char.TeletexString()),
  293. + namedtype.NamedType(
  294. + 'printableString', char.PrintableString()),
  295. + namedtype.NamedType(
  296. + 'universalString', char.UniversalString()),
  297. + namedtype.NamedType(
  298. + 'utf8String', char.UTF8String()),
  299. + namedtype.NamedType(
  300. + 'bmpString', char.BMPString()),
  301. + namedtype.NamedType(
  302. + 'ia5String', char.IA5String()),
  303. + )
  304. +
  305. +
  306. +class AttributeValue(DirectoryString):
  307. + """ASN.1 Attribute value"""
  308. +
  309. +
  310. +class AttributeType(univ.ObjectIdentifier):
  311. + """ASN.1 Attribute type"""
  312. +
  313. +
  314. +class AttributeTypeAndValue(univ.Sequence):
  315. + """ASN.1 Attribute type and value class"""
  316. + componentType = namedtype.NamedTypes(
  317. + namedtype.NamedType('type', AttributeType()),
  318. + namedtype.NamedType('value', AttributeValue()),
  319. + )
  320. +
  321. +
  322. +class RelativeDistinguishedName(univ.SetOf):
  323. + '''ASN.1 Realtive distinguished name'''
  324. + componentType = AttributeTypeAndValue()
  325. +
  326. +class RDNSequence(univ.SequenceOf):
  327. + '''ASN.1 RDN sequence class'''
  328. + componentType = RelativeDistinguishedName()
  329. +
  330. +
  331. +class Name(univ.Choice):
  332. + '''ASN.1 name class'''
  333. + componentType = namedtype.NamedTypes(
  334. + namedtype.NamedType('', RDNSequence()),
  335. + )
  336. +
  337. +
  338. +class Extension(univ.Sequence):
  339. + '''ASN.1 extension class'''
  340. + componentType = namedtype.NamedTypes(
  341. + namedtype.NamedType('extnID', univ.ObjectIdentifier()),
  342. + namedtype.DefaultedNamedType('critical', univ.Boolean('False')),
  343. + namedtype.NamedType('extnValue', univ.OctetString()),
  344. + )
  345. +
  346. +
  347. +class Extensions(univ.SequenceOf):
  348. + '''ASN.1 extensions class'''
  349. + componentType = Extension()
  350. + sizeSpec = univ.SequenceOf.sizeSpec
  351. +
  352. +
  353. +class GeneralName(univ.Choice):
  354. + '''ASN.1 configuration for X.509 certificate subjectAltNames fields'''
  355. + componentType = namedtype.NamedTypes(
  356. +# namedtype.NamedType('otherName', AnotherName().subtype(
  357. +# implicitTag=tag.Tag(tag.tagClassContext,
  358. +# tag.tagFormatSimple, 0))),
  359. + namedtype.NamedType('rfc822Name', char.IA5String().subtype(
  360. + implicitTag=tag.Tag(tag.tagClassContext,
  361. + tag.tagFormatSimple, 1))),
  362. + namedtype.NamedType('dNSName', char.IA5String().subtype(
  363. + implicitTag=tag.Tag(tag.tagClassContext,
  364. + tag.tagFormatSimple, 2))),
  365. +# namedtype.NamedType('x400Address', ORAddress().subtype(
  366. +# implicitTag=tag.Tag(tag.tagClassContext,
  367. +# tag.tagFormatSimple, 3))),
  368. + namedtype.NamedType('directoryName', Name().subtype(
  369. + implicitTag=tag.Tag(tag.tagClassContext,
  370. + tag.tagFormatSimple, 4))),
  371. +# namedtype.NamedType('ediPartyName', EDIPartyName().subtype(
  372. +# implicitTag=tag.Tag(tag.tagClassContext,
  373. +# tag.tagFormatSimple, 5))),
  374. + namedtype.NamedType('uniformResourceIdentifier', char.IA5String().\
  375. + subtype(implicitTag=tag.Tag(tag.tagClassContext,
  376. + tag.tagFormatSimple, 6))),
  377. + namedtype.NamedType('iPAddress', univ.OctetString().subtype(
  378. + implicitTag=tag.Tag(tag.tagClassContext,
  379. + tag.tagFormatSimple, 7))),
  380. + namedtype.NamedType('registeredID', univ.ObjectIdentifier().subtype(
  381. + implicitTag=tag.Tag(tag.tagClassContext,
  382. + tag.tagFormatSimple, 8))),
  383. + )
  384. +
  385. +
  386. +class GeneralNames(univ.SequenceOf):
  387. + '''Sequence of names for ASN.1 subjectAltNames settings'''
  388. + componentType = GeneralName()
  389. + sizeSpec = univ.SequenceOf.sizeSpec
  390. +
  391. +
  392. +class SubjectAltName(GeneralNames):
  393. + '''ASN.1 implementation for subjectAltNames support'''
  394. +# no-check-code
  395. diff -r d4a0055af149 -r bd1dc245a27e mercurial/sslutil.py
  396. --- a/mercurial/sslutil.py Fri Aug 23 16:16:22 2013 -0400
  397. +++ b/mercurial/sslutil.py Sun Aug 25 04:06:08 2013 -0600
  398. @@ -11,34 +11,53 @@
  399. from mercurial import util
  400. from mercurial.i18n import _
  401. try:
  402. - # avoid using deprecated/broken FakeSocket in python 2.6
  403. - import ssl
  404. - CERT_REQUIRED = ssl.CERT_REQUIRED
  405. + # Required to successfully import opensslwrap
  406. + from OpenSSL import SSL
  407. + from pyasn1 import types
  408. +
  409. + import httpclient.opensslwrap
  410. + CERT_REQUIRED = httpclient.opensslwrap.CERT_REQUIRED
  411. def ssl_wrap_socket(sock, keyfile, certfile,
  412. - cert_reqs=ssl.CERT_NONE, ca_certs=None):
  413. - sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
  414. - cert_reqs=cert_reqs, ca_certs=ca_certs,
  415. - ssl_version=ssl.PROTOCOL_SSLv3)
  416. - # check if wrap_socket failed silently because socket had been closed
  417. - # - see http://bugs.python.org/issue13721
  418. - if not sslsocket.cipher():
  419. - raise util.Abort(_('ssl connection failed'))
  420. + cert_reqs=httpclient.opensslwrap.CERT_NONE,
  421. + ca_certs=None, server_hostname=None):
  422. + wrap_socket = httpclient.opensslwrap.wrap_socket
  423. + sslsocket = wrap_socket(sock, keyfile, certfile, cert_reqs=cert_reqs,
  424. + ca_certs=ca_certs,
  425. + server_hostname=server_hostname)
  426. return sslsocket
  427. except ImportError:
  428. - CERT_REQUIRED = 2
  429. + try:
  430. + # avoid using deprecated/broken FakeSocket in python 2.6
  431. + import ssl
  432. + CERT_REQUIRED = ssl.CERT_REQUIRED
  433. + def ssl_wrap_socket(sock, keyfile, certfile,
  434. + cert_reqs=ssl.CERT_NONE, ca_certs=None,
  435. + server_hostname=None):
  436. + sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
  437. + cert_reqs=cert_reqs, ca_certs=ca_certs,
  438. + ssl_version=ssl.PROTOCOL_SSLv3)
  439. + # check if wrap_socket failed silently because socket had been
  440. + # closed
  441. + # - see http://bugs.python.org/issue13721
  442. + if not sslsocket.cipher():
  443. + raise util.Abort(_('ssl connection failed'))
  444. + return sslsocket
  445. + except ImportError:
  446. + CERT_REQUIRED = 2
  447.  
  448. - import socket, httplib
  449. + import socket, httplib
  450.  
  451. - def ssl_wrap_socket(sock, key_file, cert_file,
  452. - cert_reqs=CERT_REQUIRED, ca_certs=None):
  453. - if not util.safehasattr(socket, 'ssl'):
  454. - raise util.Abort(_('Python SSL support not found'))
  455. - if ca_certs:
  456. - raise util.Abort(_(
  457. - 'certificate checking requires Python 2.6'))
  458. + def ssl_wrap_socket(sock, key_file, cert_file,
  459. + cert_reqs=CERT_REQUIRED, ca_certs=None,
  460. + server_hostname=None):
  461. + if not util.safehasattr(socket, 'ssl'):
  462. + raise util.Abort(_('Python SSL support not found'))
  463. + if ca_certs:
  464. + raise util.Abort(_(
  465. + 'certificate checking requires Python 2.6'))
  466.  
  467. - ssl = socket.ssl(sock, key_file, cert_file)
  468. - return httplib.FakeSocket(sock, ssl)
  469. + ssl = socket.ssl(sock, key_file, cert_file)
  470. + return httplib.FakeSocket(sock, ssl)
  471.  
  472. def _verifycert(cert, hostname):
  473. '''Verify that cert (in socket.getpeercert() format) matches hostname.
  474. @@ -115,9 +134,14 @@
  475. self.ui.warn(_("warning: certificate for %s can't be verified "
  476. "(Python too old)\n") % host)
  477. return
  478. + try:
  479. + # work around http://bugs.python.org/issue13721
  480. + if not sock.cipher():
  481. + raise util.Abort(_('%s ssl connection error') % host)
  482. + except AttributeError:
  483. + # This is not the ssl object you are looking for
  484. + pass
  485.  
  486. - if not sock.cipher(): # work around http://bugs.python.org/issue13721
  487. - raise util.Abort(_('%s ssl connection error') % host)
  488. try:
  489. peercert = sock.getpeercert(True)
  490. peercert2 = sock.getpeercert()
  491. diff -r d4a0055af149 -r bd1dc245a27e mercurial/url.py
  492. --- a/mercurial/url.py Fri Aug 23 16:16:22 2013 -0400
  493. +++ b/mercurial/url.py Sun Aug 25 04:06:08 2013 -0600
  494. @@ -181,7 +181,8 @@
  495. self.sock.connect((self.host, self.port))
  496. if _generic_proxytunnel(self):
  497. # we do not support client X.509 certificates
  498. - self.sock = sslutil.ssl_wrap_socket(self.sock, None, None)
  499. + self.sock = sslutil.ssl_wrap_socket(self.sock, None, None,
  500. + server_hostname=self.host)
  501. else:
  502. keepalive.HTTPConnection.connect(self)
  503.  
  504. @@ -338,7 +339,7 @@
  505. _generic_proxytunnel(self)
  506. host = self.realhostport.rsplit(':', 1)[0]
  507. self.sock = sslutil.ssl_wrap_socket(
  508. - self.sock, self.key_file, self.cert_file,
  509. + self.sock, self.key_file, self.cert_file, server_hostname=host,
  510. **sslutil.sslkwargs(self.ui, host))
  511. sslutil.validator(self.ui, host)(self.sock)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement