#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# check_cert.py -- Petit mail de vérification du certificat d'un serveur
# ce script vérifie principalement la date d'expiration et envoie un mail
# d'avertissement si celle-ci est proche (paramétrable)
#
# Copyright (c) 2013 Daniel STAN
# Authors: Daniel STAN <daniel.stan@crans.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


from M2Crypto import SSL
from M2Crypto import X509
import ssl
import socket

import time
import datetime
import sys

# Envoyer des mails
from email.mime.text import MIMEText
import smtplib

#
# Config !
#
host = 'localhost'
port = 443
filename = False # if True, port ignored and host is in fact a path
# afficher la sortie plutôt que l'envoyer:
verb = False
# delai d'avertissement
delay = datetime.timedelta(days=15)

# infos mails
mail_src = 'root@crans.org'
mail_dest = "roots@crans.org"
mail_host = 'localhost'

#
# Argument parsing !
#
# TODO argparse + doc
for arg in sys.argv[1:]:
    if arg == '-v':
        verb = True
        continue
    if arg == '--filename':
        filename = True
    try:
        port = int(arg)
    except ValueError:
        host = arg

# Va-t-on faire du starttls ?
# pour l'instant, uniquement pour smtp, mais on peut imaginer étendre à
# l'avenir à d'autre protocole (xmpp en aurait bien besoin par exemple)
starttls = (port == 25)

#
# Getting cert ! (sauf starttls)
#
if filename:
    cert = X509.load_cert(host)
elif not starttls:
    conn = SSL.Connection(SSL.Context())
    try:
        conn.connect((host, port))
    except SSL.Checker.WrongHost:
        if host != 'localhost':
            raise

    cert = conn.get_peer_cert()
    conn.close()
#
# Getting subject, altname et expire_date
#
# Sans starttls, pour les fichiers simples comme pour les connexions
if not starttls:
    expire_date = cert.get_not_after().get_datetime()
    subject = cert.get_subject().as_text()
    try:
        altname = "(alt: %s)" % cert.get_ext('subjectAltName').get_value()
    except LookupError:
        altname = ""
# Avec starttls, on fait totalement autre chose (et on n'utilise pas la variable
# cert d'au dessus)
else:    
    smtp = socket.socket()
    smtp.connect((host, port))
    resp = smtp.recv(4096)
    smtp.send("EHLO localhost\n")
    resp = smtp.recv(4096)
    smtp.send("STARTTLS\n")
    resp = smtp.recv(4096)
    smtp = ssl.wrap_socket(smtp, cert_reqs=ssl.CERT_REQUIRED, ca_certs="/etc/ssl/certs/ca-certificates.crt")
    cert = smtp.getpeercert()
    # DROOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOGUES
    expire_date = datetime.datetime.fromtimestamp(time.mktime(time.strptime(cert['notAfter'], "%b %d %H:%M:%S %Y %Z")))
    subject = cert["subject"][0][0][1].decode("UTF-8")
    altname = ""

#
# Real computation (woah !)
#
now = datetime.datetime.now(expire_date.tzinfo)
if now + delay > expire_date or verb:
    short_sub = subject
    subject += altname
    msg = MIMEText(u"""Attention, le certificat suivant arrive bientôt à expiration :\n%s\n
Temps avant expiration: %s""" % (subject,(expire_date - now)), _charset="utf-8")
    msg['From'] = mail_src
    msg['To'] = mail_dest
    msg['Subject'] = u"Expiration imminente du certificat %s" % short_sub
    if not verb:
        conn = smtplib.SMTP(mail_host)
        conn.sendmail(mail_src, mail_dest, msg.as_string())
        conn.quit()
    else:
        print msg.get_payload(decode=True)

