воскресенье, 16 октября 2016 г.

C#: Двухфакторная аутентификация без смартфона

Возникла тут интересная проблема. Если вкратце, то одна из "программ-от-знакомства-с-которыми-нельзя-отказаться" после смены версии вдруг вообразила себя крутой финансовой системой и затребовала двухфакторную аутентификацию. Не знаю, чем уж там разработчики соблазнили заказчиков, но теперь после ввода логина-пароля эта чудесная софтина показывает некий QR-код и предлагает пользователю установить на смартфон Google Authenticator, которому надо скормить этот её QR-код, чтобы этот GA начал генерировать PIN-коды, которые теперь будут дополнительно требоваться при входе. Короче, если хочешь поработать, покупай смартфон.

Меня, гордого пользователя Samsung SGH-X100 с периодически отваливающимся от старости аккумулятором, такое положение дел устроить никак не могло. Поэтому пришлось познакомиться с этим Google Authenticator-ом поближе. Как я понял, механизм тут такой.

Пусть есть клиент и сервер, и этот сервер хочет предоставить клиенту дополнительную защиту от взлома, даже если у этого самого клиента уведут пароль. Сервер предоставляет клиенту при первом входе некое уникальное число X0, которое клиент запоминает у себя и бережно хранит.

Когда клиент хочет аутентифицироваться на сервере, он берёт это число X0, текущее время T и применяет к ним описанную в стандарте детерминированную функцию F(X0, T), которая на выходе выдаёт PIN-код, передаваемый серверу. Сервер, в свою очередь, проделывает у себя ту же операцию, сравнивает полученный и рассчитанный PIN-коды и, если они совпадают, позволяет клиенту работать.

Как видим, алгоритм сильно зависит от того, насколько хорошо синхронизированы часы на сервере и у клиента. Чтобы упростить задачу, в алгоритме используется не количество, например, миллисекунд, прошедших с начала эпохи UNIX, а количество 30-секундных интервалов. То есть, допускается разбег между часами клиента и сервера в пределах 30 секунд.

Уникальное же число X0 как раз и предоставляется сервером клиенту в виде картинки с QR-кодом - видимо, для пущей безопасности.

Вооружившись этими знаниями, написал простенькую программку, используя которую можно считать этот QR-код с экрана и получать PIN-коды для двухфакторной аутентификации. Ну, то есть, хрен бы я чего написал, если бы не поиск Google, подсказки http://stackoverflow.com и волшебная библиотека MessagingToolkit.QRCode.dll.

Вот тут научился работать с получением всемирного координированного времени по протоколу NTP:
http://stackoverflow.com/questions/1193955/how-to-query-an-ntp-server-using-c

Вот тут рассказали, как получить снимок части экрана:
http://stackoverflow.com/questions/5049122/capture-the-screen-shot-using-net

Вот тут научили работать с base 32:
http://stackoverflow.com/questions/641361/base32-decoding

Вот тут нашелся рабочий код для Google Authenticator:
http://stackoverflow.com/questions/6421950/is-there-a-tutorial-on-how-to-implement-google-authenticator-in-net-apps

Вот тут (кажется) скачал библиотеку MessagingToolkit.QRCode.dll:
http://osdn.net/projects/sfnet_qrreader/downloads/MessagingToolkit.QRCode.dll/

Ну и, собственно, сам результат:

Программа

Исходники

UPD 2017-01-20: Как оказалось, история не закончилась. Нашлись QR-коды, на которых MessagingToolkit сбоит. Например, если QR-код содержит картинку-логотип. Пришлось перетащить проект на другую библиотеку, ZXing.NET

Программа (ZXing)

Исходники (ZXing)

А тут - небольшая памятка, как этой библиотекой пользоваться.

четверг, 13 октября 2016 г.

Python: проблема с подключением к MSSQL при наличии триггера входа

История такова. Жил-был под линуксом скрипт, написанный когда-то на Python 2.7 и использующий pymssql, который успешно подключался к Microsoft SQL Server. В один прекрасный момент подключаться он перестал с ошибкой:
Logon failed for login 'huhmuh' due to trigger execution. DB-Lib error message 20018, severity 14:
General SQL Server error: Check messages from the SQL Server
DB-Lib error message 20002, severity 9:
Adaptive Server connection failed

Расследование выявило интересные вещи. Оказывается, pymssql использовал FreeTDS, который, собственно, и заглючил. Обмен FreeTDS можно подслушать примерно так:
import os
import pymssql

os.environ['TDSDUMP'] = 'stdout'

conn = pymssql.connect(host='mySQLserver', user='huhmuh', password='', charset='UTF-8', database='master', appname='my Application')
conn.close()
Анализ дампа выявил, что используется FreeTDS версии 0.91. Обновление до версии 1.00 не помогло.

При этом в соседнем каталоге лежит другая программа, написанная на C, которая через unixODBC получает данные с того же сервера без всяких проблем. Дальнейшие раскопки показали, что этот самый unixODBC взаимодействует с сервером через Microsoft SQL Server Native Client.

В конце концов оказалось, что на сервере разработчики добавили триггер входа. При наличии этого триггера FreeTDS не может нормально подключиться к базе данных.

Как перенацелить pymssql на использование нативного клиента, найти не удалось, поэтому пришлось перетаскивать скрипт на pyodbc. Тут тоже нашлась пара подводных камней. Самый крупный из которых - ошибка при выполнении пакета команд в одном запросе: Error: No results. Previous SQL was not a query. В этом случае виноваты, во-первых, всякие сообщения сервера "1 rows affected", которые легко подавить командой SET NOCOUNT ON в начале запроса, и, во-вторых, результаты работы команд print, причудливо раскиданных разработчиками внутри хранимых процедур, используемых в нашем пакете. Эти последние подавить нельзя, поэтому пришлось часть пакетов переписывать в виде группы отдельных запросов.

В общем, тот ещё опыт.

четверг, 8 сентября 2016 г.

Windows XP: переподключить удалённый компьютер к домену

Дано: компьютер по имени myWS под управлением WinXP, который пашет в другом городе с отключенной учёткой локального админа, но зато в домене myDOMAIN. Для удалённого доступа на этом компьютере установлен Remote Administrator с - какое счастье!- парольной авторизацией. В какой-то момент компьютер теряет связь с доменом, выдавая на всякие runas ошибку "Не удалось установить доверительные отношения между этой рабочей станцией и основным доменом" (The trust relationship between this workstation and the primary domain failed). Как-то надо привести его в чувства.

Решение такое.

1. При помощи Remote Administrator-а заходим на этот компьютер в режиме telnet.

2. Из Windows XP Support Tools закидываем на этот компьютер программу netdom.exe

3. В командной строке выполняем команды:
netdom remove myWS /Domain:myDOMAIN /UserD:myDOMAIN\Admin /PasswordD:***
netdom join myWS /Domain:myDOMAIN /UserD:myDOMAIN\Admin /PasswordD:***

3'. Вроде как есть альтернативный вариант - сбросить учётку компьютера на контроллере (на сервере в контекстном меню "Reset account") и на самом компьютере командой:
netdom reset myWS /Domain:myDOMAIN /UserO:myDOMAIN\Admin /PasswordO:***
но проверить его не удалось: всё заработало и так.
(кстати, забавно, почему там UserO, а не UserD ?)

Ну и в качестве бонуса - памятка, как посмотреть адреса контроллеров домена:
nslookup
> set type=all
> _ldap._tcp.dc._msdcs

UPD 2018-08-03: Как быть в Windows 7.

Выяснилось, что для того, чтобы в системе появился netdom.exe, надо проделать некоторую работу:

1. Установить некие Remote Server Administration Tools for Windows 7.

2. После установки включить соответствующую компоненту. Для этого идём в Панель управления -> Программы -> Программы и компоненты -> Включение или отключение компонентов Windows -> Средства удаленного админинстрирования сервера -> Средства администрирования ролей -> Средства доменных служб ActiveDirecotry и служб ActiveDirectory -> Средства доменных служб ActiveDirectory и ставим галочку напротив пункта "Оснастки и программы командной строки доменных служб ActiveDirectory". Тогда в c:\windows\system32 появляется долгожданная утилита netdom.exe.

Такое подозрение, что всё это через командную строку RAdmin-а проделать не удастся. Так что, видимо, надо заранее закидывать на борт эти самые "Средства удаленного администрирования сервера" - на всякий случай.

понедельник, 29 августа 2016 г.

Brother HL-2132: Сброс счетчика тонера

Неожиданно принтер Brother HL-2132 решил, что он тоже вертолёт, и отказался печатать. Горит зелёная лампочка напротив слова "Toner" - и всё, на задания не откликается, бумагу не подхватывает. По-хорошему, ему бы надо заменить картридж, но в это время суток его сложно достать. Решение нашлось такое:

1. Открыть крышку принтера и оставить её в таком положении до дальнейших распоряжений.
2. Выключить принтер.
3. Нажать кнопку "Go" и, удерживая её, включить принтер. Все индикаторы принтера должны светиться.
4. Отпустить кнопку "Go".
5. Нажать кнопку "Go" два раза.
6. Подождать.
7. Нажать кнопку "Go" пять* раз.
8. Индикатор тонера должен погаснуть.
9. Индикатор бумаги должен либо гореть, либо мигать.
10. Закрыть крышку принтера. Должен остаться гореть только индикатор "Ready".
11. Выключить принтер и включить его снова.

* - некоторые источники, правда, утверждают, что шесть раз, но мне помогло пять.

четверг, 11 августа 2016 г.

IE: удаление учетных данных авторизации на прокси

Пусть у нас есть Internet Explorer 8, который ходит в интернет через прокси, и этот прокси требует авторизацию для того, чтобы решить, пропускать ли через себя кого попало. И пусть мы сначала ввели какие-то учетные данные, и теперь браузер не запрашивает нас, а пытается с этими сохраненными учетными данными авторизовываться.

Чтобы его заставить забыть эти учетные данные, есть некий рецепт:

1. В командной строке выполнить команду:
control userpasswords2

2. В появившемся окне перейти во вкладку "Дополнительно", нажать кнопку "Управление паролями" и удалить сохраненный пароль для прокси-сервера.

firefox: разрешить неподписанные расширения

Оказывается, просто:

1. В адресной строке вбить about:config

2. В открывшемся списке опций найти xpinstall.signatures.required и сделать её false

пятница, 17 июня 2016 г.

MSSQL: подслушать обмен клиентского приложения с сервером

На самом деле иногда бывает сильно интересно, какие sql-запросы посылает клиентское приложение. Если дело происходит под линуксом, через unixodbc, то там легко - приписываем в odbcinst.ini (который находится где-то то ли в /etc, то ли в /usr/local/etc) пару строк:
[ODBC]
Trace=Yes
TraceFile=/home/huh-muh/sql.log
и в своем домашнем каталоге лицезреем весь обмен. Под windows, думалось, должны быть аналогичные механизмы, но всё оказалось чуть-чуть сложнее.

Во-первых, есть хорошая статья о том, как включить трассировку для клиентов SQL Servera. Вкратце:

1. Стаскиваем и разворачиваем самораспаковывающийся архив по этой ссылке.

2. В реестре находим (или создаём) ветку
HKEY_LOCAL_MACHINE\Software\Microsoft\BidInterface\Loader
в которой создаём ключ с именем ":Path" (двоеточие тут вроде как существенно) и значением:
"%SYSTEMROOT%\Microsoft.NET\Framework\v2.0.50727\ADONETDiag.dll", если хотим трассировать ADO.NET
или
"%SYSTEMROOT%\SYSTEM32\msdaDiag.dll", если хотим трассировать прочих клиентов (mdac, dao и т.п.)

3. в кучке каталогов из п.1 находим MOF_Files и в нем выполняем команду:
mofcomp all.mof (а чего мелочиться-то)
эта команда компилирует и регистрирует трассировочные схемы (без понятия, что это такое). Результат этой регистрации можно проверить командой:
Logman query providers

4. Затем забираемся в корневой каталог этого распакованного архива и в нем выполняем команду:
Logman start MyTrace -pf "control_GUID_files/ctrl.guid.all" -o Out.etl -ets
в результате чего в этом каталоге создастся файл Out.etl, содержащий интересующую нас трассировку.
Тут есть маленькая тонкость. Если эта команда файл ctrl.guid.all не увидит (ну там, путь неправильно указали, или ещё что-то), она сильно не огорчится, но ничего ловить не будет. Поэтому надо внимательно присмотреться, пишет ли она в момент запуска раздел "Поставщики:" , в котором перечисляет guid-ы механизмов, которые будет отслеживать.

5. Запускаем приложение, которое хотим отследить, и пока мы там работаем, в файле из п.4 копится инфа в неудобоваримом виде.

6. Когда по нашему мнению наловится достаточно информации, завершаем трассировку командой:
Logman stop MyTrace -ets

7. Наконец, перегоняем информацию из этого Out.etl во что-то более текстовое командой:
TraceRPT /y Out.etl

Правда, по изучению результата трассировки меня постигло разочарование. Нет, оно там рисует строки подключения, отображает факт обмена с сервером, показывает какие-то адреса и непонятные числа, но и только. Ни одного sql-запроса в явном виде я там не обнаружил.

Этот огорчительный момент вернул меня к старому доброму подслушиванию сети. Обычно я использую Packetyzer, а тут наткнулся на Microsoft Network Monitor 3.4 и решил попробовать его. Там, правда, было написано, что нужно ещё установить дополнительно NMDecrypt, но, по-моему, он ловит всё и так.(*) Для более новых систем есть Microsoft Message Analyzer, но у меня WinXP, так что тут без вариантов. Правда, результатом подслушивания является мешанина данных в кодировке ANSI и UCS-2e, но с этим приходится мириться, главное, что хоть какие-то обрывки получаются в читабельном виде.

* на одной машине потребовалось ещё доустановить Network Monitor Parsers и выбрать в меню Tools - пунт Options - вкладка Parser profiles профиль Default, чтобы программа перестала жаловаться "unable to build conversation"