Авторизация Пользователей В Proftpd С Помощью Аккаунтов На Форуме

Недавно возникла необходимость запретить кому угодно вход на FTP-сервер, то есть заблокировать анонимные входы.

Но вести отдельную базу данных с пользователями FTP неудобно и тем более неэффективно.

Я подумал-подумал и решил авторизовать пользователей с помощью учетной записи на форуме, в моем случае SMF. Пользователи хранятся в таблице MySQL smf_members, имя пользователя — вmemberName, а пароль — в passwd. ProFTPD имеет хорошо документированную возможность получать учетные данные из PostgreSQL/MySQL. Но проблема в том, что форум хранит пароли в SHA1-хеше, а в начало пароля добавляется имя пользователя в нижнем регистре.

А ProFTPD ожидает, что из SQL-запроса к базе данных ему будет возвращен пароль в форматах MySQL PASSWORD(), Crypt или Plaintext. Разумеется, никакого SHA1 там нет, не говоря уже о том, что к паролю добавляется еще и имя пользователя.

Гуглил день, потом другой, все на это жалуются, но готового решения никто не предлагает. Что ж, наше дело не такое уж и сложное.

Итак, что мы имеем? Существует параметр SQLAuthTypes, указывающий метод шифрования пароля.

Нужного мне варианта нет, поэтому открываем исходники.

Последняя сборка RC была загружена с домашнего сайта ProFTPD, на этот раз proftpd-1.3.3rc3. Беглая проверка contrib/mod_sql.c и contrib/mod_sql.h показала, что существует механизм добавления в функцию собственных методов авторизации:

int sql_register_authtype(const char *name, modret_t *(*callback)(cmd_rec *, const char *, const char *));

Интересный.

Немного побегав по исходному коду, найдя достаточно примеров вызова этой функции (что странно, ничего из этого действительно недокументировано, а в Интернете 0 результатов по запросу «proftpd sql_register_authtype»), стало понятно, что это все сводится к созданию callback-функции, которая принимает в качестве параметров пароль в чистом виде и хеш, полученный из mysql, проверяет, соответствует ли пароль хешу, и возвращает результат проверки с возвратом.

Ну, это звучит легко.

Я создал собственный модуль contrib/mod_sql_auth_smf.c со следующим содержимым:

#include "conf.h" #include "mod_sql.h" #define MOD_SQL_AUTH_SMF_VERSION "mod_sql_smf/0.1" #if defined(HAVE_OPENSSL) || defined(PR_USE_OPENSSL) #include <openssl/evp.h> static modret_t *auth_smf(cmd_rec *cmd, const char *c_clear, const char *c_hash) { const EVP_MD *md; EVP_MD_CTX md_ctxt; unsigned char mdval[EVP_MAX_MD_SIZE]; char hash[EVP_MAX_MD_SIZE * 2]; int mdlen, i; OpenSSL_add_all_digests(); md = EVP_get_digestbyname("sha1"); if (!md) return ERROR_INT(cmd, PR_AUTH_BADPWD); EVP_DigestInit(&md_ctxt, md); EVP_DigestUpdate(&md_ctxt, c_clear, strlen(c_clear)); EVP_DigestFinal(&md_ctxt, mdval, &mdlen); for (i=0; i<mdlen; i++) { sprintf(hash+(i*2), "x", mdval[i]); } return strcmp(hash, c_hash) ? ERROR_INT(cmd, PR_AUTH_BADPWD) : HANDLED(cmd); } static int sql_auth_smf_init(void) { (void) sql_register_authtype("SMF", auth_smf); return 0; } #endif module sql_auth_smf_module = { /* Always NULL */ NULL, NULL, /* Module API version */ 0x20, /* Module name */ "sql_auth_smf", /* Module configuration directive table */ NULL, /* Module command handler table */ NULL, /* Module auth handler table */ NULL, /* Module initialization */ sql_auth_smf_init, /* Session initialization */ NULL, /* Module Version */ MOD_SQL_AUTH_SMF_VERSION };

В этом модуле, используя OpenSSL, я реализовал хеширование SHA1 чистого пароля, сверку с полученным хэшем и возврат результата проверки в mod_sql.c. Я скомпилировал его, не забыв добавить опцию для настройки --with-shared=mod_sql_auth_smf, изменил параметр в proftpd.conf SQLAuthTypes на SMF и добавил строку LoadModule mod_sql_mysql.c. Собрал, проверил, работает! Но только в том случае, если пользователь User1 с паролем PassWd22 указывает в качестве пароля user1PassWd22. Ну, это проще.

Найдя функцию обратного вызова в mod_sql.c, я изменил ее на такой вид:

mr = sah->cb(cmd, plaintext, ciphertext);

Так:

if(strcmp(sah->name, "SMF")==0) { int i; char namepasswd[106]; strncpy(namepasswd, cmd->argv[1], 25); for(i=0;namepasswd[i];i++) namepasswd[i]=tolower(namepasswd[i]); strncat(namepasswd, cmd->argv[2], 80); mr = sah->cb(cmd, namepasswd, ciphertext); } else mr = sah->cb(cmd, plaintext, ciphertext);

Вот и все, цель достигнута.

Если кого-то интересуют подробности настройки ProFTPD для работы с MySQL, тонкости компиляции, отвечу в комментариях.

Цель этой статьи – не дать полное пошаговое руководство с нуля, а кратко показать, как при небольшом знании C можно заставить ProFTPD авторизовать пользователей, используя учетные данные любого форума, блога и скоро.

Теги: #linux #Системное администрирование #C++ #MySQL #proftpd #smf

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.