На днях столкнулся с интересной задачей. В компании несколько лет назад для создания скриншотов была написана программа, написана на делфи, программа очень удобная, но есть ряд минусов - работает только под windows и аутентификация в программе происходит путем подключения к почтовому серверу компании и проверки корректности данных через pop3 протокол. Причем имя почтового сервера жестко вшито в сам exe файл. Собственно сама программа представляет из себя один монолитный exe файл без каких либо настроек и dll, забегая немного вперед, скажу что это помогло нам решить проблему. Сам разработчик уже уволился, а так как программу он писал в свое свободное время и никто ему за это не платил, то и исходники программы при увольнении он не захотел оставлять. В принципе тут я его понимаю.
И все было хорошо, до того момента, пока мы не приняли решение о переходе на gmail. А об этой программе как то особо никто и не вспоминал, а точнее о том, что после изменения MX записей и перевода почты на gmail, аутентификация перестанет работать. Можно конечно найти альтернативы, например, тот же monosnap, но во-первых monosnap не такой удобный и более медлительный, во-вторых все уже привыкли к этой программе. А в третьих это не наш метод. Мы ведь не ищем легких путей?
Итак, первое, что пришло в голову это попробовать поискать в самом exe файле имя почтового сервера, на который происходит подключение при аутентификации пользователя. И как ни странно, само имя сервера нашлось без проблем
Пробуем поменять имя сервера на pop.gmail.com. Но тут возникает первая проблема. Новое имя сервера должно совпадать по длине с текущим, иначе изменится размер самого exe файла. Ну, если быть точным, то так как у каждой переменной есть адрес, и при изменении длины имени самой переменной, изменятся и все адреса. Именно поэтому при запуске нам и выдается подобная ошибка.
> dir screenshots.exe -c Volume in drive C is SSD Volume Serial Number is 4CA3-E2F5 Directory of c:\Utils 05.12.2014 21:54 2 593 280 screenshots.exe Directory of c:\Utils 1 File(s) 2 593 280 bytes 0 Dir(s) 65 791 164 416 bytes free
> dir screenshots.exe -c Volume in drive C is SSD Volume Serial Number is 4CA3-E2F5 Directory of c:\Utils 14.02.2015 20:38 2 593 273 screenshots.exe Directory of c:\Utils 1 File(s) 2 593 273 bytes 0 Dir(s) 65 790 615 552 bytes free
Для этого создаем в домене CNAME запись на pop.gmail.com, например scrshots.example.net, которая по длине совпадает с secure.emailsrvr.com. Смотрим размер exe файла
> dir screenshots.exe -c Volume in drive C is SSD Volume Serial Number is 4CA3-E2F5 Directory of c:\Utils 14.02.2015 20:56 2 593 280 screenshots.exe Directory of c:\Utils 1 File(s) 2 593 280 bytes 0 Dir(s) 65 792 364 544 bytes free
Как видим, размер совпадает с оригинальным exe, но md5 сумма при этом отличается, но это вполне нормально и ожидаемо.
original screenshots.exe
> md5sum screenshots.exe 542c00e88ddaa13bd90e67447e3e02c5 *screenshots.exe
modified screenshots.exe
> md5sum screenshots.exe ff676855b4374ef0cc7746fb8bfcd8ee *screenshots.exe
Теперь у нас exe запускается без проблем. И казалось бы, счастье уже у нас в руках, но не тут то было. После длительной паузы получаем ошибку Wrong login/password
Но мы так просто не здаемся, запускаем tcpdump и смотрим, что же там происходит на самом деле. По логам видно, что наша программа обращается на 110 порт! В то время, как gmail работает только по pop3s (995 порт).
# nmap -v -A pop.gmail.com Starting Nmap 6.40 ( http://nmap.org ) at 2015-02-14 19:28 UTC NSE: Loaded 110 scripts for scanning. NSE: Script Pre-scanning. Initiating Ping Scan at 19:28 Scanning pop.gmail.com (74.125.136.108) [4 ports] Completed Ping Scan at 19:28, 0.03s elapsed (1 total hosts) Initiating Parallel DNS resolution of 1 host. at 19:28 Completed Parallel DNS resolution of 1 host. at 19:28, 0.00s elapsed Initiating SYN Stealth Scan at 19:28 Scanning pop.gmail.com (74.125.136.108) [1000 ports] Discovered open port 993/tcp on 74.125.136.108 Discovered open port 587/tcp on 74.125.136.108 Discovered open port 25/tcp on 74.125.136.108 Discovered open port 995/tcp on 74.125.136.108 Discovered open port 465/tcp on 74.125.136.108 ... ... ... 995/tcp open ssl/pop3 Google Gmail pop3d (m17mb115231066wlg) |_pop3-capabilities: RESP-CODES LOGIN-DELAY(300) TOP USER EXPIRE(0) X-GOOGLE-RICO UIDL SASL(PLAIN XOAUTH2) | ssl-cert: Subject: commonName=pop.gmail.com/organizationName=Google Inc/stateOrProvinceName=California/countryName=US | Issuer: commonName=Google Internet Authority G2/organizationName=Google Inc/countryName=US | Public Key type: rsa | Public Key bits: 2048 ... ...
Даже если перенаправить все запросы со 110 порта на 995, то программа работать не будет, так как она не понимает ssl. И тут нам на помощь приходит stunnel. Мы делаем небольшой финт и направляем программу не напрямую к gmail.com, а на наш сервер, где на 110 порту как раз и будет запущен stunnel.
Установка тривиальная
# yum install stunnel
После этого создаем конфигурационный файл gmail-pop3s-client.conf в папке /etc/stunnel/ с таким содержимым
# cat /etc/stunnel/gmail-pop3s-client.conf [gmail-pop3s] client = yes accept = 176.198.xxx.xxx:110 connect = pop.gmail.com:995
Так как stunnel будет слушать порт ниже 1024, то его необходимо запускать от root.
# stunnel /etc/stunnel/gmail-pop3s-client.conf
При запуске в консоль ничего не выводится, но в /var/log/secure должны появится следующие строки.
Feb 14 19:46:18 web-srv01 stunnel: LOG5[799:140339650004928]: stunnel is in FIPS mode Feb 14 19:46:18 web-srv01 stunnel: LOG5[799:140339650004928]: stunnel 4.29 on x86_64-redhat-linux-gnu with OpenSSL 1.0.1e-fips 11 Feb 2013 Feb 14 19:46:18 web-srv01 stunnel: LOG5[799:140339650004928]: Threading:PTHREAD SSL:ENGINE,FIPS Sockets:POLL,IPv6 Auth:LIBWRAP Feb 14 19:46:18 web-srv01 stunnel: LOG5[799:140339650004928]: 500 clients allowed
На всякий случай делаем пару базовых проверок, чтобы убедится, что туннель поднялся
# netstat -lanp | grep 110 tcp 0 0 176.198.xxx.xxx:110 0.0.0.0:* LISTEN 12918/stunnel
# lsof -n -i tcp:110 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME stunnel 12918 root 12u IPv4 13882741 0t0 TCP 176.198.xxx.xxx:pop3 (LISTEN)
Отлично, теперь проверяем через telnet с удаленного сервера
# telnet 176.198.xxx.xxx 110 Trying 176.198.xxx.xxx... Connected to 176.198.xxx.xxx. Escape character is '^]'. +OK Gpop ready for requests from 176.198.xxx.xxx r195mb57795961wlb quit +OK Bye r195mb57795961wlb Connection closed by foreign host.
Отлично, это как раз то, что мы и хотели получить. Т.е. наша программа, как и прежде, обращается по «открытому» протоколу pop3 на 110й порт, а уже stunnel делает всю черную работу за нас.
Вот так вот, применив немного смекалки с linux, мы заставили программу продолжить свою работу.
Как мы все знаем, привязку к портам < 1024 может делать только суперпользователь root. Если мы попробуем запустить наш туннель от обычного пользователя, то в логах увидим подобное сообщение
Feb 15 13:32:55 web-srv01 stunnel: LOG5[25450:140050890143680]: Threading:PTHREAD SSL:ENGINE,FIPS Sockets:POLL,IPv6 Auth:LIBWRAP Feb 15 13:32:55 web-srv01 stunnel: LOG5[25450:140050890143680]: 500 clients allowed Feb 15 13:32:55 web-srv01 stunnel: LOG3[25450:140050890143680]: Error binding gmail-pop3s to 176.198.xxx.xxx:110 Feb 15 13:32:55 web-srv01 stunnel: LOG3[25450:140050890143680]: bind: Permission denied (13)
Но на самом деле это ограничение можно и обойти. Для этого достаточно из под root выполнить следующую команду
# setcap cap_net_bind_service=ep /usr/bin/stunnel
После этого снова пробуем запустить наш туннель. Но перед этим необходимо указать путь к pid файлу, так как на дефолтный файл /var/run/stunnel.pid права есть только у root
# ls -la /var/run/stunnel.pid -rw-r--r-- 1 root root 6 Feb 12 14:46 /var/run/stunnel.pid
Итак, добавляем следующую строку в файл /etc/stunnel/gmail-pop3s-client.conf
pid = /home/gmail-stunnel/gmail.pid
И снова пробуем запустить
# lsof -n -i tcp:110 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME stunnel 25834 gmail-stunnel 12u IPv4 16489394 0t0 TCP 176.198.xxx.xxx:pop3 (LISTEN)
При это в логах увидим привычные для нас строчки
Feb 15 13:44:11 web-srv01 stunnel: LOG5[25828:139677113530304]: stunnel is in FIPS mode Feb 15 13:44:11 web-srv01 stunnel: LOG5[25828:139677113530304]: stunnel 4.29 on x86_64-redhat-linux-gnu with OpenSSL 1.0.1e-fips 11 Feb 2013 Feb 15 13:44:11 web-srv01 stunnel: LOG5[25828:139677113530304]: Threading:PTHREAD SSL:ENGINE,FIPS Sockets:POLL,IPv6 Auth:LIBWRAP Feb 15 13:44:11 web-srv01 stunnel: LOG5[25828:139677113530304]: 500 clients allowed