]> git.siccegge.de Git - dane-monitoring-plugins.git/blob - check_dane/tlsa.py
Refactor TLSA service checks
[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.warning("No TLSA record returned")
99 return set()
100
101 result = set()
102 for record in r.data.data:
103 usage = record[0]
104 selector = record[1]
105 matching = record[2]
106 data = record[3:]
107 result.add(TLSARecord(usage, selector, matching, data))
108
109 return result
110
111
112 def match_tlsa_records(records, certificates):
113 """Returns all TLSA records matching the certificate"""
114
115 usedrecords = set()
116 result = 0
117
118 for certificate in certificates:
119 recfound = False
120
121 for record in records:
122 if record.match(certificate):
123 logging.info("Matched record %s", record)
124 usedrecords.add(record)
125 recfound = True
126
127 if not recfound:
128 logging.error("No TLSA record returned")
129 result = 2
130
131 for record in records:
132 if not record in usedrecords:
133 logging.warning("Unused record %s", record)
134 if result == 0:
135 result = 1
136
137 return result
138
139
140 def verify_tlsa_record(resolver, record, certificate):
141 logging.debug("searching for TLSA record on %s", record)
142 s, r = resolver.resolve(record, rrtype=RR_TYPE_TLSA)
143 if 0 != s:
144 ub_strerror(s)
145 return
146
147 if r.data is None:
148 logging.error("No TLSA record returned")
149 return 2
150
151 for record in r.data.data:
152 hexencoder = codecs.getencoder('hex')
153 usage = ord(record[0])
154 selector = ord(record[1])
155 matching = ord(record[2])
156 data = record[3:]
157
158 if usage != 3:
159 logging.warning("Only 'Domain-issued certificate' records supported\n")
160
161 if selector == 0:
162 verifieddata = certificate
163 elif selector == 1:
164 verifieddata = get_spki(certificate)
165 else:
166 # currently only 0 and 1 are assigned
167 sys.stderr.write("Only selectors 0 and 1 supported\n")
168
169 if matching == 0:
170 if verifieddata == data:
171 logging.info("Found matching record: `TLSA %d %d %d %s`",
172 usage, selector, matching, hexencoder(data)[0])
173 return 0
174 elif matching == 1:
175 if hashlib.sha256(verifieddata).digest() == data:
176 logging.info("Found matching record: `TLSA %d %d %d %s`",
177 usage, selector, matching, hexencoder(data)[0].decode())
178 return 0
179 elif matching == 2:
180 if hashlib.sha512(verifieddata).digest() == data:
181 logging.info("Found matching record: `TLSA %d %d %d %s`",
182 usage, selector, matching, hexencoder(data)[0].decode())
183 return 0
184 else:
185 # currently only 0, 1 and 2 are assigned
186 logging.warning("Only matching types 0, 1 and 2 supported\n")
187
188 logging.error("could not verify any tlsa record\n")
189 return 2