mercurial/sslutil.py
author Yuya Nishihara <yuya@tcha.org>
Thu, 07 May 2015 17:15:24 +0900
changeset 25415 21b536f01eda
parent 24614 241d98d84aed
child 25429 9d1c61715939
permissions -rw-r--r--
ssl: prompt passphrase of client key file via ui.getpass() (issue4648) This is necessary to communicate with third-party tools through command-server channel. This requires SSLContext backported to Python 2.7.9+. It doesn't look nice to pass ui by sslkwargs, but I think it is the only way to do without touching various client codes including httpclient (aka http2). ui is mandatory if certfile is specified, so it has no default value. BTW, test-check-commit-hg.t complains that ssl_wrap_socket() has foo_bar naming. Should I bulk-replace it to sslwrapsocket() ?
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
     1
# sslutil.py - SSL handling for mercurial
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
     2
#
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
     3
# Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
     4
# Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
     5
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
     6
#
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
     7
# This software may be used and distributed according to the terms of the
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
     8
# GNU General Public License version 2 or any later version.
22575
d7f7f1860f00 ssl: on OS X, use a dummy cert to trick Python/OpenSSL to use system CA certs
Mads Kiilerich <madski@unity3d.com>
parents: 22574
diff changeset
     9
import os, sys
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    10
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    11
from mercurial import util
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    12
from mercurial.i18n import _
24291
760a86865f80 ssl: load CA certificates from system's store by default on Python 2.7.9
Yuya Nishihara <yuya@tcha.org>
parents: 24290
diff changeset
    13
760a86865f80 ssl: load CA certificates from system's store by default on Python 2.7.9
Yuya Nishihara <yuya@tcha.org>
parents: 24290
diff changeset
    14
_canloaddefaultcerts = False
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    15
try:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    16
    # avoid using deprecated/broken FakeSocket in python 2.6
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    17
    import ssl
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    18
    CERT_REQUIRED = ssl.CERT_REQUIRED
23834
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    19
    try:
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    20
        ssl_context = ssl.SSLContext
24291
760a86865f80 ssl: load CA certificates from system's store by default on Python 2.7.9
Yuya Nishihara <yuya@tcha.org>
parents: 24290
diff changeset
    21
        _canloaddefaultcerts = util.safehasattr(ssl_context,
760a86865f80 ssl: load CA certificates from system's store by default on Python 2.7.9
Yuya Nishihara <yuya@tcha.org>
parents: 24290
diff changeset
    22
                                                'load_default_certs')
23834
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    23
25415
21b536f01eda ssl: prompt passphrase of client key file via ui.getpass() (issue4648)
Yuya Nishihara <yuya@tcha.org>
parents: 24614
diff changeset
    24
        def ssl_wrap_socket(sock, keyfile, certfile, ui,
21b536f01eda ssl: prompt passphrase of client key file via ui.getpass() (issue4648)
Yuya Nishihara <yuya@tcha.org>
parents: 24614
diff changeset
    25
                            cert_reqs=ssl.CERT_NONE,
23849
58080815f667 sslutil: drop support for clients of sslutil specifying a TLS version
Augie Fackler <augie@google.com>
parents: 23834
diff changeset
    26
                            ca_certs=None, serverhostname=None):
23850
e1931f7cd977 sslutil: use saner TLS settings on Python 2.7.9
Augie Fackler <augie@google.com>
parents: 23849
diff changeset
    27
            # Allow any version of SSL starting with TLSv1 and
e1931f7cd977 sslutil: use saner TLS settings on Python 2.7.9
Augie Fackler <augie@google.com>
parents: 23849
diff changeset
    28
            # up. Note that specifying TLSv1 here prohibits use of
e1931f7cd977 sslutil: use saner TLS settings on Python 2.7.9
Augie Fackler <augie@google.com>
parents: 23849
diff changeset
    29
            # newer standards (like TLSv1_2), so this is the right way
e1931f7cd977 sslutil: use saner TLS settings on Python 2.7.9
Augie Fackler <augie@google.com>
parents: 23849
diff changeset
    30
            # to do this. Note that in the future it'd be better to
e1931f7cd977 sslutil: use saner TLS settings on Python 2.7.9
Augie Fackler <augie@google.com>
parents: 23849
diff changeset
    31
            # support using ssl.create_default_context(), which sets
e1931f7cd977 sslutil: use saner TLS settings on Python 2.7.9
Augie Fackler <augie@google.com>
parents: 23849
diff changeset
    32
            # up a bunch of things in smart ways (strong ciphers,
e1931f7cd977 sslutil: use saner TLS settings on Python 2.7.9
Augie Fackler <augie@google.com>
parents: 23849
diff changeset
    33
            # protocol versions, etc) and is upgraded by Python
e1931f7cd977 sslutil: use saner TLS settings on Python 2.7.9
Augie Fackler <augie@google.com>
parents: 23849
diff changeset
    34
            # maintainers for us, but that breaks too many things to
e1931f7cd977 sslutil: use saner TLS settings on Python 2.7.9
Augie Fackler <augie@google.com>
parents: 23849
diff changeset
    35
            # do it in a hurry.
e1931f7cd977 sslutil: use saner TLS settings on Python 2.7.9
Augie Fackler <augie@google.com>
parents: 23849
diff changeset
    36
            sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
e1931f7cd977 sslutil: use saner TLS settings on Python 2.7.9
Augie Fackler <augie@google.com>
parents: 23849
diff changeset
    37
            sslcontext.options &= ssl.OP_NO_SSLv2 & ssl.OP_NO_SSLv3
23834
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    38
            if certfile is not None:
25415
21b536f01eda ssl: prompt passphrase of client key file via ui.getpass() (issue4648)
Yuya Nishihara <yuya@tcha.org>
parents: 24614
diff changeset
    39
                def password():
21b536f01eda ssl: prompt passphrase of client key file via ui.getpass() (issue4648)
Yuya Nishihara <yuya@tcha.org>
parents: 24614
diff changeset
    40
                    f = keyfile or certfile
21b536f01eda ssl: prompt passphrase of client key file via ui.getpass() (issue4648)
Yuya Nishihara <yuya@tcha.org>
parents: 24614
diff changeset
    41
                    return ui.getpass(_('passphrase for %s: ') % f, '')
21b536f01eda ssl: prompt passphrase of client key file via ui.getpass() (issue4648)
Yuya Nishihara <yuya@tcha.org>
parents: 24614
diff changeset
    42
                sslcontext.load_cert_chain(certfile, keyfile, password)
23834
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    43
            sslcontext.verify_mode = cert_reqs
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    44
            if ca_certs is not None:
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    45
                sslcontext.load_verify_locations(cafile=ca_certs)
24291
760a86865f80 ssl: load CA certificates from system's store by default on Python 2.7.9
Yuya Nishihara <yuya@tcha.org>
parents: 24290
diff changeset
    46
            elif _canloaddefaultcerts:
760a86865f80 ssl: load CA certificates from system's store by default on Python 2.7.9
Yuya Nishihara <yuya@tcha.org>
parents: 24290
diff changeset
    47
                sslcontext.load_default_certs()
23834
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    48
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    49
            sslsocket = sslcontext.wrap_socket(sock,
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    50
                                               server_hostname=serverhostname)
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    51
            # check if wrap_socket failed silently because socket had been
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    52
            # closed
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    53
            # - see http://bugs.python.org/issue13721
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    54
            if not sslsocket.cipher():
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    55
                raise util.Abort(_('ssl connection failed'))
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    56
            return sslsocket
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    57
    except AttributeError:
25415
21b536f01eda ssl: prompt passphrase of client key file via ui.getpass() (issue4648)
Yuya Nishihara <yuya@tcha.org>
parents: 24614
diff changeset
    58
        def ssl_wrap_socket(sock, keyfile, certfile, ui,
21b536f01eda ssl: prompt passphrase of client key file via ui.getpass() (issue4648)
Yuya Nishihara <yuya@tcha.org>
parents: 24614
diff changeset
    59
                            cert_reqs=ssl.CERT_NONE,
23849
58080815f667 sslutil: drop support for clients of sslutil specifying a TLS version
Augie Fackler <augie@google.com>
parents: 23834
diff changeset
    60
                            ca_certs=None, serverhostname=None):
23834
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    61
            sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    62
                                        cert_reqs=cert_reqs, ca_certs=ca_certs,
23851
948a8ca27152 sslutil: drop defunct ssl version constants
Augie Fackler <augie@google.com>
parents: 23850
diff changeset
    63
                                        ssl_version=ssl.PROTOCOL_TLSv1)
23834
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    64
            # check if wrap_socket failed silently because socket had been
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    65
            # closed
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    66
            # - see http://bugs.python.org/issue13721
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    67
            if not sslsocket.cipher():
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    68
                raise util.Abort(_('ssl connection failed'))
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
    69
            return sslsocket
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    70
except ImportError:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    71
    CERT_REQUIRED = 2
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    72
14616
64dfbe576455 sslutil: Restore missing imports of socket and httplib to sslutil
Stephen Thorne <stephen@thorne.id.au>
parents: 14204
diff changeset
    73
    import socket, httplib
64dfbe576455 sslutil: Restore missing imports of socket and httplib to sslutil
Stephen Thorne <stephen@thorne.id.au>
parents: 14204
diff changeset
    74
25415
21b536f01eda ssl: prompt passphrase of client key file via ui.getpass() (issue4648)
Yuya Nishihara <yuya@tcha.org>
parents: 24614
diff changeset
    75
    def ssl_wrap_socket(sock, keyfile, certfile, ui,
21b536f01eda ssl: prompt passphrase of client key file via ui.getpass() (issue4648)
Yuya Nishihara <yuya@tcha.org>
parents: 24614
diff changeset
    76
                        cert_reqs=CERT_REQUIRED,
23849
58080815f667 sslutil: drop support for clients of sslutil specifying a TLS version
Augie Fackler <augie@google.com>
parents: 23834
diff changeset
    77
                        ca_certs=None, serverhostname=None):
15160
b2d4400398f3 sslutil: abort when ssl module is needed but not found
Mads Kiilerich <mads@kiilerich.com>
parents: 14667
diff changeset
    78
        if not util.safehasattr(socket, 'ssl'):
b2d4400398f3 sslutil: abort when ssl module is needed but not found
Mads Kiilerich <mads@kiilerich.com>
parents: 14667
diff changeset
    79
            raise util.Abort(_('Python SSL support not found'))
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    80
        if ca_certs:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    81
            raise util.Abort(_(
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    82
                'certificate checking requires Python 2.6'))
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    83
19808
3b82d412e9e8 sslutil: make keyfile and certfile arguments consistent between 2.6+ and 2.5-
Augie Fackler <raf@durin42.com>
parents: 19806
diff changeset
    84
        ssl = socket.ssl(sock, keyfile, certfile)
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    85
        return httplib.FakeSocket(sock, ssl)
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    86
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    87
def _verifycert(cert, hostname):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    88
    '''Verify that cert (in socket.getpeercert() format) matches hostname.
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    89
    CRLs is not handled.
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    90
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    91
    Returns error message if any problems are found and None on success.
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    92
    '''
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    93
    if not cert:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    94
        return _('no certificate received')
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    95
    dnsname = hostname.lower()
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    96
    def matchdnsname(certname):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    97
        return (certname == dnsname or
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    98
                '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1])
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    99
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   100
    san = cert.get('subjectAltName', [])
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   101
    if san:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   102
        certnames = [value.lower() for key, value in san if key == 'DNS']
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   103
        for name in certnames:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   104
            if matchdnsname(name):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   105
                return None
14666
27b080aa880a sslutil: fall back to commonName when no dNSName in subjectAltName (issue2798)
Nicolas Bareil <nico@chdir.org>
parents: 14616
diff changeset
   106
        if certnames:
27b080aa880a sslutil: fall back to commonName when no dNSName in subjectAltName (issue2798)
Nicolas Bareil <nico@chdir.org>
parents: 14616
diff changeset
   107
            return _('certificate is for %s') % ', '.join(certnames)
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   108
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   109
    # subject is only checked when subjectAltName is empty
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   110
    for s in cert.get('subject', []):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   111
        key, value = s[0]
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   112
        if key == 'commonName':
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   113
            try:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   114
                # 'subject' entries are unicode
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   115
                certname = value.lower().encode('ascii')
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   116
            except UnicodeEncodeError:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   117
                return _('IDN in certificate not supported')
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   118
            if matchdnsname(certname):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   119
                return None
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   120
            return _('certificate is for %s') % certname
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   121
    return _('no commonName or subjectAltName found in certificate')
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   122
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   123
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   124
# CERT_REQUIRED means fetch the cert from the server all the time AND
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   125
# validate it against the CA store provided in web.cacerts.
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   126
#
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   127
# We COMPLETELY ignore CERT_REQUIRED on Python <= 2.5, as it's totally
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   128
# busted on those versions.
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   129
23042
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   130
def _plainapplepython():
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   131
    """return true if this seems to be a pure Apple Python that
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   132
    * is unfrozen and presumably has the whole mercurial module in the file
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   133
      system
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   134
    * presumably is an Apple Python that uses Apple OpenSSL which has patches
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   135
      for using system certificate store CAs in addition to the provided
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   136
      cacerts file
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   137
    """
24614
241d98d84aed ssl: resolve symlink before checking for Apple python executable (issue4588)
Yuya Nishihara <yuya@tcha.org>
parents: 24291
diff changeset
   138
    if sys.platform != 'darwin' or util.mainfrozen() or not sys.executable:
23042
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   139
        return False
24614
241d98d84aed ssl: resolve symlink before checking for Apple python executable (issue4588)
Yuya Nishihara <yuya@tcha.org>
parents: 24291
diff changeset
   140
    exe = os.path.realpath(sys.executable).lower()
23042
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   141
    return (exe.startswith('/usr/bin/python') or
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   142
            exe.startswith('/system/library/frameworks/python.framework/'))
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   143
24288
922e087ba158 ssl: extract function that returns dummycert path on Apple python
Yuya Nishihara <yuya@tcha.org>
parents: 23851
diff changeset
   144
def _defaultcacerts():
24291
760a86865f80 ssl: load CA certificates from system's store by default on Python 2.7.9
Yuya Nishihara <yuya@tcha.org>
parents: 24290
diff changeset
   145
    """return path to CA certificates; None for system's store; ! to disable"""
24288
922e087ba158 ssl: extract function that returns dummycert path on Apple python
Yuya Nishihara <yuya@tcha.org>
parents: 23851
diff changeset
   146
    if _plainapplepython():
922e087ba158 ssl: extract function that returns dummycert path on Apple python
Yuya Nishihara <yuya@tcha.org>
parents: 23851
diff changeset
   147
        dummycert = os.path.join(os.path.dirname(__file__), 'dummycert.pem')
922e087ba158 ssl: extract function that returns dummycert path on Apple python
Yuya Nishihara <yuya@tcha.org>
parents: 23851
diff changeset
   148
        if os.path.exists(dummycert):
922e087ba158 ssl: extract function that returns dummycert path on Apple python
Yuya Nishihara <yuya@tcha.org>
parents: 23851
diff changeset
   149
            return dummycert
24291
760a86865f80 ssl: load CA certificates from system's store by default on Python 2.7.9
Yuya Nishihara <yuya@tcha.org>
parents: 24290
diff changeset
   150
    if _canloaddefaultcerts:
760a86865f80 ssl: load CA certificates from system's store by default on Python 2.7.9
Yuya Nishihara <yuya@tcha.org>
parents: 24290
diff changeset
   151
        return None
24290
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   152
    return '!'
24288
922e087ba158 ssl: extract function that returns dummycert path on Apple python
Yuya Nishihara <yuya@tcha.org>
parents: 23851
diff changeset
   153
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   154
def sslkwargs(ui, host):
25415
21b536f01eda ssl: prompt passphrase of client key file via ui.getpass() (issue4648)
Yuya Nishihara <yuya@tcha.org>
parents: 24614
diff changeset
   155
    kws = {'ui': ui}
22574
a00a7951b20c ssl: refactor sslkwargs - move things around a bit, preparing for next change
Mads Kiilerich <madski@unity3d.com>
parents: 19808
diff changeset
   156
    hostfingerprint = ui.config('hostfingerprints', host)
a00a7951b20c ssl: refactor sslkwargs - move things around a bit, preparing for next change
Mads Kiilerich <madski@unity3d.com>
parents: 19808
diff changeset
   157
    if hostfingerprint:
a00a7951b20c ssl: refactor sslkwargs - move things around a bit, preparing for next change
Mads Kiilerich <madski@unity3d.com>
parents: 19808
diff changeset
   158
        return kws
a00a7951b20c ssl: refactor sslkwargs - move things around a bit, preparing for next change
Mads Kiilerich <madski@unity3d.com>
parents: 19808
diff changeset
   159
    cacerts = ui.config('web', 'cacerts')
24290
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   160
    if cacerts == '!':
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   161
        pass
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   162
    elif cacerts:
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   163
        cacerts = util.expandpath(cacerts)
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   164
        if not os.path.exists(cacerts):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   165
            raise util.Abort(_('could not find web.cacerts: %s') % cacerts)
24290
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   166
    else:
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   167
        cacerts = _defaultcacerts()
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   168
        if cacerts and cacerts != '!':
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   169
            ui.debug('using %s to enable OS X system CA\n' % cacerts)
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   170
        ui.setconfig('web', 'cacerts', cacerts, 'defaultcacerts')
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   171
    if cacerts != '!':
19806
47ff9d1abfa9 sslutil: add a config knob to support TLS (default) or SSLv23 (bc) (issue4038)
Augie Fackler <raf@durin42.com>
parents: 19749
diff changeset
   172
        kws.update({'ca_certs': cacerts,
47ff9d1abfa9 sslutil: add a config knob to support TLS (default) or SSLv23 (bc) (issue4038)
Augie Fackler <raf@durin42.com>
parents: 19749
diff changeset
   173
                    'cert_reqs': CERT_REQUIRED,
47ff9d1abfa9 sslutil: add a config knob to support TLS (default) or SSLv23 (bc) (issue4038)
Augie Fackler <raf@durin42.com>
parents: 19749
diff changeset
   174
                    })
47ff9d1abfa9 sslutil: add a config knob to support TLS (default) or SSLv23 (bc) (issue4038)
Augie Fackler <raf@durin42.com>
parents: 19749
diff changeset
   175
    return kws
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   176
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   177
class validator(object):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   178
    def __init__(self, ui, host):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   179
        self.ui = ui
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   180
        self.host = host
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   181
18887
2d7fac049d3a sslutil: abort if peer certificate is not verified for secure use
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18879
diff changeset
   182
    def __call__(self, sock, strict=False):
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   183
        host = self.host
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   184
        cacerts = self.ui.config('web', 'cacerts')
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   185
        hostfingerprint = self.ui.config('hostfingerprints', host)
15813
3ae04eb5e38a sslutil: handle setups without .getpeercert() early in the validator
Mads Kiilerich <mads@kiilerich.com>
parents: 15812
diff changeset
   186
        if not getattr(sock, 'getpeercert', False): # python 2.5 ?
3ae04eb5e38a sslutil: handle setups without .getpeercert() early in the validator
Mads Kiilerich <mads@kiilerich.com>
parents: 15812
diff changeset
   187
            if hostfingerprint:
3ae04eb5e38a sslutil: handle setups without .getpeercert() early in the validator
Mads Kiilerich <mads@kiilerich.com>
parents: 15812
diff changeset
   188
                raise util.Abort(_("host fingerprint for %s can't be "
3ae04eb5e38a sslutil: handle setups without .getpeercert() early in the validator
Mads Kiilerich <mads@kiilerich.com>
parents: 15812
diff changeset
   189
                                   "verified (Python too old)") % host)
18887
2d7fac049d3a sslutil: abort if peer certificate is not verified for secure use
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18879
diff changeset
   190
            if strict:
2d7fac049d3a sslutil: abort if peer certificate is not verified for secure use
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18879
diff changeset
   191
                raise util.Abort(_("certificate for %s can't be verified "
2d7fac049d3a sslutil: abort if peer certificate is not verified for secure use
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18879
diff changeset
   192
                                   "(Python too old)") % host)
16391
9cf7c9d529d0 ui: optionally quiesce ssl verification warnings on python 2.5
Steven Stallion <sstallion@gmail.com>
parents: 15997
diff changeset
   193
            if self.ui.configbool('ui', 'reportoldssl', True):
9cf7c9d529d0 ui: optionally quiesce ssl verification warnings on python 2.5
Steven Stallion <sstallion@gmail.com>
parents: 15997
diff changeset
   194
                self.ui.warn(_("warning: certificate for %s can't be verified "
9cf7c9d529d0 ui: optionally quiesce ssl verification warnings on python 2.5
Steven Stallion <sstallion@gmail.com>
parents: 15997
diff changeset
   195
                               "(Python too old)\n") % host)
15813
3ae04eb5e38a sslutil: handle setups without .getpeercert() early in the validator
Mads Kiilerich <mads@kiilerich.com>
parents: 15812
diff changeset
   196
            return
18879
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   197
15816
4bb59919c905 sslutil: work around validator crash getting certificate on failed sockets
Mads Kiilerich <mads@kiilerich.com>
parents: 15815
diff changeset
   198
        if not sock.cipher(): # work around http://bugs.python.org/issue13721
4bb59919c905 sslutil: work around validator crash getting certificate on failed sockets
Mads Kiilerich <mads@kiilerich.com>
parents: 15815
diff changeset
   199
            raise util.Abort(_('%s ssl connection error') % host)
18879
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   200
        try:
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   201
            peercert = sock.getpeercert(True)
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   202
            peercert2 = sock.getpeercert()
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   203
        except AttributeError:
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   204
            raise util.Abort(_('%s ssl connection error') % host)
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   205
15817
8f377751b510 sslutil: abort properly if no certificate received for https connection
Mads Kiilerich <mads@kiilerich.com>
parents: 15816
diff changeset
   206
        if not peercert:
8f377751b510 sslutil: abort properly if no certificate received for https connection
Mads Kiilerich <mads@kiilerich.com>
parents: 15816
diff changeset
   207
            raise util.Abort(_('%s certificate error: '
8f377751b510 sslutil: abort properly if no certificate received for https connection
Mads Kiilerich <mads@kiilerich.com>
parents: 15816
diff changeset
   208
                               'no certificate received') % host)
15814
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   209
        peerfingerprint = util.sha1(peercert).hexdigest()
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   210
        nicefingerprint = ":".join([peerfingerprint[x:x + 2]
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   211
            for x in xrange(0, len(peerfingerprint), 2)])
15815
edc3a901a63d sslutil: reorder validator code to make it more readable
Mads Kiilerich <mads@kiilerich.com>
parents: 15814
diff changeset
   212
        if hostfingerprint:
edc3a901a63d sslutil: reorder validator code to make it more readable
Mads Kiilerich <mads@kiilerich.com>
parents: 15814
diff changeset
   213
            if peerfingerprint.lower() != \
edc3a901a63d sslutil: reorder validator code to make it more readable
Mads Kiilerich <mads@kiilerich.com>
parents: 15814
diff changeset
   214
                    hostfingerprint.replace(':', '').lower():
15997
a45516cb8d9f sslutil: more helpful fingerprint mismatch message
Matt Mackall <mpm@selenic.com>
parents: 15817
diff changeset
   215
                raise util.Abort(_('certificate for %s has unexpected '
a45516cb8d9f sslutil: more helpful fingerprint mismatch message
Matt Mackall <mpm@selenic.com>
parents: 15817
diff changeset
   216
                                   'fingerprint %s') % (host, nicefingerprint),
a45516cb8d9f sslutil: more helpful fingerprint mismatch message
Matt Mackall <mpm@selenic.com>
parents: 15817
diff changeset
   217
                                 hint=_('check hostfingerprint configuration'))
15815
edc3a901a63d sslutil: reorder validator code to make it more readable
Mads Kiilerich <mads@kiilerich.com>
parents: 15814
diff changeset
   218
            self.ui.debug('%s certificate matched fingerprint %s\n' %
edc3a901a63d sslutil: reorder validator code to make it more readable
Mads Kiilerich <mads@kiilerich.com>
parents: 15814
diff changeset
   219
                          (host, nicefingerprint))
24290
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   220
        elif cacerts != '!':
18879
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   221
            msg = _verifycert(peercert2, host)
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   222
            if msg:
15814
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   223
                raise util.Abort(_('%s certificate error: %s') % (host, msg),
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   224
                                 hint=_('configure hostfingerprint %s or use '
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   225
                                        '--insecure to connect insecurely') %
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   226
                                      nicefingerprint)
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   227
            self.ui.debug('%s certificate successfully verified\n' % host)
18887
2d7fac049d3a sslutil: abort if peer certificate is not verified for secure use
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18879
diff changeset
   228
        elif strict:
2d7fac049d3a sslutil: abort if peer certificate is not verified for secure use
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18879
diff changeset
   229
            raise util.Abort(_('%s certificate with fingerprint %s not '
2d7fac049d3a sslutil: abort if peer certificate is not verified for secure use
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18879
diff changeset
   230
                               'verified') % (host, nicefingerprint),
2d7fac049d3a sslutil: abort if peer certificate is not verified for secure use
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18879
diff changeset
   231
                             hint=_('check hostfingerprints or web.cacerts '
2d7fac049d3a sslutil: abort if peer certificate is not verified for secure use
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18879
diff changeset
   232
                                     'config setting'))
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   233
        else:
15815
edc3a901a63d sslutil: reorder validator code to make it more readable
Mads Kiilerich <mads@kiilerich.com>
parents: 15814
diff changeset
   234
            self.ui.warn(_('warning: %s certificate with fingerprint %s not '
edc3a901a63d sslutil: reorder validator code to make it more readable
Mads Kiilerich <mads@kiilerich.com>
parents: 15814
diff changeset
   235
                           'verified (check hostfingerprints or web.cacerts '
edc3a901a63d sslutil: reorder validator code to make it more readable
Mads Kiilerich <mads@kiilerich.com>
parents: 15814
diff changeset
   236
                           'config setting)\n') %
edc3a901a63d sslutil: reorder validator code to make it more readable
Mads Kiilerich <mads@kiilerich.com>
parents: 15814
diff changeset
   237
                         (host, nicefingerprint))