104 if self._supportsciphers: |
104 if self._supportsciphers: |
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 def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE, |
110 def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE, |
110 ca_certs=None, serverhostname=None): |
111 ca_certs=None, serverhostname=None): |
111 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol |
112 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol |
112 # that both ends support, including TLS protocols. On legacy stacks, |
113 # that both ends support, including TLS protocols. On legacy stacks, |
113 # the highest it likely goes in TLS 1.0. On modern stacks, it can |
114 # the highest it likely goes in TLS 1.0. On modern stacks, it can |
114 # support TLS 1.2. |
115 # support TLS 1.2. |
115 # |
116 # |
116 # The PROTOCOL_TLSv* constants select a specific TLS version |
117 # The PROTOCOL_TLSv* constants select a specific TLS version |
117 # only (as opposed to multiple versions). So the method for |
118 # only (as opposed to multiple versions). So the method for |
118 # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and |
119 # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and |
119 # disable protocols via SSLContext.options and OP_NO_* constants. |
120 # disable protocols via SSLContext.options and OP_NO_* constants. |
120 # However, SSLContext.options doesn't work unless we have the |
121 # However, SSLContext.options doesn't work unless we have the |
121 # full/real SSLContext available to us. |
122 # full/real SSLContext available to us. |
122 # |
123 # |
123 # SSLv2 and SSLv3 are broken. We ban them outright. |
124 # SSLv2 and SSLv3 are broken. We ban them outright. |
124 if modernssl: |
125 if modernssl: |
125 protocol = ssl.PROTOCOL_SSLv23 |
126 protocol = ssl.PROTOCOL_SSLv23 |
126 else: |
127 else: |
127 protocol = ssl.PROTOCOL_TLSv1 |
128 protocol = ssl.PROTOCOL_TLSv1 |
128 |
129 |
129 # TODO use ssl.create_default_context() on modernssl. |
130 # TODO use ssl.create_default_context() on modernssl. |
130 sslcontext = SSLContext(protocol) |
131 sslcontext = SSLContext(protocol) |
131 |
132 |
132 # This is a no-op on old Python. |
|
133 sslcontext.options |= OP_NO_SSLv2 | OP_NO_SSLv3 |
|
134 |
|
135 if certfile is not None: |
|
136 def password(): |
|
137 f = keyfile or certfile |
|
138 return ui.getpass(_('passphrase for %s: ') % f, '') |
|
139 sslcontext.load_cert_chain(certfile, keyfile, password) |
|
140 sslcontext.verify_mode = cert_reqs |
|
141 if ca_certs is not None: |
|
142 sslcontext.load_verify_locations(cafile=ca_certs) |
|
143 else: |
133 # This is a no-op on old Python. |
144 # This is a no-op on old Python. |
134 sslcontext.options |= OP_NO_SSLv2 | OP_NO_SSLv3 |
145 sslcontext.load_default_certs() |
135 |
146 |
136 if certfile is not None: |
147 sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname) |
137 def password(): |
148 # check if wrap_socket failed silently because socket had been |
138 f = keyfile or certfile |
149 # closed |
139 return ui.getpass(_('passphrase for %s: ') % f, '') |
150 # - see http://bugs.python.org/issue13721 |
140 sslcontext.load_cert_chain(certfile, keyfile, password) |
151 if not sslsocket.cipher(): |
141 sslcontext.verify_mode = cert_reqs |
152 raise error.Abort(_('ssl connection failed')) |
142 if ca_certs is not None: |
153 return sslsocket |
143 sslcontext.load_verify_locations(cafile=ca_certs) |
|
144 else: |
|
145 # This is a no-op on old Python. |
|
146 sslcontext.load_default_certs() |
|
147 |
|
148 sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname) |
|
149 # check if wrap_socket failed silently because socket had been |
|
150 # closed |
|
151 # - see http://bugs.python.org/issue13721 |
|
152 if not sslsocket.cipher(): |
|
153 raise error.Abort(_('ssl connection failed')) |
|
154 return sslsocket |
|
155 except AttributeError: |
|
156 raise util.Abort('this should not happen') |
|
157 |
154 |
158 def _verifycert(cert, hostname): |
155 def _verifycert(cert, hostname): |
159 '''Verify that cert (in socket.getpeercert() format) matches hostname. |
156 '''Verify that cert (in socket.getpeercert() format) matches hostname. |
160 CRLs is not handled. |
157 CRLs is not handled. |
161 |
158 |