]> git.siccegge.de Git - dane-monitoring-plugins.git/blob - check_dane_smtp
Code cleanup
[dane-monitoring-plugins.git] / check_dane_smtp
1 #!/usr/bin/python3
2 #
3 #
4
5 from __future__ import print_function
6
7 import sys
8 import argparse
9 import logging
10
11 from socket import socket, AF_INET6, AF_INET, create_connection
12 from ssl import SSLError, CertificateError, SSLContext
13 from ssl import PROTOCOL_TLSv1_2, CERT_REQUIRED
14 from unbound import ub_ctx
15
16 from check_dane.tlsa import verify_tlsa_record
17 from check_dane.cert import verify_certificate, add_certificate_options
18
19 def init_connection(sslcontext, args):
20 host = args.Host
21
22 if args.ssl:
23 port = 465 if args.port == 0 else args.port
24 connection = sslcontext.wrap_socket(socket(AF_INET),
25 server_hostname=host)
26 connection.connect((host, port))
27
28 else:
29 port = 25 if args.port == 0 else args.port
30
31 connection = create_connection((host, port))
32 answer = connection.recv(512)
33 logging.debug(answer)
34
35 connection.send(b"EHLO localhost\r\n")
36 answer = connection.recv(512)
37 logging.debug(answer)
38
39 connection.send(b"STARTTLS\r\n")
40 answer = connection.recv(512)
41 logging.debug(answer)
42
43 connection = sslcontext.wrap_socket(connection, server_hostname=host)
44 connection.do_handshake()
45
46 return connection
47
48
49 def close_connection(connection):
50 connection.send(b"QUIT\r\n")
51 answer = connection.recv(512)
52 logging.debug(answer)
53
54
55 def init(args):
56 sslcontext = SSLContext(PROTOCOL_TLSv1_2)
57 sslcontext.verify_mode = CERT_REQUIRED
58 sslcontext.load_verify_locations(args.castore)
59
60 resolver = ub_ctx()
61 resolver.add_ta_file(args.ancor)
62
63 return sslcontext, resolver
64
65
66 def main():
67 logging.basicConfig(format='%(levelname)5s %(message)s')
68 parser = argparse.ArgumentParser()
69 parser.add_argument("Host")
70
71 parser.add_argument("--verbose", action="store_true")
72 parser.add_argument("--quiet", action="store_true")
73 parser.add_argument("-p", "--port",
74 action="store", type=int, default=0,
75 help="SMTP port")
76 parser.add_argument("--ssl",
77 action="store_true",
78 help="Use direct TLS connection instead of starttls (default: disabled)")
79 parser.add_argument("--check-dane",
80 action="store_false",
81 help="Verify presented certificate via DANE (default: enabled)")
82 parser.add_argument("--check-ca",
83 action="store_false",
84 help="Verify presented certificate via the CA system (default: enabled)")
85 parser.add_argument("--check-expire",
86 action="store_false",
87 help="Verify presented certificate for expiration (default: enabled)")
88
89 parser.add_argument("-a", "--ancor",
90 action="store", type=str, default="/etc/unbound/root.key",
91 help="DNSSEC root ancor")
92 parser.add_argument("--castore", action="store", type=str,
93 default="/etc/ssl/certs/ca-certificates.crt",
94 help="ca certificate bundle")
95
96 group = parser.add_mutually_exclusive_group()
97 group.add_argument("-6", "--6", action="store_true", help="check via IPv6 only")
98 group.add_argument("-4", "--4", action="store_true", help="check via IPv4 only")
99 group.add_argument("--64", action="store_false", help="check via IPv4 and IPv6 (default)")
100
101 add_certificate_options(parser)
102
103 args = parser.parse_args()
104
105 if args.verbose:
106 logging.getLogger().setLevel(logging.DEBUG)
107 elif args.quiet:
108 logging.getLogger().setLevel(logging.WARNING)
109 else:
110 logging.getLogger().setLevel(logging.INFO)
111
112 port = args.port
113 if port == 0:
114 port = 465 if args.ssl else 25
115 host = args.Host.encode('idna').decode()
116
117 sslcontext, resolver = init(args)
118 try:
119 connection = init_connection(sslcontext, args)
120 except ConnectionRefusedError:
121 logging.error("Connection refused")
122 return 2
123
124 retval = verify_certificate(connection.getpeercert(), args)
125 nretval = verify_tlsa_record(resolver, "_%d._tcp.%s" % (port, host),
126 connection.getpeercert(binary_form=True))
127 retval = max(retval, nretval)
128
129 close_connection(connection)
130 return retval
131
132
133 if __name__ == '__main__':
134 sys.exit(main())