Участник:Edward Chernenko/GNU Identd/Документация
Материал из Википедии — свободной энциклопедии
Содержание |
GNU Identd (читается гну́ идэ́нт дэ́) — программа-демон, реализующая сервер протокола ident и работающая внутри ядра операционной системы Linux.
[править] Протокол ident
Разработанный в 1993 году и опубликованный в RFC 1413, протокол ident предназначался для того, чтобы сервер мог узнать, какой именно пользователь на удалённой машине инициировал запрос к нему. Схема работает следующим образом.
- Начальные условия:
- Диалог:
- Пользователь подсоединяется к внешнему серверу.
- Внешний сервер делает обратный запрос на машину пользователя и на порт 113, указывая параметры установленного соединения (локальный и удалённый порт).
- Машина пользователя отвечает, возвращая его символьное имя.
- Внешний сервер что-то с ним делает (например, записывает в журнал), а затем отвечает на запрос пользователя.
[править] Применение
Несмотря на простоту, протокол ident нередко применяется в работе самых разных служб, в том числе
- в IRC — демоны, как правило, требуют ответа на ident-запрос, делая его результат доступным для всех участников дискуссии.
- в почтовых системах — когда на одном компьютере находится большое количество пользователей (таковы сервера хостеров и провайдеров), сервер исходящей почты способен маркировать письма, дописывая к ним настоящее имя автора сообщения. Таким образом, упрощается выявление (и последующая блокировка) локальных спаммеров.
- в локальные серверах, принимающих соединения через UNIX-сокеты, но работающих в системах без поддержки unix credentials.
[править] Реализация
[править] Поиск соединения
От identd требуется найти информацию обо всех соединениях, в которые вовлечена машина пользователя (её местонахождение зависит от системы), и отыскать в ней запрошенное соединение.
В большинстве систем класса UNIX, поддерживающих псевдофайловую систему proc, существует более-менее переносимый интерфейс, файл /proc/net/tcp. Он содержит большое количество полей и перечисляет все активные сокеты (не только соединённые, но и слушающие или ожидающие). Каждая система реализует чтение из него по-своему, создавая его по запросу на основании внутренних данных ядра.
[править] Проблемы
Если используются ненормальные системы (например, Windows), то реализовать на них переносимый identd становится практически невозможным. Вследствие этого, получили распространение так называемые псевдодемоны ident, которые выдают пустую, заданную пользователем или случайную строку. Один из таких встроен в популярный IRC-клиент mIRC.
[править] Пользователи
«Честные» демоны, работающие в UNIX, сталкиваются с ещё одной проблемой. Дело в том, что из таблицы соединений они получают не символьное имя пользователя, а его численный идентификатор. Сопоставление же происходит не в ядре, а через специальный файл /etc/passwd. Большинство реализаций просто читают его каждый раз при поступлении запроса, но некоторые считывают его только однажды и хранят результаты в хэш-таблицах, обновляя их по мере необходимости (когда файл изменяется).
[править] Безопасность
Протокол ident имеет серьёзные ограничения с точки зрения доверия результатам, полученным в результате описанных выше запросов.
- Если пользователь имеет административный контроль над системой на своей машине (таковы персональные и мобильные компьютеры, а также Windows-системы), то он может заставить демон выдавать фальшивые данные, а то и вовсе его отключить. Таким образом, никогда нельзя доверять сведениям, полученным с чужого компьютера. Даже если он находится под тем же административным контролем, что и локальный, ident никогда нельзя применять для аутентификации, так как это один из видов межсистемного доверия (взлом удалённой системы открывает дыру в локальной).
- «Правило доверия»: Демон ident гарантированно сообщает правдивые сведения тогда и только тогда, когда работает на том же компьютере, что и другой сервер, к которому пользователь направляет запрос.
- С другой стороны, иногда есть серьёзные причины скрывать, кто сделал запрос. Это характерно для разного рода ботов, работающих по какой то причине от имени суперпользователя. Обычно «правдивые» реализации (установленные, например, на сервере исходящей почты) имеют специальные списки, через которые root может замаскировать тех или иных пользователей. Они могут храниться в конфигурационном файле или изменяться вызовом специальной команды.
[править] Программные средства
[править] linux-identd
Один из распространённых ident-демонов — linux-identd, написанный в 2001 году. Его реализацию можно назвать классической. Соединения ищутся в файле /proc/net/tcp, который читается с помощью интерфейса stdio и форматного ввода, а для сопоставления идентификатора и имени пользователя используются функции библиотеки glibc. Демон может запускаться по запросу inetd/xinetd (не путать! inetd — это демон управления серверами, которые слушают сетевые порты). Специальных возможностей вроде маскировки пользователей нет.
[править] Скорость
Эта реализация работает очень быстро, когда в системе мало соединений и мало пользователей. В противном случае, парсинг большого /proc/net/tcp будет задерживать ответы, а кэш /etc/passwd (который ведёт функция внутри glibc, используя сервер nscd — name server cache daemon) будет быстро очищаться, понижая эффективность работы многих других программ, которые также от него зависят. Этот демон лучше использовать на локальных системах или на небольших серверах приложений.
[править] GNU Identd
Необычный демон, реализованный в виде модуля ядра Linux. Он читает служебные таблицы, относящиеся к установленным соединениям, напрямую (в обход proc). Результат парсинга /etc/passwd хранится в массиве, отсортированном по идентификатору (для последующего бинарного поиска). Используются два «потока» (внутри монолитного ядра нет вытесняющей многозадачности, и они выделены только потому, что это две части кода, «засыпающих» на разных системных вызовах). Один принимает входящие запросы, а другой следит за актуальностью представления /etc/passwd с помощью inotify, перезагружая массив каждый раз при его изменении.
Есть поддержка маскирования, реализованного через запись специальных команд в файл /proc/net/identd/hide.
Демон захватывает порт 113; таким образом, нельзя запустить другой identd на этой же машине. В случае, если модуль был вкомпилирован в ядро, его нельзя отключить; единственное средство — выгрузка модуля.
[править] Скорость
GNU Identd является быстрейшей из возможных реализаций identd. Он предназначен в первую очередь для крупных серверов исходящей почты с сотнями или даже тысячами пользователей. Даже при очень большой нагрузке демон большую часть времени спит и не влияет на производительность системы.
[править] Диалог ident
S 5068, 25 U 5068, 25 : USERID : UNIX : vasya
S 5070, 25 U 5070, 25 : USERID : OTHER : Vasya Pupkin
S 5072, 25 U 5072, 25 : ERROR : NO-USER
Описание диалога:
- Сервер, слушающий порт 25, запрашивает identd насчёт имени пользователя, который попытался присоединиться к нему сокетом, привязанным к порту 5068 (скорее всего, это случайный порт — клиентским сокетам обычно не назначают адрес явно).
- Демон identd отвечает, что имя этого пользователя — vasya.
- Тут демон выдал настоящее имя. Как правило, так поступают фальшивые identd из систем вроде Windows. Здесь может быть что угодно (например, номер телефона или псевдоним).
- А здесь демон не смог найти информацию о соединении. Возможно, клиент его разорвал (это может быть программа сканирования портов, например, nmap). Также причиной могут быть неполадки в сети.
- Иногда, когда используется маскирование, предоставляется возможность спрятать пользователя вообще (а не заменить другим именем). В таком случае тоже может быть возвращён такой ответ (хотя есть специальная ошибка HIDDEN-USER). Как правило, подобная практика не рекомендуется, поскольку внешний сервер (IRC?) иногда прекращает обработку пользовательского запроса в ответ.
[править] Примеры кода
Ниже продемонстрированы простейшие ident-клиенты.
[править] Си
char *ident(struct sockaddr_in *, struct sockaddr_in *) struct sockaddr_in *this, struct sockaddr_in *peer { int this_port = ntohs(this->sin_port); int peer_port = ntohs(peer->sin_port); struct sockaddr_in identd = { .sin_family = AF_INET, .sin_addr = peer->sin_addr, .sin_port = 113 } char *r; char res[201]; size_t len; int sock = socket(PF_INET, SOCK_STREAM, 0); if(sock < 0) return NULL; if(connect(sock, (struct sockaddr *) &identd, sizeof(identd)) < 0) return NULL; len = asprintf(&r, "%u, %u\n\r", this_port, peer_port); if(!r) return NULL; len = send(sock, r, len, 0); free(r); if(len < 0) return NULL; len = recv(sock, res, 200, 0); if(len <= 0) return NULL; if(close(sock) < 0) return NULL; while(len >= 0 && isspace(res[len - 1])) len --; res[len] = '\0'; if(strstr(res, "ERROR")) return NULL; r = strchr(res, ":"); if(r) { r = strchr(r, ":"); if(r) { r = strchr(r, ":"); if(r) return r; } } return NULL; }
[править] Perl
use IO::Socket::INET; sub ident { my($this, $peer) = @_; my($this_addr, $this_port) = split /:/, $this; my($peer_addr, $peer_port) = split /:/, $peer; my $c = IO::Socket::INET->new(PeerAddr => "$peer_addr:auth(113)'); print $c "$this_port, $peer_port\n"; my $res = <$c>; my($req, $code, $ext) = split /:/, $res; return undef if($code =~ /ERROR/); return $ext; }
[править] Особенности GNU Identd
[править] Маскирование
При использовании GNU Identd в псевдофайловой системе proc появляется специальный файл /proc/net/identd/hide. В него можно записывать следующие команды (например, с помощью echo):
- <UID> <ИМЯ>
- Отправить ИМЯ вместо настоящего имени пользователя в ответ на запрос о соединении, принадлежащем пользователю с идентификатором UID (иначе говоря, замаскировать пользователя UID как ИМЯ).
- <UID>
- Отменить маскирование для пользователя с идентификатором UID (то есть удалить все существовавшие ранее правила).
- <UID> :
- Выдавать ошибку NO-USER (т. н. безусловное маскирование; нежелательно — см. выше: внешний сервер может разорвать соединение с пользователем).
Пример:
echo "500 labuda" >/proc/net/identd/hide
замаскирует пользователя с идентификатором 500 как «labuda», а
echo "500" >/proc/net/identd/hide
это поведение отменит.
[править] Отчёт
При чтении из файла /proc/net/identd/hide будет распечатана таблица действующих маскировок. Следует отметить, что файл полностью POSIX-совместим (то есть можно выборочно считать область от, скажем, 22-ого байта и длиной 51 байт), так как используется интерфейс seq_file (кстати, он же помогает отображению псевдофайла /proc/net/tcp).
[править] Тестирование
Исследовалась правильность составления таблиц маскирования.
[root@sunbeam root]# cat /proc/net/identd/hide [root@sunbeam root]# echo "500 labuda" >/proc/net/identd/hide [root@sunbeam root]# cat /proc/net/identd/hide 500 labuda [root@sunbeam root]# echo "300 other" >/proc/net/identd/hide [root@sunbeam root]# cat /proc/net/identd/hide 300 other 500 labuda [root@sunbeam root]# echo "33 :" >/proc/net/identd/hide [root@sunbeam root]# cat /proc/net/identd/hide 33 : 300 other 500 labuda [root@sunbeam root]# echo "300 other2" >/proc/net/identd/hide [root@sunbeam root]# cat /proc/net/identd/hide 33 : 300 other2 500 labuda [root@sunbeam root]# echo "500" >/proc/net/identd/hide [root@sunbeam root]# cat /proc/net/identd/hide 33 : 300 other2
[править] Внутреннее строение
Внутри GNU Identd хранятся три хэш-таблицы (точнее, массивы с элементами, состоящими из идентификатора и имени, отсортированные по идентификатору). Такая схема была выбрана, чтобы можно было обновлять таблицу маскировки и результат парсинга /etc/passwd независимо. После любого из этих изменений они соединяются в общую таблицу, уже в которой происходит поиск при формировании ident-ответа.
Итоговая таблица и таблица маскировки отсортированы (по ним производится бинарный поиск), а таблица /etc/passwd — нет. Общая таблица формируется так:
- Копируется результат парсинга.
- Просматривается таблица маскировки; если в общей таблице такой UID уже есть, то перезаписывает его, иначе добавляется в конец.
[править] Разбор /etc/passwd
Используемый парсер /etc/passwd построен на конечном автомате и использует фиксированное количество памяти. Кроме того, он способен игнорировать «неправильные» и пустые строки; по сути, это один из самых «терпимых» средств (в основном потому, что ему нужно только первое и третье поле, имя и идентификатор — остальное он игнорирует) для разбора этого файла.
При парсинге (и в дальнейшей работе) предполагается, что имя пользователя не может быть больше 32 символов (современное ограничение Linux-систем). Если 33-им символом при последовательном чтении не оказывается «:», то строка игнорируется как неправильная.
[править] Исходный код
Демон GNU Identd состоит из трёх файлов — main.c, pwd.c и proc.c; кроме того, все они используют pwd_cache.h, в котором определены inline-функции для работы с хэш-таблицами.
[root@sunbeam gnuidentd]# ls -Rl .: итого 40 -rw-r--r-- 1 root root 340 Мар 15 17:08 Kconfig -rw-r--r-- 1 root root 9471 Мар 16 19:49 main.c -rw-r--r-- 1 root root 102 Мар 10 18:30 Makefile -rw-r--r-- 1 root root 6638 Мар 16 21:33 proc.c -rw-r--r-- 1 root root 6390 Май 1 20:45 pwd.c -rw-r--r-- 1 root root 2859 Мар 10 18:30 pwd_cache.h
- main.c
- Отвечает за запуск демона, прослушивание сетевого порта, приём соединений, отслеживание изменений /etc/passwd с помощью inotify, парсинг запроса и посылку ответа.
- proc.c
- Отвечает за функционирование /proc/net/identd/hide (отображение и парсинг команд) и за ведение хэш-таблицы маскировок.
- pwd.c
- Отвечает за парсинг /etc/passwd, поиск в хэш-таблицах и за объединение таблиц.
[править] Наложение патча
Патч для ядра версии 2.6.15.1 доступен здесь. Он накладывается обычным методом:
cd <путь_к_корню_исходников_ядра> patch -p1 < <путь_к_патчу>
[править] Вручную
Если патч не накладывается без проблем, то его придётся наложить вручную. Для этого получите сырой архив (gnuidentd-2.6.15.tar.gz), распакуйте его (скажем, в /tmp/build) и произведите следующие действия:
cd <путь_к_корню_исходников_ядра> mkdir -p net/gnuidentd cp -rf /tmp/build/gnuidentd-2.6.15/* net/gnuidentd/
Далее отредактируйте:
- Файл net/Makefile, добавив в конец строку
-
obj-$(CONFIG_GNU_IDENTD) += gnuidentd/
-
- Файл net/Kconfig, добавив перед «endif» строку
-
source "net/gnuidentd/Kconfig"
-
- Файл net/sock.h, добавив после
-
extern void sk_init(void);
- строку
-
extern struct socket *sock_alloc(void);
-
- Файл net/socket.c, убрав «static» в начале строки
-
static struct socket *sock_alloc(void)
- и добавив
-
EXPORT_SYMBOL(sock_alloc);
- в конец файла.
-
[править] Установка
Для компиляции демона GNU Identd в виде модуля (можно его вкомпилировать и в ядро, но это не рекомендуется из-за невозможности отключения) задайте параметр GNU_IDENTD в файле .config равным «M». На этом же этапе можно (если в такой возможности нет необходимости) отключить расширенную маскировку, указав GNU_IDENTD_HIDE_EXTENDED=N. По умолчанию она включена, если включена поддержка псевдофайловой системы proc (PROCFS=Y).
Если вы уже компилировали ядро в этом каталоге, то достаточно будет команд
make -C <путь_к_корню_исходников_ядра> M=net/gnuidentd make -C <путь_к_корню_исходников_ядра> M=net/gnuidentd modules_install
В противном случае следует скомпилировать всё ядро. Не забывайте также, что ядро и модули должны быть скомпилированы одинаковой версией компилятора: если вы обновляли компилятор с момента установки ядра, то его следует перекомпилировать.
[править] Лицензия
Демон GNU Identd распространяется под свободной лицензией GNU GPL 2. Это значит, что вы можете свободно использовать и распространять данную программу, но изменять её можно только так, чтобы в результате изменения также получилась свободная GPL-программа. Также пользователям не предоставляется абсолютно никаких гарантий, в том числе работоспособности или пригодности для какой-либо цели. Используйте её на свой страх и риск.
Copyright (C) 2006 Edward Chernenko.