]> git.siccegge.de Git - dane-monitoring-plugins.git/blob - check_dane/tlsa.py
Rework https checker
[dane-monitoring-plugins.git] / check_dane / tlsa.py
1 #!/usr/bin/python3
2
3 import sys
4 import codecs
5 import hashlib
6 import logging
7
8 from .cert import get_spki
9
10 from unbound import ub_strerror
11
12 try:
13 from unbound import RR_TYPE_TLSA
14 except ImportError:
15 RR_TYPE_TLSA = 52
16
17
18
19 class TLSARecord:
20 """Class representing a TLSA record"""
21 def __init__(self, usage, selector, matching, payload):
22 self._usage = usage
23 self._selector = selector
24 self._matching = matching
25 self._payload = payload
26
27
28 def match(self, certificate):
29 """Returns true if the certificate is covered by this TLSA record"""
30 if self._selector == 0:
31 verifieddata = certificate
32 elif self._selector == 1:
33 verifieddata = get_spki(certificate)
34 else:
35 # currently only 0 and 1 are assigned
36 sys.stderr.write("Only selectors 0 and 1 supported\n")
37
38 if self._matching == 0:
39 if verifieddata == self._payload:
40 return True
41
42 elif self._matching == 1:
43 if hashlib.sha256(verifieddata).digest() == self._payload:
44 return True
45
46 elif self._matching == 2:
47 if hashlib.sha512(verifieddata).digest() == self._payload:
48 return True
49
50 else:
51 # currently only 0, 1 and 2 are assigned
52 logging.warning("Only matching types 0, 1 and 2 supported\n")
53
54 return False
55
56
57
58 @property
59 def usage(self):
60 """Usage for this TLSA record"""
61 return self._usage
62
63
64 @property
65 def selector(self):
66 """Selector for this record"""
67 return self._selector
68
69
70 @property
71 def matching(self):
72 """Way to match data against certificate"""
73 return self._matching
74
75
76 @property
77 def payload(self):
78 """Payload data of the TLSA record"""
79 return self._payload
80
81
82 def __repr__(self):
83 hexencoder = codecs.getencoder('hex')
84 return '<TLSA %d %d %d %s>' % (self._usage, self._selector, self._matching, hexencoder(self._payload)[0].decode())
85
86
87
88 def get_tlsa_records(resolver, name):
89 """Extracts all TLSA records for a given name"""
90
91 logging.debug("searching for TLSA record on %s", name)
92 s, r = resolver.resolve(name, rrtype=RR_TYPE_TLSA)
93 if 0 != s:
94 ub_strerror(s)
95 return
96
97 if r.data is None:
98 logging.warn("No TLSA record returned")
99 return set()
100
101 result = set()
102 for record in r.data.data:
103 hexencoder = codecs.getencoder('hex')
104 usage = ord(record[0])
105 selector = ord(record[1])
106 matching = ord(record[2])
107 data = record[3:]
108 result.add(TLSARecord(usage, selector, matching, data))
109
110 return result
111
112
113 def match_tlsa_records(records, certificates):
114 """Returns all TLSA records matching the certificate"""
115
116 usedrecords = set()
117 result = 0
118
119 for certificate in certificates:
120 recfound = False
121
122 for record in records:
123 if record.match(certificate):
124 logging.info("Matched record %s", record)
125 usedrecords.add(record)
126 recfound = True
127
128 if not recfound:
129 logging.error("No TLSA record returned")
130 result = 2
131
132 for record in records:
133 if not record in usedrecords:
134 logging.warn("Unused record %s", record)
135 if result == 0:
136 result = 1
137
138 return result
139
140
141 def verify_tlsa_record(resolver, record, certificate):
142 logging.debug("searching for TLSA record on %s", record)
143 s, r = resolver.resolve(record, rrtype=RR_TYPE_TLSA)
144 if 0 != s:
145 ub_strerror(s)
146 return
147
148 if r.data is None:
149 logging.error("No TLSA record returned")
150 return 2
151
152 for record in r.data.data:
153 hexencoder = codecs.getencoder('hex')
154 usage = ord(record[0])
155 selector = ord(record[1])
156 matching = ord(record[2])
157 data = record[3:]
158
159 if usage != 3:
160 logging.warning("Only 'Domain-issued certificate' records supported\n")
161
162 if selector == 0:
163 verifieddata = certificate
164 elif selector == 1:
165 verifieddata = get_spki(certificate)
166 else:
167 # currently only 0 and 1 are assigned
168 sys.stderr.write("Only selectors 0 and 1 supported\n")
169
170 if matching == 0:
171 if verifieddata == data:
172 logging.info("Found matching record: `TLSA %d %d %d %s`",
173 usage, selector, matching, hexencoder(data)[0])
174 return 0
175 elif matching == 1:
176 if hashlib.sha256(verifieddata).digest() == data:
177 logging.info("Found matching record: `TLSA %d %d %d %s`",
178 usage, selector, matching, hexencoder(data)[0].decode())
179 return 0
180 elif matching == 2:
181 if hashlib.sha512(verifieddata).digest() == data:
182 logging.info("Found matching record: `TLSA %d %d %d %s`",
183 usage, selector, matching, hexencoder(data)[0].decode())
184 return 0
185 else:
186 # currently only 0, 1 and 2 are assigned
187 logging.warning("Only matching types 0, 1 and 2 supported\n")
188
189 logging.error("could not verify any tlsa record\n")
190 return 2