J'ai écrit cette documentation à la vue du peu de ressources en français sur ce thème. Ce document explique comment programmer son propre firewall en C pour linux, en utilisant netfilter.
Comme vous aller le constater, cette tâche n'est vraiment pas difficile en elle même, et je vais essayer de détailler un maximum.
Netfilter
Les informations de cette partie sont tirées du netfilter-hacking-HOWTO. Je vais résumer l'architecture de netfilter, pour plus de détails, je vous invite à consulter ce HOWTO.
Netfilter est un ensemble de "hook" dans la pile IP du noyau. Il nous permet d'intercepter, de modifier ou de détruire les paquets IP passants par notre machine. Il y a 5 hook netfilter :
entrée du paquet --->[1]--->[ROUTE]--->[3]--->[4]---> sortie du paquet
| ^
| |
| [ROUTE]
v |
[2] [5]
| ^
v |
machine locale
- 1 : NF_IP_PRE_ROUTING : le paquet vient juste d'arriver, il a subit le test du checksum et autres petits tests. Ce paquet peut etre destiné à la machine elle-même ou à une autre si la machine sert de routeur;
- 2 : NF_IP_LOCAL_IN : le paquet est pour la machine locale
- 3 : NF_IP_FORWARD : le paquet est destiné à une autre interface
- 4 : NF_IP_POST_ROUTING : le paquet est sur le point d'etre envoyé sur le cable
- 5 : NF_IP_LOCAL_OUT : le paquet local va sortir pour etre routé.
Les modules linux peuvent écouter chacun des hook à l'aide de la fonction
int nf_register_hook(struct nf_hook_ops *reg); (cf linux/netfilter.h)
La structure nf_hook_ops définie entre autre le numéro de hook (NF_IP_x) et la fonction à appeler.
La fonction peut décider après des tests si le paquet va etre accepté (NF_ACCEPT) ou détruit (NF_DROP) sans réponse ICMP. Il existe d'autres réponses que je ne vais pas détailler ici.
Le hook est retiré grace à la fonction nf_unregister_hook avec le meme argument que la fonction nf_register_hook.
Grâce aux trois hook NF_IP_LOCAL_IN, NF_IP_FORWARD et NF_IP_LOCAL_OUT nous pouvons contrôler tous les paquets qui passent par notre machine et décider de leur sort.
Les modules
Je vais expliquer ici le minimum à savoir sur les modules linux pour remplir notre tâche.
Les modules linux sont des programmes se greffant au noyau linux et accedant donc aux fonctions internes de celui-ci. Les modules se chargent (insmod) et se déchargent (rmmod) à la volée. Il faut etre root pour executer insmod.
Tout au début de la source de votre module, il faut ecrire :
#define __KERNEL__
#define MODULE
#include <linux/module.h>
#include <linux/kernel.h>
/* autres includes */
MODULE_LICENCE("GPL"); // pour ne pas avoir de warning lors de l'insmod
La fonction appelée lors d'insmod est :
int init_module(void)
qui remplace la fonction 'main', là où la fonction nf_register_hook sera appelée.
La fonction
void cleanup_module(void)
est appelée lors du rmmod, là où la fonction nf_unregister_hook sera appelée.
Les modules ne peuvent pas acceder aux fonctions standards du C, seules les fonctions des fichiers en-tete dans /linux/ seront accessibles. La fonction printk() permet de laisser un message système récupérable par syslog ou dmesg. Le niveau du message est indiqué entre < et >, par exemple <1> est un message normal.
Voilà, vous savez maintenant ce qu'il faut savoir sur les modules, passons tout de suite à la partie la plus importante : la programmation de notre firewall.
Le firewall
Voici la source commentée d'un squelete de firewall :
----------------------8<------------------------------------
#define __KERNEL__
#define MODULE
#include <linux/module.h> //MODULE_LICENCE, ...
#include <linux/kernel.h>
#include <linux/netfilter_ipv4.h> // fonctions netfilter
#include <linux/ip.h> // structure iphdr
#include <linux/tcp.h> // structure tcphdr
#include <linux/udp.h>
#include <linux/icmp.h>
MODULE_LICENSE("GPL"); // informations sur le module
MODULE_AUTHOR("Julien STERCKEMAN");
MODULE_DESCRIPTION("Mini firewall");
struct nf_hook_ops input_filter; // caracteristiques du hook
// fonction qui va etre appelee pour le hook
unsigned int input_hook(unsigned int hooknum, struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out)
{
struct iphdr *iph;
struct tcphdr *tcph;
struct udphdr *udph;
struct icmphdr *icmph;
iph = (*skb)->nh.iph; // skb est une structure contenant le paquet
// cf /linux/sk_buff.h
if(iph->protocol == 1) // ICMP
{
// on prend l'en-tête icmp
icmph = (struct icmphdr *)((__u32)iph+iph->ihl);
printk("<1>firewall : aime pas les ping de 0x%x!!\n", iph->saddr);
return NF_DROP; // on ignore tous les paquets icmp
// ceci est juste pour l'exemple, en réalité il ne
// faudrait refuser que les ICMP_ECHO et pas tous,
// sinon nous n'orions plus les erreurs Host Unreacheable...
}
else if(iph->protocol == 6) // TCP
{
tcph = (struct tcphdr *)((__u32 *)iph+iph->ihl);
printk("<1>firewall : paquet tcp ports=%d portd=%d\n",
tcph->source, tcph->dest);
// c'est ici que l'on vérifie les ports, les flags, ...
// et que l'on décide si l'on accepte le paquet
}
else if(iph->protocol == 17) // UDP
{
udph = (struct udphdr *) ((__u32 *)iph+iph->ihl);
printk("<1>firewall : paquet udp ports=%d portd=%d\n",
udph->source, udph->dest);
// pareil que pour tcp
}
// si ca passe les tests on accepte
return NF_ACCEPT;
}
int init_module(void) // fonction "main()" des modules
{
int result;
// on indique les caracteristiques du hook d'entree
input_filter.list.next = NULL; // le kernel va les remplir
input_filter.list.prev = NULL;
input_filter.hook = (nf_hookfn *)input_hook; // fonction hook
input_filter.pf = PF_INET; // IPv4
input_filter.hooknum = NF_IP_LOCAL_IN; // type de hook
// on peut faire aussi des hook NF_IP_LOCAL_OUT pour
// empecher des troyens ou spywares et autres
// et NF_IP_FORWARD pour filtrer au niveau de la passerelle
result = nf_register_hook(&input_filter); // retourne 0 si ok
if(result)
{
printk("<1>firewall : erreur nf_register_hook !!!\n");
return 1;
}
printk("<1>firewall : module charge.\n");
return 0;
}
void cleanup_module(void) // appelee par rmmod
{
nf_unregister_hook(&input_filter);
printk("<1>firewall : module decharge.\n");
}
----------------------->8----------------------------------------
Conclusion
Voila la source n'est pas difficile à comprendre, et vous savez maintenant tout pour créer votre firewall perso. Pour les entetes tcp et autres, je vous conseille de regarder dans les headers /linux/tcp.h et autres...
Testez votre firewall avec nmap en utilisant toutes les fonctions de scan furtif ou tout autre logiciel de ce type...
Si vous avez des questions, des remarques, des suggestions, des compliments, des insultes, envoyez-les moi à j_sterckeman@yahoo.fr je me ferai un plaisir de vous répondre :-)