112 Returns a dict of settings relevant to that hostname. |
112 Returns a dict of settings relevant to that hostname. |
113 """ |
113 """ |
114 s = { |
114 s = { |
115 # List of 2-tuple of (hash algorithm, hash). |
115 # List of 2-tuple of (hash algorithm, hash). |
116 'certfingerprints': [], |
116 'certfingerprints': [], |
|
117 # ssl.CERT_* constant used by SSLContext.verify_mode. |
|
118 'verifymode': None, |
117 } |
119 } |
118 |
120 |
119 # Fingerprints from [hostfingerprints] are always SHA-1. |
121 # Fingerprints from [hostfingerprints] are always SHA-1. |
120 for fingerprint in ui.configlist('hostfingerprints', hostname, []): |
122 for fingerprint in ui.configlist('hostfingerprints', hostname, []): |
121 fingerprint = fingerprint.replace(':', '').lower() |
123 fingerprint = fingerprint.replace(':', '').lower() |
122 s['certfingerprints'].append(('sha1', fingerprint)) |
124 s['certfingerprints'].append(('sha1', fingerprint)) |
123 |
125 |
|
126 # If a host cert fingerprint is defined, it is the only thing that |
|
127 # matters. No need to validate CA certs. |
|
128 if s['certfingerprints']: |
|
129 s['verifymode'] = ssl.CERT_NONE |
|
130 |
|
131 # If --insecure is used, don't take CAs into consideration. |
|
132 elif ui.insecureconnections: |
|
133 s['verifymode'] = ssl.CERT_NONE |
|
134 |
|
135 # TODO assert verifymode is not None once we integrate cacert |
|
136 # checking in this function. |
|
137 |
124 return s |
138 return s |
125 |
139 |
126 def _determinecertoptions(ui, host): |
140 def _determinecertoptions(ui, settings): |
127 """Determine certificate options for a connections. |
141 """Determine certificate options for a connections. |
128 |
142 |
129 Returns a tuple of (cert_reqs, ca_certs). |
143 Returns a tuple of (cert_reqs, ca_certs). |
130 """ |
144 """ |
131 # If a host key fingerprint is on file, it is the only thing that matters |
145 if settings['verifymode'] == ssl.CERT_NONE: |
132 # and CA certs don't come into play. |
|
133 hostfingerprint = ui.config('hostfingerprints', host) |
|
134 if hostfingerprint: |
|
135 return ssl.CERT_NONE, None |
|
136 |
|
137 # The code below sets up CA verification arguments. If --insecure is |
|
138 # used, we don't take CAs into consideration, so return early. |
|
139 if ui.insecureconnections: |
|
140 return ssl.CERT_NONE, None |
146 return ssl.CERT_NONE, None |
141 |
147 |
142 cacerts = ui.config('web', 'cacerts') |
148 cacerts = ui.config('web', 'cacerts') |
143 |
149 |
144 # If a value is set in the config, validate against a path and load |
150 # If a value is set in the config, validate against a path and load |
179 to use. |
185 to use. |
180 """ |
186 """ |
181 if not serverhostname: |
187 if not serverhostname: |
182 raise error.Abort('serverhostname argument is required') |
188 raise error.Abort('serverhostname argument is required') |
183 |
189 |
184 cert_reqs, ca_certs = _determinecertoptions(ui, serverhostname) |
190 settings = _hostsettings(ui, serverhostname) |
|
191 cert_reqs, ca_certs = _determinecertoptions(ui, settings) |
185 |
192 |
186 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol |
193 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol |
187 # that both ends support, including TLS protocols. On legacy stacks, |
194 # that both ends support, including TLS protocols. On legacy stacks, |
188 # the highest it likely goes in TLS 1.0. On modern stacks, it can |
195 # the highest it likely goes in TLS 1.0. On modern stacks, it can |
189 # support TLS 1.2. |
196 # support TLS 1.2. |
232 raise error.Abort(_('ssl connection failed')) |
239 raise error.Abort(_('ssl connection failed')) |
233 |
240 |
234 sslsocket._hgstate = { |
241 sslsocket._hgstate = { |
235 'caloaded': caloaded, |
242 'caloaded': caloaded, |
236 'hostname': serverhostname, |
243 'hostname': serverhostname, |
237 'settings': _hostsettings(ui, serverhostname), |
244 'settings': settings, |
238 'ui': ui, |
245 'ui': ui, |
239 } |
246 } |
240 |
247 |
241 return sslsocket |
248 return sslsocket |
242 |
249 |