Efendy
|
|
« : 11 Августа 2009, 17:35:41 » |
|
Протокол авторизации L2-авторизатора.
Комментарий: L2 не имеет никакой связи с layer2 или подобной, просто изначально много лет назад авторизатор был сделан для сети с именем L2, оттуда и пошло название.
Авторизация осуществляется по протоколу UDP, порт 7723. Внимание! Существует серьезный нюанс, касаемый UDP-соединения. Обычно, как происходит обмен данными по UDP протоколу:
Хост_1 посылает UDP пакет на Хост_2 порт, допустим, 7723. При этом у пакета src-port устанавливается системой (обычно из верхних портов 1024...65535). В дальнейшем хост_2 посылает ответы именно на этот порт.
В NoDeny иначе. Сначало объясню почему иначе. В L2-авторизатор встроен механизм пингования между авторизаторами по команде сервера. Т.е. администратор может дать команду "авторизатор такого-то клиента пропингуй авторизатор такого-то клиента и предоставь статистику по потерям". Такой пинг интересен двумя моментами:
1) позволяет отловить проблемный линк. Допустим, мы находимся в центре сети и пингуем клиента А и клиента Б. Проблемный линк (плохо обжатая/поврежденная витая пара) находится между нами и ближайшим к нам клиентом. Следовательно, мы получим потери как при пинговании клиента А, так и клиента Б. Если же мы дадим команду авторизатору А пропинговать Б, то можем увидеть - есть ли проблемы на всем протяжении коммуникакций от А до Б.
2) пингование идет не по icmp протоколу, а по udp и, как вы могли дагадаться, на порт 7723. Почему так? Потому что icmp протокол часто запрещен фаерволом, а udp порт 7723 клиент вынужден открыть для работы авторизатора.
Пока фича не активирована (в nol2auth.pl) т.к. с появлением управляемого оборудования появились иные методы отлова проблемных линков. Тем не менее, изначальный протокол построен исходя из предположения, что авторизатор будет слушать 7723 порт, т.е принимать входящие соединения.
Еще раз. Когда хост_1 посылает куда-то пакет, он открывает сокет, на который принимает ответные пакеты. В авторизаторе мы изначально биндим 7723 порт, т.е принимаем пакеты, это уже иной сокет!
Пример:
Авторизатор посылает на сервер пакет, dst_port=7723, src_port=12345 (выбирается системой случайным образом). Сервер авторизации, получив пакет, отправляет отвен не на порт 12345, а на порт 7723, т.е создает новый сокет. Итого: все udp пакеты между сервером авторизации и авторизатором идут каждый на новом сокете.
Фактически после посылки каждого пакета, мы закрываем сокет т.к не ждем на него ответа.
Описание протокола авторизации.
Протокол авторизации основан на том, что сервер формирует случайную строку, шифрует ее своим методом, отсылает клиенту, клиент расшифровывает строку, шифрует ее своим методом, отсылает серверу, сервер расшифровывает. Если в конечном итоге сервер получит исходную строку - процесс авторизации успешен.
Перехват данных не принесет пользы злоумышленнику т.к. у него нет данных для расшифровки и последующей шифровки случайно строки. В каждом сеанса авторизации случайная строка новая, т.е. злоумышленник не сможет подделать авторизацию путем повторной посылки перехваченного пакета.
Авторизация осуществляется по:
1) ip+пароль 2) ip+логин+пароль
В любом случае участвует ip, что является недостатком. Сервер, получив пакет, всегда смотрит на ip и ассоциирует его с клиентом. Если переданные данные правильные, то данный ip считается авторизированным клиентом иначе отвергается.
1) авторизатор (клиент) посылает серверу авторизации пакет, содержащий id-запроса. Каждый сеанс авторизации рекомендуется делать с новым id запроса. id запроса может быть строкой или просто числом. Важно чтобы длина этого идентификатора была меньше 16 байт. Сервер, получая все что меньше 16 байт, считает id-запроса
2) Сервер формирует случайную строку. Берет пароль клиента и разбивает на 2 части. Первую часть использует как ключ для шифрования случайно строки по AES. Зашифрованная строка отсылается клиенту
3) Клиент, получив зашифрованную строку, расшифровывает ее, используя 1-ю часть своего пароля как ключ шифрования. Т.е получает исходуную случайную строку.
Используя 2-ю часть пароля как ключ - заново шифрует случайную строку и отсылает ее серверу.
4) Сервер, получив, зашифрованные данные, расшифровывает их и, если они идентичны исходной случайной строке, открывает доступ клиенту.
5) Сервер отсылает статистику авторизатору (баланс, трафик, состояние авторизации и т.д)
Теперь подробней. Рассматриваем авторизацию ip+пароль.
Сперва сервер авторизации разбивает пароль каждого клиента на 2 части:
pass2 = первый 3 символа пароля pass1 = оставшиеся символы пароля
Например, у клиента пароль 'killemall'
pass2 = 'kil' pass1 = 'lemall'
Затем каждый пароль делается длиной 16 символов т.к. это необходимое требования для алгоритма шифрования AES (Rijndael в прошлом), а именно:
к pass2 дописываются буквы 'Z' (большие), а к pass1 - '0' (ноль). Если посмотреть внуть nol2auth.pl, То это строки:
$U{$ip}{passwd2}=substr(substr($pass,0,3).'Z' x 16,0,16); $U{$ip}{passwd1}=substr($pass.'0' x 19,3,16);
Естественно, учитываем что пароли иногда могут меняться, т.е периодически обновляем информацию.
Перейдем ко второму этапу (прием идентификатора запроса). Сервер смотрит, а не отключена ли авторизация для данного клиента, если отключена - просто отсылаем ему строку `eri`. В nol2auth.pl вы могли заметить отсылку еще и кода `erw` - это просьба авторизатору умерить свой пыл, т.е пытаться авторизоваться реже.
Обычно так происходит, когда фаервол клиента блокирует входящие пакеты - клиент отсылает и отсылает запросы, а ничего не получает в ответ.
Если же пароль клиента существует, сервер формирует случайную строку длиной 16 байт. В nol2auth это код:
$str=substr(rand().rand().'errorinlastline',2,16); $str=new Crypt::Rijndael $str,Crypt::Rijndael::MODE_CBC; $str=$str->encrypt(substr(rand().rand().'qazxswedcvfrtgbn',2,16)); $str=~s/,/-/g;
Почему именно так? Дело втом, что rand - возвращает случайное число, а нам нужно использовать как можно более случайную строку - с полным алфавитом символов от 0 до 255. Поэтому после получения случайного числа, мы его шифруем другим случайным числом и в итоге получаем хорошо сформированную случайную строку. Строки `errorinlastline` и `qazxswedcvfrtgbn` - здесь просто заполнители на случай, если число окажется длинной менее 16 символов - как я уже говорил, для AES нужны именно 16 байтные (128 битные) данные.
Шифруем случаную строку паролем pass2:
$crypt=new Crypt::Rijndael $U{$ip}{passwd2},Crypt::Rijndael::MODE_CBC; $crypt_str=$crypt->encrypt($str);
Клиенту отправляем такую строку:
'id'.$crypt_str.$id_session;
Т.е сперва идут 2 символа `id`, затем зашифрованная случайная строка, затем идентификатор сессии авторизации $id_session (полученный в пункте 1).
Клиент, получив пакет с начальными символами `id` - сравнивает свой идентификатор сессии с последними символами пакета, если не совпадает - игнорирует пакет. Иначе извлекает $crypt_str, расшифровывает, зашифровывает паролем pass1 и отсылает серверу в виде:
зашифрованная строка + состояние авторизации + идентификатор сесии
зашифрованная строка - 16 байт всегда состояние авторизации - 1 байт: a - запрос на включение полного доступа b - запрос на блокирование доступа c - запрос на включение доступа к сетям 2 направления e - запрос на включение полного доступа с просьбой разрешить пингование
продолжение следует...
|