115 # List of 2-tuple of (hash algorithm, hash). |
115 # List of 2-tuple of (hash algorithm, hash). |
116 'certfingerprints': [], |
116 'certfingerprints': [], |
117 # Path to file containing concatenated CA certs. Used by |
117 # Path to file containing concatenated CA certs. Used by |
118 # SSLContext.load_verify_locations(). |
118 # SSLContext.load_verify_locations(). |
119 'cafile': None, |
119 'cafile': None, |
|
120 # Whether the legacy [hostfingerprints] section has data for this host. |
|
121 'legacyfingerprint': False, |
120 # ssl.CERT_* constant used by SSLContext.verify_mode. |
122 # ssl.CERT_* constant used by SSLContext.verify_mode. |
121 'verifymode': None, |
123 'verifymode': None, |
122 } |
124 } |
123 |
125 |
124 # Look for fingerprints in [hostsecurity] section. Value is a list |
126 # Look for fingerprints in [hostsecurity] section. Value is a list |
138 |
140 |
139 # Fingerprints from [hostfingerprints] are always SHA-1. |
141 # Fingerprints from [hostfingerprints] are always SHA-1. |
140 for fingerprint in ui.configlist('hostfingerprints', hostname, []): |
142 for fingerprint in ui.configlist('hostfingerprints', hostname, []): |
141 fingerprint = fingerprint.replace(':', '').lower() |
143 fingerprint = fingerprint.replace(':', '').lower() |
142 s['certfingerprints'].append(('sha1', fingerprint)) |
144 s['certfingerprints'].append(('sha1', fingerprint)) |
|
145 s['legacyfingerprint'] = True |
143 |
146 |
144 # If a host cert fingerprint is defined, it is the only thing that |
147 # If a host cert fingerprint is defined, it is the only thing that |
145 # matters. No need to validate CA certs. |
148 # matters. No need to validate CA certs. |
146 if s['certfingerprints']: |
149 if s['certfingerprints']: |
147 s['verifymode'] = ssl.CERT_NONE |
150 s['verifymode'] = ssl.CERT_NONE |
348 'sha512': util.sha512(peercert).hexdigest(), |
351 'sha512': util.sha512(peercert).hexdigest(), |
349 } |
352 } |
350 nicefingerprint = ':'.join([peerfingerprints['sha1'][x:x + 2] |
353 nicefingerprint = ':'.join([peerfingerprints['sha1'][x:x + 2] |
351 for x in range(0, len(peerfingerprints['sha1']), 2)]) |
354 for x in range(0, len(peerfingerprints['sha1']), 2)]) |
352 |
355 |
|
356 if settings['legacyfingerprint']: |
|
357 section = 'hostfingerprint' |
|
358 else: |
|
359 section = 'hostsecurity' |
|
360 |
353 if settings['certfingerprints']: |
361 if settings['certfingerprints']: |
354 fingerprintmatch = False |
362 fingerprintmatch = False |
355 for hash, fingerprint in settings['certfingerprints']: |
363 for hash, fingerprint in settings['certfingerprints']: |
356 if peerfingerprints[hash].lower() == fingerprint: |
364 if peerfingerprints[hash].lower() == fingerprint: |
357 fingerprintmatch = True |
365 fingerprintmatch = True |
358 break |
366 break |
359 if not fingerprintmatch: |
367 if not fingerprintmatch: |
360 raise error.Abort(_('certificate for %s has unexpected ' |
368 raise error.Abort(_('certificate for %s has unexpected ' |
361 'fingerprint %s') % (host, nicefingerprint), |
369 'fingerprint %s') % (host, nicefingerprint), |
362 hint=_('check hostfingerprint configuration')) |
370 hint=_('check %s configuration') % section) |
363 ui.debug('%s certificate matched fingerprint %s\n' % |
371 ui.debug('%s certificate matched fingerprint %s\n' % |
364 (host, nicefingerprint)) |
372 (host, nicefingerprint)) |
365 return |
373 return |
366 |
374 |
367 # If insecure connections were explicitly requested via --insecure, |
375 # If insecure connections were explicitly requested via --insecure, |
370 # It may seem odd that this is checked *after* host fingerprint pinning. |
378 # It may seem odd that this is checked *after* host fingerprint pinning. |
371 # This is for backwards compatibility (for now). The message is also |
379 # This is for backwards compatibility (for now). The message is also |
372 # the same as below for BC. |
380 # the same as below for BC. |
373 if ui.insecureconnections: |
381 if ui.insecureconnections: |
374 ui.warn(_('warning: %s certificate with fingerprint %s not ' |
382 ui.warn(_('warning: %s certificate with fingerprint %s not ' |
375 'verified (check hostfingerprints or web.cacerts ' |
383 'verified (check %s or web.cacerts ' |
376 'config setting)\n') % |
384 'config setting)\n') % |
377 (host, nicefingerprint)) |
385 (host, nicefingerprint, section)) |
378 return |
386 return |
379 |
387 |
380 if not sock._hgstate['caloaded']: |
388 if not sock._hgstate['caloaded']: |
381 if strict: |
389 if strict: |
382 raise error.Abort(_('%s certificate with fingerprint %s not ' |
390 raise error.Abort(_('%s certificate with fingerprint %s not ' |
383 'verified') % (host, nicefingerprint), |
391 'verified') % (host, nicefingerprint), |
384 hint=_('check hostfingerprints or ' |
392 hint=_('check %s or web.cacerts config ' |
385 'web.cacerts config setting')) |
393 'setting') % section) |
386 else: |
394 else: |
387 ui.warn(_('warning: %s certificate with fingerprint %s ' |
395 ui.warn(_('warning: %s certificate with fingerprint %s ' |
388 'not verified (check hostfingerprints or ' |
396 'not verified (check %s or web.cacerts config ' |
389 'web.cacerts config setting)\n') % |
397 'setting)\n') % |
390 (host, nicefingerprint)) |
398 (host, nicefingerprint, section)) |
391 |
399 |
392 return |
400 return |
393 |
401 |
394 msg = _verifycert(peercert2, host) |
402 msg = _verifycert(peercert2, host) |
395 if msg: |
403 if msg: |
396 raise error.Abort(_('%s certificate error: %s') % (host, msg), |
404 raise error.Abort(_('%s certificate error: %s') % (host, msg), |
397 hint=_('configure hostfingerprint %s or use ' |
405 hint=_('configure %s %s or use ' |
398 '--insecure to connect insecurely') % |
406 '--insecure to connect insecurely') % |
399 nicefingerprint) |
407 (section, nicefingerprint)) |