mercurial/sslutil.py
author Gregory Szorc <gregory.szorc@gmail.com>
Thu, 05 May 2016 00:38:18 -0700
changeset 29113 5b9577edf745
parent 29112 5edc5acecc83
child 29115 ef316c653b7f
permissions -rw-r--r--
sslutil: use CA loaded state to drive validation logic Until now, sslkwargs may set web.cacerts=! to indicate that system certs could not be found. This is really obtuse because sslkwargs effectively sets state on a global object which bypasses wrapsocket() and is later consulted by validator.__call__. This is madness. This patch introduces an attribute on the wrapped socket instance indicating whether system CAs were loaded. We can set this directly inside wrapsocket() because that function knows everything that sslkwargs() does - and more. With this attribute set on the socket, we refactor validator.__call__ to use it. Since we no longer have a need for setting web.cacerts=! in sslkwargs, we remove that. I think the new logic is much easier to understand and will enable behavior to be changed more easily.
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')
29111
843df550b465 sslutil: check for ui.insecureconnections in sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29108
diff changeset
   257
    # TODO remove check when we stop setting this config.
24290
b76d8c641746 ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 24288
diff changeset
   258
    if cacerts == '!':
29105
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   259
        return kws
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   260
29106
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   261
    # 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
   262
    # and require those certs.
29105
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   263
    if cacerts:
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   264
        cacerts = util.expandpath(cacerts)
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   265
        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
   266
            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
   267
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   268
        kws.update({'ca_certs': cacerts,
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   269
                    'cert_reqs': ssl.CERT_REQUIRED})
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   270
        return kws
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   271
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   272
    # 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
   273
    cacerts = _defaultcacerts()
29108
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   274
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   275
    # 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
   276
    if cacerts:
29106
fe7ebef8796a sslutil: further refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29105
diff changeset
   277
        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
   278
        ui.setconfig('web', 'cacerts', cacerts, 'defaultcacerts')
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   279
        kws.update({'ca_certs': cacerts,
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   280
                    'cert_reqs': ssl.CERT_REQUIRED})
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   281
        return kws
29107
c8fbfb9163ce sslutil: move code examining _canloaddefaultcerts out of _defaultcacerts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29106
diff changeset
   282
29108
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   283
    # 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
   284
    if _canloaddefaultcerts:
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   285
        kws['cert_reqs'] = ssl.CERT_REQUIRED
16021d58c5ca sslutil: make sslkwargs code even more explicit
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29107
diff changeset
   286
        return kws
29105
548e9c8c2841 sslutil: document and slightly refactor sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29042
diff changeset
   287
19806
47ff9d1abfa9 sslutil: add a config knob to support TLS (default) or SSLv23 (bc) (issue4038)
Augie Fackler <raf@durin42.com>
parents: 19749
diff changeset
   288
    return kws
14204
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   289
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   290
class validator(object):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   291
    def __init__(self, ui, host):
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   292
        self.ui = ui
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   293
        self.host = host
5fa21960b2f4 sslutil: extracted ssl methods from httpsconnection in url.py
Augie Fackler <durin42@gmail.com>
parents:
diff changeset
   294
18887
2d7fac049d3a sslutil: abort if peer certificate is not verified for secure use
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18879
diff changeset
   295
    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
   296
        host = self.host
18879
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   297
15816
4bb59919c905 sslutil: work around validator crash getting certificate on failed sockets
Mads Kiilerich <mads@kiilerich.com>
parents: 15815
diff changeset
   298
        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
   299
            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
   300
        try:
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   301
            peercert = sock.getpeercert(True)
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   302
            peercert2 = sock.getpeercert()
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   303
        except AttributeError:
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 ssl connection error') % host)
18879
93b03a222c3e sslutil: try harder to avoid getpeercert problems
Matt Mackall <mpm@selenic.com>
parents: 16391
diff changeset
   305
15817
8f377751b510 sslutil: abort properly if no certificate received for https connection
Mads Kiilerich <mads@kiilerich.com>
parents: 15816
diff changeset
   306
        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
   307
            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
   308
                               'no certificate received') % host)
28850
3819c349b194 sslutil: document and slightly refactor validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28849
diff changeset
   309
3819c349b194 sslutil: document and slightly refactor validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28849
diff changeset
   310
        # 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
   311
        # validate the remote cert.
3819c349b194 sslutil: document and slightly refactor validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28849
diff changeset
   312
        hostfingerprints = self.ui.configlist('hostfingerprints', host)
15814
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   313
        peerfingerprint = util.sha1(peercert).hexdigest()
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   314
        nicefingerprint = ":".join([peerfingerprint[x:x + 2]
c3e958b50a22 sslutil: show fingerprint when cacerts validation fails
Mads Kiilerich <mads@kiilerich.com>
parents: 15813
diff changeset
   315
            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
   316
        if hostfingerprints:
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   317
            fingerprintmatch = False
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   318
            for hostfingerprint in hostfingerprints:
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   319
                if peerfingerprint.lower() == \
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   320
                        hostfingerprint.replace(':', '').lower():
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   321
                    fingerprintmatch = True
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   322
                    break
dfb21c34e07d sslutil: allow multiple fingerprints per host
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27688
diff changeset
   323
            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
   324
                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
   325
                                   'fingerprint %s') % (host, nicefingerprint),
a45516cb8d9f sslutil: more helpful fingerprint mismatch message
Matt Mackall <mpm@selenic.com>
parents: 15817
diff changeset
   326
                                 hint=_('check hostfingerprint configuration'))
15815
edc3a901a63d sslutil: reorder validator code to make it more readable
Mads Kiilerich <mads@kiilerich.com>
parents: 15814
diff changeset
   327
            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
   328
                          (host, nicefingerprint))
28850
3819c349b194 sslutil: document and slightly refactor validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28849
diff changeset
   329
            return
3819c349b194 sslutil: document and slightly refactor validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28849
diff changeset
   330
29112
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   331
        # 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
   332
        # print a warning and do no verification.
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   333
        #
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   334
        # 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
   335
        # 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
   336
        # the same as below for BC.
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   337
        if self.ui.insecureconnections:
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   338
            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
   339
                           'verified (check hostfingerprints or web.cacerts '
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   340
                           'config setting)\n') %
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   341
                         (host, nicefingerprint))
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   342
            return
5edc5acecc83 sslutil: handle ui.insecureconnections in validator
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29111
diff changeset
   343
29113
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   344
        if not sock._hgcaloaded:
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   345
            if strict:
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   346
                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
   347
                                    'verified') % (host, nicefingerprint),
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   348
                                  hint=_('check hostfingerprints or '
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   349
                                         'web.cacerts config setting'))
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   350
            else:
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   351
                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
   352
                               '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
   353
                               '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
   354
                             (host, nicefingerprint))
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   355
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   356
            return
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   357
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   358
        msg = _verifycert(peercert2, host)
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   359
        if msg:
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   360
            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
   361
                             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
   362
                                    '--insecure to connect insecurely') %
5b9577edf745 sslutil: use CA loaded state to drive validation logic
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29112
diff changeset
   363
                                  nicefingerprint)