Show More
@@ -113,6 +113,7 b' def _hostsettings(ui, hostname):' | |||||
113 |
|
113 | |||
114 | Returns a dict of settings relevant to that hostname. |
|
114 | Returns a dict of settings relevant to that hostname. | |
115 | """ |
|
115 | """ | |
|
116 | bhostname = pycompat.bytesurl(hostname) | |||
116 | s = { |
|
117 | s = { | |
117 | # Whether we should attempt to load default/available CA certs |
|
118 | # Whether we should attempt to load default/available CA certs | |
118 | # if an explicit ``cafile`` is not defined. |
|
119 | # if an explicit ``cafile`` is not defined. | |
@@ -162,14 +163,14 b' def _hostsettings(ui, hostname):' | |||||
162 | ui.warn(_('warning: connecting to %s using legacy security ' |
|
163 | ui.warn(_('warning: connecting to %s using legacy security ' | |
163 | 'technology (TLS 1.0); see ' |
|
164 | 'technology (TLS 1.0); see ' | |
164 | 'https://mercurial-scm.org/wiki/SecureConnections for ' |
|
165 | 'https://mercurial-scm.org/wiki/SecureConnections for ' | |
165 | 'more info\n') % hostname) |
|
166 | 'more info\n') % bhostname) | |
166 | defaultprotocol = 'tls1.0' |
|
167 | defaultprotocol = 'tls1.0' | |
167 |
|
168 | |||
168 | key = 'minimumprotocol' |
|
169 | key = 'minimumprotocol' | |
169 | protocol = ui.config('hostsecurity', key, defaultprotocol) |
|
170 | protocol = ui.config('hostsecurity', key, defaultprotocol) | |
170 | validateprotocol(protocol, key) |
|
171 | validateprotocol(protocol, key) | |
171 |
|
172 | |||
172 | key = '%s:minimumprotocol' % hostname |
|
173 | key = '%s:minimumprotocol' % bhostname | |
173 | protocol = ui.config('hostsecurity', key, protocol) |
|
174 | protocol = ui.config('hostsecurity', key, protocol) | |
174 | validateprotocol(protocol, key) |
|
175 | validateprotocol(protocol, key) | |
175 |
|
176 | |||
@@ -182,16 +183,16 b' def _hostsettings(ui, hostname):' | |||||
182 | s['protocol'], s['ctxoptions'], s['protocolui'] = protocolsettings(protocol) |
|
183 | s['protocol'], s['ctxoptions'], s['protocolui'] = protocolsettings(protocol) | |
183 |
|
184 | |||
184 | ciphers = ui.config('hostsecurity', 'ciphers') |
|
185 | ciphers = ui.config('hostsecurity', 'ciphers') | |
185 | ciphers = ui.config('hostsecurity', '%s:ciphers' % hostname, ciphers) |
|
186 | ciphers = ui.config('hostsecurity', '%s:ciphers' % bhostname, ciphers) | |
186 | s['ciphers'] = ciphers |
|
187 | s['ciphers'] = ciphers | |
187 |
|
188 | |||
188 | # Look for fingerprints in [hostsecurity] section. Value is a list |
|
189 | # Look for fingerprints in [hostsecurity] section. Value is a list | |
189 | # of <alg>:<fingerprint> strings. |
|
190 | # of <alg>:<fingerprint> strings. | |
190 | fingerprints = ui.configlist('hostsecurity', '%s:fingerprints' % hostname) |
|
191 | fingerprints = ui.configlist('hostsecurity', '%s:fingerprints' % bhostname) | |
191 | for fingerprint in fingerprints: |
|
192 | for fingerprint in fingerprints: | |
192 | if not (fingerprint.startswith(('sha1:', 'sha256:', 'sha512:'))): |
|
193 | if not (fingerprint.startswith(('sha1:', 'sha256:', 'sha512:'))): | |
193 | raise error.Abort(_('invalid fingerprint for %s: %s') % ( |
|
194 | raise error.Abort(_('invalid fingerprint for %s: %s') % ( | |
194 | hostname, fingerprint), |
|
195 | bhostname, fingerprint), | |
195 | hint=_('must begin with "sha1:", "sha256:", ' |
|
196 | hint=_('must begin with "sha1:", "sha256:", ' | |
196 | 'or "sha512:"')) |
|
197 | 'or "sha512:"')) | |
197 |
|
198 | |||
@@ -200,7 +201,7 b' def _hostsettings(ui, hostname):' | |||||
200 | s['certfingerprints'].append((alg, fingerprint)) |
|
201 | s['certfingerprints'].append((alg, fingerprint)) | |
201 |
|
202 | |||
202 | # Fingerprints from [hostfingerprints] are always SHA-1. |
|
203 | # Fingerprints from [hostfingerprints] are always SHA-1. | |
203 | for fingerprint in ui.configlist('hostfingerprints', hostname): |
|
204 | for fingerprint in ui.configlist('hostfingerprints', bhostname): | |
204 | fingerprint = fingerprint.replace(':', '').lower() |
|
205 | fingerprint = fingerprint.replace(':', '').lower() | |
205 | s['certfingerprints'].append(('sha1', fingerprint)) |
|
206 | s['certfingerprints'].append(('sha1', fingerprint)) | |
206 | s['legacyfingerprint'] = True |
|
207 | s['legacyfingerprint'] = True | |
@@ -223,11 +224,11 b' def _hostsettings(ui, hostname):' | |||||
223 | # If both fingerprints and a per-host ca file are specified, issue a warning |
|
224 | # If both fingerprints and a per-host ca file are specified, issue a warning | |
224 | # because users should not be surprised about what security is or isn't |
|
225 | # because users should not be surprised about what security is or isn't | |
225 | # being performed. |
|
226 | # being performed. | |
226 | cafile = ui.config('hostsecurity', '%s:verifycertsfile' % hostname) |
|
227 | cafile = ui.config('hostsecurity', '%s:verifycertsfile' % bhostname) | |
227 | if s['certfingerprints'] and cafile: |
|
228 | if s['certfingerprints'] and cafile: | |
228 | ui.warn(_('(hostsecurity.%s:verifycertsfile ignored when host ' |
|
229 | ui.warn(_('(hostsecurity.%s:verifycertsfile ignored when host ' | |
229 | 'fingerprints defined; using host fingerprints for ' |
|
230 | 'fingerprints defined; using host fingerprints for ' | |
230 | 'verification)\n') % hostname) |
|
231 | 'verification)\n') % bhostname) | |
231 |
|
232 | |||
232 | # Try to hook up CA certificate validation unless something above |
|
233 | # Try to hook up CA certificate validation unless something above | |
233 | # makes it not necessary. |
|
234 | # makes it not necessary. | |
@@ -237,8 +238,8 b' def _hostsettings(ui, hostname):' | |||||
237 | cafile = util.expandpath(cafile) |
|
238 | cafile = util.expandpath(cafile) | |
238 | if not os.path.exists(cafile): |
|
239 | if not os.path.exists(cafile): | |
239 | raise error.Abort(_('path specified by %s does not exist: %s') % |
|
240 | raise error.Abort(_('path specified by %s does not exist: %s') % | |
240 |
('hostsecurity.%s:verifycertsfile' % |
|
241 | ('hostsecurity.%s:verifycertsfile' % ( | |
241 | cafile)) |
|
242 | bhostname,), cafile)) | |
242 | s['cafile'] = cafile |
|
243 | s['cafile'] = cafile | |
243 | else: |
|
244 | else: | |
244 | # Find global certificates file in config. |
|
245 | # Find global certificates file in config. | |
@@ -390,7 +391,7 b' def wrapsocket(sock, keyfile, certfile, ' | |||||
390 | else: |
|
391 | else: | |
391 | msg = e.args[1] |
|
392 | msg = e.args[1] | |
392 | raise error.Abort(_('error loading CA file %s: %s') % ( |
|
393 | raise error.Abort(_('error loading CA file %s: %s') % ( | |
393 | settings['cafile'], msg), |
|
394 | settings['cafile'], util.forcebytestr(msg)), | |
394 | hint=_('file is empty or malformed?')) |
|
395 | hint=_('file is empty or malformed?')) | |
395 | caloaded = True |
|
396 | caloaded = True | |
396 | elif settings['allowloaddefaultcerts']: |
|
397 | elif settings['allowloaddefaultcerts']: | |
@@ -583,8 +584,10 b' def _dnsnamematch(dn, hostname, maxwildc' | |||||
583 | pats = [] |
|
584 | pats = [] | |
584 | if not dn: |
|
585 | if not dn: | |
585 | return False |
|
586 | return False | |
|
587 | dn = pycompat.bytesurl(dn) | |||
|
588 | hostname = pycompat.bytesurl(hostname) | |||
586 |
|
589 | |||
587 |
pieces = dn.split( |
|
590 | pieces = dn.split('.') | |
588 | leftmost = pieces[0] |
|
591 | leftmost = pieces[0] | |
589 | remainder = pieces[1:] |
|
592 | remainder = pieces[1:] | |
590 | wildcards = leftmost.count('*') |
|
593 | wildcards = leftmost.count('*') | |
@@ -637,17 +640,17 b' def _verifycert(cert, hostname):' | |||||
637 | if _dnsnamematch(value, hostname): |
|
640 | if _dnsnamematch(value, hostname): | |
638 | return |
|
641 | return | |
639 | except wildcarderror as e: |
|
642 | except wildcarderror as e: | |
640 | return e.args[0] |
|
643 | return util.forcebytestr(e.args[0]) | |
641 |
|
644 | |||
642 | dnsnames.append(value) |
|
645 | dnsnames.append(value) | |
643 |
|
646 | |||
644 | if not dnsnames: |
|
647 | if not dnsnames: | |
645 | # The subject is only checked when there is no DNS in subjectAltName. |
|
648 | # The subject is only checked when there is no DNS in subjectAltName. | |
646 | for sub in cert.get('subject', []): |
|
649 | for sub in cert.get(r'subject', []): | |
647 | for key, value in sub: |
|
650 | for key, value in sub: | |
648 | # According to RFC 2818 the most specific Common Name must |
|
651 | # According to RFC 2818 the most specific Common Name must | |
649 | # be used. |
|
652 | # be used. | |
650 | if key == 'commonName': |
|
653 | if key == r'commonName': | |
651 | # 'subject' entries are unicode. |
|
654 | # 'subject' entries are unicode. | |
652 | try: |
|
655 | try: | |
653 | value = value.encode('ascii') |
|
656 | value = value.encode('ascii') | |
@@ -658,7 +661,7 b' def _verifycert(cert, hostname):' | |||||
658 | if _dnsnamematch(value, hostname): |
|
661 | if _dnsnamematch(value, hostname): | |
659 | return |
|
662 | return | |
660 | except wildcarderror as e: |
|
663 | except wildcarderror as e: | |
661 | return e.args[0] |
|
664 | return util.forcebytestr(e.args[0]) | |
662 |
|
665 | |||
663 | dnsnames.append(value) |
|
666 | dnsnames.append(value) | |
664 |
|
667 | |||
@@ -780,7 +783,8 b' def validatesocket(sock):' | |||||
780 |
|
783 | |||
781 | The passed socket must have been created with ``wrapsocket()``. |
|
784 | The passed socket must have been created with ``wrapsocket()``. | |
782 | """ |
|
785 | """ | |
783 | host = sock._hgstate['hostname'] |
|
786 | shost = sock._hgstate['hostname'] | |
|
787 | host = pycompat.bytesurl(shost) | |||
784 | ui = sock._hgstate['ui'] |
|
788 | ui = sock._hgstate['ui'] | |
785 | settings = sock._hgstate['settings'] |
|
789 | settings = sock._hgstate['settings'] | |
786 |
|
790 | |||
@@ -856,7 +860,7 b' def validatesocket(sock):' | |||||
856 | 'hostsecurity.%s:fingerprints=%s to trust this server') % |
|
860 | 'hostsecurity.%s:fingerprints=%s to trust this server') % | |
857 | (host, nicefingerprint)) |
|
861 | (host, nicefingerprint)) | |
858 |
|
862 | |||
859 | msg = _verifycert(peercert2, host) |
|
863 | msg = _verifycert(peercert2, shost) | |
860 | if msg: |
|
864 | if msg: | |
861 | raise error.Abort(_('%s certificate error: %s') % (host, msg), |
|
865 | raise error.Abort(_('%s certificate error: %s') % (host, msg), | |
862 | hint=_('set hostsecurity.%s:certfingerprints=%s ' |
|
866 | hint=_('set hostsecurity.%s:certfingerprints=%s ' |
General Comments 0
You need to be logged in to leave comments.
Login now