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 def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE, |
109 def _determinecertoptions(ui, host): |
110 ca_certs=None, serverhostname=None): |
110 """Determine certificate options for a connections. |
|
111 |
|
112 Returns a tuple of (cert_reqs, ca_certs). |
|
113 """ |
|
114 # If a host key fingerprint is on file, it is the only thing that matters |
|
115 # and CA certs don't come into play. |
|
116 hostfingerprint = ui.config('hostfingerprints', host) |
|
117 if hostfingerprint: |
|
118 return ssl.CERT_NONE, None |
|
119 |
|
120 # The code below sets up CA verification arguments. If --insecure is |
|
121 # used, we don't take CAs into consideration, so return early. |
|
122 if ui.insecureconnections: |
|
123 return ssl.CERT_NONE, None |
|
124 |
|
125 cacerts = ui.config('web', 'cacerts') |
|
126 |
|
127 # If a value is set in the config, validate against a path and load |
|
128 # and require those certs. |
|
129 if cacerts: |
|
130 cacerts = util.expandpath(cacerts) |
|
131 if not os.path.exists(cacerts): |
|
132 raise error.Abort(_('could not find web.cacerts: %s') % cacerts) |
|
133 |
|
134 return ssl.CERT_REQUIRED, cacerts |
|
135 |
|
136 # No CAs in config. See if we can load defaults. |
|
137 cacerts = _defaultcacerts() |
|
138 |
|
139 # We found an alternate CA bundle to use. Load it. |
|
140 if cacerts: |
|
141 ui.debug('using %s to enable OS X system CA\n' % cacerts) |
|
142 ui.setconfig('web', 'cacerts', cacerts, 'defaultcacerts') |
|
143 return ssl.CERT_REQUIRED, cacerts |
|
144 |
|
145 # FUTURE this can disappear once wrapsocket() is secure by default. |
|
146 if _canloaddefaultcerts: |
|
147 return ssl.CERT_REQUIRED, None |
|
148 |
|
149 return ssl.CERT_NONE, None |
|
150 |
|
151 def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None): |
111 """Add SSL/TLS to a socket. |
152 """Add SSL/TLS to a socket. |
112 |
153 |
113 This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane |
154 This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane |
114 choices based on what security options are available. |
155 choices based on what security options are available. |
115 |
156 |
120 server (and client) support SNI, this tells the server which certificate |
161 server (and client) support SNI, this tells the server which certificate |
121 to use. |
162 to use. |
122 """ |
163 """ |
123 if not serverhostname: |
164 if not serverhostname: |
124 raise error.Abort('serverhostname argument is required') |
165 raise error.Abort('serverhostname argument is required') |
|
166 |
|
167 cert_reqs, ca_certs = _determinecertoptions(ui, serverhostname) |
125 |
168 |
126 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol |
169 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol |
127 # that both ends support, including TLS protocols. On legacy stacks, |
170 # that both ends support, including TLS protocols. On legacy stacks, |
128 # the highest it likely goes in TLS 1.0. On modern stacks, it can |
171 # the highest it likely goes in TLS 1.0. On modern stacks, it can |
129 # support TLS 1.2. |
172 # support TLS 1.2. |
241 return dummycert |
284 return dummycert |
242 |
285 |
243 return None |
286 return None |
244 |
287 |
245 def sslkwargs(ui, host): |
288 def sslkwargs(ui, host): |
246 """Determine arguments to pass to wrapsocket(). |
289 return {} |
247 |
|
248 ``host`` is the hostname being connected to. |
|
249 """ |
|
250 kws = {} |
|
251 |
|
252 # If a host key fingerprint is on file, it is the only thing that matters |
|
253 # and CA certs don't come into play. |
|
254 hostfingerprint = ui.config('hostfingerprints', host) |
|
255 if hostfingerprint: |
|
256 return kws |
|
257 |
|
258 # The code below sets up CA verification arguments. If --insecure is |
|
259 # used, we don't take CAs into consideration, so return early. |
|
260 if ui.insecureconnections: |
|
261 return kws |
|
262 |
|
263 cacerts = ui.config('web', 'cacerts') |
|
264 |
|
265 # If a value is set in the config, validate against a path and load |
|
266 # and require those certs. |
|
267 if cacerts: |
|
268 cacerts = util.expandpath(cacerts) |
|
269 if not os.path.exists(cacerts): |
|
270 raise error.Abort(_('could not find web.cacerts: %s') % cacerts) |
|
271 |
|
272 kws.update({'ca_certs': cacerts, |
|
273 'cert_reqs': ssl.CERT_REQUIRED}) |
|
274 return kws |
|
275 |
|
276 # No CAs in config. See if we can load defaults. |
|
277 cacerts = _defaultcacerts() |
|
278 |
|
279 # We found an alternate CA bundle to use. Load it. |
|
280 if cacerts: |
|
281 ui.debug('using %s to enable OS X system CA\n' % cacerts) |
|
282 ui.setconfig('web', 'cacerts', cacerts, 'defaultcacerts') |
|
283 kws.update({'ca_certs': cacerts, |
|
284 'cert_reqs': ssl.CERT_REQUIRED}) |
|
285 return kws |
|
286 |
|
287 # FUTURE this can disappear once wrapsocket() is secure by default. |
|
288 if _canloaddefaultcerts: |
|
289 kws['cert_reqs'] = ssl.CERT_REQUIRED |
|
290 return kws |
|
291 |
|
292 return kws |
|
293 |
290 |
294 def validatesocket(sock, strict=False): |
291 def validatesocket(sock, strict=False): |
295 """Validate a socket meets security requiremnets. |
292 """Validate a socket meets security requiremnets. |
296 |
293 |
297 The passed socket must have been created with ``wrapsocket()``. |
294 The passed socket must have been created with ``wrapsocket()``. |