Sélectionner une page

Nous avons parlé à plusieurs reprises de la mise en place de SSL/TLS dans différentes situations (dans  « il était une fois internet« , sur jeveuxhttps.fr, pour AlternC, ou plus récemment sur la faille POODLE). Autant il est facile d’enlever le protocole SSLv3 des serveurs web (seul le navigateur obsolète Internet Explorer 6 ne pourra plus se connecter), autant il peut être utile de se poser la question pour les mails.

Une des solutions possible consiste à copier automatiquement tous les mails dans un répertoire indépendant, prendre les informations qui nous intéressent dans les en-têtes, puis effacer ces copies. On peut ainsi récupérer ce type de ligne (issu de l’en-tête Received: ajouté par Postfix) qui nous indique le chiffrement de la connexion :

(using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA (256/256 bits))

La mise en place d’un processus de collecte de statistiques est réellement simple :

  • créer une nouvelle adresse mail en pop/imap dédiée à cette tâche
  • modifier le fichier /etc/postfix/main.cf pour y ajouter les 2 lignes suivantes :
always_bcc = L_ADRESSE_EMAIL_DEDIEE
smtpd_sasl_authenticated_header = yes

Puis on recharge Postfix via la commande service postfix reload . Après avoir vérifié que tout fonctionne bien (par exemple avec /var/log/mail.log ), on passe à la suite.

  • vous pouvez copier le script ci-desssous dans un fichier que l’on appellera ssl.php .
#!/usr/bin/env php
<?php

// postfix must have always_bcc = this mailbox
// and smtpd_sasl_authenticated_header = yes
// you can cronify this as follow: 
// * * * * * root php /root/ssl.php >>/var/log/ssl.log
// Set this to be the dedicated mailbox's root Maildir:
$ROOT="/var/mail/alternc/t/tls_octopuce.fr/Maildir";

$message_list=array();
$hostname=trim(file_get_contents("/etc/mailname"));

$d=opendir($ROOT."/cur");
while (($c=readdir($d))!==false) {
  if (is_file($ROOT."/cur/".$c)) {
    addmail($ROOT."/cur/".$c);
  }
}
closedir($d);

$d=opendir($ROOT."/new");
while (($c=readdir($d))!==false) {
  if (is_file($ROOT."/new/".$c)) {
    addmail($ROOT."/new/".$c);
  }
}
closedir($d);

// This function get a maildir email and extract the relevent informations from it.
// then it DELETES this email!
function addmail($file) {
  global $message_list,$hostname;
  $f=fopen($file,"rb");
  $status=0;
  $received_list=array();
  $messageid="";
  // automaton having status: 0=in headers  1=in a received: header     2=had a message-id header
  while ($s=fgets($f,1024)) {
    if (!trim($s)) {
      break; // we leave the header part, quit
    }
    if ($status==1) {
      if (substr($s,0,1)==" " || substr($s,0,1)==chr(9)) {
        $received.=" ".trim($s);
      } else {
        $received_list[]=$received;
        $received="";
        $status=0;
      }
    }

    if ($status==2) {
      if (substr($s,0,1)==" " || substr($s,0,1)==chr(9)) {
        $messageid.=" ".trim($s);
        $messageid=trim($messageid);
      } else {
        $status=0;
      }
    }

    if (substr($s,0,9)=="Received:") {
      $received=$s;
      $status=1;
    }
    if (preg_match("#^Message-ID:#i",$s)) {
      $messageid=trim(substr($s,11));
      $status=2;
    }
  } // read the mail headers

  fclose($f);

  if ($status==1) {
    $received_list[]=$received;
  }

  if (!in_array($messageid,$message_list)) {
    $message_list[]=$messageid;
    // find the proper header of OUR SERVER receiving this mail
    foreach($received_list as $rec) {
      // skip localhost! (this is amavis talking to us ;) ) 
      if (strpos($rec,"from localhost ")!==false) {
        continue;
      }
      if (strpos($rec,"from ".$hostname." ")!==false) {
        continue;
      }
      if (strpos($rec,"by ".$hostname." ")!==false) {

        // extract the sender of this email:
        if (preg_match("#\(Authenticated sender: ([^)]*)\)#",$rec,$from)) {
        } else {
          if (preg_match("#from ([^ ]*) #",$rec,$from)) {
            if ($from[1]=="userid") {
              continue; // skip locally generated mails
            }
          } else {
            $from=array("","<unknown>");
          }
        }

        // this is the received header we want to find, parse it :
        if (preg_match("#\(using ([^ ]*) with cipher ([^ ]*) #",$rec,$mat)) {
          echo time()." ".$from[1]." ".$mat[1]." ".$mat[2]."\n";
        } else {
          echo time()." ".$from[1]." NO NO\n"; // NO crypto was used ;(((
        }
      }
    }
  }

  // and delete this mail :) 
  unlink($file);
}

N’oubliez pas de modifier la ligne suivante qui se trouve au début du script :

$ROOT="PATH_DU_MAILDIR";
  • On teste le fichier en le lançant :
php ssl.php

Si le test ne renvoie pas d’erreurs, on le planifie dans le cron pour le lancer toutes les minutes avec l’aide du fichier /etc/cron.d/ssl :

* * * * * root php /root/ssl.php 2>&1 >>/var/log/ssl.log

Après quelques minutes/heures de fonctionnement, on commencera à avoir dans le fichier /var/log/ssl.log  les premiers détails comme ceux là :

1414506421 solo.fdn.fr TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384
1414506541 pi.octopuce.fr TLSv1 DHE-RSA-AES256-SHA
1414506601 pi.octopuce.fr TLSv1 DHE-RSA-AES256-SHA
1414506601 brassens.heberge.info TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384
1414506661 sf01smtp2.securityfocus.com NO NO
1414506781 out.smtpout.orange.fr NO NO

Ce qui permet de faire des stats rapides comme celle ci (combien de mails a-t-on reçu utilisant chaque protocole cryptographique) :

cat ssl.log|awk '{print $3}'|sort|uniq -c
    932 NO
      4 SSLv3
    594 TLSv1
     10 TLSv1.1
    429 TLSv1.2