mercurial/sslutil.py
author Jun Wu <quark@fb.com>
Sat, 12 Mar 2016 04:24:11 +0000
changeset 28536 a979f5b03320
parent 28525 dfb21c34e07d
child 28577 7efff6ce9826
permissions -rw-r--r--
chgserver: invalidate the server if extensions fail to load Previously, if extensions fail to load, chg server will just keep working without those extensions. It will print a warning message but only if a new server starts. This patch invalidates the server if any extension failed to load, but still serve the client (hopefully just) once. It will help chg pass some test cases of test-bad-extension.t.
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.
25977
696f6e2be282 sslutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25432
diff changeset
     9
696f6e2be282 sslutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25432
diff changeset
    10
from __future__ import absolute_import
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    11
25977
696f6e2be282 sslutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25432
diff changeset
    12
import os
696f6e2be282 sslutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25432
diff changeset
    13
import ssl
696f6e2be282 sslutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25432
diff changeset
    14
import sys
696f6e2be282 sslutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25432
diff changeset
    15
696f6e2be282 sslutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25432
diff changeset
    16
from .i18n import _
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25977
diff changeset
    17
from . import error, util
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
    18
26622
9e15286609ae sslutil: expose attribute indicating whether SNI is supported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26587
diff changeset
    19
hassni = getattr(ssl, 'HAS_SNI', False)
9e15286609ae sslutil: expose attribute indicating whether SNI is supported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26587
diff changeset
    20
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 = False
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    22
try:
25431
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    23
    ssl_context = ssl.SSLContext
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    24
    _canloaddefaultcerts = util.safehasattr(ssl_context, '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
    25
25431
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    26
    def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE,
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    27
                   ca_certs=None, serverhostname=None):
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    28
        # Allow any version of SSL starting with TLSv1 and
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    29
        # up. Note that specifying TLSv1 here prohibits use of
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    30
        # newer standards (like TLSv1_2), so this is the right way
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    31
        # to do this. Note that in the future it'd be better to
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    32
        # support using ssl.create_default_context(), which sets
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    33
        # up a bunch of things in smart ways (strong ciphers,
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    34
        # protocol versions, etc) and is upgraded by Python
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    35
        # maintainers for us, but that breaks too many things to
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    36
        # do it in a hurry.
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    37
        sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
27688
6c7d26cef0cd sslutil: fix reversed logic (issue5034)
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 26622
diff changeset
    38
        sslcontext.options |= ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
25431
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    39
        if certfile is not None:
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    40
            def password():
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    41
                f = keyfile or certfile
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    42
                return ui.getpass(_('passphrase for %s: ') % f, '')
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    43
            sslcontext.load_cert_chain(certfile, keyfile, password)
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    44
        sslcontext.verify_mode = cert_reqs
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    45
        if ca_certs is not None:
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    46
            sslcontext.load_verify_locations(cafile=ca_certs)
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    47
        elif _canloaddefaultcerts:
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    48
            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
    49
25431
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    50
        sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    51
        # check if wrap_socket failed silently because socket had been
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    52
        # closed
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    53
        # - see http://bugs.python.org/issue13721
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    54
        if not sslsocket.cipher():
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25977
diff changeset
    55
            raise error.Abort(_('ssl connection failed'))
25431
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    56
        return sslsocket
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    57
except AttributeError:
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    58
    def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE,
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    59
                   ca_certs=None, serverhostname=None):
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    60
        sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    61
                                    cert_reqs=cert_reqs, ca_certs=ca_certs,
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    62
                                    ssl_version=ssl.PROTOCOL_TLSv1)
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    63
        # check if wrap_socket failed silently because socket had been
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    64
        # closed
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    65
        # - see http://bugs.python.org/issue13721
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    66
        if not sslsocket.cipher():
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25977
diff changeset
    67
            raise error.Abort(_('ssl connection failed'))
25431
96159068c506 ssl: drop try-except clause that was necessary for ancient Python
Yuya Nishihara <yuya@tcha.org>
parents: 25430
diff changeset
    68
        return sslsocket
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    69
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    70
def _verifycert(cert, hostname):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    71
    '''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
    72
    CRLs is not handled.
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    73
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    74
    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
    75
    '''
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    76
    if not cert:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    77
        return _('no certificate received')
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    78
    dnsname = hostname.lower()
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    79
    def matchdnsname(certname):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    80
        return (certname == dnsname or
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    81
                '.' 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
    82
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    83
    san = cert.get('subjectAltName', [])
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    84
    if san:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    85
        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
    86
        for name in certnames:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    87
            if matchdnsname(name):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    88
                return None
14666
27b080aa880a sslutil: fall back to commonName when no dNSName in subjectAltName (issue2798)
Nicolas Bareil <nico@chdir.org>
parents: 14616
diff changeset
    89
        if certnames:
27b080aa880a sslutil: fall back to commonName when no dNSName in subjectAltName (issue2798)
Nicolas Bareil <nico@chdir.org>
parents: 14616
diff changeset
    90
            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
    91
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    92
    # 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
    93
    for s in cert.get('subject', []):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    94
        key, value = s[0]
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    95
        if key == 'commonName':
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    96
            try:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    97
                # 'subject' entries are unicode
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    98
                certname = value.lower().encode('ascii')
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
    99
            except UnicodeEncodeError:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   100
                return _('IDN in certificate not supported')
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   101
            if matchdnsname(certname):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   102
                return None
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   103
            return _('certificate is for %s') % certname
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   104
    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
   105
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   106
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   107
# 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
   108
# 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
   109
23042
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   110
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
   111
    """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
   112
    * 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
   113
      system
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   114
    * 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
   115
      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
   116
      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
   117
    """
24614
241d98d84aed ssl: resolve symlink before checking for Apple python executable (issue4588)
Yuya Nishihara <yuya@tcha.org>
parents: 24291
diff changeset
   118
    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
   119
        return False
24614
241d98d84aed ssl: resolve symlink before checking for Apple python executable (issue4588)
Yuya Nishihara <yuya@tcha.org>
parents: 24291
diff changeset
   120
    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
   121
    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
   122
            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
   123
24288
922e087ba158 ssl: extract function that returns dummycert path on Apple python
Yuya Nishihara <yuya@tcha.org>
parents: 23851
diff changeset
   124
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
   125
    """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
   126
    if _plainapplepython():
922e087ba158 ssl: extract function that returns dummycert path on Apple python
Yuya Nishihara <yuya@tcha.org>
parents: 23851
diff changeset
   127
        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
   128
        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
   129
            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
   130
    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
   131
        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
   132
    return '!'
24288
922e087ba158 ssl: extract function that returns dummycert path on Apple python
Yuya Nishihara <yuya@tcha.org>
parents: 23851
diff changeset
   133
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   134
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
   135
    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
   136
    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
   137
    if hostfingerprint:
a00a7951b20c ssl: refactor sslkwargs - move things around a bit, preparing for next change
Mads Kiilerich <madski@unity3d.com>
parents: 19808
diff changeset
   138
        return kws
a00a7951b20c ssl: refactor sslkwargs - move things around a bit, preparing for next change
Mads Kiilerich <madski@unity3d.com>
parents: 19808
diff changeset
   139
    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
   140
    if cacerts == '!':
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   141
        pass
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   142
    elif cacerts:
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   143
        cacerts = util.expandpath(cacerts)
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   144
        if not os.path.exists(cacerts):
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25977
diff changeset
   145
            raise error.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
   146
    else:
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   147
        cacerts = _defaultcacerts()
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   148
        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
   149
            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
   150
        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
   151
    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
   152
        kws.update({'ca_certs': cacerts,
25432
bdc15b3c9bdb ssl: remove CERT_REQUIRED constant that was necessary for compatibility
Yuya Nishihara <yuya@tcha.org>
parents: 25431
diff changeset
   153
                    'cert_reqs': ssl.CERT_REQUIRED,
19806
47ff9d1abfa9 sslutil: add a config knob to support TLS (default) or SSLv23 (bc) (issue4038)
Augie Fackler <raf@durin42.com>
parents: 19749
diff changeset
   154
                    })
47ff9d1abfa9 sslutil: add a config knob to support TLS (default) or SSLv23 (bc) (issue4038)
Augie Fackler <raf@durin42.com>
parents: 19749
diff changeset
   155
    return kws
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   156
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   157
class validator(object):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   158
    def __init__(self, ui, host):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   159
        self.ui = ui
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   160
        self.host = host
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   161
18887
2d7fac049d3a sslutil: abort if peer certificate is not verified for secure use
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18879
diff changeset
   162
    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
   163
        host = self.host
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   164
        cacerts = self.ui.config('web', 'cacerts')
28525
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   165
        hostfingerprints = self.ui.configlist('hostfingerprints', host)
18879
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   166
15816
4bb59919c905 sslutil: work around validator crash getting certificate on failed sockets
Mads Kiilerich <mads@kiilerich.com>
parents: 15815
diff changeset
   167
        if not sock.cipher(): # work around http://bugs.python.org/issue13721
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25977
diff changeset
   168
            raise error.Abort(_('%s ssl connection error') % host)
18879
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   169
        try:
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   170
            peercert = sock.getpeercert(True)
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   171
            peercert2 = sock.getpeercert()
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   172
        except AttributeError:
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25977
diff changeset
   173
            raise error.Abort(_('%s ssl connection error') % host)
18879
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   174
15817
8f377751b510 sslutil: abort properly if no certificate received for https connection
Mads Kiilerich <mads@kiilerich.com>
parents: 15816
diff changeset
   175
        if not peercert:
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25977
diff changeset
   176
            raise error.Abort(_('%s certificate error: '
15817
8f377751b510 sslutil: abort properly if no certificate received for https connection
Mads Kiilerich <mads@kiilerich.com>
parents: 15816
diff changeset
   177
                               'no certificate received') % host)
15814
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   178
        peerfingerprint = util.sha1(peercert).hexdigest()
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   179
        nicefingerprint = ":".join([peerfingerprint[x:x + 2]
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   180
            for x in xrange(0, len(peerfingerprint), 2)])
28525
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   181
        if hostfingerprints:
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   182
            fingerprintmatch = False
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   183
            for hostfingerprint in hostfingerprints:
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   184
                if peerfingerprint.lower() == \
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   185
                        hostfingerprint.replace(':', '').lower():
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   186
                    fingerprintmatch = True
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   187
                    break
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   188
            if not fingerprintmatch:
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25977
diff changeset
   189
                raise error.Abort(_('certificate for %s has unexpected '
15997
a45516cb8d9f sslutil: more helpful fingerprint mismatch message
Matt Mackall <mpm@selenic.com>
parents: 15817
diff changeset
   190
                                   'fingerprint %s') % (host, nicefingerprint),
a45516cb8d9f sslutil: more helpful fingerprint mismatch message
Matt Mackall <mpm@selenic.com>
parents: 15817
diff changeset
   191
                                 hint=_('check hostfingerprint configuration'))
15815
edc3a901a63d sslutil: reorder validator code to make it more readable
Mads Kiilerich <mads@kiilerich.com>
parents: 15814
diff changeset
   192
            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
   193
                          (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
   194
        elif cacerts != '!':
18879
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   195
            msg = _verifycert(peercert2, host)
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   196
            if msg:
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25977
diff changeset
   197
                raise error.Abort(_('%s certificate error: %s') % (host, msg),
15814
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   198
                                 hint=_('configure hostfingerprint %s or use '
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   199
                                        '--insecure to connect insecurely') %
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   200
                                      nicefingerprint)
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   201
            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
   202
        elif strict:
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25977
diff changeset
   203
            raise error.Abort(_('%s certificate with fingerprint %s not '
18887
2d7fac049d3a sslutil: abort if peer certificate is not verified for secure use
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18879
diff changeset
   204
                               '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
   205
                             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
   206
                                     'config setting'))
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   207
        else:
15815
edc3a901a63d sslutil: reorder validator code to make it more readable
Mads Kiilerich <mads@kiilerich.com>
parents: 15814
diff changeset
   208
            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
   209
                           '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
   210
                           'config setting)\n') %
edc3a901a63d sslutil: reorder validator code to make it more readable
Mads Kiilerich <mads@kiilerich.com>
parents: 15814
diff changeset
   211
                         (host, nicefingerprint))