mercurial/sslutil.py
author timeless <timeless@mozdev.org>
Fri, 06 May 2016 19:17:49 +0000
changeset 29179 ce1c6ab15807
parent 29115 ef316c653b7f
child 29224 7424f4294199
permissions -rw-r--r--
tests: test-archive.t use absolute_import This is a step to adding a mercurial dependency to simplify py3 compat
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 _
28577
7efff6ce9826 sslutil: use preferred formatting for import syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28525
diff changeset
    17
from . import (
7efff6ce9826 sslutil: use preferred formatting for import syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28525
diff changeset
    18
    error,
7efff6ce9826 sslutil: use preferred formatting for import syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28525
diff changeset
    19
    util,
7efff6ce9826 sslutil: use preferred formatting for import syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28525
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
28647
834d1c4ba749 sslutil: better document state of security/ssl module
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28577
diff changeset
    22
# Python 2.7.9+ overhauled the built-in SSL/TLS features of Python. It added
834d1c4ba749 sslutil: better document state of security/ssl module
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28577
diff changeset
    23
# support for TLS 1.1, TLS 1.2, SNI, system CA stores, etc. These features are
834d1c4ba749 sslutil: better document state of security/ssl module
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28577
diff changeset
    24
# all exposed via the "ssl" module.
834d1c4ba749 sslutil: better document state of security/ssl module
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28577
diff changeset
    25
#
834d1c4ba749 sslutil: better document state of security/ssl module
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28577
diff changeset
    26
# Depending on the version of Python being used, SSL/TLS support is either
834d1c4ba749 sslutil: better document state of security/ssl module
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28577
diff changeset
    27
# modern/secure or legacy/insecure. Many operations in this module have
834d1c4ba749 sslutil: better document state of security/ssl module
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28577
diff changeset
    28
# separate code paths depending on support in Python.
834d1c4ba749 sslutil: better document state of security/ssl module
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28577
diff changeset
    29
26622
9e15286609ae sslutil: expose attribute indicating whether SNI is supported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26587
diff changeset
    30
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
    31
28648
7fc787e5d8ec sslutil: store OP_NO_SSL* constants in module scope
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28647
diff changeset
    32
try:
7fc787e5d8ec sslutil: store OP_NO_SSL* constants in module scope
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28647
diff changeset
    33
    OP_NO_SSLv2 = ssl.OP_NO_SSLv2
7fc787e5d8ec sslutil: store OP_NO_SSL* constants in module scope
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28647
diff changeset
    34
    OP_NO_SSLv3 = ssl.OP_NO_SSLv3
7fc787e5d8ec sslutil: store OP_NO_SSL* constants in module scope
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28647
diff changeset
    35
except AttributeError:
7fc787e5d8ec sslutil: store OP_NO_SSL* constants in module scope
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28647
diff changeset
    36
    OP_NO_SSLv2 = 0x1000000
7fc787e5d8ec sslutil: store OP_NO_SSL* constants in module scope
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28647
diff changeset
    37
    OP_NO_SSLv3 = 0x2000000
7fc787e5d8ec sslutil: store OP_NO_SSL* constants in module scope
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28647
diff changeset
    38
28649
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    39
try:
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    40
    # ssl.SSLContext was added in 2.7.9 and presence indicates modern
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    41
    # SSL/TLS features are available.
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    42
    SSLContext = ssl.SSLContext
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    43
    modernssl = True
28650
737863b01d9f sslutil: move _canloaddefaultcerts logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28649
diff changeset
    44
    _canloaddefaultcerts = util.safehasattr(SSLContext, 'load_default_certs')
28649
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    45
except AttributeError:
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    46
    modernssl = False
28650
737863b01d9f sslutil: move _canloaddefaultcerts logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28649
diff changeset
    47
    _canloaddefaultcerts = False
28649
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    48
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    49
    # We implement SSLContext using the interface from the standard library.
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    50
    class SSLContext(object):
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    51
        # ssl.wrap_socket gained the "ciphers" named argument in 2.7.
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    52
        _supportsciphers = sys.version_info >= (2, 7)
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    53
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    54
        def __init__(self, protocol):
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    55
            # From the public interface of SSLContext
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    56
            self.protocol = protocol
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    57
            self.check_hostname = False
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    58
            self.options = 0
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    59
            self.verify_mode = ssl.CERT_NONE
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    60
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    61
            # Used by our implementation.
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    62
            self._certfile = None
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    63
            self._keyfile = None
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    64
            self._certpassword = None
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    65
            self._cacerts = None
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    66
            self._ciphers = None
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    67
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    68
        def load_cert_chain(self, certfile, keyfile=None, password=None):
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    69
            self._certfile = certfile
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    70
            self._keyfile = keyfile
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    71
            self._certpassword = password
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    72
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    73
        def load_default_certs(self, purpose=None):
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    74
            pass
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    75
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    76
        def load_verify_locations(self, cafile=None, capath=None, cadata=None):
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    77
            if capath:
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    78
                raise error.Abort('capath not supported')
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    79
            if cadata:
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    80
                raise error.Abort('cadata not supported')
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    81
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    82
            self._cacerts = cafile
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    83
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    84
        def set_ciphers(self, ciphers):
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    85
            if not self._supportsciphers:
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    86
                raise error.Abort('setting ciphers not supported')
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    87
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    88
            self._ciphers = ciphers
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    89
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    90
        def wrap_socket(self, socket, server_hostname=None, server_side=False):
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    91
            # server_hostname is unique to SSLContext.wrap_socket and is used
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    92
            # for SNI in that context. So there's nothing for us to do with it
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    93
            # in this legacy code since we don't support SNI.
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    94
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    95
            args = {
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    96
                'keyfile': self._keyfile,
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    97
                'certfile': self._certfile,
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    98
                'server_side': server_side,
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
    99
                'cert_reqs': self.verify_mode,
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
   100
                'ssl_version': self.protocol,
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
   101
                'ca_certs': self._cacerts,
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
   102
            }
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
   103
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
   104
            if self._supportsciphers:
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
   105
                args['ciphers'] = self._ciphers
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
   106
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
   107
            return ssl.wrap_socket(socket, **args)
7acab42ef184 sslutil: implement SSLContext class
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28648
diff changeset
   108
28652
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   109
def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE,
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   110
               ca_certs=None, serverhostname=None):
28653
1eb0bd8adf39 sslutil: add docstring to wrapsocket()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28652
diff changeset
   111
    """Add SSL/TLS to a socket.
1eb0bd8adf39 sslutil: add docstring to wrapsocket()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28652
diff changeset
   112
1eb0bd8adf39 sslutil: add docstring to wrapsocket()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28652
diff changeset
   113
    This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane
1eb0bd8adf39 sslutil: add docstring to wrapsocket()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28652
diff changeset
   114
    choices based on what security options are available.
1eb0bd8adf39 sslutil: add docstring to wrapsocket()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28652
diff changeset
   115
1eb0bd8adf39 sslutil: add docstring to wrapsocket()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28652
diff changeset
   116
    In addition to the arguments supported by ``ssl.wrap_socket``, we allow
1eb0bd8adf39 sslutil: add docstring to wrapsocket()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28652
diff changeset
   117
    the following additional arguments:
1eb0bd8adf39 sslutil: add docstring to wrapsocket()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28652
diff changeset
   118
1eb0bd8adf39 sslutil: add docstring to wrapsocket()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28652
diff changeset
   119
    * serverhostname - The expected hostname of the remote server. If the
1eb0bd8adf39 sslutil: add docstring to wrapsocket()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28652
diff changeset
   120
      server (and client) support SNI, this tells the server which certificate
1eb0bd8adf39 sslutil: add docstring to wrapsocket()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28652
diff changeset
   121
      to use.
1eb0bd8adf39 sslutil: add docstring to wrapsocket()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28652
diff changeset
   122
    """
28652
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   123
    # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   124
    # that both ends support, including TLS protocols. On legacy stacks,
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   125
    # the highest it likely goes in TLS 1.0. On modern stacks, it can
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   126
    # support TLS 1.2.
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   127
    #
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   128
    # The PROTOCOL_TLSv* constants select a specific TLS version
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   129
    # only (as opposed to multiple versions). So the method for
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   130
    # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   131
    # disable protocols via SSLContext.options and OP_NO_* constants.
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   132
    # However, SSLContext.options doesn't work unless we have the
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   133
    # full/real SSLContext available to us.
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   134
    #
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   135
    # SSLv2 and SSLv3 are broken. We ban them outright.
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   136
    if modernssl:
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   137
        protocol = ssl.PROTOCOL_SSLv23
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   138
    else:
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   139
        protocol = ssl.PROTOCOL_TLSv1
28651
4827d07073e6 sslutil: always use SSLContext
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28650
diff changeset
   140
28652
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   141
    # TODO use ssl.create_default_context() on modernssl.
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   142
    sslcontext = SSLContext(protocol)
28651
4827d07073e6 sslutil: always use SSLContext
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28650
diff changeset
   143
28652
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   144
    # This is a no-op on old Python.
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   145
    sslcontext.options |= OP_NO_SSLv2 | OP_NO_SSLv3
28651
4827d07073e6 sslutil: always use SSLContext
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28650
diff changeset
   146
28848
e330db205b20 sslutil: move and document verify_mode assignment
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28653
diff changeset
   147
    # This still works on our fake SSLContext.
e330db205b20 sslutil: move and document verify_mode assignment
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28653
diff changeset
   148
    sslcontext.verify_mode = cert_reqs
e330db205b20 sslutil: move and document verify_mode assignment
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28653
diff changeset
   149
28652
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   150
    if certfile is not None:
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   151
        def password():
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   152
            f = keyfile or certfile
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   153
            return ui.getpass(_('passphrase for %s: ') % f, '')
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   154
        sslcontext.load_cert_chain(certfile, keyfile, password)
28848
e330db205b20 sslutil: move and document verify_mode assignment
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28653
diff changeset
   155
28652
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   156
    if ca_certs is not None:
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   157
        sslcontext.load_verify_locations(cafile=ca_certs)
29113
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   158
        caloaded = True
28652
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   159
    else:
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   160
        # This is a no-op on old Python.
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   161
        sslcontext.load_default_certs()
29113
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   162
        caloaded = _canloaddefaultcerts
23834
bf07c19b4c82 https: support tls sni (server name indication) for https urls (issue3090)
Alex Orange <crazycasta@gmail.com>
parents: 23069
diff changeset
   163
28652
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   164
    sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   165
    # check if wrap_socket failed silently because socket had been
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   166
    # closed
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   167
    # - see http://bugs.python.org/issue13721
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   168
    if not sslsocket.cipher():
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   169
        raise error.Abort(_('ssl connection failed'))
29113
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   170
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   171
    sslsocket._hgcaloaded = caloaded
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   172
28652
c617614aefd2 sslutil: remove indentation in wrapsocket declaration
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28651
diff changeset
   173
    return sslsocket
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   174
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   175
def _verifycert(cert, hostname):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   176
    '''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
   177
    CRLs is not handled.
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   178
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   179
    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
   180
    '''
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   181
    if not cert:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   182
        return _('no certificate received')
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   183
    dnsname = hostname.lower()
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   184
    def matchdnsname(certname):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   185
        return (certname == dnsname or
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   186
                '.' 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
   187
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   188
    san = cert.get('subjectAltName', [])
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   189
    if san:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   190
        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
   191
        for name in certnames:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   192
            if matchdnsname(name):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   193
                return None
14666
27b080aa880a sslutil: fall back to commonName when no dNSName in subjectAltName (issue2798)
Nicolas Bareil <nico@chdir.org>
parents: 14616
diff changeset
   194
        if certnames:
27b080aa880a sslutil: fall back to commonName when no dNSName in subjectAltName (issue2798)
Nicolas Bareil <nico@chdir.org>
parents: 14616
diff changeset
   195
            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
   196
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   197
    # 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
   198
    for s in cert.get('subject', []):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   199
        key, value = s[0]
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   200
        if key == 'commonName':
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   201
            try:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   202
                # 'subject' entries are unicode
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   203
                certname = value.lower().encode('ascii')
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   204
            except UnicodeEncodeError:
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   205
                return _('IDN in certificate not supported')
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   206
            if matchdnsname(certname):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   207
                return None
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   208
            return _('certificate is for %s') % certname
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   209
    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
   210
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   211
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   212
# 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
   213
# 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
   214
23042
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   215
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
   216
    """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
   217
    * 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
   218
      system
2cd3fa4412dc ssl: only use the dummy cert hack if using an Apple Python (issue4410)
Mads Kiilerich <madski@unity3d.com>
parents: 22575
diff changeset
   219
    * 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
   220
      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
   221
      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
   222
    """
24614
241d98d84aed ssl: resolve symlink before checking for Apple python executable (issue4588)
Yuya Nishihara <yuya@tcha.org>
parents: 24291
diff changeset
   223
    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
   224
        return False
24614
241d98d84aed ssl: resolve symlink before checking for Apple python executable (issue4588)
Yuya Nishihara <yuya@tcha.org>
parents: 24291
diff changeset
   225
    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
   226
    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
   227
            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
   228
24288
922e087ba158 ssl: extract function that returns dummycert path on Apple python
Yuya Nishihara <yuya@tcha.org>
parents: 23851
diff changeset
   229
def _defaultcacerts():
29107
c8fbfb9163ce sslutil: move code examining _canloaddefaultcerts out of _defaultcacerts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29106
diff changeset
   230
    """return path to default CA certificates or None."""
24288
922e087ba158 ssl: extract function that returns dummycert path on Apple python
Yuya Nishihara <yuya@tcha.org>
parents: 23851
diff changeset
   231
    if _plainapplepython():
922e087ba158 ssl: extract function that returns dummycert path on Apple python
Yuya Nishihara <yuya@tcha.org>
parents: 23851
diff changeset
   232
        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
   233
        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
   234
            return dummycert
29107
c8fbfb9163ce sslutil: move code examining _canloaddefaultcerts out of _defaultcacerts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29106
diff changeset
   235
c8fbfb9163ce sslutil: move code examining _canloaddefaultcerts out of _defaultcacerts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29106
diff changeset
   236
    return None
24288
922e087ba158 ssl: extract function that returns dummycert path on Apple python
Yuya Nishihara <yuya@tcha.org>
parents: 23851
diff changeset
   237
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   238
def sslkwargs(ui, host):
29105
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   239
    """Determine arguments to pass to wrapsocket().
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   240
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   241
    ``host`` is the hostname being connected to.
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   242
    """
25415
21b536f01eda ssl: prompt passphrase of client key file via ui.getpass() (issue4648)
Yuya Nishihara <yuya@tcha.org>
parents: 24614
diff changeset
   243
    kws = {'ui': ui}
29105
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   244
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   245
    # If a host key fingerprint is on file, it is the only thing that matters
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   246
    # and CA certs don't come into play.
22574
a00a7951b20c ssl: refactor sslkwargs - move things around a bit, preparing for next change
Mads Kiilerich <madski@unity3d.com>
parents: 19808
diff changeset
   247
    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
   248
    if hostfingerprint:
a00a7951b20c ssl: refactor sslkwargs - move things around a bit, preparing for next change
Mads Kiilerich <madski@unity3d.com>
parents: 19808
diff changeset
   249
        return kws
29105
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   250
29111
843df550b465 sslutil: check for ui.insecureconnections in sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29108
diff changeset
   251
    # The code below sets up CA verification arguments. If --insecure is
843df550b465 sslutil: check for ui.insecureconnections in sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29108
diff changeset
   252
    # used, we don't take CAs into consideration, so return early.
843df550b465 sslutil: check for ui.insecureconnections in sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29108
diff changeset
   253
    if ui.insecureconnections:
843df550b465 sslutil: check for ui.insecureconnections in sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29108
diff changeset
   254
        return kws
843df550b465 sslutil: check for ui.insecureconnections in sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29108
diff changeset
   255
22574
a00a7951b20c ssl: refactor sslkwargs - move things around a bit, preparing for next change
Mads Kiilerich <madski@unity3d.com>
parents: 19808
diff changeset
   256
    cacerts = ui.config('web', 'cacerts')
29105
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   257
29106
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   258
    # If a value is set in the config, validate against a path and load
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   259
    # and require those certs.
29105
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   260
    if cacerts:
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   261
        cacerts = util.expandpath(cacerts)
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   262
        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
   263
            raise error.Abort(_('could not find web.cacerts: %s') % cacerts)
29106
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   264
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   265
        kws.update({'ca_certs': cacerts,
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   266
                    'cert_reqs': ssl.CERT_REQUIRED})
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   267
        return kws
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   268
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   269
    # No CAs in config. See if we can load defaults.
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   270
    cacerts = _defaultcacerts()
29108
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   271
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   272
    # We found an alternate CA bundle to use. Load it.
29107
c8fbfb9163ce sslutil: move code examining _canloaddefaultcerts out of _defaultcacerts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29106
diff changeset
   273
    if cacerts:
29106
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   274
        ui.debug('using %s to enable OS X system CA\n' % cacerts)
29108
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   275
        ui.setconfig('web', 'cacerts', cacerts, 'defaultcacerts')
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   276
        kws.update({'ca_certs': cacerts,
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   277
                    'cert_reqs': ssl.CERT_REQUIRED})
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   278
        return kws
29107
c8fbfb9163ce sslutil: move code examining _canloaddefaultcerts out of _defaultcacerts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29106
diff changeset
   279
29108
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   280
    # FUTURE this can disappear once wrapsocket() is secure by default.
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   281
    if _canloaddefaultcerts:
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   282
        kws['cert_reqs'] = ssl.CERT_REQUIRED
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   283
        return kws
29105
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   284
19806
47ff9d1abfa9 sslutil: add a config knob to support TLS (default) or SSLv23 (bc) (issue4038)
Augie Fackler <raf@durin42.com>
parents: 19749
diff changeset
   285
    return kws
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   286
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   287
class validator(object):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   288
    def __init__(self, ui, host):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   289
        self.ui = ui
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   290
        self.host = host
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   291
18887
2d7fac049d3a sslutil: abort if peer certificate is not verified for secure use
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18879
diff changeset
   292
    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
   293
        host = self.host
18879
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   294
15816
4bb59919c905 sslutil: work around validator crash getting certificate on failed sockets
Mads Kiilerich <mads@kiilerich.com>
parents: 15815
diff changeset
   295
        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
   296
            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
   297
        try:
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   298
            peercert = sock.getpeercert(True)
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   299
            peercert2 = sock.getpeercert()
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   300
        except AttributeError:
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25977
diff changeset
   301
            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
   302
15817
8f377751b510 sslutil: abort properly if no certificate received for https connection
Mads Kiilerich <mads@kiilerich.com>
parents: 15816
diff changeset
   303
        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
   304
            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
   305
                               'no certificate received') % host)
28850
3819c349b194 sslutil: document and slightly refactor validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28849
diff changeset
   306
3819c349b194 sslutil: document and slightly refactor validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28849
diff changeset
   307
        # If a certificate fingerprint is pinned, use it and only it to
3819c349b194 sslutil: document and slightly refactor validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28849
diff changeset
   308
        # validate the remote cert.
3819c349b194 sslutil: document and slightly refactor validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28849
diff changeset
   309
        hostfingerprints = self.ui.configlist('hostfingerprints', host)
15814
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   310
        peerfingerprint = util.sha1(peercert).hexdigest()
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   311
        nicefingerprint = ":".join([peerfingerprint[x:x + 2]
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   312
            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
   313
        if hostfingerprints:
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   314
            fingerprintmatch = False
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   315
            for hostfingerprint in hostfingerprints:
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   316
                if peerfingerprint.lower() == \
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   317
                        hostfingerprint.replace(':', '').lower():
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   318
                    fingerprintmatch = True
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   319
                    break
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   320
            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
   321
                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
   322
                                   'fingerprint %s') % (host, nicefingerprint),
a45516cb8d9f sslutil: more helpful fingerprint mismatch message
Matt Mackall <mpm@selenic.com>
parents: 15817
diff changeset
   323
                                 hint=_('check hostfingerprint configuration'))
15815
edc3a901a63d sslutil: reorder validator code to make it more readable
Mads Kiilerich <mads@kiilerich.com>
parents: 15814
diff changeset
   324
            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
   325
                          (host, nicefingerprint))
28850
3819c349b194 sslutil: document and slightly refactor validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28849
diff changeset
   326
            return
3819c349b194 sslutil: document and slightly refactor validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28849
diff changeset
   327
29112
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   328
        # If insecure connections were explicitly requested via --insecure,
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   329
        # print a warning and do no verification.
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   330
        #
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   331
        # It may seem odd that this is checked *after* host fingerprint pinning.
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   332
        # This is for backwards compatibility (for now). The message is also
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   333
        # the same as below for BC.
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   334
        if self.ui.insecureconnections:
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   335
            self.ui.warn(_('warning: %s certificate with fingerprint %s not '
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   336
                           'verified (check hostfingerprints or web.cacerts '
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   337
                           'config setting)\n') %
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   338
                         (host, nicefingerprint))
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   339
            return
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   340
29113
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   341
        if not sock._hgcaloaded:
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   342
            if strict:
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   343
                raise error.Abort(_('%s certificate with fingerprint %s not '
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   344
                                    'verified') % (host, nicefingerprint),
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   345
                                  hint=_('check hostfingerprints or '
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   346
                                         'web.cacerts config setting'))
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   347
            else:
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   348
                self.ui.warn(_('warning: %s certificate with fingerprint %s '
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   349
                               'not verified (check hostfingerprints or '
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   350
                               'web.cacerts config setting)\n') %
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   351
                             (host, nicefingerprint))
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   352
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   353
            return
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   354
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   355
        msg = _verifycert(peercert2, host)
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   356
        if msg:
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   357
            raise error.Abort(_('%s certificate error: %s') % (host, msg),
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   358
                             hint=_('configure hostfingerprint %s or use '
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   359
                                    '--insecure to connect insecurely') %
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   360
                                  nicefingerprint)