From 25cd67c03d81a2f5ca4ca8fcb8133387318ec664 2024-01-07 19:43:11 From: x Date: 2024-01-07 19:43:11 Subject: [PATCH] feat: add smtp and imap connections, with starttls --- diff --git a/backend/check_domains.py b/backend/check_domains.py index 50963475a6b81c4d1c4b56a26c9d79ccd1f7fa94..0c86db38c8f33791c7507d27b557fb3756a7b6bf 100644 --- a/backend/check_domains.py +++ b/backend/check_domains.py @@ -1,29 +1,28 @@ #!/usr/bin/env python3 import json import ssl +import smtplib +import imaplib import socket from rich.console import Console from cryptography import x509 import datetime import math -def get_expiry_days(expiry_timestamp: int, now_timestamp: int = datetime.datetime.now().timestamp()) -> int: - expiry_seconds = now_timestamp - expiry_timestamp - expiry_days = math.floor(expiry_seconds / 86400) - return expiry_days +def get_expiry_timestamps(expiry_timestamp: int, now_timestamp: int = datetime.datetime.now().timestamp()) -> (bool, int): + seconds_left = expiry_timestamp - now_timestamp + days_left = math.floor(seconds_left / 86400) + return (seconds_left >= 0,days_left) -def web_noconn_expiry_days(web_domain: str) -> int | None: - try: - pem_cert = ssl.get_server_certificate((web_domain, 443), timeout=5) - cert = x509.load_pem_x509_certificate(pem_cert.encode()) - except Exception as e: - console = Console() - console.log("Could not grab server cert for", "[orange bold underline]"+web_domain, ":", e, style="orange") - return None - - not_after = cert.not_valid_after.timestamp() - return get_expiry_days(not_after) +def get_validity_days(cert) -> (bool, int): + # Get expiry date + notAfter = cert['notAfter'] + notAfter_date = datetime.datetime.strptime(notAfter, '%b %d %H:%M:%S %Y %Z') + # datetime to UNIX time + notAfter_timestamp = notAfter_date.timestamp() + expiry = get_expiry_timestamps(notAfter_timestamp) + return (expiry[0], abs(expiry[1])) console = Console() @@ -35,6 +34,18 @@ console.log("[white]Checking web domains...") context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH) +def web_noconn_expiry_days(web_domain: str) -> int | None: + try: + pem_cert = ssl.get_server_certificate((web_domain, 443), timeout=5) + cert = x509.load_pem_x509_certificate(pem_cert.encode()) + except Exception as e: + console = Console() + console.log("Could not grab server cert for", "[orange bold underline]"+web_domain, ":", e, style="orange") + return None + + not_after = cert.not_valid_after.timestamp() + return get_expiry_timestamps(not_after) + for web_domain in input["domains"]["web"]: # Initiate TLS connection with context.wrap_socket(socket.socket(), server_hostname=web_domain) as s: @@ -44,12 +55,12 @@ for web_domain in input["domains"]["web"]: except ssl.SSLCertVerificationError as e: saved = e if e.verify_code == 10: - expiry = web_noconn_expiry_days(web_domain) + expiry = web_noconn_expiry_days(web_domain)[1] if(expiry != None): # TODO: add the TLS expiry stuff here # possibly a list of domains that have expired # if its already in here, dont add it again - console.log("[red bold underline]" + web_domain, "expired", expiry, "days ago.", style="red") + console.log("[red bold underline]" + web_domain, "expired", abs(expiry), "days ago.", style="red") elif e.verify_code == 23: console.log("[red bold underline]" + web_domain, "was revoked.", style="red") elif e.verify_code == 18: @@ -66,16 +77,58 @@ for web_domain in input["domains"]["web"]: print(e) continue - # Get expiry date - expiry = cert['notAfter'] - expiry = datetime.datetime.strptime(expiry, '%b %d %H:%M:%S %Y %Z') - - # datetime to UNIX time - expiry = expiry.timestamp() - validity = abs(get_expiry_days(expiry)) - + validity = get_validity_days(cert)[1] # Print expiry date console.log("[green bold underline]" + web_domain, "expires in", validity, "days", style="green") # TODO: remove known expired certs # If the cert was expired before, we know that it is now valid - # -> remove it from the list of expired certs + # -> remove it from the list of expirjuded certs + +def __mail_connection(host, port, verification: bool, initializer, starttls_args, closer) -> (bool, int): + connection = initializer(host, port) + if verification: + connection.starttls(**starttls_args) + else: + connection.starttls() + cert = connection.sock.getpeercert() + closer(connection) + return get_validity_days(cert) + +def __smtp_closing(connection): + connection.quit() + +def __smtp_connection(domain, port, verification: bool) -> (bool, int): + initializer = smtplib.SMTP + starttls_args = {"context": context} + return __mail_connection(domain, port, verification, initializer, starttls_args, __smtp_closing) + +def __imap_closing(connection): + connection.logout() + +def __imap_connection(domain, port, verification: bool) -> (bool, int): + initializer = imaplib.IMAP4 + starttls_args = {"ssl_context": context} + return __mail_connection(domain, port, verification, initializer, starttls_args, __imap_closing) + +def __mail_connect(domain, port, protocol_func): + try: + expiry = protocol_func(domain, port, True)[1] + console.log("[green bold underline]" + domain, "expires in", expiry, "days", style="green") + except ssl.SSLCertVerificationError as e: + if (e.verify_code == 10): + expiry = protocol_func(domain, port, False)[1] + console.log("[red bold underline]" + web_domain, "expired", abs(expiry), "days ago.", style="red") + else: + console.log("[red bold underline]" + domain, "failed verification:", e.verify_message + ".", style="red") + +def smtp_connect(domain, port): + __mail_connect(domain, port, __smtp_connection) + +def imap_connect(domain, port): + __mail_connect(domain, port, __imap_connection) + +for smtp_entry in input["domains"]["smtp"]: + smtp_connect(smtp_entry["host"], smtp_entry["port"]) + +for imap_entry in input["domains"]["imap"]: + imap_connect(imap_entry["host"], imap_entry["port"]) diff --git a/backend/input.json b/backend/input.json index a27f8596fefc0afa4b436a104625e3fd5719018a..cb40cfbf146ccef1a1ca9287ce99bdd1ba7517c7 100644 --- a/backend/input.json +++ b/backend/input.json @@ -32,6 +32,12 @@ "api.freifunk.lu", "firmware.freifunk.lu", "map.freifunk.lu" + ], + "smtp": [ + {"host": "smtp.c3l.lu", "port": 587} + ], + "imap": [ + {"host": "imap.c3l.lu", "port": 143} ] } } \ No newline at end of file