sensu-plugins-mongodb-mrtrotl 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1 -0
  3. data/LICENSE +22 -0
  4. data/README.md +27 -0
  5. data/bin/check-mongodb-metric.rb +144 -0
  6. data/bin/check-mongodb-query-count.rb +267 -0
  7. data/bin/check-mongodb.py +1644 -0
  8. data/bin/check-mongodb.rb +5 -0
  9. data/bin/metrics-mongodb-replication.rb +254 -0
  10. data/bin/metrics-mongodb.rb +133 -0
  11. data/lib/bson/__init__.py +1347 -0
  12. data/lib/bson/__pycache__/__init__.cpython-310.pyc +0 -0
  13. data/lib/bson/__pycache__/_helpers.cpython-310.pyc +0 -0
  14. data/lib/bson/__pycache__/binary.cpython-310.pyc +0 -0
  15. data/lib/bson/__pycache__/code.cpython-310.pyc +0 -0
  16. data/lib/bson/__pycache__/codec_options.cpython-310.pyc +0 -0
  17. data/lib/bson/__pycache__/dbref.cpython-310.pyc +0 -0
  18. data/lib/bson/__pycache__/decimal128.cpython-310.pyc +0 -0
  19. data/lib/bson/__pycache__/errors.cpython-310.pyc +0 -0
  20. data/lib/bson/__pycache__/int64.cpython-310.pyc +0 -0
  21. data/lib/bson/__pycache__/json_util.cpython-310.pyc +0 -0
  22. data/lib/bson/__pycache__/max_key.cpython-310.pyc +0 -0
  23. data/lib/bson/__pycache__/min_key.cpython-310.pyc +0 -0
  24. data/lib/bson/__pycache__/objectid.cpython-310.pyc +0 -0
  25. data/lib/bson/__pycache__/raw_bson.cpython-310.pyc +0 -0
  26. data/lib/bson/__pycache__/regex.cpython-310.pyc +0 -0
  27. data/lib/bson/__pycache__/son.cpython-310.pyc +0 -0
  28. data/lib/bson/__pycache__/timestamp.cpython-310.pyc +0 -0
  29. data/lib/bson/__pycache__/tz_util.cpython-310.pyc +0 -0
  30. data/lib/bson/_cbson.cpython-310-x86_64-linux-gnu.so +0 -0
  31. data/lib/bson/_helpers.py +41 -0
  32. data/lib/bson/binary.py +364 -0
  33. data/lib/bson/code.py +101 -0
  34. data/lib/bson/codec_options.py +414 -0
  35. data/lib/bson/codec_options.pyi +100 -0
  36. data/lib/bson/dbref.py +133 -0
  37. data/lib/bson/decimal128.py +314 -0
  38. data/lib/bson/errors.py +35 -0
  39. data/lib/bson/int64.py +39 -0
  40. data/lib/bson/json_util.py +874 -0
  41. data/lib/bson/max_key.py +55 -0
  42. data/lib/bson/min_key.py +55 -0
  43. data/lib/bson/objectid.py +286 -0
  44. data/lib/bson/py.typed +2 -0
  45. data/lib/bson/raw_bson.py +175 -0
  46. data/lib/bson/regex.py +135 -0
  47. data/lib/bson/son.py +208 -0
  48. data/lib/bson/timestamp.py +124 -0
  49. data/lib/bson/tz_util.py +52 -0
  50. data/lib/gridfs/__init__.py +1015 -0
  51. data/lib/gridfs/__pycache__/__init__.cpython-310.pyc +0 -0
  52. data/lib/gridfs/__pycache__/errors.cpython-310.pyc +0 -0
  53. data/lib/gridfs/__pycache__/grid_file.cpython-310.pyc +0 -0
  54. data/lib/gridfs/errors.py +33 -0
  55. data/lib/gridfs/grid_file.py +907 -0
  56. data/lib/gridfs/py.typed +2 -0
  57. data/lib/pymongo/__init__.py +185 -0
  58. data/lib/pymongo/__pycache__/__init__.cpython-310.pyc +0 -0
  59. data/lib/pymongo/__pycache__/_csot.cpython-310.pyc +0 -0
  60. data/lib/pymongo/__pycache__/aggregation.cpython-310.pyc +0 -0
  61. data/lib/pymongo/__pycache__/auth.cpython-310.pyc +0 -0
  62. data/lib/pymongo/__pycache__/auth_aws.cpython-310.pyc +0 -0
  63. data/lib/pymongo/__pycache__/bulk.cpython-310.pyc +0 -0
  64. data/lib/pymongo/__pycache__/change_stream.cpython-310.pyc +0 -0
  65. data/lib/pymongo/__pycache__/client_options.cpython-310.pyc +0 -0
  66. data/lib/pymongo/__pycache__/client_session.cpython-310.pyc +0 -0
  67. data/lib/pymongo/__pycache__/collation.cpython-310.pyc +0 -0
  68. data/lib/pymongo/__pycache__/collection.cpython-310.pyc +0 -0
  69. data/lib/pymongo/__pycache__/command_cursor.cpython-310.pyc +0 -0
  70. data/lib/pymongo/__pycache__/common.cpython-310.pyc +0 -0
  71. data/lib/pymongo/__pycache__/compression_support.cpython-310.pyc +0 -0
  72. data/lib/pymongo/__pycache__/cursor.cpython-310.pyc +0 -0
  73. data/lib/pymongo/__pycache__/daemon.cpython-310.pyc +0 -0
  74. data/lib/pymongo/__pycache__/database.cpython-310.pyc +0 -0
  75. data/lib/pymongo/__pycache__/driver_info.cpython-310.pyc +0 -0
  76. data/lib/pymongo/__pycache__/encryption.cpython-310.pyc +0 -0
  77. data/lib/pymongo/__pycache__/encryption_options.cpython-310.pyc +0 -0
  78. data/lib/pymongo/__pycache__/errors.cpython-310.pyc +0 -0
  79. data/lib/pymongo/__pycache__/event_loggers.cpython-310.pyc +0 -0
  80. data/lib/pymongo/__pycache__/hello.cpython-310.pyc +0 -0
  81. data/lib/pymongo/__pycache__/helpers.cpython-310.pyc +0 -0
  82. data/lib/pymongo/__pycache__/max_staleness_selectors.cpython-310.pyc +0 -0
  83. data/lib/pymongo/__pycache__/message.cpython-310.pyc +0 -0
  84. data/lib/pymongo/__pycache__/mongo_client.cpython-310.pyc +0 -0
  85. data/lib/pymongo/__pycache__/monitor.cpython-310.pyc +0 -0
  86. data/lib/pymongo/__pycache__/monitoring.cpython-310.pyc +0 -0
  87. data/lib/pymongo/__pycache__/network.cpython-310.pyc +0 -0
  88. data/lib/pymongo/__pycache__/ocsp_cache.cpython-310.pyc +0 -0
  89. data/lib/pymongo/__pycache__/ocsp_support.cpython-310.pyc +0 -0
  90. data/lib/pymongo/__pycache__/operations.cpython-310.pyc +0 -0
  91. data/lib/pymongo/__pycache__/periodic_executor.cpython-310.pyc +0 -0
  92. data/lib/pymongo/__pycache__/pool.cpython-310.pyc +0 -0
  93. data/lib/pymongo/__pycache__/pyopenssl_context.cpython-310.pyc +0 -0
  94. data/lib/pymongo/__pycache__/read_concern.cpython-310.pyc +0 -0
  95. data/lib/pymongo/__pycache__/read_preferences.cpython-310.pyc +0 -0
  96. data/lib/pymongo/__pycache__/response.cpython-310.pyc +0 -0
  97. data/lib/pymongo/__pycache__/results.cpython-310.pyc +0 -0
  98. data/lib/pymongo/__pycache__/saslprep.cpython-310.pyc +0 -0
  99. data/lib/pymongo/__pycache__/server.cpython-310.pyc +0 -0
  100. data/lib/pymongo/__pycache__/server_api.cpython-310.pyc +0 -0
  101. data/lib/pymongo/__pycache__/server_description.cpython-310.pyc +0 -0
  102. data/lib/pymongo/__pycache__/server_selectors.cpython-310.pyc +0 -0
  103. data/lib/pymongo/__pycache__/server_type.cpython-310.pyc +0 -0
  104. data/lib/pymongo/__pycache__/settings.cpython-310.pyc +0 -0
  105. data/lib/pymongo/__pycache__/socket_checker.cpython-310.pyc +0 -0
  106. data/lib/pymongo/__pycache__/srv_resolver.cpython-310.pyc +0 -0
  107. data/lib/pymongo/__pycache__/ssl_context.cpython-310.pyc +0 -0
  108. data/lib/pymongo/__pycache__/ssl_support.cpython-310.pyc +0 -0
  109. data/lib/pymongo/__pycache__/topology.cpython-310.pyc +0 -0
  110. data/lib/pymongo/__pycache__/topology_description.cpython-310.pyc +0 -0
  111. data/lib/pymongo/__pycache__/typings.cpython-310.pyc +0 -0
  112. data/lib/pymongo/__pycache__/uri_parser.cpython-310.pyc +0 -0
  113. data/lib/pymongo/__pycache__/write_concern.cpython-310.pyc +0 -0
  114. data/lib/pymongo/_cmessage.cpython-310-x86_64-linux-gnu.so +0 -0
  115. data/lib/pymongo/_csot.py +118 -0
  116. data/lib/pymongo/aggregation.py +229 -0
  117. data/lib/pymongo/auth.py +549 -0
  118. data/lib/pymongo/auth_aws.py +94 -0
  119. data/lib/pymongo/bulk.py +513 -0
  120. data/lib/pymongo/change_stream.py +457 -0
  121. data/lib/pymongo/client_options.py +302 -0
  122. data/lib/pymongo/client_session.py +1112 -0
  123. data/lib/pymongo/collation.py +224 -0
  124. data/lib/pymongo/collection.py +3204 -0
  125. data/lib/pymongo/command_cursor.py +353 -0
  126. data/lib/pymongo/common.py +984 -0
  127. data/lib/pymongo/compression_support.py +149 -0
  128. data/lib/pymongo/cursor.py +1345 -0
  129. data/lib/pymongo/daemon.py +141 -0
  130. data/lib/pymongo/database.py +1202 -0
  131. data/lib/pymongo/driver_info.py +42 -0
  132. data/lib/pymongo/encryption.py +884 -0
  133. data/lib/pymongo/encryption_options.py +221 -0
  134. data/lib/pymongo/errors.py +365 -0
  135. data/lib/pymongo/event_loggers.py +221 -0
  136. data/lib/pymongo/hello.py +219 -0
  137. data/lib/pymongo/helpers.py +259 -0
  138. data/lib/pymongo/max_staleness_selectors.py +114 -0
  139. data/lib/pymongo/message.py +1440 -0
  140. data/lib/pymongo/mongo_client.py +2144 -0
  141. data/lib/pymongo/monitor.py +440 -0
  142. data/lib/pymongo/monitoring.py +1801 -0
  143. data/lib/pymongo/network.py +311 -0
  144. data/lib/pymongo/ocsp_cache.py +87 -0
  145. data/lib/pymongo/ocsp_support.py +372 -0
  146. data/lib/pymongo/operations.py +507 -0
  147. data/lib/pymongo/periodic_executor.py +183 -0
  148. data/lib/pymongo/pool.py +1660 -0
  149. data/lib/pymongo/py.typed +2 -0
  150. data/lib/pymongo/pyopenssl_context.py +383 -0
  151. data/lib/pymongo/read_concern.py +75 -0
  152. data/lib/pymongo/read_preferences.py +609 -0
  153. data/lib/pymongo/response.py +109 -0
  154. data/lib/pymongo/results.py +217 -0
  155. data/lib/pymongo/saslprep.py +113 -0
  156. data/lib/pymongo/server.py +247 -0
  157. data/lib/pymongo/server_api.py +170 -0
  158. data/lib/pymongo/server_description.py +285 -0
  159. data/lib/pymongo/server_selectors.py +153 -0
  160. data/lib/pymongo/server_type.py +32 -0
  161. data/lib/pymongo/settings.py +159 -0
  162. data/lib/pymongo/socket_checker.py +104 -0
  163. data/lib/pymongo/srv_resolver.py +126 -0
  164. data/lib/pymongo/ssl_context.py +39 -0
  165. data/lib/pymongo/ssl_support.py +99 -0
  166. data/lib/pymongo/topology.py +890 -0
  167. data/lib/pymongo/topology_description.py +639 -0
  168. data/lib/pymongo/typings.py +39 -0
  169. data/lib/pymongo/uri_parser.py +624 -0
  170. data/lib/pymongo/write_concern.py +129 -0
  171. data/lib/pymongo-4.2.0.dist-info/INSTALLER +1 -0
  172. data/lib/pymongo-4.2.0.dist-info/LICENSE +201 -0
  173. data/lib/pymongo-4.2.0.dist-info/METADATA +250 -0
  174. data/lib/pymongo-4.2.0.dist-info/RECORD +167 -0
  175. data/lib/pymongo-4.2.0.dist-info/REQUESTED +0 -0
  176. data/lib/pymongo-4.2.0.dist-info/WHEEL +6 -0
  177. data/lib/pymongo-4.2.0.dist-info/top_level.txt +3 -0
  178. data/lib/sensu-plugins-mongodb/metrics.rb +391 -0
  179. data/lib/sensu-plugins-mongodb/version.rb +9 -0
  180. data/lib/sensu-plugins-mongodb.rb +1 -0
  181. metadata +407 -0
@@ -0,0 +1,372 @@
1
+ # Copyright 2020-present 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 requesting and verifying OCSP responses."""
16
+
17
+ import logging as _logging
18
+ import re as _re
19
+ from datetime import datetime as _datetime
20
+
21
+ from cryptography.exceptions import InvalidSignature as _InvalidSignature
22
+ from cryptography.hazmat.backends import default_backend as _default_backend
23
+ from cryptography.hazmat.primitives.asymmetric.dsa import DSAPublicKey as _DSAPublicKey
24
+ from cryptography.hazmat.primitives.asymmetric.ec import ECDSA as _ECDSA
25
+ from cryptography.hazmat.primitives.asymmetric.ec import (
26
+ EllipticCurvePublicKey as _EllipticCurvePublicKey,
27
+ )
28
+ from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15 as _PKCS1v15
29
+ from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey as _RSAPublicKey
30
+ from cryptography.hazmat.primitives.hashes import SHA1 as _SHA1
31
+ from cryptography.hazmat.primitives.hashes import Hash as _Hash
32
+ from cryptography.hazmat.primitives.serialization import Encoding as _Encoding
33
+ from cryptography.hazmat.primitives.serialization import PublicFormat as _PublicFormat
34
+ from cryptography.x509 import AuthorityInformationAccess as _AuthorityInformationAccess
35
+ from cryptography.x509 import ExtendedKeyUsage as _ExtendedKeyUsage
36
+ from cryptography.x509 import ExtensionNotFound as _ExtensionNotFound
37
+ from cryptography.x509 import TLSFeature as _TLSFeature
38
+ from cryptography.x509 import TLSFeatureType as _TLSFeatureType
39
+ from cryptography.x509 import load_pem_x509_certificate as _load_pem_x509_certificate
40
+ from cryptography.x509.ocsp import OCSPCertStatus as _OCSPCertStatus
41
+ from cryptography.x509.ocsp import OCSPRequestBuilder as _OCSPRequestBuilder
42
+ from cryptography.x509.ocsp import OCSPResponseStatus as _OCSPResponseStatus
43
+ from cryptography.x509.ocsp import load_der_ocsp_response as _load_der_ocsp_response
44
+ from cryptography.x509.oid import (
45
+ AuthorityInformationAccessOID as _AuthorityInformationAccessOID,
46
+ )
47
+ from cryptography.x509.oid import ExtendedKeyUsageOID as _ExtendedKeyUsageOID
48
+ from requests import post as _post
49
+ from requests.exceptions import RequestException as _RequestException
50
+
51
+ from pymongo import _csot
52
+
53
+ # Note: the functions in this module generally return 1 or 0. The reason
54
+ # is simple. The entry point, ocsp_callback, is registered as a callback
55
+ # with OpenSSL through PyOpenSSL. The callback must return 1 (success) or
56
+ # 0 (failure).
57
+
58
+ _LOGGER = _logging.getLogger(__name__)
59
+
60
+ _CERT_REGEX = _re.compile(
61
+ b"-----BEGIN CERTIFICATE[^\r\n]+.+?-----END CERTIFICATE[^\r\n]+", _re.DOTALL
62
+ )
63
+
64
+
65
+ def _load_trusted_ca_certs(cafile):
66
+ """Parse the tlsCAFile into a list of certificates."""
67
+ with open(cafile, "rb") as f:
68
+ data = f.read()
69
+
70
+ # Load all the certs in the file.
71
+ trusted_ca_certs = []
72
+ backend = _default_backend()
73
+ for cert_data in _re.findall(_CERT_REGEX, data):
74
+ trusted_ca_certs.append(_load_pem_x509_certificate(cert_data, backend))
75
+ return trusted_ca_certs
76
+
77
+
78
+ def _get_issuer_cert(cert, chain, trusted_ca_certs):
79
+ issuer_name = cert.issuer
80
+ for candidate in chain:
81
+ if candidate.subject == issuer_name:
82
+ return candidate
83
+
84
+ # Depending on the server's TLS library, the peer's cert chain may not
85
+ # include the self signed root CA. In this case we check the user
86
+ # provided tlsCAFile for the issuer.
87
+ # Remove once we use the verified peer cert chain in PYTHON-2147.
88
+ if trusted_ca_certs:
89
+ for candidate in trusted_ca_certs:
90
+ if candidate.subject == issuer_name:
91
+ return candidate
92
+ return None
93
+
94
+
95
+ def _verify_signature(key, signature, algorithm, data):
96
+ # See cryptography.x509.Certificate.public_key
97
+ # for the public key types.
98
+ try:
99
+ if isinstance(key, _RSAPublicKey):
100
+ key.verify(signature, data, _PKCS1v15(), algorithm)
101
+ elif isinstance(key, _DSAPublicKey):
102
+ key.verify(signature, data, algorithm)
103
+ elif isinstance(key, _EllipticCurvePublicKey):
104
+ key.verify(signature, data, _ECDSA(algorithm))
105
+ else:
106
+ key.verify(signature, data)
107
+ except _InvalidSignature:
108
+ return 0
109
+ return 1
110
+
111
+
112
+ def _get_extension(cert, klass):
113
+ try:
114
+ return cert.extensions.get_extension_for_class(klass)
115
+ except _ExtensionNotFound:
116
+ return None
117
+
118
+
119
+ def _public_key_hash(cert):
120
+ public_key = cert.public_key()
121
+ # https://tools.ietf.org/html/rfc2560#section-4.2.1
122
+ # "KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
123
+ # (excluding the tag and length fields)"
124
+ # https://stackoverflow.com/a/46309453/600498
125
+ if isinstance(public_key, _RSAPublicKey):
126
+ pbytes = public_key.public_bytes(_Encoding.DER, _PublicFormat.PKCS1)
127
+ elif isinstance(public_key, _EllipticCurvePublicKey):
128
+ pbytes = public_key.public_bytes(_Encoding.X962, _PublicFormat.UncompressedPoint)
129
+ else:
130
+ pbytes = public_key.public_bytes(_Encoding.DER, _PublicFormat.SubjectPublicKeyInfo)
131
+ digest = _Hash(_SHA1(), backend=_default_backend())
132
+ digest.update(pbytes)
133
+ return digest.finalize()
134
+
135
+
136
+ def _get_certs_by_key_hash(certificates, issuer, responder_key_hash):
137
+ return [
138
+ cert
139
+ for cert in certificates
140
+ if _public_key_hash(cert) == responder_key_hash and cert.issuer == issuer.subject
141
+ ]
142
+
143
+
144
+ def _get_certs_by_name(certificates, issuer, responder_name):
145
+ return [
146
+ cert
147
+ for cert in certificates
148
+ if cert.subject == responder_name and cert.issuer == issuer.subject
149
+ ]
150
+
151
+
152
+ def _verify_response_signature(issuer, response):
153
+ # Response object will have a responder_name or responder_key_hash
154
+ # not both.
155
+ name = response.responder_name
156
+ rkey_hash = response.responder_key_hash
157
+ ikey_hash = response.issuer_key_hash
158
+ if name is not None and name == issuer.subject or rkey_hash == ikey_hash:
159
+ _LOGGER.debug("Responder is issuer")
160
+ # Responder is the issuer
161
+ responder_cert = issuer
162
+ else:
163
+ _LOGGER.debug("Responder is a delegate")
164
+ # Responder is a delegate
165
+ # https://tools.ietf.org/html/rfc6960#section-2.6
166
+ # RFC6960, Section 3.2, Number 3
167
+ certs = response.certificates
168
+ if response.responder_name is not None:
169
+ responder_certs = _get_certs_by_name(certs, issuer, name)
170
+ _LOGGER.debug("Using responder name")
171
+ else:
172
+ responder_certs = _get_certs_by_key_hash(certs, issuer, rkey_hash)
173
+ _LOGGER.debug("Using key hash")
174
+ if not responder_certs:
175
+ _LOGGER.debug("No matching or valid responder certs.")
176
+ return 0
177
+ # XXX: Can there be more than one? If so, should we try each one
178
+ # until we find one that passes signature verification?
179
+ responder_cert = responder_certs[0]
180
+
181
+ # RFC6960, Section 3.2, Number 4
182
+ ext = _get_extension(responder_cert, _ExtendedKeyUsage)
183
+ if not ext or _ExtendedKeyUsageOID.OCSP_SIGNING not in ext.value:
184
+ _LOGGER.debug("Delegate not authorized for OCSP signing")
185
+ return 0
186
+ if not _verify_signature(
187
+ issuer.public_key(),
188
+ responder_cert.signature,
189
+ responder_cert.signature_hash_algorithm,
190
+ responder_cert.tbs_certificate_bytes,
191
+ ):
192
+ _LOGGER.debug("Delegate signature verification failed")
193
+ return 0
194
+ # RFC6960, Section 3.2, Number 2
195
+ ret = _verify_signature(
196
+ responder_cert.public_key(),
197
+ response.signature,
198
+ response.signature_hash_algorithm,
199
+ response.tbs_response_bytes,
200
+ )
201
+ if not ret:
202
+ _LOGGER.debug("Response signature verification failed")
203
+ return ret
204
+
205
+
206
+ def _build_ocsp_request(cert, issuer):
207
+ # https://cryptography.io/en/latest/x509/ocsp/#creating-requests
208
+ builder = _OCSPRequestBuilder()
209
+ builder = builder.add_certificate(cert, issuer, _SHA1())
210
+ return builder.build()
211
+
212
+
213
+ def _verify_response(issuer, response):
214
+ _LOGGER.debug("Verifying response")
215
+ # RFC6960, Section 3.2, Number 2, 3 and 4 happen here.
216
+ res = _verify_response_signature(issuer, response)
217
+ if not res:
218
+ return 0
219
+
220
+ # Note that we are not using a "tolerance period" as discussed in
221
+ # https://tools.ietf.org/rfc/rfc5019.txt?
222
+ now = _datetime.utcnow()
223
+ # RFC6960, Section 3.2, Number 5
224
+ if response.this_update > now:
225
+ _LOGGER.debug("thisUpdate is in the future")
226
+ return 0
227
+ # RFC6960, Section 3.2, Number 6
228
+ if response.next_update and response.next_update < now:
229
+ _LOGGER.debug("nextUpdate is in the past")
230
+ return 0
231
+ return 1
232
+
233
+
234
+ def _get_ocsp_response(cert, issuer, uri, ocsp_response_cache):
235
+ ocsp_request = _build_ocsp_request(cert, issuer)
236
+ try:
237
+ ocsp_response = ocsp_response_cache[ocsp_request]
238
+ _LOGGER.debug("Using cached OCSP response.")
239
+ except KeyError:
240
+ # CSOT: use the configured timeout or 5 seconds, whichever is smaller.
241
+ # Note that request's timeout works differently and does not imply an absolute
242
+ # deadline: https://requests.readthedocs.io/en/stable/user/quickstart/#timeouts
243
+ timeout = max(_csot.clamp_remaining(5), 0.001)
244
+ try:
245
+ response = _post(
246
+ uri,
247
+ data=ocsp_request.public_bytes(_Encoding.DER),
248
+ headers={"Content-Type": "application/ocsp-request"},
249
+ timeout=timeout,
250
+ )
251
+ except _RequestException as exc:
252
+ _LOGGER.debug("HTTP request failed: %s", exc)
253
+ return None
254
+ if response.status_code != 200:
255
+ _LOGGER.debug("HTTP request returned %d", response.status_code)
256
+ return None
257
+ ocsp_response = _load_der_ocsp_response(response.content)
258
+ _LOGGER.debug("OCSP response status: %r", ocsp_response.response_status)
259
+ if ocsp_response.response_status != _OCSPResponseStatus.SUCCESSFUL:
260
+ return None
261
+ # RFC6960, Section 3.2, Number 1. Only relevant if we need to
262
+ # talk to the responder directly.
263
+ # Accessing response.serial_number raises if response status is not
264
+ # SUCCESSFUL.
265
+ if ocsp_response.serial_number != ocsp_request.serial_number:
266
+ _LOGGER.debug("Response serial number does not match request")
267
+ return None
268
+ if not _verify_response(issuer, ocsp_response):
269
+ # The response failed verification.
270
+ return None
271
+ _LOGGER.debug("Caching OCSP response.")
272
+ ocsp_response_cache[ocsp_request] = ocsp_response
273
+
274
+ return ocsp_response
275
+
276
+
277
+ def _ocsp_callback(conn, ocsp_bytes, user_data):
278
+ """Callback for use with OpenSSL.SSL.Context.set_ocsp_client_callback."""
279
+ cert = conn.get_peer_certificate()
280
+ if cert is None:
281
+ _LOGGER.debug("No peer cert?")
282
+ return 0
283
+ cert = cert.to_cryptography()
284
+ # Use the verified chain when available (pyopenssl>=20.0).
285
+ if hasattr(conn, "get_verified_chain"):
286
+ chain = conn.get_verified_chain()
287
+ trusted_ca_certs = None
288
+ else:
289
+ chain = conn.get_peer_cert_chain()
290
+ trusted_ca_certs = user_data.trusted_ca_certs
291
+ if not chain:
292
+ _LOGGER.debug("No peer cert chain?")
293
+ return 0
294
+ chain = [cer.to_cryptography() for cer in chain]
295
+ issuer = _get_issuer_cert(cert, chain, trusted_ca_certs)
296
+ must_staple = False
297
+ # https://tools.ietf.org/html/rfc7633#section-4.2.3.1
298
+ ext = _get_extension(cert, _TLSFeature)
299
+ if ext is not None:
300
+ for feature in ext.value:
301
+ if feature == _TLSFeatureType.status_request:
302
+ _LOGGER.debug("Peer presented a must-staple cert")
303
+ must_staple = True
304
+ break
305
+ ocsp_response_cache = user_data.ocsp_response_cache
306
+
307
+ # No stapled OCSP response
308
+ if ocsp_bytes == b"":
309
+ _LOGGER.debug("Peer did not staple an OCSP response")
310
+ if must_staple:
311
+ _LOGGER.debug("Must-staple cert with no stapled response, hard fail.")
312
+ return 0
313
+ if not user_data.check_ocsp_endpoint:
314
+ _LOGGER.debug("OCSP endpoint checking is disabled, soft fail.")
315
+ # No stapled OCSP response, checking responder URI diabled, soft fail.
316
+ return 1
317
+ # https://tools.ietf.org/html/rfc6960#section-3.1
318
+ ext = _get_extension(cert, _AuthorityInformationAccess)
319
+ if ext is None:
320
+ _LOGGER.debug("No authority access information, soft fail")
321
+ # No stapled OCSP response, no responder URI, soft fail.
322
+ return 1
323
+ uris = [
324
+ desc.access_location.value
325
+ for desc in ext.value
326
+ if desc.access_method == _AuthorityInformationAccessOID.OCSP
327
+ ]
328
+ if not uris:
329
+ _LOGGER.debug("No OCSP URI, soft fail")
330
+ # No responder URI, soft fail.
331
+ return 1
332
+ if issuer is None:
333
+ _LOGGER.debug("No issuer cert?")
334
+ return 0
335
+ _LOGGER.debug("Requesting OCSP data")
336
+ # When requesting data from an OCSP endpoint we only fail on
337
+ # successful, valid responses with a certificate status of REVOKED.
338
+ for uri in uris:
339
+ _LOGGER.debug("Trying %s", uri)
340
+ response = _get_ocsp_response(cert, issuer, uri, ocsp_response_cache)
341
+ if response is None:
342
+ # The endpoint didn't respond in time, or the response was
343
+ # unsuccessful or didn't match the request, or the response
344
+ # failed verification.
345
+ continue
346
+ _LOGGER.debug("OCSP cert status: %r", response.certificate_status)
347
+ if response.certificate_status == _OCSPCertStatus.GOOD:
348
+ return 1
349
+ if response.certificate_status == _OCSPCertStatus.REVOKED:
350
+ return 0
351
+ # Soft fail if we couldn't get a definitive status.
352
+ _LOGGER.debug("No definitive OCSP cert status, soft fail")
353
+ return 1
354
+
355
+ _LOGGER.debug("Peer stapled an OCSP response")
356
+ if issuer is None:
357
+ _LOGGER.debug("No issuer cert?")
358
+ return 0
359
+ response = _load_der_ocsp_response(ocsp_bytes)
360
+ _LOGGER.debug("OCSP response status: %r", response.response_status)
361
+ # This happens in _request_ocsp when there is no stapled response so
362
+ # we know if we can compare serial numbers for the request and response.
363
+ if response.response_status != _OCSPResponseStatus.SUCCESSFUL:
364
+ return 0
365
+ if not _verify_response(issuer, response):
366
+ return 0
367
+ # Cache the verified, stapled response.
368
+ ocsp_response_cache[_build_ocsp_request(cert, issuer)] = response
369
+ _LOGGER.debug("OCSP cert status: %r", response.certificate_status)
370
+ if response.certificate_status == _OCSPCertStatus.REVOKED:
371
+ return 0
372
+ return 1