unix4fun

Aller au contenu | Aller au menu | Aller à la recherche

vendredi 30 décembre 2011

Et son nom, il le signe de la pointe de l'épée ...

... ou à la fin de son mail (je sais c'est pas ça la chanson).

Et comme il est fainéant, il aimerait bien que ce soit automatique. Enfin surtout ses copains (qui sont pires que lui).

Si vous avez suivi vous avez vu que je découvre Postfix et son monde merveilleux. Ainsi j'ai découvert (bin oui puisque je découvre) que dans ce contexte là, quand on parle de signature, on en revient souvent à altermime.

J'ai donc mis en place cet outil plutôt sympa grâce à tous les supers tutoriaux qu'on trouve sur le net. Ma maigre contribution de cette fin d'année sera de vous donner le script qui permet d'avoir :

  • un mécanisme simple pour gérer les signatures user par user
  • un mécanisme simple pour gérer un disclaimer générique à un domaine
  • un mécanisme, je vous le donne en mille, simple, pour ne pas bégayer (signature en double etc)

Voilà le bousin :

#!/bin/sh
ALTERMIME=/usr/bin/altermime
ALTERMIME_DIR=/var/spool/altermime
SENDMAIL=/usr/sbin/sendmail
TEMPFAIL=75
UNAVAILABLE=69
cd $ALTERMIME_DIR || { echo $ALTERMIME_DIR does not exist; exit $TEMPFAIL; }
trap "rm -f in.$$" 0 1 2 3 15
cat >in.$$ || { echo Cannot write to $ALTERMIME_DIR; exit $TEMPFAIL; }

# d'abord on teste le mail pour voir si on n'a pas déjà rajouté le disclaimer
processed=`grep -m 1 "X-Disclaimer: yes" in.$$`
if [ -z "$processed" ]; then
        from_address=`grep -m 1 "From:" in.$$ | cut -d '<' -f 2 | cut -d '>' -f 1 | tr '@' '_'`
        from_domain=`grep -m 1 "From:" in.$$ | cut -d '@' -f 2 | cut -d '>' -f 1`
        signature_file="/etc/postfix/altermime/signatures/$from_address"
        disclaimer_file="/etc/postfix/altermime/disclaimers/$from_domain"
        # on cherche si on a une signature pour cet expéditeur
        if [ -e ${signature_file}.txt -a -e ${signature_file}.html ]; then
                $ALTERMIME --input=in.$$ \
                --disclaimer=${signature_file}.txt \
                --disclaimer-html=${signature_file}.html \
                --xheader="X-Disclaimer: yes" || \
                { echo Message content rejected; exit $EX_UNAVAILABLE; }
        fi
        # puis si on a un disclaimer générique pour son domaine
        if [ -e ${disclaimer_file}.txt -a -e ${disclaimer_file}.html ]; then
                $ALTERMIME --input=in.$$ \
                --disclaimer=${disclaimer_file}.txt \
                --disclaimer-html=${disclaimer_file}.html \
                --xheader="X-Disclaimer: yes" || \
                { echo Message content rejected; exit $EX_UNAVAILABLE; }
        fi
fi

$SENDMAIL "$@" <in.$$
exit $?

Si vous vous intéressez un peu au sujet vous verrez qu'une grosse partie de ce script se trouve sur quasiment toutes les pages qui parlent d'altermime, je remercie donc son auteur initial.

En ce qui concerne mes ajouts :

  • pour les signatures il suffit de rajouter des fichiers utilisateur_domaine.tld.{txt,html} dans /etc/postfix/altermime/signatures avec dedans ce qui va bien
  • pour les disclaimers génériques, dans /etc/postfix/altermime/disclaimers des fichiers domaine.tld.{txt,html} selon la même philosophie
  • pour l'anti bégaiement (qui se produit rarement mais dans des cas de redirection entre domaines), un simple X-Header

Voilà, je pense que c'est à peu près tout !

mardi 18 octobre 2011

ahhh il reste des gens drôles! pfiiiouuu j'ai flippe!

En ces temps de gang bang d'ego généralisé, de benchmarking de performance sur l'échelle Ritchie (RIP) et autre courses a la réussite sociale, il reste des gens dont la carriere et la publication frénétique d'information pour voir son nombre de hits et ses fans augmenter toute en mimant le désintérêt et le detachement n'est pas au centre de leur vie et arrivent a s'amuser et surtout a avoir de l'auto derision, ca m'a fait hurler de rire alors que c'est completement debile, mais c'est pas grave, j'ai ris, ca fait du bien! donc je "share" :)

Sa premiere idee farfelue (hahahaha):

/*
** pam_happy_hour.c - PAM happy hour auth module. Solaris 2.6+
**      During happy hour passwords aren't required, any will do.
**
[...]

pam_happy_hour.c

Allez j'en pioche une autre dans le tas (:~)):

# maybe - companion to /usr/bin/true and /usr/bin/false.
#
# This should be installed in your system location, eg /usr/bin/maybe.
[...]

maybe

Et il en a fait pleins d'autre aussi farfelue a voir la:

huhuhuh

lundi 17 octobre 2011

Chacun sa route, chacun son chemin, passe le message à ton voisin ...

En ces temps de restriction budgétaire et de dégraissage du mammouth, chacun se doit d'y aller de son petit effort pour réduire les coût. En ce qui me concerne, cela s'est concrétisé par un peu de tuning sur un serveur de mail postfix/docevot/amavis-et-ses-copains increvable afin de l'utiliser pour plusieurs domaines qui n'ont rien à voir entre eux.

Mon besoin, simple en soit, et, au final, simple à traiter, se résume ainsi :

  • 2 organisations, avec deux domaines (organisation1.fr et organisation2.com)
  • 2 smarthosts (mx.organisation1.fr et mx.organisation2.com)
  • 1 serveur mail pour gérer les deux domaines, mais chacun utilisant en entrée et en sortie son smarthost (relayhost chez postfix) respectif


Je n'insulterai personne en expliquant comment gérer un smarthost (transport.map et relayhost sont nos amis) ou comment rajouter le support d'un domaine ...

Pour ce qui est d'utiliser un smarthost spécifique en sortie, c'est là que la magie de postfix opère : il suffit d'utiliser la directive bien nommée sender_dependent_relayhost_map avec en paramètre un fichier au format "transport", ce qui donne la chose suivante :

@organisation1.fr       [mx.organisation1.fr]
@organisation2.com      [mx.organisation2.com]


Et voilà, comme ça tout le trafic entrant et sortant passe par les smarthosts respectifs des deux organisations.

vendredi 7 octobre 2011

Mon Toy-project du moment ! Un FS sur le cloud !

Beh voilà, encore un peu d'auto-promotion. Et en fait j'ai un peu menti, ça fait plusieurs mois que je ne commit qu'un bout de code par-ci ou par-là.

Ma société a écrit une bibliothèque en open source pour travailler sur le cloud (différents protocoles supportés, mais je vous laisse découvrir ça). Bref, je me suis basé sur cette lib pour implémenter un filesystem en utilisant FUSE : dropletfs ; c'est lent (car le design est simpliste et pas optimisé), mais rigolo.

Toutes les features d'un FS posix ne sont pas présentes, loin de là (gérer les hardlinks est plutôt compliqué), mais pour un usage basique, ça 'juste marche'.

Une petite démo intéractive (nécessite le package ttyrec) : ici

vendredi 16 septembre 2011

SPLONK !

Si, comme moi, vous vous battez continuellement dans vos logs avec grep/sed/awk/cut/tail/paprika (cherchez pas c'est de l'humour), alors vous avez certainement deja entendu parlé de splunk. Pour faire très court, splunk, c'est un peu le Google des logs : c'est ultra pratique (et intuitif) si vous cherchez à faire une recherche dans 45 millions de logs.

Seulement voilà, splunk est payant, et même plutot cher si vous souhaitez l'intégrer dans un environnement de hosting par exemple. Alors le reflexe habituel quand on est un gros radin comme moi, c'est de trouver une alternative gratuite, mais jusqu'à présent, c'etait un peu l'age de pierre chez la concurrence (non je ne citerai personne).

Jusqu'à present :)

Tout a fait par hasard, je suis tombe sur un billet de ce monsieur en chemise a carreaux (kof). Et la.. ce fut l'illumination alors je souhaitais -sous la menace- vous faire partager cette découverte !

mercredi 13 juillet 2011

et dire qu'il y a des gens qui se vantent sans arrêt d'être des "hackers"...

Apprenez l'humilité et un descriptif comme:
"(The World's First (and Last?!) IPv6 Cat Feeder!)"

Qui est (selon moi) un super joli "hack" d'un mec un peu dingue et super poilu, en tout cas je trouve ca mortel (et assez incroyable)! congrats! :)

Enjoy the fun: http://www.newtonnet.co.uk/catfeeder/

samedi 18 juin 2011

des piécettes électroniques! donc l'avènement d'e-mendiants!

Apres quelques conversations avec un pote sur cette nouvelle "monnaie" électronique, il m'a fait constater un ensemble de propriétés marrantes et interessantes (pas de trusted third party, la resolution d'un problème mathématique pour limiter l'emission de monnaie, etc..), j'ai beaucoup de lacunes quand au fonctionnement économique actuel, mais le fonctionnement de cette monnaie et l'utilisation de la crypto et du réseau pour definir ces propriétés, a lire et suivre!

lundi 6 juin 2011

La resolution de puzzle...

Le puzzle, oui, je parle bien de ce truc, ou on reconstitue une peinture de cheval dans un pre ou un voilier sur un soleil couchant.

Je ne sais pas pourquoi, quand je pense puzzle, j'associe les mots bibelot, kitsch et j'ai l'image testosterone d'un type (sans visage) portant ces t-shirts tres "male" d'un aigle attaquant un serpent au dessus d'un volcan flamboyant, l'image ultime de la determination masculine et motorise... bref...

Quand on est un déchet de l'informatique moderne comme moi, que la crypto c'est un peu comme apprendre le thaïlandais avec victor le matin sur france3 et que malgré tout ces "signes", on s'acharne a vouloir mal prononcer "bonjour" en thai, on en arrive a se retrouver face a des puzzles.. effectivement c'est étrange.. mais il y a parfois des liens marrants a lire sur ces petits "challenges" qui réclament un cerveau et de la curiosité...

Hope you enjoy: http://www.thephoenixsociety.org/puzzles/puzzlesolving.htm

jeudi 28 avril 2011

c'est quoi SCTP!?

Alors je suis en mode honey badger cette semaine et je "dig" pleins de trucs que je ne connais pas tres bien, je mattais comment marche SCTP, le soit disant "potentiel remplaçant de TCP et UDP" (oui je vous jure j ai lu ca qqes part, mais je sais plus ou...).

Si comme moi vous aviez juste une feuille de platane pour cacher votre nudité devant SCTP, voila une introduction a SCTP, de quoi avoir un short en lieu et place de la feuille de platane.

Je me demande si TCP/IP illustrated et cie sont mis a jour avec tout ces nouveaux protocoles, IPv6, SCTP etc... ou si il y a de nouvelles references aussi détaillées..

bref enjoy!

mercredi 16 mars 2011

Ché le printemps Che fais le ménach'

Ach ja, je suis en pleine immersion Clean-Slate Internet depuis quelques mois, et je pense qu'il est effectivement plus que temps de réfléchir à comment faire évoluer Internet avec les nouveaux usages qu'il est en train de se prendre direct dans la face. Alors si vous vous dites que mais à quoi bon vu que IPv6 arrive? Eh bien je vous propose de lire cette introduction aux problèmes sous-jacents sur le blog du laboratoire i2CAT de Barcelone:

Pas assez concret? Une douche froide sur socket(2), une:

Alors pour se débarrasser de toutes ces poussières et repartir sur de bonnes bases, je vous conseille de faire les courses de détergents, serviettes, éponges, tablier voire acide borique ici:

Commencez par la fin, il y a même le mode d'emploi. Voila, frottez bien!

jeudi 17 février 2011

Des hooks sympa de fonctions avec gcc

Yo yo yo.

J'avais une base de code, avec pas mal de fonctions déjà implémentées, et puis je me suis dit "ooooomm, faudrait que je trace le temps d'exécution de chacune de ces fonctions, pour savoir où concentrer mes efforts, en faisant telle et telle stat'". Bon alors je vous entends déjà barir au loin "hey mais pauvre naze, y'a plein d'outils de profiling", et c'est vrai. Seulement je voulais faire un truc à ma sauce d'une part (histoire de voir un peu comment ça fonctionne), et surtout pouvoir ajouter les stats qui m'intéressent, faire des trucs aux p'tits oignons quoi. Et puis c'est pas comme si ça prenait des jours à coder.

Alors après avoir cherché un peu, je tombe sur la doc GCC (même le man en parle, à vrai dire) :


void __cyg_profile_func_enter(void *func, void *callsite); 
void __cyg_profile_func_exit(void *func, void *callsite);


Ces fonctions sont appelées respectivement à l'entrée et la sortie de chacune des fonctions de votre programme. Il y a une linuxerie ensuite pour obtenir le nom de l'appelant, ie. passer par dladdr() ou __build_return_address(0). Ensuite vous stockez les infos comme vous voulez, perso j'utilise une hashtable dont les clefs sont les symboles (aka noms de fonctions), avec chaque cellule contenant des stats, sur le nombre d'appel, le temps d'exécution moyen, etc.

J'ai utilisé ça pour un code sur lequel je passe un peu de temps en ce moment, et la sortie ressemble à (le > correspond à l'entrée dans la fonction, le < à la sortie, faudrait rendre le truc plus joli avec un système de pile pour avoir la profondeur d'appel de chacune en fonction d'un décalage horizontal, mais bon, ça viendra après) :


[...]
1297941768.719531 > dfs_mkdir@0x40f534
1297941768.719607 > dfs_mkdir_timeout@0x41398f
1297941769.416415 < dfs_mkdir_timeout@0x41398f -- 696ms
1297941769.416445 < dfs_mkdir@0x40f534 -- 696ms
1297941769.416469 > dfs_getattr@0x40c16c
1297941769.416684 > dfs_namei_timeout@0x413ef7
1297941769.534098 < dfs_namei_timeout@0x413ef7 -- 117ms
1297941769.534161 > dfs_getattr_timeout@0x413690
1297941770.104592 < dfs_getattr_timeout@0x413690 -- 570ms
1297941770.104874 < dfs_getattr@0x40c16c -- 570ms

                                -- report --
symbol dfs_getattr_timeout: #calls: 46, average call duration: 266ms
symbol dfs_opendir_timeout: #calls: 1, average call duration: 780ms
symbol dfs_namei_timeout: #calls: 47, average call duration: 299ms
symbol dfs_chdir_timeout: #calls: 1, average call duration: 0ms
symbol dfs_mkdir: #calls: 1, average call duration: 696ms
symbol dfs_getattr: #calls: 48, average call duration: 275ms
symbol dfs_mkdir_timeout: #calls: 1, average call duration: 696ms


Le code (crados, pas encore nettoyé) est visible ici. Comme je disais, le truc vraiment sympa c'est juste d'avoir à appeler profile_init() et profile_fini() dans mon code. Et encore, on peut faire mieux en générant un .so qu'on charge avec LD_PRELOAD, histoire d'avoir vraiment 0 code impacté. Faut juste compiler les objets avec -fPIC et -finstrument-functions. That's all folks.

OK c'est un profiler du pauvre, mais ici à unix4fun, on est prolos. Et on vous emmerde !

mercredi 16 février 2011

mirrroiiirr, mon bon mirrroir, donne moi la clef de ce routeur!

Un document marrant, une analyze du recovery des clef WEP qui sont generees a partir d'un algo "secret" mais en fonction de parametre public comme la MAC.

C'est marrant a lire: http://websec.ca/blog/view/mac2wepkey_huawei

enjoy!

mardi 21 décembre 2010

Pour les addicts du sniffing réseau: Junkie

Un peu de proselytisme ne fait jamais de mal, alors voilà Junkie, un sniffer releasé en Open Source par la société SecurActive.

Pour l'instant il a été programmé pour tourner sur du Linux debian-like, donc...


Un extrait du README:


Compared to previously available tools junkie lies in between tcpdump and
wireshark. Unlike tcpdump, its purpose is to parse protocols of any depth;
unlike wireshark, through, junkie is designed to analyze traffic in real-time
and so cannot parse traffic as completely as wireshark does.

In addition, junkie's design encompasses extendability and speed:

- plug-in system + high-level extension language that eases the development and
  combination of new functionalities;
- threaded packet capture and analysis for handling of high bandwidth network;
- modular architecture to ease the addition of any protocol layer;
- based on libpcap for portability;
- well tested on professional settings.


Il manque un "IPv6 compliant" je pense, dans ce README.

Seuls les parseurs sont releasés en Open Source, mais c'est déjà bien sympa. Le design est fait de telle sorte qu'il est facile d'ajouter un plugin d'analyse pour un protocole particulier 1. Et pour ceux qui n'aiment pas le C, il y a possibilité d'exercer vos talents de codeurs Scheme (guile plus précisemment), car il est scriptable à l'aide de ce langage ! Ouh ouh !


Un petit exemple d'utilisation ? OK, c'est parti :


$ sudo ./src/junkie -i wlan0 -c config/start-repl.scm
2010-12-22 00:36:45: J-main: log.c/log_set_file: Opening log file.


OK, là on lui a dit de loguer sur stdout, d'écouter l'interface wireless wlan0, et de prendre le fichier de conf par défaut qui active le REPL. Donc, dans un autre terminal, je vais me connecter sur la machine, en tcp/29000 pour ajouter des settings sur la mouche comme disent nos amis anglais (ie. "on the fly"... ok c'est nul).


$ rlwrap telnet localhost 29000
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
junkie> (set-iface-filter "wlan0" "icmp")
#t
junkie> (set-log-level 6)
#<unspecified>
junkie>


Les valeurs de retour affichées sont celles de guile, à savoir #t pour true, et #<unspecified> pour dire qu'il n'y a pas de code de retour. C'est un peu curieux au début, mais ceux qui ont utilisé ce langage ne seront pas dépaysés.

Là je lui ai demandé de prendre en compte le filtre BPF (tcpdump, vous connaissez, hein ?) "icmp" pour l'interface wlan0.

Hey ouais, c'est ça qui est cool, on peut écouter sur différentes interfaces avec des filtres différents. Bref, dans un autre terminal je tape :


$ ping free.fr -c1
PING free.fr (212.27.48.10) 56(84) bytes of data.
64 bytes from www.free.fr (212.27.48.10): icmp_seq=1 ttl=118 time=37.1 ms

--- free.fr ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 37.176/37.176/37.176/0.000 ms


Et si on repasse dans la console de log, on a :


Capture@0x23f0068: head_len=48, payload=60, dev_id=0, tv=1293009623s 788239us
Ethernet@0x2413ea8: head_len=14, payload=46, vlan_id=0, source=00:1b:2f:2e:e0:fa, dest=ff:ff:ff:ff:ff:ff, proto=2054

Capture@0x23f0068: head_len=48, payload=74, dev_id=0, tv=1293009880s 139307us
Ethernet@0x2413ea8: head_len=14, payload=60, vlan_id=0, source=00:1c:bf:9d:2e:21, dest=00:15:e9:f2:30:67, proto=2048
IPv4@0x2414e38: head_len=20, payload=40, version=4, addr=10.0.63.10->82.237.175.5, proto=6, ttl=64
TCP@0x23bcf08: head_len=40, payload=0, ports=42007->1234, flags=Syn, win=5840, ack=0, seq=902941316

Capture@0x23f0068: head_len=48, payload=98, dev_id=0, tv=1293009631s 741103us
Ethernet@0x2413ea8: head_len=14, payload=84, vlan_id=0, source=00:1c:bf:9d:2e:21, dest=00:15:e9:f2:30:67, proto=2048
IPv4@0x2414e38: head_len=20, payload=64, version=4, addr=10.0.63.10->212.27.48.10, proto=1, ttl=64
ICMP@0x23bb198: head_len=64, payload=0, type=EchoRequest, err=NONE

Capture@0x23f0068: head_len=48, payload=98, dev_id=0, tv=1293009631s 744391us
Ethernet@0x2413ea8: head_len=14, payload=84, vlan_id=0, source=00:15:e9:f2:30:67, dest=00:1c:bf:9d:2e:21, proto=2048
IPv4@0x2414e38: head_len=20, payload=64, version=4, addr=212.27.48.10->10.0.63.10 (hashed the other way), proto=1, ttl=122
ICMP@0x23bb198: head_len=64, payload=0, type=EchoReply, err=NONE

Un lecteur attentif verra qu'il n'y a pas que de l'ICMP qui a été logué (cf. les premières lignes). Il a raison ! Mais comme je l'ai dit plus haut, on a changé la config à chaud, donc Junkie a eu le temps de loguer quelques paquets avant de prendre en compte le filtre BPF.

Et voilà ! Bon, c'est un exemple parmi des tonnes hein, chacun peut y trouver son compte, là c'est vraiment pour expliquer le fonctionnement. Pour plus d'infos :

$ junkie -h

Ou bien pour des détails vraiment poussés, dans le REPL :


junkie> (help)
[...]
junkie> (help 'commande)



1 Tutorial de codage d'un parseur : parser implementation


mercredi 10 novembre 2010

Je ... c'est quoi une LED?

Pour ceux qui comme moi, n'ont pas de super pouvoir, ne voit pas la matrice, ne sont pas comme un type qui a soit-disant ouvert la mer en deux parce que les ferry étaient en grève et qu'il se faisait courser en sortant de boite avec ses potes.

Il faut attendre le ferry, il faut apprendre des trucs souvent simples, les refaire les relire et se rendre compte chaque jour plus on apprends, moins on en sait.

En fait, on ne sait RIEN, oui je ne sais RIEN, c'est triste et je prends ma claque sur la joue chaque jour...

Bref, j'ai trouve un tutoriel postée sur hack-a-day pour les débutants-sans-super-pouvoirs-ouvre-la-mer comme moi, qui introduit au concept simple des LEDs. J'ai bien aimé, alors je partage:

mardi 9 novembre 2010

Mon compilo est un beau salaud!

Je lis du code généré en ce moment... en particulier du code généré par Visual C++ avec des options "star-wars-security" blablabla mais c'est pas le sujet du jour...

Le sujet du jour c'est ma relative incompétence face a des choses comme:

[...]
.text:0069979F mov     eax, 66666667h          
.text:006997A4 imul    ecx
.text:006997A6 sar     edx, 1                          
.text:006997A8 mov    eax, edx                     
.text:006997AA shr     eax, 1Fh                       
.text:006997AD add     eax, edx
[...]

alors dans ecx, j'ai la valeur 0x20000, après des tentatives rapides de compréhension, je me suis bon les "magic values", j'en vois de temps en temps, ca pue la "magic value" des trucs de compilo-salaud pour tenter d'avoir de la précisions en continuant a travailler avec des entiers et en base2, bref..

En cherchant un peu je suis tombe la dessus:

Et j'ai enfin compris ce que ce petit snippet de code faisait, alors d’après vous il se passe quoi ? et quel est l’algèbre applique a la valeur dans ecx!? :) (je mettrais a jour avec une explication si le lien n'est vraiment pas clair...)

peace!

jeudi 4 novembre 2010

Le strict aliasing en C

Un billet assez exhaustif, avec des exemples de code et d'output assembleur : c'est ici

jeudi 7 octobre 2010

Pwnage par libpcap

Yo. Dans ce post je vais expliquer comment je me suis battu pour faire un truc assez simple avec la libpcap sous linux.

Au début était un sniffer, je voulais écouter sur une interface à grands coups de SOCK_RAW + recvfrom(). À l'ancienne, quoi. L'idée était de ne pas utiliser de handler pcap (pcap_t *). Au bout d'un moment je me dis "tiens, ça serait sympa d'ajouter les filtres BPF comme dans tcpdump". Brillante idée, non ? Non, ok. Bref, le code de départ est le suivant :

// -*- c-basic-offset: 4; indent-tabs-mode: nil -*-
// vim:sw=4 ts=4 sts=4 expandtab
#include <limits.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <linux/if_ether.h>
#include <arpa/inet.h>
#include <netpacket/packet.h>
#include <poll.h>
#include <linux/types.h>
#include <errno.h>
#include <pcap.h>
#include <linux/filter.h>

#define FRAME_SIZE 65536

/* iface_setprom() sets "device" in promiscuous mode */
static int
iface_setprom(int sock_fd, const char *device)
{
    struct ifreq ifr;

    memset(&ifr, 0, sizeof ifr);
    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", device);

    if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
        printf("[%s] ioctl: %s.\n", device, strerror(errno));
        return -1;
    }

    ifr.ifr_flags |= (IFF_PROMISC | IFF_UP);

    if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
        printf("[%s] ioctl: %s.\n", device, strerror(errno));
        return -1;
    }

    printf("[%s] enter in promiscuous mode\n", device);
    return 0;
}



À partir d'une chaîne de caractère entrée, par exemple "tcp and port 22", on veut obtenir une structure comprise par pcap_compile*(). Comme dit précédemment, je ne veux pas de pcap_t* à manipuler, je vais donc utiliser pcap_compile_nopcap() (non mentionné dans le man de pcap_compile sous linux, évidemment).

/* iface_setfilter() applies a LSF filter to the file descriptor */
static int
iface_setfilter(int sock_fd, const char * const device, const char * const filter)
{
    if (!filter || !*filter) {
        printf("[%s] no filter to install\n", device);
        return 0;
    }

    struct bpf_program bpf;
    memset(&bpf, 0, sizeof bpf);

    char errbuf[PCAP_ERRBUF_SIZE = "";

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, DLT_RAW, &bpf, filter, 1, 0)) {
        printf("[%s] pcap_compile_nopcap failed for '%s'\n", device, filter);
        return -1;
    }

    if (-1 == setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof bpf)) {
        printf("[%s] setsockopt: %s (filter '%s')\n", device, strerror(errno), filter);
        return -1;
    }

    printf("[%s] filter '%s' successfully installed\n", device, filter);
    return 0;
}

/* iface_init() is the main device initialization function */
static int
iface_init(struct pollfd *fds, const char * const device, const char * const filter)
{
    printf("Initializing interface %s (filter '%s')\n", device, filter);

    fds->fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (fds->fd == -1) {
        printf("[%s] socket: %s. Exiting...\n", device, strerror(errno));
        exit(EXIT_FAILURE);
    }

    fds->events = POLLIN | POLLPRI;

    if (0 != iface_setprom(fds->fd, device)) return -1;
    if (0 != iface_bind(fds->fd, device)) return -1;
    if (0 != iface_setfilter(fds->fd, device, filter)) return -1;

    return 0;
}

static void
frame_manager(const char * const device, struct pollfd *fd, struct timeval const *now)
{
    uint32_t i;

    if ((fd->revents & (POLLIN | POLLPRI)) == 0)
        return;

    unsigned char buf[1500] = "";
    ssize_t n = recvfrom(fd->fd, buf, sizeof buf, MSG_DONTWAIT, NULL, NULL);
    if (n < 0) {
        printf("[%s] recvfrom: %s (i=%d, fd=%d)\n", device, strerror(errno), i, fd->fd);
        return;
    }

    printf("[%d,%d] packet received\n", (int)now->tv_sec, (int)now->tv_usec);
}

int main(int ac, char **av)
{
    char *filter = "";
    char *device = "dummy0";

    if (ac > 1) {
        filter = av[1];
    }

    struct pollfd fd;
    (void)iface_init(&fd, device, filter);

    while (/*CONSTCOND*/ 1) {
        if (poll(&fd, 1, -1) > 0) {
            struct timeval now;
            gettimeofday(&now, NULL);
            frame_manager(device, &fd, &now);
        }
    }

    close(fd.fd);
    return EXIT_SUCCESS;
}



Tout d'abord, je monte une interface virtuelle pour éviter le bruit généré par les paquets circulants sur mes vraies interfaces :

$ sudo modprobe dummy
$ sudo ifconfig dummy0 mtu 1600 up



Maintenant, si je compile et j'exécute le code, et que je balance du ssh sur dummy0, j'ai ça :

$ cc -o cap cap.c -lpcap
$ sudo ./cap "tcp and port 22"
[...]



Et dans un autre terminal :

$ sudo tcpreplay -i dummy0 ssh.pcap



Mais aucun "packet received" ne s'affiche. Damned ! En inspectant un peu le code, on voit ça :

    if (-1 == setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof bpf)) {



Ça a l'air bien comme ça, hein. Malheureusement, le 'struct bpf_program' n'est pas compris sous linux, qui, ne voulant pas faire comme ses potes *BSD attend un 'struct sock_fprog *' en avant dernier argument. Bon, ici je me dis "il doit bien y avoir une fonction qui fait la conversion, cherchons dans le source de libpcap". En jetant un coup d'oeil rapide, on tombe sur ça :

libpcap-0.9.8/pcap-linux.c:215 static int       fix_program(pcap_t *handle, struct sock_fprog *fcode);



Je suis content, mais visiblement je dois passer par un pcap_t. Du coup, je reprends le code en l'adaptant pour taper directement dans la structure interne du handler pcap, et j'écris la fonction suivante :

#define SLL_HDR_LEN 16
static int lcc = 0; // linux cooked capture

static int
fix_offset(struct bpf_insn *p)
{
    if (p->k >= SLL_HDR_LEN) {
        p->k -= SLL_HDR_LEN;
    } else if (p->k == 14) {
        p->k = SKF_AD_OFF + SKF_AD_PROTOCOL;
    } else {
        return -1;
    }
    return 0;
}

static int
fix_code(struct bpf_program *bpf, struct sock_fprog *fcode)
{
    size_t prog_size;
    register int i;
    register struct bpf_insn *p;
    struct bpf_insn *f;
    int len;

    len = bpf->bf_len;
    prog_size = sizeof(bpf->bf_insns) * len;
    f = malloc(prog_size);
    if (f == NULL) {
        printf("malloc: %s\n", strerror(errno));
        return -1;
    }
    memcpy(f, bpf->bf_insns, prog_size);
    fcode->len = len;
    fcode->filter = (struct sock_filter *)f;

    for (i = 0; i < len; ++i) {
        p = &f[i];
        switch (BPF_CLASS(p->code)) {

        case BPF_RET:
            if (BPF_MODE(p->code) == BPF_K) {
                if (p->k != 0)
                    p->k = 65535;
            }
            break;

        case BPF_LD:
        case BPF_LDX:
            switch (BPF_MODE(p->code)) {

            case BPF_ABS:
            case BPF_IND:
            case BPF_MSH:
                if (lcc) {
                    if (fix_offset(p) < 0) {
                        return 0;
                    }
                }
                break;
            }
            break;
        }
    }
    return 1;       /* success! */
}



Maintenant je réécris ma fonction iface_setfilter() pour convertir le code BPF :

static int
iface_setfilter(int sock_fd, const char * const device, const char * const filter)
{
    if (! filter || ! *filter) {
        printf("[%s] no filter to install\n", device);
        return 0;
    }

    struct bpf_program bpf;
    memset(&bpf, 0, sizeof bpf);

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, DLT_RAW, &bpf, filter, 1, 0)) {
        printf("[%s] pcap_compile_nopcap failed for '%s'\n", device, filter);
        return -1;
    }

    struct sock_fprog fcode;
    memset(&fcode, 0, sizeof fcode);
    int ret = fix_code(&bpf, &fcode);

    if (1 != ret) {
        printf("[%s] fix_code failed (rcode = %d) for filter '%s'\n", device, ret, filter);
        return -1;
    } else {
        printf("[%s] fix_code worked (rcode = %d) for filter '%s'\n", device, ret, filter);
    }

    if (-1 == setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_FILTER, &fcode, sizeof fcode)) {
        printf("[%s] setsockopt: %s (filter '%s')\n", device, strerror(errno), filter);
        return -1;
    }

    printf("[%s] filter '%s' successfully installed\n", device, filter);
    return 0;
}



Là, logiquement je me dis "Youpi les knakis¸ ça va marcher !". Mais si je reteste, toujours rien. Rien ne s'affiche. La blague est même que si j'invoque :

$ sudo ./cap "not port 80"



... et que je joue du traffic http (sur le port 80 bien sûr), mon outil logue qu'il a vu un paquet ! Par contre avec "port 80" il n'affiche rien. Bon je me suis pas mal gratté la tête ici, j'avoue. Du coup j'appelle mon copain strace à la rescousse, et je compare le raw des filtres passés à setsockopt() entre mon code et l'appel tcpdump :

$ sudo strace -s 256 -f -v ./cap "port 80" 2>&1 | grep ATTACH_FILTER
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\33\0\0\0\0\0\0\0\20\240\363\0\0\0\0\0", 16) = 0
^C
$ sudo strace -s 256 -f -v tcpdump -ni dummy0 port 80 2>&1 | grep ATTACH_FILTER
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\1\0\0\0\0\0\0\0h\303\315E(\177\0\0", 16) = 0
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\30\0\0\0\0\0\0\0\360V1G(\177\0\0", 16) = 0
^C



Les outputs ne sont pas les mêmes. La génération du filtre est donc foireuse, je vérifie maintenant comme suit :

$ sudo tcpdump -nd port 80
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2    jf 10
(002) ldb      [20]
(003) jeq      #0x84            jt 6    jf 4
(004) jeq      #0x6             jt 6    jf 5
(005) jeq      #0x11            jt 6    jf 23
(006) ldh      [54]
(007) jeq      #0x50            jt 22   jf 8
(008) ldh      [56]
(009) jeq      #0x50            jt 22   jf 23
(010) jeq      #0x800           jt 11   jf 23
(011) ldb      [23]
(012) jeq      #0x84            jt 15   jf 13
(013) jeq      #0x6             jt 15   jf 14
(014) jeq      #0x11            jt 15   jf 23
(015) ldh      [20]
(016) jset     #0x1fff          jt 23   jf 17
(017) ldxb     4*([14]&0xf)
(018) ldh      [x + 14]
(019) jeq      #0x50            jt 22   jf 20
(020) ldh      [x + 16]
(021) jeq      #0x50            jt 22   jf 23
(022) ret      #96
(023) ret      #0



Et j'invoque

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, DLT_RAW, &bpf, filter, 1, 0)) {
        printf("[%s] pcap_compile_nopcap failed for '%s'\n", device, filter);
        return -1;
    }
    bpf_dump(&bpf, 0);



J'ai en sortie :

Initializing interface dummy0 (filter 'port 80')
[dummy0] enter in promiscuous mode
[dummy0] bind succeed: index 1634 <--> dummy0
(000) ldb      [0]
(001) and      #0xf0
(002) jeq      #0x60            jt 3    jf 11
(003) ldb      [6]
(004) jeq      #0x84            jt 7    jf 5
(005) jeq      #0x6             jt 7    jf 6
(006) jeq      #0x11            jt 7    jf 26
(007) ldh      [40]
(008) jeq      #0x50            jt 25   jf 9
(009) ldh      [42]
(010) jeq      #0x50            jt 25   jf 26
(011) ldb      [0]
(012) and      #0xf0
(013) jeq      #0x40            jt 14   jf 26
(014) ldb      [9]
(015) jeq      #0x84            jt 18   jf 16
(016) jeq      #0x6             jt 18   jf 17
(017) jeq      #0x11            jt 18   jf 26
(018) ldh      [6]
(019) jset     #0x1fff          jt 26   jf 20
(020) ldxb     4*([0]&0xf)
(021) ldh      [x + 0]
(022) jeq      #0x50            jt 25   jf 23
(023) ldh      [x + 2]
(024) jeq      #0x50            jt 25   jf 26
(025) ret      #65536
(026) ret      #0
[dummy0] fix_code worked (rcode = 1) for filter 'port 80'
[dummy0] filter 'port 80' successfully installed
^C



On voit bien que c'est différent au début. Le reste est plutôt cohérent

(016) jeq      #0x6             jt 18   jf 17  # 0x06 c'est l'IPPROTO_TCP (6)
(017) jeq      #0x11            jt 18   jf 26  # 0x11 = 17 = IPPROTO_UDP



Si on n'est dans aucun des deux cas, on saute en 26 (jf 26), soit la fin. Sinon, on regarde si le port vaut 80 (0x50) :

(022) jeq      #0x50            jt 25   jf 23
(023) ldh      [x + 2]
(024) jeq      #0x50            jt 25   jf 26



Le problème est vraiment le prologue. Et là je on me souffle à l'oreille que c'est peut-être le datalink type. Réaction: "bon dieu de !#@@!#@!# mais c'est bien sûr, ça doit être mon datalink (DLT) qui est moisi !" Si j'écris :

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, DLT_IEEE802, &bpf, filter, 1, 0)) {



J'obtiens en sortie :

$ cc -o cap cap.c -lpcap && sudo ./cap "port 80" 
Initializing interface dummy0 (filter 'port 80')
[dummy0] enter in promiscuous mode
[dummy0] bind succeed: index 1634 <--> dummy0
(000) ldh      [20]
(001) jeq      #0x86dd          jt 2    jf 10  # 0x86dd est l'ethertype IPv6
(002) ldb      [28]
(003) jeq      #0x84            jt 6    jf 4
(004) jeq      #0x6             jt 6    jf 5
(005) jeq      #0x11            jt 6    jf 23
(006) ldh      [62]
(007) jeq      #0x50            jt 22   jf 8
(008) ldh      [64]
(009) jeq      #0x50            jt 22   jf 23
(010) jeq      #0x800           jt 11   jf 23  # 0x800 est l'ethertype IPv4
[...]



Mais toujours rien n'est logué si je joue du http ! En remplaçant DLT_IEEE802 par DLT_EN10MB en revanche...

[...]
[dummy0] enter in promiscuous mode
[dummy0] bind succeed: index 1634 <--> dummy0
[dummy0] fix_code worked (rcode = 1) for filter 'port 80'
[dummy0] filter 'port 80' successfully installed
[1286457559,615193] packet received



Hallelujah ! Hosanna hosanna !

Enfin bon, c'est quand même moche d'écrire le DLT en dur dans le code, donc je l'ai récupéré via la libpcap (mais on est obligé de passer par un handler pcap_t *, ce que je ne voulais pas trop au début...)

    char errbuf[256] = "";
    pcap_t *p = pcap_open_live(device, FRAME_SIZE, 1, 0, errbuf);
    if (! p) {
        printf("[%s] pcap_open_live failed: %s\n", device, errbuf);
        return -1;
    }

    int dlt = pcap_datalink(p);
    pcap_close(p);

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, dlt, &bpf, filter, 1, 0)) {
        printf("[%s] pcap_compile_nopcap failed for '%s'\n", device, filter);
        return -1;
    }



En fait, comme on dispose du handler pcap_t, on peut même dégager pcap_compile_nopcap() et utiliser pcap_compile(), qui lui n'a même pas besoin d'un DLT en paramètre... Tout ça parce que j'ai voulu me passer de ce foutu handler, en somme. Bref le code est disponible ici

mercredi 15 septembre 2010

GCC ifunc!

Pendant ma petite pause lecture du matin, je suis tombé sur ce post concernant les nouvelles updates de la GNU toolchain, et en particulier un nouvel attribut: "ifunc"

Ca permet de définir dynamiquement une fonction qui va "résoudre" vers la fonction de votre choix en fonction de critères que vous pouvez définir, le monsieur @ http://nickclifton.livejournal.com/6612.html donne un exemple très simple et très clair (plus clair que mes explications foireuses) que je reproduirais ici :

void *slow_memcpy (void *dst, const void *src, size_t len)
{
         char *d = dst; char *s = src;

         while (len--)
           *d++ = *s++;

         return dst;
}

void *fast_memcpy (void *dst, const void *src, size_t len)
{
         __asm("foo %0 %1 %2" : "=m" (dst) :  "m" (src), "r" (len) : memory);
         return dst;
}

static void (* resolve_memcpy (void)) (void)
{
         return __cpu_has_foo () ? fast_memcpy : slow_memcpy;
}

void *memcpy (void *, const void *, size_t) __attribute__ ((ifunc ("resolve_memcpy")));

Les details chez http://nickclifton.livejournal.com/6612.html

lundi 30 août 2010

Du Sprite avec beaucoup de bulles pour ces hacks !

L'autre jour, quand j'étais en train d'enregistrer tout l'internet sur un DVD, je suis tombé sur le blog de "sprites": http://spritesmods.com/.

En voilà de beaux projets, je reste épaté, ou "maravilhado" pour les plus portugais d'entre nous. Sprites a attaqué plusieurs systèmes embarqués qui offrent une certaine sécurité: Biostick, Secustick, y'a rien à dire, c'est fantastick I

Tout ça me fait aussi penser que une firme Suisse, Victorinox offre plein d'argent si vous arrivez à péter la rondelle à leur clé USB.

lundi 16 août 2010

si toi aussi tu aimes le poulet routi!

Je suis une grosse feignasse et je lis pas énormément de code, en ce moment je lis des trucs fait sur de l'ARM. En bonne grosse tanche, je n'avais jamais vu ces CMP suivi d'un CMPNE et d'un BXX ou un MOVXX etc.. (c'est en mode ARM only) ailleurs:

CMP      R1,#1 
CMPNE    R2,#2 
BEQ      bleh 

Heureusement qu'il y a l'internet poilu pour éclairer et donner des belles recettes.

J'ai trouve des gens qui parlent de comment on fait un poulet roti et comment voir si il est bien cuit!

Apparemment tu peux chainer des conditions tranquillement avec un petit "suffix" dans l'instruction (il y a des bits réservés dans l'instruction pour ces options ARM-only)

voila ca m'a rendu service aujourd'hui enjoy!

- page 1 de 10