Рассел Сейдж. Приемы профессиональной работы в unix перевод "Tricks of the unix masters" by Russel G

Вид материалаДокументы

Содержание


Подробнее о ловушках
Формат вызова
Текст программы
Переменные среды выполнения
Подобный материал:
1   ...   25   26   27   28   29   30   31   32   ...   45
ния работы программы. Обращение к "самому себе" в операторе kill вы-

полняется с использованием метасимволов $$, которые представляют собой

идентификационный номер выполняющегося shell-процесса. Обработчик ло-

вушек включается сигналами 2 и 15, которыми являются прерывание и

программное завершение соответственно. Отметим, что сигнал выхода из

программы (сигнал 3) здесь не используется. Причину этого мы объясним

позже.


Строка 6 устанавливает маршрут, по которому может обращаться

inuse. Тем самым inuse никогда не сможет быть "застигнут врасплох"

кем-нибудь, кто проник незаметно (в "троянском коне"). Строка 7 иници-

ализирует секретный пароль значением "secret", которое совсем не сек-

ретно. Вы можете изменить пароль на любое слово перед установкой ко-

мандного файла в вашей системе. В правах доступа к файлу, в котором

хранится текст данной shell-программы, вы должны запретить возможность

чтения. В противном случае другие пользователи смогут увидеть секрет-

ное слово.


Строка 9 отключает эхо-отображение, строка 10 печатает запрос па-

роля, а строка 11 читает пароль, который вы вводите, и заносит его в

переменную BUF1.


Строки 14-23 представляют собой вечный цикл while, который можно

прервать только вводом правильного пароля. Строка 16 читает ввод с

клавиатуры. При нажатии возврата каретки строка 17 проверяет, соот-

ветствует ли то, что введено с клавиатуры, паролю пользователя. Если

нет, переменная BUF2 сравнивается с секретным паролем. Если какой-то

из паролей совпадает, оператор break производит выход из цикла while,

тем самым прекращая выполнение программы. Если введенные данные не

соответствуют ни одному из паролей, то в строке 22 выдается звуковой

сигнал и снова начинает выполняться оператор чтения клавиатуры.


Если пароль введен правильно, в строке 24 включается эхо-отобра-

жение на терминал и программа завершается. Если происходит прерывание,

активизируется оператор trap. Данная операция подробно рассматривается

ниже.


ПОДРОБНЕЕ О ЛОВУШКАХ


Нам нужно рассмотреть смысл клавиши выхода из программы. Она про-

изводит прерывание, похожее на все другие прерывания, но кроме того

выводит дамп памяти для запущенного процесса. Мы оставляем клавишу вы-

хода нетронутой оператором trap, поскольку она становится нашей

последней надеждой на приостановление командного файла inuse. Когда

ваш терминал заблокирован, он эхо-отображает вводимые с клавиатуры

символы, но не реагирует на них. Тот, кто нажимает на клавиши, видит

это и пытается выйти из ситуации, нажимая на клавишу прерывания (обыч-

но это клавиша DEL). Когда он это делает, на экран выводится сообщение

"you're busted", эхо-отображение снова включается и программа сама се-

бя уничтожает (сигнал 15). Когда сигнал уничтожения принимается прог-

раммой, этот сигнал ловится, печатается сообщение и программа снова

сама себя уничтожает. Эта последовательность выполняется снова и сно-

ва, как в вечном цикле. Каждый раз, когда ловушка уничтожается и снова

запускается, используется стек. Если все это будет выполняться доста-

точно долго, то весь стек заполнится записями об активизации и пере-

полнится, аварийно завершая весь сеанс работы.


Если клавиша выхода будет нажата до активизации оператора trap,

то программа завершится чисто. Если же клавиша выхода будет нажата

после начала работы оператора trap, то произойдет выдача дампа памяти

процесса и программа завершится. Это не совсем честный прием, но прог-

раммирование на языке shell вынуждено быть именно таким, и это предуп-

реждает вас о том, что что= то не в порядке.


Текущие значения клавиш для сигналов прерывания и выхода отобра-

жаются командой stty(1). Эти значения можно переустановить в любые по

вашему желанию. У меня текущие установки такие:


speed 9600 baud; intr = DEL; quit = |; erase = h; kill = u;

eof = d;


Набрав на клавиатуре "stty intr z", вы можете установить символ z

в качестве сигнала прерывания ваших процессов, поэтому такое изменение

клавиши прерывания и запуск бесконечного цикла представляет собой еще

один способ защиты вашего сеанса работы. Поскольку вам потом нужно бу-

дет вернуть старое значение, вы должны запомнить то, что вы делали.

Такой настройкой сигналов вы можете делать с вашим терминалом почти

все, что хотите. Этот подход дает меньшую степень защиты, чем перехват

прерываний, но может обеспечить вас минимальной защитой, не приводя к

выдаче дампа памяти.


Теперь мы представляем версию на языке Си.


ТЕКСТ ПРОГРАММЫ inuse НА ЯЗЫКЕ СИ


1 char id[] = "@(#) inuse v1.0 Disable terminal Author: Russ Sage";


3 #include

4 #include

5 #include


7 #define SSIZ 7

8 #define BSIZ 512

9 #define BELL "\07"

10 #define LF "\n"


12 main()

13 {

14 register int fd, sig, n;

15 char secret[SSIZ];

16 char buf1[BSIZ], buf2[BSIZ];

17 struct sgttyb sav_tty, chg_tty;


19 secret[0] = 's';

20 secret[1] = 'e';

21 secret[2] = 'c';

22 secret[3] = 'r';

23 secret[4] = 'e';

24 secret[5] = 't';

25 secret[6] = '\n';


27 buf1[0] = buf2[0] = '\0';

28 if ((fd = open("/dev/tty",O_RDONLY)) == -1)

29 exit(1);


31 for (sig = 2; sig <= 15; sig++)

32 signal(sig, SIG_IGN);


34 if (gtty(0, &sav_tty))

35 exit(2);

36 chg_tty = sav_tty;

37 chg_tty.sg_flags &= ~ECHO;

38 if (stty(0, &chg_tty))

39 exit(3);


41 write(1,"Lock string: ",13);

42 read(fd, buf1, BSIZ);

43 write(1, LF, 1);


45 for (;;) {

46 n = read(fd, buf2, BSIZ);

47 buf2[n] = '\0';


49 if (strcmp(buf2, buf1) == 0)

50 break;

51 if (strcmp(buf2, secret) == 0)

52 break;

53 write(1, BELL, 1);

54 }

55 stty(0, &sav_tty);

56 close(fd);

57 }


ОПИСАНИЕ


ЗАЧЕМ НАМ НУЖНА ПРОГРАММА inuse (Си)?


Версия inuse на языке Си работает почти так же, как и

версия на языке shell. Основное отличие заключается в том, что

командные файлы на языке shell пользуются командами раздела

(1), в то время как программы на Си используют команды разделов

(2) и (3).


ЧТО ДЕЛАЕТ ПРОГРАММА inuse (Си)?


Теоретические основы функционирования такие же, как и в

shell-версии. Инициализируется секретный пароль (в данном случае при-

меняется такой синтаксис, чтобы команда strings(1) не смогла посмот-

реть его в исполняемом модуле), перехватываются сигналы, читается па-

роль пользователя и начинается бесконечный цикл, который читает симво-

лы с клавиатуры. Как только на клавиатуре что-то набрано и нажата кла-

виша возврата каретки, входные данные сравниваются с двумя известными

паролями. Если они соответствуют одному из паролей, программа пере-

устанавливает терминал и завершается. Если совпадения не произошло,

терминал выдает звуковой сигнал и снова читает клавиатуру.


Поскольку здесь ловушки не работают, попытка прервать запущенную

программу не срабатывает. Единственный способ прекратить ее выполнение

- использовать команду "kill -9". Сигнал 9 является единственным, ко-

торый нельзя перехватить. Если бы это можно было сделать, то не было

бы никакого способа прекратить выполнение процесса, кроме как вытащить

вилку из розетки.


ПОЯСНЕНИЯ


Строка 1 помещает документирующую информацию в символьный массив.

При наличии этого текста в объектном модуле команда what(1) может вы-

нуть его оттуда, чтобы мы могли посмотреть его для идентифицирования

нашей программы.


Строка 3 подключает файл fcntl.h. Этот файл содержит все определе-

ния языка Си для открытия, закрытия, чтения и записи файлов. Строка 4

подключает файл signal.h. Мы используем этот файл для определения пе-

ременной SIG_IGN, которая является отметкой игнорирования сигналов

(signal_ignore). Строка 5 подключает файл sgtty.h, который мы исполь-

зуем для определения всего, что относится к получению информации о

терминале посредством вызова ioctl(2).


Строка 7 определяет размер секретного пароля. Этот размер не обя-

зательно должен быть точно таким, как длина пароля. Этот размер указан

для удобства программирования.


Строка 8 объявляет размер буфера, в который каждый раз произво-

дится чтение с клавиатуры. Хотя 512 символов слишком много для такого

считывания, на самом деле чтение прекращается с приходом символа возв-

рата каретки. Наличие большого буфера дает нам запас памяти.


Строки 9 и 10 определяют управляющие символы звукового сигнала и

перевода строки.


Строка 14 объявляет некоторые рабочие переменные. Обратите внима-

ние, что мы используем регистровые целые. Использование регистровых

переменных для ускорения работы - полезный прием. Если вы объявили

слишком много переменных по сравнению с количеством регистров в вашей

машине, не будет никакой ошибки. Оставшиеся переменные рассматриваются

как обычные переменные. Переменная fd используется в качестве файлово-

го дескриптора при открытии файла /dev/tty, переменной sig последова-

тельно присваиваются значения всех сигналов, а переменная n представ-

ляет собой число прочитанных символов.


Строка 15 определяет секретный массив. Этот символьный массив со-

держит наш секретный пароль, который прямо закодирован в программе.

Строка 16 определяет два буфера, в которые мы читаем вводимые символы.

Buf1 предназначен для нашего пользовательского пароля, а buf2 для по-

пытки ввода пароля, который считывается, когда мы хотим прекратить вы-

полнение программы. Строка 17 определяет две рабочие структуры, кото-

рые содержат информацию об установках терминала (ioctl). Здесь у нас

две структуры, поскольку одна из них - первоначальная, а вторая - та,

на которую мы хотим изменить, чтобы не забыть первоначальные установ-

ки.


Строки 19-25 загружают пароль в секретный массив. Мы выполняем

посимвольное присвоение, поскольку при таком присвоении любая строка

символов в объектном модуле получается разорванной. Это мера безо-

пасности для предотвращения возможности зрительного просмотра с целью

извлечения ценной информации.


В строке 27 эти два буфера инициализируются в нулевой размер.


Строки 28 и 29 открывают устройство /dev/tty. Если возвращаемый

дескриптор файла равен -1, это говорит об ошибке и программа заверша-

ется.


Строки 31 и 32 перехватывают все сигналы. Цикл for работает с

сигналами, имеющими номера от 2 до 15. Для каждого из этих значений

выполняется системный вызов signal с целью игнорирования сигналов с

такими значениями.


В строках 34-39 выполняется модификация терминальных характе-

ристик для отключения эхо-отображения символов. Строка 34 получает ин-

формацию об установках терминала в структуру sav_tty. Системный вызов

gtty - это просто программный интерфейс с системным вызовом

ioctl(get_values). Если этот вызов неудачен, программа завершается.


Строка 36 заносит данные из структуры sav_tty в структуру

chg_tty. Затем строка 37 присваивает элементу sg_flags результат опе-

рации отрицания над его же значением и символом ECHO, что означает

"отключить эхо-отображение". После этого строки 38 и 39 записывают из-

мененные значения обратно на терминальное устройство. Системный вызов

stty - это просто программный интерфейс с системным вызовом

ioctl(set_values).


Строка 41 выводит на экран запрос на ввод пароля. Дескриптор фай-

ла 1 является стандартным устройством вывода, а 13 - длина строки сим-

волов. Строка 42 читает BSIZ символов из файла /dev/tty. После чтения

на экран выдается символ перевода строки. Это необходимо сделать,

поскольку при отсутствии эхо-отображения на экран не выводится символ

перевода строки, когда вы вводите свой пароль. Поэтому мы вынуждены

вставить этот символ здесь сами.


Строки 45-54 представляют собой бесконечный цикл, который читает

символы с клавиатуры. Строка 46 выполняет чтение терминала для распоз-

навания пароля. В этой строке введенный пароль помещается в buf2, а не

в buf1. Мы выясняем количество символов, прочитанных в buf2 (n).

Поскольку индексирование массивов начинается с нуля, а не с 1, при

вводе n символов мы попадаем в конец текста и здесь мы вставляем ноль

для того, чтобы все, что было введено, представляло собой строку сим-

волов. Мы делаем это потому, что команда read не производит обработку

символьной строки. Это делают системные вызовы stdio. Их описание на-

ходится в разделе (3) руководства по системе, а не в разделе (2). Нам

нужно оформить прочитанные символы в виде строки, чтобы ее можно было

сравнить с паролями.


Строка 49 сравнивает то, что ввели с клавиатуры, с тем паролем,

который вы ввели в начале работы программы. Если эти символьные строки

одинаковы, strcmp возвращает значение ноль, которое сообщает о совпа-

дении. Команда break выводит выполнение из цикла for, и программа про-

должается. Строка 51 выполняет такое же сравнение с секретным паролем.

Если происходит совпадение, вы также выходите из цикла.


Если совпадения не произошло, строка 53 выдает на терминал звуко-

вой сигнал и управление передается оператору read в начало цикла for.


Если произошел выход из цикла for, управление передается строке

55. Происходит запись первоначальной информации об установках термина-

ла, тем самым включается эхо-отображение. Строка 56 закрывает файл

/dev/tty, и происходит нормальное завершение работы программы.


Вы могли видите, программа на языке Си, хотя и несколько сложнее,

чем на языке shell, но имеет некоторые преимущества. Она не может быть

остановлена или нарушена ничем, кроме команды kill -9. Пароль в испол-

няемом модуле спрятан от любопытных глаз. Вы получаете больший уровень

защиты и более ясный подход за счет использования языка низкого уровня

и написания более длинной программы.


------------------------------------------------------------------------


ИМЯ: lock

------------------------------------------------------------------------


lock Блокирование и разблокирование файлов


НАЗНАЧЕНИЕ


Изменяет права доступа к файлам на запись и чтение, что выглядит

как блокирование и разблокирование.


ФОРМАТ ВЫЗОВА


lock [-u] file [...]


ПРИМЕР ВЫЗОВА


lock $HOME


Отключить возможность записи для меня и возможность чтения/записи

для группы и других пользователей по отношению к моему регистрационно-

му каталогу.


ТЕКСТ ПРОГРАММЫ


1 :

2 # @(#) lock v1.0 Lock and unlock files Author: Russ Sage

2а Блокирование и разблокирование файлов


4 if [ $# -eq 0 ]

5 then echo "lock: incorrect argument count" >&2

6 echo "usage: lock [-u] file [...]" >&2

7 exit 1

8 fi


10 if [ "`echo $1 | cut -c1`" = "-" -a "$1" != "-u" ]

11 then echo "lock: invalid argument $1" >&2

12 echo "usage: lock [-u] file [...]" >&2

13 exit 1

14 fi


16 MODE1="go-rw"

17 MODE2="u-w"


19 if [ "$1" = "-u" ]

20 then shift

21 MODE1="go+r"

22 MODE2="u+w"

23 fi


25 chmod $MODE1 $@

26 chmod $MODE2 $@


ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ


MODE1 Режимы доступа к файлу, относящиеся к группе

пользователей и другим пользователям

MODE2 Режимы доступа к файлу, относящиеся к владельцу


ОПИСАНИЕ


ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ lock?


Все файлы в системе UNIX имеют некоторые права доступа. Эти режи-

мы изменяются в соответствии с тем, как используется этот файл. Для

каждого файла должны быть установлены три набора прав доступа - для

владельца, группы и других пользователей. Обычно текстовые файлы имеют

режим 644, а исполняемые и каталоги - 755. Некоторые системы присваи-

вают по умолчанию другие значения.


Если вы хотите ограничить права чтения или записи, следует

использовать команду chmod(1). Новый режим должен указываться либо как

абсолютное восьмеричное число (например, 777), либо как буквенное вы-

ражение, указывающее, какая категория пользователей что может делать

(например, ugo+rwx). Если вы хотите добавить или запретить определен-

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

даже в таком случае нам будет полезно средство, позволяющее уменьшить

число нажатий на клавиши и избавляющее от необходимости точно запоми-

нать все, что касается прав доступа к файлу.


ЧТО ДЕЛАЕТ lock?


Lock - это средство, которое управляет правами доступа, обеспечи-

вающими безопасность всех ваших файлов. Этот командный файл обеспечи-

вает необходимые режимы доступа пользователей или запрета доступа в

ограниченной степени. Имея заранее определенные режимы доступа, наши

файлы лучше сохраняются в безопасном состоянии.


Входными данными для lock являются имена файлов. Допускается

использование символьной строки с любым набором имен файлов. В ней

должно находиться по крайней мере одно имя файла. Имена файлов с ука-

занием каталогов также допускаются.


Действие lock по умолчанию - блокирование указанного файла. Опция

-u разблокирует указанный файл.


Если команде chmod передано неверное имя файла, это создает проб-

лемы для нее и в этом случае выводится сообщение об ошибке.


ПРИМЕРЫ


1. $ lock -u $HOME/src *.c


Разблокирование моего каталога с исходными текстами и всех исход-

ных файлов на языке Си в текущем каталоге. Разблокирование дает воз-

можность чтения всем и возможность записи только мне.


2. $ lock $HOME/bin


Блокирует мой каталог bin так, чтобы никто не мог читать или

писать в него файлы. Даже хотя мой каталог нельзя читать, любой посто-

ронний может все же войти в него командой cd, если установлен бит x.

Если он попытается выполнить команду ls, каждый файл будет выдавать

сообщение об ошибке вида "filename not found" (файл с именем

"filename" не найден). Никто не может получить информацию из индексно-

го дескриптора файла, такую как временные характеристики и права

доступа, но любой может увидеть имена всех файлов из сообщения об

ошибке.


ПОЯСНЕНИЯ


Строки 4-8 проверяют счетчик аргументов. Если не был указан ни

один аргумент, выводится сообщение об ошибке. Должно быть указано хотя

бы одно имя файла.


Строки 10-14 проверяют, является ли первый символ первого позици-

онного параметра знаком "минус" и отличается ли первая опция от до-

пустимой опции -u. Если эти условия выполняются, выводится сообщение

об ошибке и программа завершается.


Строки 16 и 17 инициализируют установки режимов прав доступа по

умолчанию. MODE1 устанавливается для запрета чтения и записи категори-

ям пользователей "группа" и "другие". MODE2 устанавливается для запре-

та пользователю (т.е. мне) права записи. Это страховка для меня от

случайной записи в файл. Нам нужны две такие переменные, поскольку эти

два режима довольно разные. Единственный способ сделать это - дважды

вызвать команду chmod с двумя различными установками.


Строки 19-23 проверяют, была ли указана в командной строке опция

-u. Если была, она убирается из командной строки командой shift и пе-