105 args['ciphers'] = self._ciphers |
105 args['ciphers'] = self._ciphers |
106 |
106 |
107 return ssl.wrap_socket(socket, **args) |
107 return ssl.wrap_socket(socket, **args) |
108 |
108 |
109 try: |
109 try: |
110 # ssl.SSLContext was added in 2.7.9 and presence indicates modern |
|
111 # SSL/TLS features are available. |
|
112 ssl_context = ssl.SSLContext |
|
113 |
|
114 def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE, |
110 def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE, |
115 ca_certs=None, serverhostname=None): |
111 ca_certs=None, serverhostname=None): |
116 # Allow any version of SSL starting with TLSv1 and |
112 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol |
117 # up. Note that specifying TLSv1 here prohibits use of |
113 # that both ends support, including TLS protocols. On legacy stacks, |
118 # newer standards (like TLSv1_2), so this is the right way |
114 # the highest it likely goes in TLS 1.0. On modern stacks, it can |
119 # to do this. Note that in the future it'd be better to |
115 # support TLS 1.2. |
120 # support using ssl.create_default_context(), which sets |
116 # |
121 # up a bunch of things in smart ways (strong ciphers, |
117 # The PROTOCOL_TLSv* constants select a specific TLS version |
122 # protocol versions, etc) and is upgraded by Python |
118 # only (as opposed to multiple versions). So the method for |
123 # maintainers for us, but that breaks too many things to |
119 # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and |
124 # do it in a hurry. |
120 # disable protocols via SSLContext.options and OP_NO_* constants. |
125 sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23) |
121 # However, SSLContext.options doesn't work unless we have the |
|
122 # full/real SSLContext available to us. |
|
123 # |
|
124 # SSLv2 and SSLv3 are broken. We ban them outright. |
|
125 if modernssl: |
|
126 protocol = ssl.PROTOCOL_SSLv23 |
|
127 else: |
|
128 protocol = ssl.PROTOCOL_TLSv1 |
|
129 |
|
130 # TODO use ssl.create_default_context() on modernssl. |
|
131 sslcontext = SSLContext(protocol) |
|
132 |
|
133 # This is a no-op on old Python. |
126 sslcontext.options |= OP_NO_SSLv2 | OP_NO_SSLv3 |
134 sslcontext.options |= OP_NO_SSLv2 | OP_NO_SSLv3 |
|
135 |
127 if certfile is not None: |
136 if certfile is not None: |
128 def password(): |
137 def password(): |
129 f = keyfile or certfile |
138 f = keyfile or certfile |
130 return ui.getpass(_('passphrase for %s: ') % f, '') |
139 return ui.getpass(_('passphrase for %s: ') % f, '') |
131 sslcontext.load_cert_chain(certfile, keyfile, password) |
140 sslcontext.load_cert_chain(certfile, keyfile, password) |
132 sslcontext.verify_mode = cert_reqs |
141 sslcontext.verify_mode = cert_reqs |
133 if ca_certs is not None: |
142 if ca_certs is not None: |
134 sslcontext.load_verify_locations(cafile=ca_certs) |
143 sslcontext.load_verify_locations(cafile=ca_certs) |
135 elif _canloaddefaultcerts: |
144 else: |
|
145 # This is a no-op on old Python. |
136 sslcontext.load_default_certs() |
146 sslcontext.load_default_certs() |
137 |
147 |
138 sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname) |
148 sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname) |
139 # check if wrap_socket failed silently because socket had been |
149 # check if wrap_socket failed silently because socket had been |
140 # closed |
150 # closed |
141 # - see http://bugs.python.org/issue13721 |
151 # - see http://bugs.python.org/issue13721 |
142 if not sslsocket.cipher(): |
152 if not sslsocket.cipher(): |
143 raise error.Abort(_('ssl connection failed')) |
153 raise error.Abort(_('ssl connection failed')) |
144 return sslsocket |
154 return sslsocket |
145 except AttributeError: |
155 except AttributeError: |
146 # We don't have a modern version of the "ssl" module and are running |
156 raise util.Abort('this should not happen') |
147 # Python <2.7.9. |
|
148 def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE, |
|
149 ca_certs=None, serverhostname=None): |
|
150 sslsocket = ssl.wrap_socket(sock, keyfile, certfile, |
|
151 cert_reqs=cert_reqs, ca_certs=ca_certs, |
|
152 ssl_version=ssl.PROTOCOL_TLSv1) |
|
153 # check if wrap_socket failed silently because socket had been |
|
154 # closed |
|
155 # - see http://bugs.python.org/issue13721 |
|
156 if not sslsocket.cipher(): |
|
157 raise error.Abort(_('ssl connection failed')) |
|
158 return sslsocket |
|
159 |
157 |
160 def _verifycert(cert, hostname): |
158 def _verifycert(cert, hostname): |
161 '''Verify that cert (in socket.getpeercert() format) matches hostname. |
159 '''Verify that cert (in socket.getpeercert() format) matches hostname. |
162 CRLs is not handled. |
160 CRLs is not handled. |
163 |
161 |