Как работает firewall в Linux

Статьи Безопасность Linux

Дата публикации: 29.08.2007
Автор статьи:
Источник: (Оригинал: Перейти)

 

Взято с http://www.opennet.ru    

 

Оригинал: http://www.hackzona.ru/hz.php?name=News&file=article&sid=3605

Каждый уважающий себя администратор Linux должен уметь не только
настраивать iptables, но и знать, как он работает. В этой статье речь
пойдет не о том, как правильно настраивать iptables или какой-нибудь
другой firewall, а о том, как работают firewall'ы в Linux.
В первую очередь, эта статья нацелена на читателей, которые занимаются
(начинают или только хотят начать) программированием модулей ядра
Linux (Linux Kernel Module — LKM), а также, надеюсь, поможет некоторым
администраторам более детально разобраться в работе iptables.

Оглавление :
1) Netfilter Hacking
2) Firewall своими руками
3) Пример norm.c


Netfilter Hacking

Что такое netfilter и какое отношение он имеет к Firewall'ам?
В основном, Netfilter представляет собой набор функций (hook)
,расположенных в ядре, при помощи которых firewall'ы могут получать
доступ к пакетам и,основываясь на правилах, решать, как с ними
поступать дальше. Netfilter содержит 5 основных hook-функций, которые
описаны в linux/netfilter_ipv4.h.

Графически их можно изобразить так :

[INPUT]--→ --→[ROUTE]--→[3]--→[4]--→[OUTPUT]]]
---------------------------|--------------------^
---------------------------|---------------------|
---------------------------|---------------- [ROUTE]
---------------------------v---------------------|
-------------------------[5]
---------------------------|--------------------- ^
---------------------------|----------------------|
---------------------------v----------------------|
---------------------- [INPUT*]-----------[OUTPUT*]
[1] NF_IP_PRE_ROUTING
[2] NF_IP_LOCAL_IN
[3] NF_IP_FORWARD
[4] NF_IP_POST_ROUTING
[5] NF_IP_LOCAL_OUT


NF_IP_PRE_ROUTING — функция срабатывает как только мы получаем пакет,
даже если он проходящий. Если мы хотим иметь доступ ко всем пакетам,
проходящим через наш интерфейс, то мы должны использовать эту функцию.

NF_IP_LOCAL_IN — срабатывает в случае, когда пакет адресован нам,
перед поступлением пакета в сетевой стек.

NF_IP_FORWARD — если пакет необходимо смаршрутизировать с одного
интерфейса на другой.

NF_IP_POST_ROUTING — для исходящих пакетов из нашего сетевого стека.

NF_IP_LOCAL_OUT — для всех исходящих пакетов.

Более подробную схему обработки пакетов вы можете посмотреть :
http://open-source.arkoon.net/kernel/kernel_net.png
После вызова функции и проведения нехитрых проверок над пакетом, нам
нужно вынести вердикт, что делать с этим пакетом дальше. В нашем
распоряжение 5 вариантов :

[1] NF_ACCEPT : пропускает пакет дальше
[2] NF_DROP : отбрасывает пакет
[3] NF_REPEAT : повторный вызов функции
[4] NF_STOLEN : забирает пакет (прекращается передвижение)
[5] NF_QUEUE : ставит пакет в очередь, как правило для передачи в

пользовательское пространство (мы ведь работаем в пространстве ядра)
Вот собственно и все, что нужно для нормальной работы любого
firewall'а в Linux. С одной стороны, набор функций, позволяющий
получать доступ к пакетам практически в любой точке сетевого стека, а
с другой, набор решений, как поступить с пакетом дальше.

/* Я думаю, что администраторам дальше можно не читать, там пойдет
объяснение структур, правильность их заполнение, а также примеры
использования. Вся теория работы firewall'а заканчивается

*/

Теперь попытаемся разобраться, как все это работает!
Первым делом нам нужно познакомиться со структурой nf_hook_ops, она и
будет нашим проводником в мир netfilter'a. Описание её можно найти в
/Linux/netfilter.h :

44 struct nf_hook_ops
45 {
46 struct list_head list;
47
48 /* User fills in from here down. */
49 nf_hookfn *hook;
50 int pf;
51 int hooknum;
52 /* Hooks are ordered in ascending priority. */
53 int priority;
54 };


Первое, что мы видим, это <<struct list_head list>> — это структура,
которая содержит список всех hook-функций, но нас она не сильно
интересует.

nf_hookfn *hook — указатель на нашу функцию, в которой мы и будем
проводить все наши проверки. Возвращаемое значение должно быть одно из
5-и поведений (NF_ACCEPT, NF_DROP, …).

int pf — служит для определения протокола, с которым мы хотим работать
(PF_INET)

int hooknum — а вот и место нашего вызова. (например
NF_IP_PRE_ROUTING)

int priority — приоритет. В случае, если определено несколько функций
на один вызов, первым сработает тот, у кого выше приоритет. Мы будем
использовать — NF_IP_PRI_FIRST.

Не поверите, но это все!
Остается лишь маленькое дополнение.
После того, как мы объявим и заполним нашу структуру, её необходимо
зарегистрировать.

Для этого служат 2-е функции, которые объявлены все в том же
/Linux/netfilter.h :

89 /* Function to register/unregister hook points. */
90 int nf_register_hook (struct nf_hook_ops *reg);
91 void nf_unregister_hook (struct nf_hook_ops *reg);

nf_register_hook — для регистрации нашей hook-функции
nf_unregister_hook — для удаление нашей функции из цепочки.


Ничего особенного, просто банальное предупреждение. Обязательно
выгружайте ваши функции при выгрузке модуля из памяти при помощи
nf_unregister_hook.

Если этого не сделать, произойдет очень неприятная вещь.
Придет пакет, сработает наш вызов, ядро попытается обратиться к
странице памяти для вызова нашей функции для обработки, а там…. Эээ
в лучшем случае ничего, в худшем. .кто-то занял наше место и тогда
результат будет непредсказуем.


Firewall своими руками

Для примера напишем маленький firewall.
Который будет беспощадно уничтожать все входящие и исходящие пакеты.
bash$ > cat firewall.c

#define __KERNEL__
#define MODULE
#define LINUX
#include <module.h>
#include <kernel.h>
#include <netfilter.h>
#include <netfilter_ipv4.h>
MODULE_LICENSE ( «GPL»);
MODULE_AUTHOR ( «ilya»);
/* Объявление структур. Мы объявим 2-е структуры. */
/* 1-я для входящих пакетов */
/* 2-я для исходящих пакетов */
struct nf_hook_ops nf_incoming;
struct nf_hook_ops nf_outgoing;
/* наша функция обработки */
unsigned int main_hook (unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn) (struct sk_buff*))
{
/* Для примера, мы будем отбрасывать все пакеты */
return NF_DROP;
}
int init_module ()
{
/* Заполнение структур */
/* Сначала, заполним структуру для входящих пакетов */
nf_incoming.hook = main_hook; /* указатель на нашу функцию */
nf_incoming.pf = PF_INET;
nf_incoming.hooknum = NF_IP_PRE_ROUTING;
nf_incoming.priority = NF_IP_PRI_FIRST;
/* Теперь для исходящих */
nf_outgoing.hook = main_hook;
nf_outgoing.pf = PF_INET;
nf_outgoing.hooknum = NF_IP_PRE_ROUTING;
nf_outgoing.priority = NF_IP_PRI_FIRST;
/* Вроде все, осталось только зарегистрировать наши функции */
nf_register_hook (&nf_incoming);
nf_register_hook (&nf_outgoing);
printk ( «FireWall loaded обратный слеш n»);
return 0;
}
void cleanup_module ()
{
/* Не забываем удалить наши вызовы J, а то конфуз может случится */
nf_unregister_hook (&nf_incoming);
nf_unregister_hook (&nf_outgoing);
printk ( «FireWall unload обратный слэш n „);
}


Ну вот. Все очень просто!
Теперь компилируем наш модуль :

Gcc -c firewall.c -I- -I /usr/src/Linux/include


А еще лучше, написать makefile!
Но это уже на ваше усмотрение.

И запускаем : insmod firewall.o (иногда приходится запускать с ключем
-f : insmod -f firewall.o, а то ему версии не нравятся, но кому не
лень, можно в модуле прописать все данные о версии ядра)
Посмотрите /var/log/messages — увидите <<FireWall loaded>>
Значит наш модуль загрузился.

Теперь, если вы попробуйте подключится к кому-нибудь или, наоборот,
кто-то захочет к вам подключится, ничего не выйдет. Наш модуль не
пропустит ни одного пакета.

Что бы вернуть все на место, просто выгрузите модуль :

Rmmod firewall


Вот пример firewall'a в 60 строк, включая заголовки. Не сложно
правда..!!! :))
Теперь перейдем к более сложным вещам.
Но совсем на чуть-чуть :).
Пример norm.c

В этом примере мы будем проводить небольшой анализ захваченного нами
пакета.
Наша программа будет анализировать заголовки пакета и в случае
неудовлетворения правилам, будет удалять его или править. Правила я
брал из статьи <<Нормализация пакета>> (Спасибо автору, очень
познавательная).

Итак, небольшое введение в структуру sk_buff:

Sk_buff — это буфер для работы с пакетами. Как только приходит пакет
 или появляется необходимость его отправить, создается sk_buff, куда и
помещается пакет, а также сопутствующая информация, откуда, куда, для
чего… На протяжение всего путешествия пакета в сетевом стеке
используется sk_buff. Как только пакет отправлен или данные переданы
пользователю, структура уничтожается, тем самым освобождая память.

Описание этой структуры можно найти в linux/skbuff.h
Она очень большая, я не буду выкладывать её сюда :)
Все что мы будем использовать из неё, это :

Protocol — чтобы знать, с каким протоколом серевого уровня мы имеем
дело

Data — место, где лежит пакет.

Более подробно о работе sk_buff можно почитать в Интернете, информации
о нем море, а что касается практической части, советую почитать статью
из phrack No. 55 „Building Into The Linux Network Layer“ http://www.phrack.org/show.php?p=55&a=12

Ну вроде все, с теорией маленько разобрались.
Теперь определимся, что и как мы будем делать. Так как это лишь пример
использования, я не буду заострять внимание над нормализацией
конкретного протокола, просто пробежимся немного по протоколам и все :

1) IP — проверка протокола следующего уровня (пропускать только TCP,
UDP, ICMP)

2) IP — если поле TTL cat norm.c


#define __KERNEL__
#define MODULE
#define LINUX
#include <module.h>* Эйй, мы ведь пишем модуль к ядру */
#include <kernel.h>
#include <netfilter.h>
#include <netfilter_ipv4.h>
/*подключаем заголовки для работы с сетевыми протоколами */
/* и собственно говоря sk_buff */
#include <skbuff.h>
#include <inet.h>
#include <ip.h>
#include <ip.h>
#include <tcp.h>
#include <icmp.h>
#include <uaccess.h>
/* определяем дериктивы для преобразования байт из сетевого в
нормальный :) */
/* и наоборот */
#define ntohs (x) __be16_to_cpu (x)
#define htons (x) __cpu_to_be16 (x)
/* Увековечим свое имя Aha-ha… */
MODULE_LICENSE („GPL“);
MODULE_AUTHOR ( «llya“);
struct nf_hook_ops nf_incoming;
struct sk_buff *skbf;
struct tcphdr *th;
struct icmphdr *icmph;
struct iphdr *iph;
unsigned int main_hook (unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn) (struct sk_buff*))
{
skbf=*skb;
/* Работаем только с IP */
if (skbf→protocol! = htons (ETH_P_IP))
return NF_ACCEPT;
/* Пропускаем только ICMP, TCP & UDP */
if (skbf→nh.iph→protocol! = IPPROTO_TCP && skbf→nh.iph→protocol! =
IPPROTO_ICMP && skbf→nh.iph→protocol! = IPPROTO_UDP)
return NF_DROP;
/* Проверка поля ttl */
if (ntohs (skbf→nh.iph→ttl)nh.iph→ttl=htons (100);
ip_send_check (skbf→nh.iph); /* подсчет checksum */
return NF_ACCEPT;
}
/* Проверка ICMP, что мы здесь делаем я думаю вы знаете :) */
if (skbf→nh.iph→protocol==IPPROTO_ICMP)
{
skbf→h.icmph= (struct icmphdr *) (skbf→data+ (skbf→nh.iph→ihl*4));
if (skbf→h.icmph→type==ICMP_ECHOREPLY && skbf→h.icmph→code==0)
return NF_ACCEPT;
if (skbf→h.icmph→type==ICMP_DEST_UNREACH)
return NF_ACCEPT;
if (skbf→h.icmph→type==ICMP_ECHO && skbf→h.icmph→code==0)
return NF_ACCEPT;
return NF_DROP;
}
/* Блокируем TCP, если порт источника или назначение 31337 и при этом
делаем запись */
/* в messages */
if (skbf→nh.iph→protocol==IPPROTO_TCP)
{
skbf→h.th= (struct tcphdr *) (skbf→data+ (skbf→nh.iph→ihl*4));
if (skbf→h.th→dest==htons (31337) ||
skbf→h.th→source==htons (31337))
{
printk ( «Hacking attempt :) Good bye, young kiddies «);
return NF_DROP;
}
return NF_ACCEPT;
}
/* Хех, если все прошло гладко, и никто не попал под наш
мини-нормализатор */
/* милости просим в сетевой стек!!! */
return NF_ACCEPT;
}
int init_module ()
{
nf_incoming.hook = main_hook;
nf_incoming.pf = PF_INET;
nf_incoming.hooknum = NF_IP_PRE_ROUTING;
nf_incoming.priority = NF_IP_PRI_FIRST;
nf_register_hook (&nf_incoming);
printk ( «FireWall loaded обратный слэш n «);
return 0;
}
void cleanup_module ()
{
nf_unregister_hook (&nf_incoming);
printk ( «FireWall unload обратный слэш n «);
}

Взято с http://www.opennet.ru

Copyright © 2006—2011 "Портал RusCentOS"
Хостинг нашего сайта в ООО "КосмоХост" (на HostCMS v. 4.0)

Ссылки на сайты партнеров