~ubuntu-branches/ubuntu/wily/pymongo/wily-proposed

« back to all changes in this revision

Viewing changes to pymongo/ssl_support.py

  • Committer: Package Import Robot
  • Author(s): Federico Ceratto
  • Date: 2015-04-26 22:43:13 UTC
  • mfrom: (24.1.5 sid)
  • Revision ID: package-import@ubuntu.com-20150426224313-0hga2jphvf0rrmfe
Tags: 3.0.1-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2014-2015 MongoDB, Inc.
 
2
#
 
3
# Licensed under the Apache License, Version 2.0 (the "License"); you
 
4
# may not use this file except in compliance with the License.  You
 
5
# may obtain a copy of the License at
 
6
#
 
7
# http://www.apache.org/licenses/LICENSE-2.0
 
8
#
 
9
# Unless required by applicable law or agreed to in writing, software
 
10
# distributed under the License is distributed on an "AS IS" BASIS,
 
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 
12
# implied.  See the License for the specific language governing
 
13
# permissions and limitations under the License.
 
14
 
 
15
"""Support for SSL in PyMongo."""
 
16
 
 
17
import atexit
 
18
import sys
 
19
import threading
 
20
 
 
21
HAVE_SSL = True
 
22
try:
 
23
    import ssl
 
24
except ImportError:
 
25
    HAVE_SSL = False
 
26
 
 
27
HAVE_CERTIFI = False
 
28
try:
 
29
    import certifi
 
30
    HAVE_CERTIFI = True
 
31
except ImportError:
 
32
    pass
 
33
 
 
34
HAVE_WINCERTSTORE = False
 
35
try:
 
36
    from wincertstore import CertFile
 
37
    HAVE_WINCERTSTORE = True
 
38
except ImportError:
 
39
    pass
 
40
 
 
41
from bson.py3compat import string_type
 
42
from pymongo.errors import ConfigurationError
 
43
 
 
44
_WINCERTSLOCK = threading.Lock()
 
45
_WINCERTS = None
 
46
 
 
47
if HAVE_SSL:
 
48
    try:
 
49
        # Python 3.2 and above.
 
50
        from ssl import SSLContext
 
51
    except ImportError:
 
52
        from pymongo.ssl_context import SSLContext
 
53
 
 
54
    def validate_cert_reqs(option, value):
 
55
        """Validate the cert reqs are valid. It must be None or one of the
 
56
        three values ``ssl.CERT_NONE``, ``ssl.CERT_OPTIONAL`` or
 
57
        ``ssl.CERT_REQUIRED``.
 
58
        """
 
59
        if value is None:
 
60
            return value
 
61
        elif isinstance(value, string_type) and hasattr(ssl, value):
 
62
            value = getattr(ssl, value)
 
63
 
 
64
        if value in (ssl.CERT_NONE, ssl.CERT_OPTIONAL, ssl.CERT_REQUIRED):
 
65
            return value
 
66
        raise ValueError("The value of %s must be one of: "
 
67
                         "`ssl.CERT_NONE`, `ssl.CERT_OPTIONAL` or "
 
68
                         "`ssl.CERT_REQUIRED" % (option,))
 
69
 
 
70
    def _load_wincerts():
 
71
        """Set _WINCERTS to an instance of wincertstore.Certfile."""
 
72
        global _WINCERTS
 
73
 
 
74
        certfile = CertFile()
 
75
        certfile.addstore("CA")
 
76
        certfile.addstore("ROOT")
 
77
        atexit.register(certfile.close)
 
78
 
 
79
        _WINCERTS = certfile
 
80
 
 
81
    # XXX: Possible future work.
 
82
    # - Support CRL files? Only supported by CPython >= 2.7.9 and >= 3.4
 
83
    #   http://bugs.python.org/issue8813
 
84
    # - OCSP? Not supported by python at all.
 
85
    #   http://bugs.python.org/issue17123
 
86
    # - Setting OP_NO_COMPRESSION? The server doesn't yet.
 
87
    # - Adding an ssl_context keyword argument to MongoClient? This might
 
88
    #   be useful for sites that have unusual requirements rather than
 
89
    #   trying to expose every SSLContext option through a keyword/uri
 
90
    #   parameter.
 
91
    def get_ssl_context(*args):
 
92
        """Create and return an SSLContext object."""
 
93
        certfile, keyfile, ca_certs, cert_reqs = args
 
94
        # Note PROTOCOL_SSLv23 is about the most misleading name imaginable.
 
95
        # This configures the server and client to negotiate the
 
96
        # highest protocol version they both support. A very good thing.
 
97
        ctx = SSLContext(ssl.PROTOCOL_SSLv23)
 
98
        if hasattr(ctx, "options"):
 
99
            # Explicitly disable SSLv2 and SSLv3. Note that up to
 
100
            # date versions of MongoDB 2.4 and above already do this,
 
101
            # python disables SSLv2 by default in >= 2.7.7 and >= 3.3.4
 
102
            # and SSLv3 in >= 3.4.3. There is no way for us to do this
 
103
            # explicitly for python 2.6 or 2.7 before 2.7.9.
 
104
            ctx.options |= getattr(ssl, "OP_NO_SSLv2", 0)
 
105
            ctx.options |= getattr(ssl, "OP_NO_SSLv3", 0)
 
106
        if certfile is not None:
 
107
            ctx.load_cert_chain(certfile, keyfile)
 
108
        if ca_certs is not None:
 
109
            ctx.load_verify_locations(ca_certs)
 
110
        elif cert_reqs != ssl.CERT_NONE:
 
111
            # CPython >= 2.7.9 or >= 3.4.0, pypy >= 2.5.1
 
112
            if hasattr(ctx, "load_default_certs"):
 
113
                ctx.load_default_certs()
 
114
            # Python >= 3.2.0, useless on Windows.
 
115
            elif (sys.platform != "win32" and
 
116
                  hasattr(ctx, "set_default_verify_paths")):
 
117
                ctx.set_default_verify_paths()
 
118
            elif sys.platform == "win32" and HAVE_WINCERTSTORE:
 
119
                with _WINCERTSLOCK:
 
120
                    if _WINCERTS is None:
 
121
                        _load_wincerts()
 
122
                ctx.load_verify_locations(_WINCERTS.name)
 
123
            elif HAVE_CERTIFI:
 
124
                ctx.load_verify_locations(certifi.where())
 
125
            else:
 
126
                raise ConfigurationError(
 
127
                    "`ssl_cert_reqs` is not ssl.CERT_NONE and no system "
 
128
                    "CA certificates could be loaded. `ssl_ca_certs` is "
 
129
                    "required.")
 
130
        ctx.verify_mode = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs
 
131
        return ctx
 
132
else:
 
133
    def validate_cert_reqs(option, dummy):
 
134
        """No ssl module, raise ConfigurationError."""
 
135
        raise ConfigurationError("The value of %s is set but can't be "
 
136
                                 "validated. The ssl module is not available"
 
137
                                 % (option,))
 
138
 
 
139
    def get_ssl_context(*dummy):
 
140
        """No ssl module, raise ConfigurationError."""
 
141
        raise ConfigurationError("The ssl module is not available.")