Вот что рекламируют? Неужто именно то Хуйло-Хуйло?
Monthly Archives: Январь 2020
Выход из X через консоль (и желательно удаленно)
Долго искал, как выйти из X через консоль, и в мануалах ничего не мог найти. Почему-то это нигде толком не описано. Хотя, я думаю, завершить X на другой машине удаленно, имея под рукой только консоль/удаленный терминал, возникала не у меня одного.
И оказалось, что какого-то единственного универсального способа нет.
Runlevel или уровень запуска — это программная конфигурация системы, которая позволяет запускать только выбранную группу процессов на определенном этапе. Их до 10, но нас интересует уровень 3 — многопользовательский (консольный) режим, и уровень 5 (в Slackware — 4), многопользовательский графический режим, в котором X-server запускается по умолчанию.
Если система находится на уровне 4 (5), то способы как либо пришибить иксы могут не сработать, иксы перезапустятся. С уровня 3 можно запустить X-сервер вручную, для этого надо в консоли ввести (обычно) startx
. Если система на уровне 3, то иксы сравнительно легко прибить (см. ниже).
Визуально уровень запуска обычно определить легко. 3 — после загрузки ОС будет консольное приглашение ввести логин и пароль, например:
Welcome to Linux 4.4.14-smp (tty1)
wolfsсhanze login:
На уровне 4 (5) на экране будет предложение ввести логин/пароль, но уже в иксовой форточке.
Правда, некоторые не очень популярные дистрибутивы хитрят. Например, Puppy Slacko запускается на уровне 3, а X-сервер вызывает уже из своих инициализационных скриптов.
Можно проверить runlevel и в консоли/терминале:
runlevel
Вывод:
N 3
или
who -r
Вывод:
run-level 3 2020-01-28 07:12 last=S
Неверное, самый универсальный способ завершить работу X-server на лету и через консоль, это переключить runlevel. Команда должна быть выполнена от root
.
— Для Slackware:
init 3
— Для дистрибутивов с systemd:
systemctl isolate runlevel3.target
Вернуться в иксы.
Для Slackware:
init 4
— Для дистрибутивов с systemd:
systemctl isolate graphical.target
— Для Slackware:
1. Под root
запускаем mc
и идем в /etc
2. Ищем там файл inittab
и открываем его в редакторе.
3. Ищем строчки:
# Default runlevel. (Do not set to 0 or 6)
id:4:initdefault:
Они обычно в начале файла.
4. Меняем 4
на 3
и сохраняем файл. Если надо X при старте — меняем 3
на 4
. Если что, обычно в файле есть комментарий-подсказка (на буржуйском).
— Для дистрибутивов с systemd:
Чтоб X был выключен по умолчанию:
systemctl set-default runlevel3.target
Чтоб X по умолчанию был включен:
systemctl set-default multi-user.target
Опять же, повторюсь, стопроцентно это сработает только если X-server запущен вручную (или через скрипты), когда система находится в runlevel 3.
— Придушить X-сервер совсем:
killall Xorg
— Более аккуратно придушить иксы (для систем с systemd). Надо отправить команду завершения оконному менеджеру.
В общем виде:
systemctl stop display-manager.service
Вместо display-manager.service
подставляем свой оконный менеджер (наверное, не все поддерживают, но у меня systemd нет, так что не тестировал):
systemctl stop gdm
— Способ для xfce
:
xfce4-session-logout --logout --display :0.0
Корректно срабатывает только с локальной консоли. Удаленно может не работать.
В некоторых системах до сих пор работает старый способ переключиться в голую консоль, не завершая X-сервер.
Для этого надо нажать Ctrl+Alt+F2…F6 и вам откроется чистый терминал. На Ctrl+Alt+F7 обычно сидят сами иксы, и таким образом, можно к ним вернуться. А первый терминал (Ctrl+Alt+F1) иксы занимают под служебные нужды. В некоторых системах для выхода из иксов срабатывает такой способ:
1. Переключиться в первую консоль (Ctrl+Alt+F1)
2. Нажать Ctrl+C/Ctrl+Break
В некоторых системах для выхода из иксов может сработать комбинация Ctrl+Alt+Backspace
В системах, запускающихся в графическом runlevel по умолчанию, это можно использовать для перезагрузки графического окружения, если X зависли (как soft-restart в Windows 98). Так же можно использовать и команду killall Xorg
Об опасности использования eval в bash-скриптах.
В недавнем примере я допустил, что называется, «детский мат», грубую и далеко неочевидную новичку ошибку, о которой, впрочем, все знают. Но прописные истины неплохо повторять, так что не стоит кидаться на эту заметку с криками «боян!!!111пыщ».
Для кого-то может и боян, а кому-то это может быть неизвестно. Или вот я, зная в теории об ошибке, все равно ее допустил, и даже не сразу понял, что я так грубо наебался, и мой скрипт стал небезопасным от слова «совсем».
Имелся следующий набор данных в виде форматированного текста (таблицы в текстовом файле):
verb666,Misha Verbitsky,+415314499922,42,11:00-16:00
ktvs421,Vasiliy Kotov,+415314499966,77a,00:00-06:00
dkldn89,Dmitry Kaledin,+415314499949,65b,22:00-00:00
vfurry1,Veniamin Furman,+415314499900,99,12:20-19:25
tpunk56,Tolik Punkoff,+415314499911,59,00:00-11:00
Исходная задача состояла в том, чтобы раскидать вывод awk
в переменные bash, так, чтоб значение из соответствующей колонки таблицы попало в указанную переменную bash, и изначальный код был таким:
# ... цикл по строкам for TMPSTRING in $(cat "testfile.txt") do eval $(echo "$TMPSTRING"|awk -F "," '{print "LOGIN=" dq $1 dq; print "FULLNAME=" dq $2 dq; print "PHONE=" dq $3 dq; print"ROOM=" dq $4 dq; print "WORKTIME=" dq $5 dq}' dq='"') # ... делаем что-то с данными в переменных done # ...
Т.е. тут мы сначала получаем строчку текстового файла, передаем ее awk
, awk
, в свою очередь, возвращает строки:
...
LOGIN="vfurry1"
FULLNAME="Veniamin Furman"
PHONE="+415314499922"
ROOM="99"
WORKTIME="12:20-19:25"
...
Похоже на команду присваивания значений переменным. Да так оно и есть!
Далее, полученная строка передается команде eval
.
В чем опасность eval
? Да в том, что эта команда преобразует любую переданную ей строку в команду или команды bash, и немедленно их выполняет. Любую, абсолютно любую строку.
И тут возникает ошибка безопасности. Мы получаем данные из внешнего источника. Это может быть файл с внешнего носителя, или скачанный из Интернета. В нем в худшем случае, который всегда надо держать в голове, работая с внешними источниками данных, может содержаться все, что угодно. И злоумышленнику не надо получать доступ к скрипту, достаточно изменить входные данные:
verb666,Misha Verbitsky,+415314499922,42,11:00-16:00
ktvs421,Vasiliy Kotov,+415314499966,77a,00:00-06:00
dkldn89,Dmitry Kaledin,+415314499949,65b,22:00-00:00
vfurry1,Veniamin Furman `rm -rf ./testdir`,+415314499900,99,12:20-19:25
tpunk56,Tolik Punkoff,+415314499911,59,00:00-11:00
Тут, после Veniamin Furman
вписана команда удаления директории ./testdir
в текущем каталоге, а могло быть вписано и $HOME
или даже /
.
Строки, взятые в двойные кавычки, интерпретируются bash’ем не только, как строки с чисто тестовыми данными. В двойных кавычках можно и нужно сделать подстановку, если это возможно. А символы ``
(обратные кавычки) значат, что нужно выполнить команду внктри них, а потом подставить результат в то место, где указано выражение в обратных кавычках.
Таким образом, команда
FULLNAME="Veniamin Furman `rm -rf ./testdir`"
выполнится так:
1. В переменную записывается строка Veniamin Furman
2. Выполняется команда rm -rf ./testdir
3. Вывод команды дописывается в переменную
Соответственно, каталог ./testdir
в процессе будет удален.
Всегда держите в голове, что данные внутри eval$()
будут исполнены bash’ем, как код (команды оболочки).
1. Заменить двойные кавычки ("
) на одинарные ('
). Строки в одинарных кавычках будут интерпретированы просто как текстовые последовательности, и без всякого выполнения и изменения записаны в переменные, так как есть:
eval $(echo "$TMPSTRING"|awk -F "," '{print "LOGIN=" sq $1 sq;
print "FULLNAME=" sq $2 sq; print "PHONE=" sq $3 sq; print"ROOM=" sq $4 sq;
print "WORKTIME=" sq $5 sq}' sq="'")
Вывод:
vfurry1 Veniamin Furman `rm -rf ./testdir` +415314499900 99 12:20-19:25
2. Но и это еще не конец, злоумышленник может обмануть и одинарные кавычки, обернув свою команду в …одинарные кавычки:
vfurry1,Veniamin Furman '`rm -rf ./testdir`',+415314499900,99,12:20-19:25
Тогда выполнение будет происходить так. Когда очередь дойдет до FULLNAME
awk
добросовестно выведет:
FULLNAME='Veniamin Furman '`rm -rf ./testdir`'
eval
же это также добросовестно интерпретирует. В $FULLNAME
будет записано Veniamin Furman
, далее закроется одинарная кавычка, т.е. больше ничего в переменную писаться не будет, а далее выполнится rm -rf ./testdir
.
Решение таково — удалить или заменить на что-либо безобидное, одинарные кавычки из входной строки перед передачей ее awk
. Можно удалить sed
‘ом:
sed -e 's~'\''~~g'
Код целиком:
eval $(echo "$TMPSTRING"|sed -e 's~'\''~~g'|awk -F "," '{print "LOGIN=" sq $1 sq;
print "FULLNAME=" sq $2 sq; print "PHONE=" sq $3 sq; print"ROOM=" sq $4 sq;
print "WORKTIME=" sq $5 sq}' sq="'")
Вывод зловредной сткроки будет:
vfurry1 Veniamin Furman `rm -rf ./testdir` +415314499900 99 12:20-19:25
Файлы:
maketestdir
— скрипт для создания тестовых каталогов.
testfile.txt
— Тестовый файл. Ошибку безопасности можно обойти, заменив двойные кавычки одинарными в коде.
testfile2.txt
— Замена двойных кавычек одинарными не поможет. Необходимо дополнительно удалять одинарные кавычки во входной строке.
wrongcode
— файл без исправлений кода (в коде используются двойные кавычки)
wrongcode2
— только первое исправление (в коде используются одинарные кавычки)
goodcode
— окончательно исправленный код.
Вызов скриптов: <скрипт> <тестовый файл>
Например:
wrongcode testfile2.txt
Отключение всплывающих уведомлений в Firefox.
А заодно и другой лишней ерунды вроде доступа к микрофону или запроса геолокации.
1. Заходим в Инструменты —> Настройки
2. Переходим на вкладку «Приватность и защита»
Или сразу пишем в строке адреса: about:preferences#privacy
3. Мотаем вниз до раздела Разрешения:
4. Жмем кнопку «Параметры» напротив уведомлений, очищаем список, если в нем что-то есть, и ставим галочку Блокировать новые запросы на отправку вам уведомлений
5. Жмем Сохранить изменения
6. ФАНФАРЫ!
Таким же образом можно поступить с местоположением, камерой и микрофоном.
Записать вывод awk в несколько переменных bash
Или как раскидать результат работы awk по нескольким переменным.
Предположим, у нас есть некоторая таблица в виде файла CSV с набором полей, например таких Login,FullName,Phone,Room,WorkTime
и разделителем полей ,
(запятая):
verb666,Misha Verbitsky,+415314499922,42,11:00-16:00
ktvs421,Vasiliy Kotov,+415314499966,77a,00:00-06:00
dkldn89,Dmitry Kaledin,+415314499949,65b,22:00-00:00
vfurry1,Veniamin Furman,+415314499900,99,12:20-19:25
tpunk56,Tolik Punkoff,+415314499911,59,00:00-11:00
Нужно вытащить из нее некоторые данные, и далее как-либо обработать. Вытащить данные можно с помощью awk
, используя оператор print
, но возникает вопрос, как передать данные обратно в bash.
Предположим, что заголовок удален, в файле остались только данные.
В bash есть встроенная команда eval
, преобразующая переданную ей строку в команду или набор команд оболочки, и запускающая ее на выполнение. Этим и воспользуемся.
1. Организуем цикл, в котором будем производить обработку данных:
IFS_=$IFS
IFS=$'\n'
for TMPSTRING in $(cat "demotable.txt")
do
#тут будет код
done
IFS=$IFS_
Перед циклом я подправил переменную $IFS
содержащую глобальные разделители, в нее, в частности, «смотрят» операторы циклов, чтобы определить, где начинается следующий элемент. По умолчанию переменная $IFS
содержит пробел, табуляцию и перевод строки, но поскольку у нас есть данные с пробелом, то это не подходит, цикл будет работать неверно. Потому сохраняем старое значение во временную переменную, устанавливаем новое значение в перевод строки (\n
). После цикла возвращаем значение на место.
В цикле организуем разбор данных:
echo "$TMPSTRING"|awk -F "," '{print "LOGIN=" $1; print "FULLNAME=" $2
print "PHONE=" $3; print "ROOM=" $4; print "WORKTIME=" $5 }'
Если запустить скрипт сейчас, то он выведет следующее:
LOGIN=verb666
FULLNAME=Misha Verbitsky
PHONE=+415314499922
ROOM=42
WORKTIME=11:00-16:00
Т.е. уже похоже на присваивание значений переменным bash, но есть проблема. Если мы сейчас скормим вывод awk
eval
‘у, то получим ошибку, например такую:
./awk2vars01: line 8: Verbitsky: command not found
А если бы и не получили, то в переменных могла бы оказаться всякая ерунда, строки необходимо экранировать кавычками.
awk print
и вывод кавычкиКавычки для оператора print
awk
являются служебными символами, в двойные кавычки берутся строковые литералы, т.е. те строки, которые нужно вывести без изменений, как например, "LOGIN="
в коде выше, а в одинарные — вся программа awk
. Экранирование (\"
или \'
) в операторе print
приведет к ошибке.
Решение — завести внутреннюю переменную awk
, содержащую кавычку, и печатать ее в нужном месте:
echo "$TMPSTRING"|sed -e 's~'\''~~g'|awk -F "," '{print "LOGIN=" sq $1 sq; print "FULLNAME=" sq $2 sq; print "PHONE=" sq $3 sq; print"ROOM=" sq $4 sq; print "WORKTIME=" sq $5 sq}' sq="'"
Поскольку данные строки далее будут переданы в eval
и обработаны как команды оболочки, то необходимо позаботиться о безопасности, и использовать только одинарные кавычки, а также удалять одинарные кавычки из входных строк, при передаче их awk:
Об опасности использования eval в bash-скриптах. Копия
Вывод:
LOGIN='verb666'
FULLNAME='Misha Verbitsky'
PHONE='+415314499922'
ROOM='42'
Теперь можно обернуть все это в eval
, чтобы раскидать результат работы awk
по переменным.
eval $(echo "$TMPSTRING"|sed -e 's~'\''~~g'|awk -F "," '{print "LOGIN=" sq $1 sq; print "FULLNAME=" sq $2 sq; print "PHONE=" sq $3 sq; print"ROOM=" sq $4 sq; print "WORKTIME=" sq $5 sq}' sq="'")
В демо-скрипте я просто вывожу данные на консоль, в реальном скрипте, что понятно, можно делать обработку данных в переменных bash.
echo "Login: $LOGIN"
echo "Full name: $FULLNAME"
echo "Phone: $PHONE"
echo "Room: $ROOM"
echo "Work time: $WORKTIME"
Вывод:
Login: verb666
Full name: Misha Verbitsky
Phone: +415314499922
Room: 42
Work time: 11:00-16:00
...
Скрипты полностью можно посмотреть на GitHub
Получение строки с определенным номером из файла в Linux
sed
sed -n 5p /path/to/file
Получить 5 строку из текстового файла с путем /path/to/file
awk
awk 'NR == 5' /path/to/file
На мой взгляд sed
‘ом несколько проще, потому что не нужны лишние движения для подстановки переменных shell/bash-скрипта:
TEXTFILE="/etc/group"
STRNO=5
TMPSTRING=`sed -n "$STRNO"p "$TEXTFILE"`
Почти межлокальное
Герман Grep
(из классификатора «Эсхатологические мутанты»)
Open-AL library for Slackware (библиотека OpenAL для Slackware)
Исходники: openal-soft-1.17.1.tar.bz2
SlackBuild: OpenAL.tar.gz
Он, кстати, почему-то отрабатывает не до конца, библиотеку собирает, а готовый пакет нет.
Готовый пакет: openal-soft-1.17.1.txz
libpng-12 legacy for slackware (libpng-12 legacy для Slackware)
Фразочка
базар-квазар
Подключение Riseup VPN в Windows XP
Приходится кое-где сидеть на старой машине с XP (хорошо, не с 2000), а безопасно посидеть в интернете хочется. Так что от склероза, и мало ли кому надо будет. Аналогичным образом можно подключить на XP и VPN от calyx.net
1. Необходимо установить OpenVPN для Windows XP:
Скачать с официального сайта:
— Версия x86 (32-разрядная)
— Версия x64 (64-разрядная)
Копии, на случай, если с официального сайта пропадет:
— Версия x86 (32-разрядная)
— Версия x64 (64-разрядная)
2. Далее, качаем конфиги:
Для Riseup:
— C Mega.nz
— C Google.Drive
Для Calyx:
— C Mega.nz
— C Google.Drive
3. Конфиги необходимо распаковать в каталог для файлов конфигурации OpenVPN (C:\Program Files\OpenVPN\config
)
Получение ключей для VPN сделано обычным BAT-файлом, без проверок fingerprint’а сертификата провайдера, а в wget
, которым все и выкачивается, указан ключ --no-check-certificate
, так что все это дело не сильно секурно.
1. Скачиваем архив с GitHub
На всякий случай копия на Google.Drive и Mega.nz
2. Распаковываем архив в отдельный каталог, пусть будет C:\GetKeys
3. Запускаем командную строку (Пуск —> Выполнить и вводим cmd
)
4. Переходим в каталог:
С:
cd \GetKeys
5. Запускаем получение ключей.
Для Riseup:
getkeys.bat riseup.net
Для Calyx, соответственно:
getkeys.bat calyx.net
После того, как батник отработает, в C:\GetKeys
образуется подкаталог data
с подкаталогами провайдеров. Из C:\GetKeys\data\riseup.net
надо скопировать в C:\Program Files\OpenVPN\config
файлы cacert.pem
и openvpn.pem
для Riseup VPN, а для Calyx из C:\GetKeys\data\calyx.net
те же файлы, переименовав их, соответственно как cacert_calyx.pem
и openvpn_calyx.pem
. Или можете отредактировать конфиг calyx-net.ovpn
и задать там любые желательные имена.
Теперь можно запускать OpenVPN GUI, выбирать, щелкнув по иконке в трее, нужный конфиг, и соединяться.
До соединения:
Riseup:
Получение ключей и сертификатов для Riseup VPN и calyx.net в Windows.
Без использования клиента Riseup или Bitmask для Calyx.
Наскриптил тут утилитку, которая выкачивает ключи для Riseup VPN копия, а заодно уж и для calyx копия под Windows. Изначально писалось для XP, т.к. оказалось, что официальный клиент Riseup в XP не работает, а Calyx вообще использует Bitmask, которого для винды не предвидится.
Моя утилита тоже, как оказалось, не всегда работает в XP, но для XP я таки придумал, как выгрузить ключи и подключиться к Riseup, но об этом в другой раз.
Так что получилась такая утилита, чтобы подключаться к Riseup и Calyx не качая официального клиента. Пусть уж будет.
1. Ставим OpenVPN
2. Качаем виндоконфиги:
Для Riseup:
— C Mega.nz
— C Google.Drive
Для Calyx:
— C Mega.nz
— C Google.Drive
3. Распаковываем конфиги в каталог конфигов OpenVPN (Обычно C:\Users\<имя_пользователя>\OpenVPN\config\
).
4. Качаем утилиту отсюда и распаковываем в отдельный каталог. Она портабельная, никакой установки не надо.
5. Запускаем.
6. Выбираем в выпадающем списке провайдера.
7. Жмем на кнопку «Получить ключ пользователя» Если данных и сертификата провайдера нет, то программа сначала получит данные провайдера, а потом пользователя. Если данные провайдера есть, то обновится только пользовательский ключ.
Если в дальнейшем надо будет принудительно обновить данные провайдера, жмем кнопку «Обновить данные провайдера«
Примечание: Если надо добавить нового провайдера, то дописываем адрес без http://, https:// и www в файл providers.txt
в подкаталоге data
, по адресу на строку.
Должно получиться как на скриншоте:
8. Жмем кнопку «Сохранить ключи…» и сохраняем их в каталог с конфигами OpenVPN. Имена ключей для Riseup оставляем без изменений, а для Calyx сохраняем как cacert_calyx.pem
и openvpn_calyx.pem
. Или можете отредактировать конфиг calyx-net.ovpn
и задать там любые желательные имена.
Полезные фишки в BAT/CMD файлах.
exit /B
Ключ /B
обязателен! Если использовать просто exit
— закроется командный интерпретатор.
Можно использовать для проверки параметров в командной строке.
if "%~1"=="" ( echo Use %0 ^<address^> echo Address must be without http://, https:// or www echo e.g. riseup.net pause exit /B )
Аналог VAR=`program`
в bash.
for /f %%i in ('program') do set "API_URI=%%i"& goto f1
:f1
Пример:
for /f %%i in ('bin\jq .api_uri %WORKDIR%/provider.json') do set "API_URI=%%i"& goto f1
:f1
Статья на Киберфоруме Копия в PDF
В BAT/CMD файле некоторые символы (перенаправления >
и <
, конвейера |
, символ &
, указание переменной %
) считаются специальными. Если символ должен быть включен в команду как символ, могут быть глюки. Перед символом нужно указать символ экранирования: ^
(крышку).
Пример:
echo Use batfile.bat ^<address^>
(выведет на экран Use batfile.bat <address>
)
Источник Копия в PDF Там есть и другие полезные штуковины.
Например, надо удалить из переменной %VAR%
все кавычки:
set VAR=%VAR:"=%
Источник Копия в PDF В источнике есть и другие примеры работы со строками.
C#, HttpWebRequest: использование самоподписанного (self-signed) сертификата.
Если попытаться соединиться с HTTPS-сервером, имеющим самоподписанный сертификат, то HttpWebRequest
сгенерирует исключение WebException
Базовое соединение закрыто: Не удалось установить доверительные отношения для защищенного канала SSL/TLS.
(The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
). Оно и понятно, HttpWebRequest
не смог проверить сертификат узла и закономерно нас послал.
Решение заключается в переопределении глобального обработчика System.Net.ServicePointManager.ServerCertificateValidationCallback
.
Внимание! Изменение ServerCertificateValidationCallback
повлияет на все соединения в программе, пока обработчик не будет установлен по умолчанию, надо не забывать это, и возвращать обработчик к стандартному значению, после того, как работа с сервером, имеющим самоподписанный сертификат, завершена.
Предположим, есть класс-обертка над HttpWebRequest
, надо его дополнить так, чтоб можно было работать с серверами с self-signed сертификатами.
Самый простой способ, но не самый безопасный. ServerCertificateValidationCallback
передается делегат, который всегда возвращает true
. Добавляем в класс функцию:
public void EnableIgnoreCertError() { ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; }
И функцию, которая возвращает ServerCertificateValidationCallback
к значению по умолчанию:
public void DisableIgnoreCertError() { ServicePointManager.ServerCertificateValidationCallback = null; }
Начали работать с сервером — вызвали первую, закончили — вызвали вторую.
Немного более сложный, но более безопасный способ. Необходимо заранее либо иметь сам сертификат, либо знать его хэш. В следующем примере будем проверять как раз хэш SHA1 сертификата.
1. Подключим в References’ах System.Security.Cryptography.
2. Подключим необходимые пространства имен:
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
3. Заведем в классе поле, куда из вызывающей программы будем передавать заранее известный хэш сертификата:
public string CertHashString { get; set; }
4. Заведем две функции, для включения и выключения самостоятельной проверки сертификата:
public void EnableValidateCert() { ServicePointManager.ServerCertificateValidationCallback = ValidateCert; } public void DisableValidateCert() { ServicePointManager.ServerCertificateValidationCallback = null; }
В первой функции вместо делегата, возвращающего true
, указываем нашу функцию проверки.
Функция проверки сертификата обязательно должна иметь следующий заголовок:
bool FunctionName (object, X509Certificate, X509Chain, SslPolicyErrors)
, т.е., например:
private bool ValidateCert(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors sslPolicyErrors)
В функции:
1. Проверяем наличие ошибок SSL, если их нет, значит сертификат уже был опознан:
if (sslPolicyErrors == SslPolicyErrors.None) { return true; }
2. Если известный хэш сертификата не был передан вызывающей программой, значит и проверять нечего:
if (string.IsNullOrEmpty(CertHashString)) { return false; }
3. Получаем хэш сертификата сервера в виде строки:
string hashstring = cert.GetCertHashString();
4. Если хэши полученного и известного сертификата совпадают — все ок, возвращаем true
, иначе false
.
if (hashstring == CertHashString) { return true; } return false;
Функция целиком:
private bool ValidateCert(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors sslPolicyErrors) { //все и так хорошо if (sslPolicyErrors == SslPolicyErrors.None) { return true; } //не передан хэш сертификата //значит и проверять нечего if (string.IsNullOrEmpty(CertHashString)) { return false; } //получаем хэш сертификата сервера в виде строки string hashstring = cert.GetCertHashString(); //если хэши полученного и известного //сертификата совпадают - все ок. if (hashstring == CertHashString) { return true; } return false; }
C#, получение хэша (fingerprint) сертификата X509.
Понадобилось проверить сертификат X509 на целостность и подлинность. Поставщик сертификата отдельно передает его fingerprint и алгоритм хэширования по которому тот вычисляется.
Судя по документации от MS, получить fingerprint, он же хэш, можно функцией GetCertHashString()
(или GetCertHash()
в виде массива байт) класса X509Certificate2
, однако, в .NET 2.0 отсутствует перегрузка функции GetCertHashString(HashAlgorithmName)
, которая позволяет выбрать алгоритм хеширования (GetCertHashString()
возвращает хэш SHA1). Потому сделаем руками, благо ничего сложного в этом нет.
1. Подключаем в References System.Security.Cryptography
2. Подключаем необходимые пространства имен в классе:
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
3. Загружать сертификат будем из файла, потому проверяем его наличие:
if (!File.Exists(CertFile)) { Console.WriteLine("File not found."); return null; }
4. Загружаем сертификат (создаем экземпляр класса X509Certificate2
). Если вдруг файл сертификата поврежден, то конструктор X509Certificate2
вызовет Exception Требуемый объект не найден.
Поэтому конструктор надо обернуть в try...catch
:
try { X509 = new X509Certificate2(CertFile); } catch (Exception ex) { Console.WriteLine(ex.Message); return null; }
5. Получаем загруженный сертификат в виде массива байт:
byte[] cert = X509.GetRawCertData();
6. Создаем объект HashAlgorithm
, который и займется вычислением хэша/fingerprint’а. Я завел в функции входную переменную AlgName
, куда записывается строка с названием алгоритма, что бы иметь возможность выбора алгоритма хэширования если что:
HashAlgorithm alg = null; switch (AlgName.ToUpperInvariant()) { case "MD5": alg = MD5.Create(); break; case "SHA1": alg = SHA1.Create(); break; case "SHA256": alg = SHA256.Create(); break; case "SHA384": alg = SHA384.Create(); break; case "SHA512": alg = SHA512.Create(); break; default: { Console.WriteLine("Unknow algorithm."); return null; } }
7. Получаем хэш в виде массива байт:
byte[] hash = alg.ComputeHash(cert);
8. Преобразуем массив байт в строку шестнадцатеричных чисел:
string hex = BitConverter.ToString(hash).ToLowerInvariant(). Replace("-", "");
ToLowerInvariant()
преобразует буквы в строке, полученной BitConverter
в строчные, а Replace("-", "")
удаляет разделители (-
) между значениями байтов, которые BitConverter
вставляет в строку.
C#, парсинг и прочая работа с JSON. В том числе и в .NET 2.0
Понадобилось достать несколько значений из JSON-файла, искал, чем это сделать и нашел просто офигенную библиотеку Json.NET. Это не просто парсер, это целый набор инструментов: парсер, сериализаторы, десериализаторы, конвертеры. Кроме банального вытаскивания значений, можно формировать JSON, конвертировать его в XML и XML в JSON, обращаться к объектам JSON через запрос SelectToken
или LINQ. А можно и просто вытащить нужное значение, обращаясь к полям JSON, используя встроенные функции объектов.
Несомненный плюс библиотеки в том, что даже в последней версии она поддерживает .NET Framework 2.0 Правда нельзя будет использовать синтаксис LINQ, но и без него инструментов хватает.
С помощью JObject
и JToken
1. Подключаем библиотеку для своего Framework’а через References и в исходнике:
using Newtonsoft.Json.Linq;
2. Загружаем JSON в переменную, например из файла.
3. Парсим JSON в JObject
:
JObject JSONObj = JObject.Parse(JSONBuf);
JObject.Parse
может вызвать exception, если есть синтаксические ошибки в JSON, так что лучше поместить его в try...catch
:
public static JObject ParseJSON(string JSONBuf) { JObject JSONObj = null; try { JSONObj = JObject.Parse(JSONBuf); } catch (Exception ex) { Console.WriteLine("ERROR: " + ex.Message); return null; } return JSONObj; }
Пример реакции на ошибку синтаксиса:
Invalid character after parsing property name. Expected ':' but got: {. Path description.languages', line 15, position 9.
4. Далее, можно вытащить значение поля JSON в объект JToken
помощью функции JSONObj.GetValue(FieldName);
, если поле со значением находится сразу в корне JSON, как, например, api_uri
в этом примере, то JToken
можно сразу преобразовать в строку с помощью функции .ToString
:
JToken tok = JSONObj.GetValue("api_uri");
tok.ToString();
Однако, если поле отсутствует, то это вызовет exception:
В экземпляре объекта не задана ссылка на объект.
Этого можно избежать, отловив исключение в try...catch
, либо проверив наличие поля функцией JObject.ContainsKey("имя_поля")
перед вызовом GetValue
, либо воспользовавшись функцией TryGetValue
вместо GetValue
Примеры кода на PasteBin
Тестовый проект
Если же нужное значение находится где-то глубоко в недрах JSON, то необходимо воспользоваться функцией JObject.SelectToken()
Пример в официальной документации Копия
Как я уже говорил, инструмент очень мощный, так что лучше начать с изучения официальной документации
OpenVPN для Windows XP.
Последняя поддерживающая Windows XP версия OpenVPN это 2.3.18.
— Версия x86 (32-разрядная)
— Версия x64 (64-разрядная)
Mozilla Firefox для Windows XP
Чтоб не пролюбилось, мало ли с официального сайта пропадет, а понадобится:
C#, поиск файла по маске.
Или небольшая заметка про странный глюк (на самом деле фичу) MS и кривофикс к ней.
Словил странный баг при поиске файлов по маске, функцией Directory.GetFiles();
Оказалось, что при задании маски вида *.htm
, в выборке окажутся все файлы с расширением, длина которого больше трех совпадающих символов, т.е. и *.htm
и *.html
и *.htmепрст
, и т.д. Срабатывает это для файлов с расширениями размером три символа и больше.
Т.е. на *.ph
оно найдет только файлы с расширением ph
, а на *.a
— только файлы с расширением a
Этот баг распространяется только на последнее расширение. Если в имени файла есть конструкция типа *.tmp.php
, например admin.tmp.php
, то при задании маски *.php
расширение tmp
будет, слава Ктулху, проигнорировано.
Оказалось, что это не баг, а фича, и об этом прямо написано в MSDN:
Не знаю, как создатели DOS смогли заговнять и испортить самую простую операцию, которая работала у них, как минимум, с 1989 года, но факт. Заговняли.
В общем, я теряюсь в догадках.
Это, конечно же, довольно коряво. Наверное, можно было бы решить с помощью регулярных выражений, но я поступил следующим образом:
1. Создал функцию, которая будет вытаскивать однозначное расширение и из заданной маски, и из поданного на вход файла:
private string GetExtension(string FileName) { FileName = FileName.Replace('*', '_'); FileName = FileName.Replace('?', '-'); FileInfo fi = new FileInfo(FileName); return fi.Extension; }
Замены Replace(...)
тут для того, чтобы класс FileInfo
не выпал в Exeption, если ему подать что-то вида *.html
, т.к. FileInfo
не принимает имен с недопустимыми символами, к которым относятся и маски подстановки. В определении расширений класс FileInfo
такого глюка не имеет, и отличает file.html
от file.htm
. И тоже не признает двойные расширения, учитывая только последнее.
2. В функции, где будем вызывать поиск файлов, вызываем, собственно, функцию поиска:
string[] files = Directory.GetFiles(Path, Mask, SearchOptions);
3. Получаем расширение маски:
string MaskExt = GetExtension(Mask);
4. Далее обрабатываем выходной массив. Например, тут я добавлял его в List<string>
с именем FoundFiles
. Мне надо было искать файлы по маскам, и нужно было, чтоб *.htm
и, например, *.html
различались.
Например, я просто сравнивал расширение от маски файла с расширением от имени файла, и если оно совпадало — добавлял в результирующий список FoundFiles
:
string FileExt = GetExtension(filename); if (FileExt == MaskExt) { FoundFiles.Add(filename); }
Или относительно полностью:
string MaskExt = GetExtension(Mask); //[...] string[] files = Directory.GetFiles(Path, Mask, SearchOptions); foreach (string filename in files) { string FileExt = GetExtension(filename); if (FileExt == MaskExt) { FoundCtr++; FoundFiles.Add(filename); } }