]> git.siccegge.de Git - dane-monitoring-plugins.git/blob - check_dane/xmpp.py
Refactor TLSA service checks
[dane-monitoring-plugins.git] / check_dane / xmpp.py
1 #!/usr/bin/python3
2
3 #!/usr/bin/python3
4
5 from __future__ import print_function
6
7 import argparse
8 import logging
9
10 from ssl import SSLContext, PROTOCOL_TLSv1_2, CERT_REQUIRED
11 from socket import socket
12
13 from check_dane.tlsa import get_tlsa_records
14 from check_dane.cert import add_certificate_options
15 from check_dane.abstract import DaneChecker
16 from check_dane.resolve import Resolver, srv_lookup
17
18 XMPP_OPEN = ("<stream:stream xmlns='jabber:{0}' xmlns:stream='"
19 "http://etherx.jabber.org/streams' xmlns:tls='http://www.ietf.org/rfc/"
20 "rfc2595.txt' to='{1}' xml:lang='en' version='1.0'>")
21 XMPP_CLOSE = "</stream:stream>"
22 XMPP_STARTTLS = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
23
24 class XmppDaneChecker(DaneChecker):
25 def _init_connection(self, family, host, port):
26
27 logging.debug("Connecting to %s:%d", host, port)
28
29 connection = socket(family=family)
30 connection.connect((host, port))
31
32 connection.sendall(XMPP_OPEN.format(self.servicetype, self._hostname).encode())
33 answer = connection.recv(4096)
34 logging.debug(answer)
35
36 if not b'</stream:features>' in answer:
37 answer = connection.recv(4096)
38 logging.debug(answer)
39
40 connection.sendall(XMPP_STARTTLS.encode())
41 answer = connection.recv(4096)
42 logging.debug(answer)
43
44 print(host, self._hostname)
45 connection = self._sslcontext.wrap_socket(connection, server_hostname=self._hostname)
46 connection.do_handshake()
47
48 connection.sendall(XMPP_OPEN.format(self.servicetype, self._hostname).encode())
49 answer = connection.recv(4096)
50 logging.debug(answer)
51
52 if not b'</stream:features>' in answer:
53 answer = connection.recv(4096)
54 logging.debug(answer)
55
56 return connection
57
58
59 @property
60 def port(self):
61 return self._port
62
63
64 @property
65 def servicetype(self):
66 return self._type
67
68
69 def _gather_certificates(self):
70 result = set()
71 for (host, port), meta in self._endpoints:
72 self._host = host
73 self._port = port
74 self._type = meta['type']
75 result.update(DaneChecker._gather_certificates(self))
76
77 return result
78
79
80 def _gather_records(self):
81 result = set()
82 for (host, port), _ in self._endpoints:
83 print(repr((host, port)))
84 result.update(get_tlsa_records(self._resolver, "_%d._tcp.%s" % (port, host)))
85
86 return result
87
88
89 def _close_connection(self, connection):
90 connection.send(XMPP_CLOSE.encode())
91 answer = connection.recv(512)
92 logging.debug(answer)
93 connection.close()
94
95
96 def __init__(self):
97 self._port = None
98 self._ssl = None
99 self._host = None
100 self._hostname = None
101 DaneChecker.__init__(self)
102
103
104 def set_args(self, args):
105 DaneChecker.set_args(self, args)
106
107 sslcontext = SSLContext(PROTOCOL_TLSv1_2)
108 sslcontext.verify_mode = CERT_REQUIRED
109 sslcontext.load_verify_locations(args.castore)
110
111 cresolver = Resolver(args.ancor)
112 self._sslcontext = sslcontext
113
114 self._hostname = args.Host.encode('idna').decode()
115 endpoints = []
116 if not args.s2s:
117 for endpoint, meta in srv_lookup("_xmpp-client._tcp.%s" %
118 self._hostname,
119 cresolver):
120 meta['type'] = 'client'
121 endpoints.append((endpoint, meta))
122 if not args.c2s:
123 for endpoint, meta in srv_lookup("_xmpp-server._tcp.%s" %
124 self._hostname,
125 cresolver):
126 meta['type'] = 'server'
127 endpoints.append((endpoint, meta))
128
129 self._endpoints = endpoints
130
131
132 def generate_menu(self, argparser):
133 DaneChecker.generate_menu(self, argparser)
134 group = argparser.add_mutually_exclusive_group()
135 group.add_argument("--s2s", action="store_true",
136 help="Only check server-to-server connections")
137 group.add_argument("--c2s", action="store_true",
138 help="Only check client-to-server connections")
139 argparser.add_argument("-p", "--port",
140 action="store", type=int, default=0,
141 help="SMTP port")
142
143
144
145
146 def main():
147 logging.basicConfig(format='%(levelname)5s %(message)s')
148 checker = XmppDaneChecker()
149 parser = argparse.ArgumentParser()
150
151 parser.add_argument("--verbose", action="store_true")
152 parser.add_argument("--quiet", action="store_true")
153
154 checker.generate_menu(parser)
155 add_certificate_options(parser)
156
157 args = parser.parse_args()
158 checker.set_args(args)
159
160 if args.verbose:
161 logging.getLogger().setLevel(logging.DEBUG)
162 elif args.quiet:
163 logging.getLogger().setLevel(logging.WARNING)
164 else:
165 logging.getLogger().setLevel(logging.INFO)
166
167 return checker.check()
168
169
170 if __name__ == '__main__':
171 import sys
172 sys.exit(main())