Mercurial > hg
comparison mercurial/sslutil.py @ 29334:ecc9b788fd69
sslutil: per-host config option to define certificates
Recent work has introduced the [hostsecurity] config section for
defining per-host security settings. This patch builds on top
of this foundation and implements the ability to define a per-host
path to a file containing certificates used for verifying the server
certificate. It is logically a per-host web.cacerts setting.
This patch also introduces a warning when both per-host
certificates and fingerprints are defined. These are mutually
exclusive for host verification and I think the user should be
alerted when security settings are ambiguous because, well,
security is important.
Tests validating the new behavior have been added.
I decided against putting "ca" in the option name because a
non-CA certificate can be specified and used to validate the server
certificate (commonly this will be the exact public certificate
used by the server). It's worth noting that the underlying
Python API used is load_verify_locations(cafile=X) and it calls
into OpenSSL's SSL_CTX_load_verify_locations(). Even OpenSSL's
documentation seems to omit that the file can contain a non-CA
certificate if it matches the server's certificate exactly. I
thought a CA certificate was a special kind of x509 certificate.
Perhaps I'm wrong and any x509 certificate can be used as a
CA certificate [as far as OpenSSL is concerned]. In any case,
I thought it best to drop "ca" from the name because this reflects
reality.
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Tue, 07 Jun 2016 20:29:54 -0700 |
parents | 1b3a0b0c414f |
children | 0d83ad967bf8 |
comparison
equal
deleted
inserted
replaced
29333:cdef60d9f442 | 29334:ecc9b788fd69 |
---|---|
160 s['verifymode'] = ssl.CERT_NONE | 160 s['verifymode'] = ssl.CERT_NONE |
161 | 161 |
162 if ui.configbool('devel', 'disableloaddefaultcerts'): | 162 if ui.configbool('devel', 'disableloaddefaultcerts'): |
163 s['allowloaddefaultcerts'] = False | 163 s['allowloaddefaultcerts'] = False |
164 | 164 |
165 # If both fingerprints and a per-host ca file are specified, issue a warning | |
166 # because users should not be surprised about what security is or isn't | |
167 # being performed. | |
168 cafile = ui.config('hostsecurity', '%s:verifycertsfile' % hostname) | |
169 if s['certfingerprints'] and cafile: | |
170 ui.warn(_('(hostsecurity.%s:verifycertsfile ignored when host ' | |
171 'fingerprints defined; using host fingerprints for ' | |
172 'verification)\n') % hostname) | |
173 | |
165 # Try to hook up CA certificate validation unless something above | 174 # Try to hook up CA certificate validation unless something above |
166 # makes it not necessary. | 175 # makes it not necessary. |
167 if s['verifymode'] is None: | 176 if s['verifymode'] is None: |
168 # Find global certificates file in config. | 177 # Look at per-host ca file first. |
169 cafile = ui.config('web', 'cacerts') | |
170 | |
171 if cafile: | 178 if cafile: |
172 cafile = util.expandpath(cafile) | 179 cafile = util.expandpath(cafile) |
173 if not os.path.exists(cafile): | 180 if not os.path.exists(cafile): |
174 raise error.Abort(_('could not find web.cacerts: %s') % cafile) | 181 raise error.Abort(_('path specified by %s does not exist: %s') % |
182 ('hostsecurity.%s:verifycertsfile' % hostname, | |
183 cafile)) | |
184 s['cafile'] = cafile | |
175 else: | 185 else: |
176 # No global CA certs. See if we can load defaults. | 186 # Find global certificates file in config. |
177 cafile = _defaultcacerts() | 187 cafile = ui.config('web', 'cacerts') |
188 | |
178 if cafile: | 189 if cafile: |
179 ui.debug('using %s to enable OS X system CA\n' % cafile) | 190 cafile = util.expandpath(cafile) |
180 | 191 if not os.path.exists(cafile): |
181 s['cafile'] = cafile | 192 raise error.Abort(_('could not find web.cacerts: %s') % |
193 cafile) | |
194 else: | |
195 # No global CA certs. See if we can load defaults. | |
196 cafile = _defaultcacerts() | |
197 if cafile: | |
198 ui.debug('using %s to enable OS X system CA\n' % cafile) | |
199 | |
200 s['cafile'] = cafile | |
182 | 201 |
183 # Require certificate validation if CA certs are being loaded and | 202 # Require certificate validation if CA certs are being loaded and |
184 # verification hasn't been disabled above. | 203 # verification hasn't been disabled above. |
185 if cafile or (_canloaddefaultcerts and s['allowloaddefaultcerts']): | 204 if cafile or (_canloaddefaultcerts and s['allowloaddefaultcerts']): |
186 s['verifymode'] = ssl.CERT_REQUIRED | 205 s['verifymode'] = ssl.CERT_REQUIRED |