В один прекрасный или не очень момент на работе я ощутил нехватку местного IM-сервера.
И тут возникла идея, что такой сервачок было бы не дурственно взять и запилить. Сказано-сделано, благо я и есть администратор, соответственно, мне и карты в руки. Запинка возникла в том моменте, где происходит процесс авторизации пользователя. А именно, сопоставление указанного им логина и пароля тому, что есть в некоей базе, мне хотелось, чтобы такой базой была база почтовика, а именно - dovecot'а.
На данный момент логин-пароли хранятся в отдельно стоящей MySQL базе данных. Изначально идея была авторизовываться через сокет dovecot, но протокол общения по этому сокету документирован... да никак он не документирован - типа смотри исходники dovecot, благо они есть. Разбираться в чужом коде - занятие, как вы понимаете, мягко скажем, не сильно благодарное, да и выхлопа от этого будет не очень-то много.
Во втором подходе я думал организовать процесс авторизации по почтовому протоколу, например POP3 или IMAP. С почтовиками взаимодействовать нам не впервой, были скрипты для мониторинга прохождения почты через релэй головной организации (там стояли безопасники, чьё оборудование регулярно начинало вести себя нагло и резало даже исходящую почту, что недопустимо, нам надо было оперативно это обнаруживать и пинать их). Решил, что на сей раз можно жизнь разнообразить и попытать IMAP. Это удалось на славу. Благо задача сводилась к попытке авторизации и закрытию соединения.
Однако на пути возникло другое препятствие - extauth-скрипт должен возвращать 1 или 0 в каком-то бинарном формате. Такой подставы я конечно же не ожидал, ибо в подавляющем большинстве случаев подобные программы возвращают данные в human-readable формате-то, а тут какая-то бинарная херня, которую неудобно дебажить. Хотя сам ejabberd вообще работает по принципу обмена xml-сообщениями. Все примеры скриптов, которые мне попадались это и с родного сайта и например отсюда <a title="http://habrahabr.ru/post/99803/" href="http://habrahabr.ru/post/99803/" target="_blank">http://habrahabr.ru/post/99803/</a> или <a title="http://allgnu.com/xmpp/ejabberd-authenticate-against-imap-with-perl/" href="http://allgnu.com/xmpp/ejabberd-authenticate-against-imap-with-perl/" target="_blank">http://allgnu.com/xmpp/ejabberd-authenticate-against-imap-with-perl/</a> и тд - это всё наглый реврайт и копи-паста оригинальных скриптов, которые содержат ошибки.
Отвлекусь немного от темы и скажу, что довольно давно заметил, что на популярном ресурсе habrahabr.ru очень много статей-реврайтов чего-то англоязычного с бездумной копи-пастой содержащей порой довольно глупые ляпы. Но благодаря активному "продвижению" хабр пользуется незаслуженным авторитетом. В данном случае очень чётко прослеживается работа цитаты: "Ложь, повторенная тысячу раз, становится правдой." тов. Пауля Йозефа Геббельса, чей глубокий вклад в вопросы про-германской пропаганды во время 2-й Мировой Войны вряд ли подлежит сомнению, учитывая тот факт, что отголоски его противо-советской деятельнсти сегодня в нежно лелеятся в российских СМИ.
Итак время для опытов в какой-то момент кончилось и я подзабыл об этом сервисе, но не прошло и года, как я обнаружил раскуроченные скрипты и конфиг на сервере и решил таки добить этот вопрос раз и навсегда. Результатом стали очередные выкапывания реликтов в интернете, но таки нашёлся скрипт на гитхабе, который тупо говорил, что "да это валидный юзер" - то есть отвечал этому самому Ejabberd в правильном формате, что мне и требовалось.
Далее на его основе был запилен уже полноценный скрпит скрипт авторизации, но на сей раз я решил не заморачиваться с обращением к imap-серверу а напрямую стучаться в mysql-бд, в которой хранятся логины и хэши паролей. Алгоритм генерации пароля я знал как воспроизвести, так что итоговый скрипт получился на редкость простым.
Ниже приведён скрипт авторизации, всю логигку, обрабатывающую сравнение логин-паролей в с правильными значениями, я умышленно опустил, здесь свобода действий, что и подразумевает такой метод авторизации.
#!/usr/bin/perl
use Unix::Syslog qw(:macros :subs);
my $domain = $ARGV[0] || "eltechs.com";
while(1)
{
# my $rin = '',$rout;
# vec($rin,fileno(STDIN),1) = 1;
# $ein = $rin;
# my $nfound = select($rout=$rin,undef,undef,undef);
my $buf = "";
syslog LOG_INFO,"waiting for packet";
my $nread = sysread STDIN,$buf,2;
do { syslog LOG_INFO,"port closed"; exit; } unless $nread == 2;
my $len = unpack "n",$buf;
my $nread = sysread STDIN,$buf,$len;
my ($op,$user,$host,$password) = split /:/,$buf;
#$user =~ s/\./\//og;
my $jid = "$user\@$domain";
my $result;
syslog(LOG_INFO,"request (%s)", $op);
SWITCH:
{
$op eq 'auth' and do
{
$result = 1;
},last SWITCH;
$op eq 'setpass' and do
{
$result = 1;
},last SWITCH;
$op eq 'isuser' and do
{
# password is null. Return 1 if the user $user\@$domain exitst.
$result = 1;
},last SWITCH;
$op eq 'tryregister' and do
{
$result = 1;
},last SWITCH;
$op eq 'removeuser' and do
{
# password is null. Return 1 if the user $user\@$domain exitst.
$result = 1;
},last SWITCH;
$op eq 'removeuser3' and do
{
$result = 1;
},last SWITCH;
};
my $out = pack "nn",2,$result ? 1 : 0;
syswrite STDOUT,$out;
}
closelog;
Спешу заметить, что сервер, при использовании внешнего скрипта авторизации работает в несколько неполном режиме. - в shared roster он не может отобразить список ВСЕХ пользователей, только тех, что в текущий момент онлайн, а, например, в случае использования LDAP, внутреннего мех-ма, или sql-базы Ejabbrd такой список изобразить может.