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