ПРИЕМЫ ПРОФЕССИОНАЛЬНОЙ РАБОТЫ В UNIX ГЛАВА 6 УПРАВЛЕНИЕ ЛИЧНОЙ ИНФОРМАЦИЕЙ II: ОСВЕДОМЛЕННОСТЬ ПОЛЬЗОВАТЕЛЯ И ЗАЩИТА ЛИЧНОЙ ИНФОРМАЦИИ ОСВЕДОМЛЕННОСТЬ ПОЛЬЗОВАТЕЛЯ activ показать активность терминалов info показать информацию о паролях пользователей uchk посмотреть процессы других пользователей watch наблюдать за регистрацией в системе whox команда who с дополнительными возможностями ЗАЩИТА ЛИЧНОЙ ИНФОРМАЦИИ acme показать учетную информацию inuse запретить использование терминала lock блокировать и разблокировать файлы УПРАВЛЕНИЕ ЛИЧНОЙ ИНФОРМАЦИЕЙ II: ОСВЕДОМЛЕННОСТЬ ПОЛЬЗОВАТЕЛЯ И ЗАЩИТА ЛИЧНОЙ ИНФОРМАЦИИ ВВЕДЕНИЕ Мы уже разработали целый ряд инструментальных средств, которые помогают нам более эффективно управлять нашей личной информацией. Од- нако мы работаем не в вакууме. Каждый из нас работает с другими людьми и обычно использует систему вместе с другими пользователями. По нескольким причинам важно иметь представление о том, что делают в системе другие люди. Во-первых, у нас может возникнуть необходимость наметить работу, интенсивно использующую процессор, на то время, когда загрузка системы невелика. Изучение типичных вариантов загрузки систе- мы может помочь нам спланировать такую работу и сотрудничать с другими пользователями со взаимной выгодой. Нам также может понадобиться знать, находится ли кто-то на своем рабочем месте, чтобы мы могли поговорить с ним, или знать, что кто-то очень занят и был бы недоволен, если его прервут. Наконец, существуют вопросы защиты информации, которым мы уделим больше внимания в последующих главах. Основой защиты информации явля- ется осведомленность о том, что делают другие - и, возможно, о том, что они не должны делать. Поэтому мы здесь представим две группы инструментальных средств. Средства ОСВЕДОМЛЕННОСТИ ПОЛЬЗОВАТЕЛЯ помогают нам следить за тем, что делают другие, и тем самым упростить связь с ними. Средства ЛИЧНОЙ ЗА- ЩИТЫ важны для защиты нашей учетной информации и наших данных и для получения информации о наших собственных действиях. Первая группа рассматриваемых здесь средств, относится к "осве- домленности пользователя". Этими средствами являются activ, info, uchk, watch и whox. Командный файл activ представляет собой модификацию команды who, показывающую имя каждого пользователя, номер терминала и сведения о том, насколько давно данное лицо вводило что либо с клавиатуры. По этой информации мы можем сказать, кто еще работает в системе, активно ли они работают и, если они используют закрепленные линии, то где они находятся. Следующее средство - info. Оно предоставляет возможность получить информацию о паролях всех указанных в списке пользователей. С помощью этой утилиты вы можете получить общую информацию об этих пользователях и увидеть, где размещаются их регистрационные каталоги. Еще одно информативное средство - uchk. Этот командный файл поле- зен для выяснения того, что делает указанный пользователь в настоящее время. По этой информации вы можете сказать, простаивают ли они или же делают что-то важное и их нельзя прерывать. Следующее средство - watch. Эта программа похожа на "демон", ко- торый выполняется в фоновом режиме и постоянно следит за регистрацией определенных пользователей. Когда нужное вам лицо регистрируется, на ваш терминал посылается сообщение о том, что данный пользователь вошел в систему. Таким образом, вы можете продолжать работу вместо того, чтобы постоянно прерывать ее. Последняя утилита в данном разделе - это whox. Whox дает вам исчерпывающие сведения обо всех пользователях, зарегистрировавшихся на машине. Вы можете получить информацию, похожую на результат команды who, которая отсортирована различным образом, отобразить информацию о терминалах или даже передать почтовое сообщение любому пользователю. ОСВЕДОМЛЕННОСТЬ ПОЛЬЗОВАТЕЛЯ ------------------------------------------------------------ ИМЯ: activ ------------------------------------------------------------- activ Показывает активность терминалов НАЗНАЧЕНИЕ Показывает имена всех зарегистрированных пользователей, номера их терминалов и время, когда они последний раз работали с клавиатурой. ФОРМАТ ВЫЗОВА activ ПРИМЕР ВЫЗОВА activ Выводит на экран информацию об активности пользователей на своих терминалах ТЕКСТ ПРОГРАММЫ 1 : 2 # @(#) activ v1.0 Display terminal activity Author: Russ Sage 2а Показать активность терминалов 3 4 if [ $# -gt 0 ] 5 then echo "activ: argument error" >&2 6 echo "usage: activ" >&2 7 exit 1 8 fi 9 10 who -u | cut -c1-17,38-42 ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ activ? Первоначально компьютеры использовались исключительно для запуска замкнутых, независимых задач обработки информации. Появление много- пользовательских систем сделало возможным по крайней мере элементарное общение пользователей между собой. Такое общение в настоящее время яв- ляется аспектом возрастающей важности при работе с компьютером. Система UNIX предоставляет команды mail и write для пакетного и интерактивного диалога соответственно. С помощью телефона мы не можем сказать, доступен ли кто-либо для общения с нами, пока не позвоним и не поговорим, возможно, с одним или несколькими другими людьми. Поскольку система UNIX постоянно записывает информацию о деятельности пользователей, было бы полезным уметь использовать эту информацию, чтобы узнать, кто доступен для общения, и получить общее представление о том, чем они занимаются. После этого мы можем решать, как и когда мы хотим связаться с ними. Activ может рассказать нам о том, сколько времени прошло с тех пор, когда кто-либо нажимал клавиши на своей клавиатуре. Если в вашей машине есть выделенные линии, то вы также можете узнать, находится ли данное лицо на своем рабочем месте. ЧТО ДЕЛАЕТ activ? Activ печатает информацию об активности всех пользователей систе- мы. Распечатка содержит имя пользователя, номер терминала, на котором он зарегистрировался, и время последнего обращения к клавиатуре. Если клавиатура была использована в течение последней минуты, этот терминал отмечается точкой. Если терминал не был использован в течение послед- них 24 часов, напротив него печатается слово "old". Во всех остальных случаях выводится время последнего использования терминала в минутах и секундах. Вот пример такой распечатки: ------------------------ | | root tty01 01:23 | sage tty05 . | batch tty12 old | nuucp tty16 . | Важным вопросом является то, как система узнает, когда кто-либо работает с клавиатурой? Поскольку терминальные устройства являются файлами, мы можем собрать информацию о терминале путем просмотра состояния файла, связанного с ним. В данном случае искомая информация - время последнего использования клавиатуры. То, что мы ищем, уже есть в системе. Это опция -u команды who. Эта опция, однако, существует только в системе AT&T UNIX System V, по- этому команда activ, по крайней мере в представленном виде, может быть реализована только в этой системе. Activ берет выход команды "who -u" и "отрезает" некоторые данные, не имеющие отношения к нашей цели. Путем проб и ошибок, а затем проверки исходного кода я обнаружил, что значение, используемое в команде "who -u", является временем "мо- дификации" файла устройства, которое сообщает системный вызов stat(2). На страницах руководства по stat дается полная таблица системных вызо- вов, которые изменяют временные характеристики файлов. В табл. 6.1 представлены эти характеристики: Таблица 6.1 Временные характеристики файла -------------------------------------------------------------- Временные Системные вызовы, изменяющие временные характеристики характеристики -------------------------------------------------------------- Время доступа creat, mknod, pipe, utime, read Время модификации creat, mknod, pipe, utime, write Время создания creat, mknod, pipe, utime, write, chmod, chown, link -------------------------------------------------------------- Давайте на некоторое время отвлечемся и поговорим о том, что эти временные характеристики означают и как они могут быть модифицированы. Одно интересное замечание: UNIX дает вам как обычному пользователю возможность установить любое время доступа и время модификации файла по вашему желанию. Это и хорошо, и плохо. Это обеспечивает гибкость, когда дело касается информации индексного дескриптора файла, но также позволяет скрывать следы при нарушении защищенной информации. Возьмем, например, файл login. Кто-нибудь мог бы вставить новую строку регист- рации перед старой и затем вернуть время доступа и время модификации в исходные значения. Тогда бы никто, просматривая файл /bin/login коман- дой ls, не смог бы сказать, что файл был изменен. Однако в системе UNIX имеется третье время - время создания. Система не позволяет вам изменить это время при помощи обычных команд. Системный вызов utime(2) обеспечивает изменение только времени доступа и модификации. Команда touch(1) также может изменить время доступа и модификации, но не время создания. Команда touch строится только на системном вызове. Она может делать не больше того, что обеспечивает системный вызов. Команда fsdb(1) (отладчик файловой системы) является единственным способом преодоления защиты от изменения времени созда- ния. Но даже fsdb не может управлять временем создания. Вы должны вый- ти за пределы поддерживаемых fsdb требований безопасности. Как это сделать, показано в последующих главах. В случае изменения регистрационной записи, рассмотренном выше, вы могли бы посмотреть на время создания и увидеть, что оно очень близко к текущему времени. Если же кто-либо использовал fsdb для подделки времени создания, вы никогда не будете иметь уверенности в том, что файл не был изменен. Какое же все это имеет отношение к определению того, делал ли пользователь что-либо недавно за своим терминалом? Время модификации изменяется вызовом write(2). Поэтому представляется разумным, что за- пись на терминал будет иметь место, когда драйвер читает символ, а за- тем посылает его обратно на экран. По мере того, как кто-то нажимает на клавиши, время модификации постоянно обновляется при эхо-отображе- нии символов. Когда символы перестают поступать с клавиатуры, приоста- навливается запись в файл терминала. Формула для определения последне- го времени работы за терминалом (last_activity) такова: last_activity = time(NULL) - mod_time, где mod_time - время модификации. Эта формула закодирована внутри ко- манды who(1). Отметим, что вызов команды time со значением NULL возв- ращает истинное текущее время. Команда activ не имеет опций. Если вы используете опции, напеча- тается сообщение об ошибке. ПОЯСНЕНИЯ В строках 4-8 выполняется проверка на наличие ошибок. Если число аргументов командной строки больше нуля, на стандартное устройство ре- гистрации ошибок выводится сообщение и программа завершается с неудач- ным статусом. Строка 10 - это команда, выполняющая вывод. Команда who вызвана с опцией -u для получения основных данных. Затем ее выход по конвейеру передается команде cut, которая отображает колонки 1-17 и 38-42. Тем самым печатается только три поля, как показано в нашем предыдущем при- мере. ------------------------------------------------------------ ИМЯ: info ------------------------------------------------------------ info Вывод на экран информации о пароле пользователя НАЗНАЧЕНИЕ Печатает информацию поля комментария из файла /etc/ passwd для указанного пользователя. ФОРМАТ ВЫЗОВА info login_name [ login_name ... ] ПРИМЕР ВЫЗОВА info russ Печатает информацию, которая хранится о пользователе russ ТЕКСТ ПРОГРАММЫ 1 : 2 # @(#) info v1.0 Display password info on a user Author: Russ Sage 2а Отобразить парольную информацию пользователя 3 4 for NAME in $@ 5 do 6 USER=`grep "^${NAME}:" /etc/passwd` 7 echo "$NAME:\t`echo ${USER}|cut -d: -f6`\t` echo ${USER}|cut -d: -f5`" 8 done ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ NAME Каждое имя, указанное в командной строке USER Полная запись в файле /etc/passwd для данного имени ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ info? Система UNIX использует конфигурационные файлы для хранения основной информации о пользователях и другой системной информации. Од- ними из наиболее популярных конфигурационных файлов являются /etc/group, /etc/passwd и /etc/inittab. Для получения информации о пользователях нам необходимо заглянуть в эти файлы. В системе Berkeley имеется команда finger, которая получает ин- формацию о пользователях из их регистрационных каталогов и из файла паролей. В System V не так много программ, выполняющих такого рода ра- боту, поэтому нам необходимо разработать их. ЧТО ДЕЛАЕТ info? Info - это командный файл, который получает информацию из регист- рационного каталога и комментарий о пользователе из файла /etc/passwd. Результат выглядит так: ------------------- | | name: home dir comments | имя: регистрационный каталог комментарии | Если в вашей системе используется это поле комментариев, то эта информация может быть полезной. "Вручную" выполнить просмотр пользова- телей в файле паролей можно следующим образом: grep login_name /etc/passwd При этом распечатывается вся строка со всеми полями данных. Info берет эти необработанные данные и выделяет из них каталог регистрации пользователя и поле комментария. В командной строке можно указать несколько регистрационных имен. Каждое имя берется по порядку из командной строки. ПРИМЕР $ for NAME in `cat /etc/passwd | cut -d: -f1` > do > NAMELIST="$NAMELIST $NAME" > done; info $NAMELIST Имя каждого пользователя в файле паролей добавляется к списку имен. Затем этот список передается в командную строку info, которая печатает все данные. Смысл команды: "Дай мне информацию обо всех поль- зователях системы". ПОЯСНЕНИЯ Строки 4-8 - это цикл for, который обрабатывает все имена, пере- данные в командной строке. Для каждого переданного имени выполняются строки 6 и 7. В строке 6 в переменную USER заносится результат команды grep, заключенной между символами ударения (`). Начиная с начала строки (обозначено символом ^), grep ищет имя, за которым следует символ дво- еточия (:). Такое указание заставляет выполнять поиск образца только в первом поле файла паролей. В строке 7 мы отображаем нашу выходную строку командой echo, внутри которой вложены другие команды echo. Мы могли бы получать эле- менты информации отдельно, присваивая их значения отдельным перемен- ным, и затем создавать необходимый формат вывода, используя значения этих переменных. Однако помещение всей информации в командную строку работает быстрее, и текст программы более компактный, хотя и не очень читабельный. Мы можем также использовать символы форматирования в ко- манде echo для форматирования нашей распечатки. Сначала отображается имя в том же виде, как оно получено из ко- мандной строки. Затем выводится табуляция (\t). За первой табуляцией следует поле номер шесть из файла паролей. Поскольку мы еще не имеем этих данных, мы должны выделить их из значения переменной USER, кото- рую мы уже имеем из предыдущей строки. Чтобы сделать это, мы командой echo выводим всю строку и выделяем шестое поле, используя разделяющие двоеточия. После этого поля мы выводим еще одну табуляцию и затем пя- тое поле файла паролей. Мы получили это поле таким же образом, как и шестое поле - эхо-отображением и выделением. Такая техника получения данных медленная, поскольку вовлечены все процессы, но это самый быстрый путь сделать это на языке shell. Коман- да awk была бы понятнее и, возможно, быстрее, но наша реализация де- монстрирует гибкость языка shell. Язык shell может выполнять почти все, но не всегда лучшим образом. Вот почему в некоторых случаях мы используем язык Си, в чем вы убедитесь по мере продвижения по нашей книге. ----------------------------------------------------------- ИМЯ: uchk ----------------------------------------------------------- uchk Проверка процессов, запущенных другим пользователем НАЗНАЧЕНИЕ Отобразить все процессы каждого пользователя, указанного в ко- мандной строке ФОРМАТ ВЫЗОВА uchk [-a] login_name [...] ПРИМЕР ВЫЗОВА uchk -a Вывод всех процессов, запущенных администраторами ТЕКСТ ПРОГРАММЫ 1 : 2 # @(#) uchk v1.0 Check processes of another user Author: Russ Sage 2а Проверка процессов другого пользователя 3 4 trap "rm /tmp/ps$$ 2> /dev/null" 0 1 2 3 15 5 6 if [ $# -eq 0 ] 7 then echo "uchk: argument error" >&2 8 echo "usage: uchk [-a] login_name [ ... ]" >&2 9 exit 1 10 fi 11 12 if [ "`echo $1 | cut -c1`" = "-" -a "$1" != "-a" ] 13 then echo "uchk: invalid argument $1" >&2 14 echo "usage: uchk [-a] login_name [ ... ]" >&2 15 exit 1 16 fi 17 18 ADMIN="administrators names go here" 19 if [ "$1" = "-a" ] 20 then shift 21 set $ADMIN $@ 22 fi 23 24 ps -ef > /tmp/ps$$ 25 for NAME 26 do 27 echo 28 fgrep "$NAME" /tmp/ps$$ 29 done ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ ADMIN Строка с именами пользователей, являющихся администраторами в вашей системе NAME Содержит каждое имя, считываемое из командной строки ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ uchk? Поскольку UNIX является многопользовательской системой, множество задач выполняются одновременно. Единственный способ следить за такими задачами - с помощью команды ps. Ps - это довольно специфичная команда в том смысле, что она должна обращаться к памяти (/dev/mem) и прохо- дить по связанному списку структур процесса. Это тяжелая вещь, поэтому нелегко сделать свою собственную команду для вывода такого рода инфор- мации. Вдобавок память защищена, и доступ к ней имеет только пользова- тель root. Так что мы должны или удовлетвориться опциями, которые дает ко- манда ps, или модифицировать вывод этой команды. Uchk скорее относится к последней категории. Мне хотелось получить список состояний про- цессов по имени пользователя, а не смесь информации, относящейся к разным пользователям. Хотя то, что я хотел, не соответствует ни одной из стандартных опций команды ps, я смог модифицировать ее выход для получения того, что мне надо. ЧТО ДЕЛАЕТ uchk? Uchk - это средство, которое генерирует, анализирует и сообщает о процессах, запущенных каждым указанным пользователем. За один раз вы можете проверить столько пользователей, сколько вам нужно. Все про- цессы для каждого пользователя печатаются вместе. Чтобы делать это, uchk должен работать с временными файлами. Система UNIX весьма элегантно управляет временными файлами. Shell поз- воляет использовать идентификатор процесса для создания уникальных имен файлов для каждого запуска. После того как утилита запущена, вре- менные файлы очищаются путем использования команды trap в shell'е. Ко- манда trap удаляет файл, если что-либо прервало выполнение командного файла или когда программа завершается. Это прекрасная особенность, ко- торая исключает накопление случайных файлов в системе. Если uchk была вызвана без аргументов или была использована недо- пустимая опция, то печатается сообщение об ошибке и выполнение завер- шается. Uchk имеет переменную ADMIN, которая определяет всех администра- торов вашей системы. Отредактируйте символьную строку, присваиваемую переменной ADMIN, указав имена администраторов, которых вы хотите про- верить. Имена должны быть разделены пробелами. Это позволит вам прове- рять процессы ваших администраторов, указывая опцию -a в командной строке. Все остальные имена должны быть указаны в командной строке. Естественно, вы таким же образом могли бы установить другие группы пользователей. Слежение за администраторами поможет вам оценить, как они управляют сохранностью информации, а также распознать администра- тивные задачи, которые имеют склонность загружать процессор. ПРИМЕР $ uchk -a russ uucp Показывает процессы всех администраторов, мои собственные, и про- цессы uucp по порядку. Все сообщения об ошибках выводятся на стандарт- ное устройство регистрации ошибок, а списки процессов выводятся на стандартное устройство вывода. ПОЯСНЕНИЯ Строка 4 - это оператор trap. Символьная строка между двойными кавычками содержит команды, которые должны быть выполнены, когда про- исходит прерывание. В этом случае мы удаляем временный файл и переад- ресовываем все выходные данные на нулевое устройство. Когда команда rm пытается удалить несуществующий файл, выводятся сообщения об ошибках. Поскольку мы не знаем, какие файлы могут быть в наличии в тот момент, когда возникнет прерывание, то мы хотим избавиться от сообщений об ошибках. Оператор trap активизируется по выходу из программы (program exit, сигнал 0), разрыву линии (hangup, сигнал 1), прерыванию (interrupt, сигнал 2), выходу (quit, сигнал 3) или программному завер- шению (software termination, сигнал 15). В строках 6-10 проверяется, переданы ли какие-либо аргументы. Если вы вызвали uchk без аргументов, выводится сообщение об ошибке и uchk завершается. В строках 12-16 проверяется, указана ли какая-то опция со знаком минус и является ли она опцией -a - единственно допустимой опцией. Ко- мандный файл делает это, применяя команды проверки для сравнения двух различных случаев. Первая проверка вырезает первый символ первого по- зиционного параметра и смотрит, является ли он символом "-". Следующая проверка делается для того, чтобы увидеть, что первый позиционный па- раметр не является опцией -a. Поскольку обе проверки соединены опера- цией AND, то для получения значения "истина" они обе должны быть истинными. Если они обе истинны, выводится сообщение об ошибке и uchk завершается. Почему мы должны выполнять такую сложную проверку? Проблема в том, что у нас нет иного способа определить, является первый позицион- ный параметр опцией или нет. Он может быть опцией, а может быть име- нем, которое нужно искать. Таким образом, мы должны задать вопрос: "Является ли это опцией, и если да, то допустима ли эта опция?" В строке 18 переменная ADMIN инициализируется символьной строкой, следующей за ней. Это строка, которую вы должны модифицировать в соот- ветствии с вашей системой. Все, что вы должны сделать - это использо- вать редактор vi и вставить в эту строку имена администраторов, разде- ленные пробелами. Ниже мы используем переменную ADMIN для обработки аргументов командной строки. В строках 19-22 проверяется, была ли указана в командной строке опция -a. Если да, эта опция удаляется из командной строки с целью из- бавления от нее. Строка 21 использует команду set для того, чтобы по- местить символьную строку ADMIN в позиционные параметры. Команда set вставляет значение переменной ADMIN, начиная с первого позиционного параметра, и сдвигает все настоящие параметры вправо. Тем самым в цикл for передается множество имен, которые должны быть обработаны. В строке 24 выполняется команда ps для всех пользователей. Эта команда использует опцию f для вывода большого количества данных. Ре- зультат помещается во временный файл, в имени которого применяется идентификационный номер процесса. Этот один большой файл представляет собой источник для остальных распечаток. Возможно, все это немного отстает от реального времени, но программа выполняется гораздо быст- рее, чем при многократном вызове команды ps. Строки 25-29 представляют собой цикл for, который выполняется от $1 до $x, где x - последний позиционный параметр. Для каждого имени выполняется следующее: печатается пустая строка для разделения листин- га (строка 27), затем командой fgreps (для ускорения) выбираются из временного файла все процессы, принадлежащие данному пользователю. Благодаря применению команды fgrep для каждого имени пользователя, все процессы данного пользователя печатаются за один раз. Когда закончится проверка всех имен, указанных в командной строке, цикл завершится, за- вершится командный файл и сработает оператор trap, который удаляет вре- менный файл. ------------------------------------------------------------- ИМЯ: watch ------------------------------------------------------------- watch Наблюдение за регистрацией указанных пользователей НАЗНАЧЕНИЕ Следит за тем, кто работает в системе, и сообщает о регистрации указанных пользователей. ФОРМАТ ВЫЗОВА watch [-k] [login_name ...] ПРИМЕР ВЫЗОВА watch Наблюдение за регистрацией всех пользователей, указанных во внутренней переменной LIST ТЕКСТ ПРОГРАММЫ 1 : 2 # @(#) watch v1.0 Watch for specific logins Author: Russ Sage 2а Наблюдение за регистрацией пользователей 3 4 if [ "`echo $1 | cut -c1`" = "=" -a "$1" != "-k" ] 5 then echo "watch: invalid argument $1" >&2 6 echo "usage: watch [-k] [login_name ...]" >&2 7 echo " -k kill background process" 8 exit 1 9 fi 10 11 if [ "$1" = "-k" ] 12 then if [ -s $HOME/.watch ] 13 then echo "killed `cat $HOME/.watch`" 14 kill `cat $HOME/.watch` 15 rm $HOME/.watch 16 exit 0 17 fi 18 fi 19 20 echo $$ > $HOME/.watch 21 22 LIST="root sys bin administrator1 administrator2 $*" 23 24 while : 25 do 26 for NAME in `who | cut -d" " -f1` 27 do 28 for PERSON in $LIST 29 do 30 if [ "$NAME" = $PERSON" ] 31 then echo ONLINE: $NAME 32 fi 33 done 34 done 35 sleep 10 36 done & ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ HOME Полный маршрут к вашему регистрационному каталогу LIST Список имен пользователей системы, разделенных пробелами NAME Содержит имена зарегистрированных в настоящий момент пользователей PERSON Отдельное имя из списка имен в переменной LIST ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ watch? В течение рабочего дня множество людей входят в систему и выходят из нее. Иногда единственный способ связаться с человеком - через маши- ну. Нам нужно средство, которое автоматически сообщит нам, что нужное лицо на связи. ЧТО ДЕЛАЕТ watch? Подразумевается, что watch является фоновой задачей, которая постоянно следит за тем, кто зарегистрировался. Когда лицо или лица, предварительно вами отмеченные, регистрируются в системе, на ваш экран выводится сообщение о том, что они на связи. Количество имен, за которыми вы можете следить, не ограничено. Общий перечень имен - это объединение имен, указанных в командной строке, и списка системных пользователей, т.е. пользователя root и всех сопровождающих его администраторов. В командном файле watch список системных имен ВСЕГДА включен. Это вызвано тем, что это важные пользователи и вы хотите всегда знать об их входе в систему и выходе из нее. Это отличает watch от uchk, поскольку последний требует указа- ния опции -a для включения списка с системными именами. Хотя мы устанавливаем watch с точки зрения пользователя, который желает следить за администраторами и другими пользователями, админист- раторы тоже могут иметь список "критических пользователей" или предпо- лагаемых нарушителей защиты информации и использовать watch для полу- чения сигнала о регистрации таких пользователей. За кем бы мы ни следили, мы сталкиваемся с одной проблемой. После регистрации в системе указанного лица сообщение об этом поступает на ваш экран, не обращая внимания на то, чем вы в данное время занима- лись, что не очень приятно. Единственный способ остановить вывод сооб- щения - аварийно завершить watch командой kill. Это легко сделать пу- тем помещения идентификатора этого процесса в файл $HOME/.watch. Затем этот номер может быть использован в операторе kill для остановки вы- полнения командного файла. Для того чтобы попроще избавиться от watch, возможность аварийного завершения оформлена в виде опции -k данного командного файла. ПРИМЕРЫ 1. $ LIST="root bin" watch daemon Если переменная LIST не была инициализирована внутри самого ко- мандного файла watch, то мы можем инициализировать ее на командном уровне shell'а, а затем вызвать watch. Список пользователей для watch будет такой: root, bin и daemon по порядку, поскольку watch добавляет имена, указанные в ее командной строке к именам, имеющимся в перемен- ной LIST. Такой способ указания имен более гибок, чем жесткое програм- мирование части списка в тексте командного файла watch, но он требует помнить больше информации и больше нужно набирать на клавиатуре. 2. echo "Watch (y/n): \c" read ANS if [ "$ANS" = "y" ] then watch fi Это фрагмент, который вы можете вставить в ваш .profile. Когда вы регистрируетесь, вам автоматически предлагается запустить watch. Если вы ответите "y", watch станет фоновой задачей и запустится по умолча- нию (просматривая пользователей согласно списку в переменной LIST). Если будет введен любой символ, отличный от y, watch не запустится. ПОЯСНЕНИЯ Строки 4-9 выполняют проверку на наличие ошибок в опциях команд- ной строки. Для первого позиционного параметра проверяется, что он имеет тире и не является единственной допустимой опцией "-k". Если ре- зультатом проверки является истина, печатается сообщение об ошибке и командный файл завершается. Строки 11-16 проверяют, что первый позиционный параметр - это -k. Если это так, значит пользователь хочет уничтожить уже запущенный про- цесс watch. В этом случае выводится сообщение, указывающее идентифика- тор процесса, который будет уничтожен, и выполнение продолжается. В строке 12 мы смотрим, существует ли в нашем регистрационном каталоге файл с именем .watch. Если нет, то это означает, что предыдущий эк- земпляр watch предположительно уже уничтожен и нет необходимости пы- таться сделать это снова, поэтому происходит переход к оставшейся части программы и выполнение watch происходит так, как будто опция "-k" не была указана. Если же файл .watch имеется, то в строке 14 используется команда kill для уничтожения фонового процесса watch. Напомним, что при использовании опции -k мы подразумеваем, что watch был вызван ранее, поэтому файл $HOME/.watch имеет идентификационный номер самого про- цесса watch. В строке 15 удаляется временный файл watch и в строке 16 происходит выход из программы. Теперь командный файл watch более не выполняется как фоновый. Строка 20 выполняется, если опция -k не была указана или если нет файла .watch. (Последнее может произойти, если пользователь пытается уничтожить процесс, забыв, что он уже был уничтожен.) Если опция -k не была указана, мы можем считать, что watch был вызван, чтобы стать фо- новым процессом и выполнять свою работу. Для того чтобы сделать это, текущий процесс отображает свой идентификатор процесса в файл .watch. Этот файл остается в вашем регистрационном каталоге до тех пор, пока он не будет удален вручную или же изменен путем повторного запуска watch. В строке 22 инициализируется переменная LIST. Ее значением явля- ется символьная строка с именами, разделенными пробелами. Вам нужно вручную отредактировать переменную LIST перед запуском в вашей систе- ме. Просто уберите ее нынешнее содержимое и вставьте туда имена адми- нистраторов вашей системы. Если в командной строке будут указаны до- полнительные имена пользователей, они будут добавлены в переменную LIST посредством символов расширения параметров $*. Тем самым перемен- ная LIST станет основным списком всех имен пользователей, за которыми будет вестись наблюдение. Строки 24-36 выполняют цикл постоянного наблюдения. В начале каж- дой итерации с помощью команды who создается список имен пользовате- лей, который передается циклу for в строке 26. Цикл for использует ко- мандную подстановку для получения списка слов, образованного из перво- го поля команды who. Каждое зарегистрированное имя сравнивается со списком предварительно определенных имен, за которыми мы наблюдаем. Обратите внимание, что внешний цикл while сам себя помещает на выпол- нение в фоновом режиме. Это означает, что вам нет необходимости вво- дить это с клавиатуры. Строки 29-33 управляют внутренним циклом, который проходит по именам, содержащимся в нашем основном списке, и сравнивает их с имена- ми, полученными от команды who. Когда имя, полученное от команды who (имя зарегистрированного пользователя) совпадает с именем в нашем списке, на экран выводится сообщение о том, что данное лицо зарегист- рировалось. После того как все имена проверены, командный файл watch приоста- навливается на 10 секунд (строка 35). Когда он снова пробуждается, вы- полняется следующая итерация вечного цикла while. Все зарегистрирован- ные имена вновь сравниваются со списком. Это будет продолжаться до тех пор, пока вы не прекратите выполнение watch. Как отмечалось ранее, watch можно легко уничтожить с помощью опции -k или же вручную путем ввода команды "kill `cat $HOME/.watch`". МОДИФИКАЦИИ Watch выполняет довольно мало работы и использует какую-то часть времени центрального процессора. Вы можете поэкспериментировать с уве- личением интервала паузы (sleep), чтобы watch запускался не так часто. Большинство пользователей находятся в системе по крайней мере минуту, поэтому вы можете попробовать значение sleep(60). Вы по-прежнему може- те обнаружить регистрацию всех интересующих вас пользователей? --------------------------------------------------------------- ИМЯ: whox --------------------------------------------------------------- whox Команда who с дополнительными возможностями НАЗНАЧЕНИЕ Предоставляет много дополнений к выходу команды who и позволяет применять данные who для других приложений. ФОРМАТ ВЫЗОВА whox [-f] [-n] [-m] [-p] [-t] [-w] [-x] где -f указывает каждого зарегистрированного пользователя -n сортирует выход команды who по именам -m передает почту каждому пользователю -p выводит информацию о паролях пользователей -t сортирует выход команды who по времени (умолчание) -w показывает возможность записи на зарегистрированные терминальные устройства -x дополнительная информация о регистрационном каталоге и паролях ПРИМЕР ВЫЗОВА whox -w Показывает права доступа к файлу (возможность чтения и записи) для каждого зарегистрированного терминального устройства ТЕКСТ ПРОГРАММЫ 1 : 2 # @(#) whox v1.0 Who with expanded options Author: Russ Sage 2а Команда who с дополнительными опциями 3 4 XTRA="no" 5 SORT="sort -b +2" 6 DISPLAY="norm" 7 8 CUT1="cut -d' ' -f1" 9 CUT5="cut -d: -f5" 10 CUT6="cut -d: -f6" 11 12 for ARG in $@ 13 do 14 case $ARG in 15 -f) DISPLAY="finger" 16 COMMAND="finger \$NAME; echo";; 17 -n) SORT="sort";; 18 -m) DISPLAY="mail";; 19 -p) DISPLAY="pass" 20 COMMAND="grep \"^\$NAME:\" /etc/passwd";; 21 -t) SORT="sort -b +2";; 22 -w) DISPLAY="write";; 23 -x) XTRA="yes";; 24 *) echo "whox: invalid option $ARG" 25 echo "usage: whox [-f] [-n] [-m] [-p] [-t] [-w] [-x]" 26 echo " -f finger users" 27 echo " -n sort by name" 28 echo " -m mail to each user" 29 echo " -p password info on users" 30 echo " -t sort by time (default)" 31 echo " -w show writeability of devices" 32 echo " -x extra home dir and gcos info" 33 exit 1;; 34 esac 35 done 36 37 if [ "$XTRA" = "yes" ] 38 then EXTRA="| while read LINE; do \ 39 NAME=\`echo \$LINE | cut -d' ' -f1\`;\ 40 ENTRY=\`grep \"^\$NAME:\" /etc/passwd\`;\ 41 echo \"\$LINE\t\`echo \$ENTRY|\$CUT6\`\t\`echo \$ENTRY|\$CUT5\` \";done" 42 else EXTRA="" 43 fi 44 45 case $DISPLAY in 46 norm) eval "who | $SORT $EXTRA";; 47 finger|pass) for NAME in `who | $SORT | cut -d' ' -f1` 48 do 49 eval $COMMAND 50 done;; 51 mail) who | cut -d' ' -f1 | while read NAME 52 do 53 echo "mail to $NAME (y/n): \c" 54 KB=`line < /dev/tty` 55 if [ "$KB" = "y" ] 56 then mail $NAME < /dev/tty 57 fi 58 done;; 59 write) ls -il `who | sed "s/...........\(.......\).* /\/dev\/\1/"`;; 60 esac ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ ARG Аргументы командной строки COMMAND Команда, которую следует выполнить при использовании команды who со списком имен CUT1 Содержит синтаксис для выделения первого поля строки CUT5 Содержит синтаксис для выделения пятого поля строки CUT6 Содержит синтаксис для выделения шестого поля строки DISPLAY Определяет, какой режим отображения использовать ENTRY Запись в файле паролей для указанного пользователя EXTRA Данная переменная содержит полный цикл shell-команд, хранимых в виде одной строки KB Входные данные от клавиатуры, полученные в цикле NAME Содержит в каждый данный момент времени одно имя из списка всех регистрационных имен SORT Содержит выполняемый тип сортировки XTRA Флаг, определяющий, должны ли быть активизированы дополнительные опции ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ whox? Как уже ранее обсуждалось в других местах этой книги, система UNIX стремится обеспечить минимум возможностей в любой заданной об- ласти интересов. Это не значит, что UNIX плохая система. Наоборот, она делает гораздо больше, чем большинство других операционных систем. Но очень часто мы хотим сделать немного больше того, что нам предоставля- ет базовая система. Получение информации о том, кто использует машину, может быть применено для многих целей. Основная информация предоставляется коман- дой who, но она может быть не в том виде, который вам нужен для конк- ретной цели. В любом случае, все, что делает команда who - дает вам моментальную информацию о том, кто зарегистрировался. Нам необходим некоторый способ автоматического доступа к этому списку зарегистриро- ванных имен и использования его для наблюдения, общения или других це- лей. ЧТО ДЕЛАЕТ whox? Whox - это инструментальное средство, расширяющее возможности ко- манды who. Оно не только может переупорядочить в соответствии с вашими требованиями список, полученный от команды who, но также может указать каждого зарегистрированного пользователя, передать почтовое сообщение любому зарегистрированному пользователю, просмотреть парольную инфор- мацию всех зарегистрированных пользователей и показать информацию, взятую из индексного дескриптора файла, соответствующего терминальному устройству каждого зарегистрированного пользователя. По умолчанию действие whox заключается в печати обычного выхода команды who в порядке времени регистрации от более раннего до более позднего. Опция -x добавляет к этому списку информацию из регистраци- онного каталога и поле комментария из файла паролей. Если эта опция -x кажется вам знакомой, то так оно и есть, поскольку это то же самое, что и команда info, представленная ранее. Whox имеет четыре различных режима отображения. Первый это формат обычного выхода команды who. Whox позволяет вам сортировать его двумя разными способами. Опция -n сортирует по именам, а опция -t (которую не нужно указывать, поскольку она используется по умолчанию) сортирует по времени регистрации. Второй режим отображения состоит из режимов указания и паролей, включаемых опциями -f и -p. Основное отличие от первого режима заклю- чается в том, что выход команды who не печатается, а используется для генерации списка имен пользователей, который применяется для других целей. Мы указываем каждого пользователя или печатаем парольную запись каждого пользователя. Выполняемая команда хранится в переменной, поэ- тому мы можем иметь общий цикл, использующий особым образом перемен- ные. (Команда finger имеется в системе Berkeley UNIX и в некоторых других, но не во всех реализациях. Посмотрите руководство, чтобы вы- яснить, что выводится на экран по этой команде.) Третий режим - это почтовый режим, в котором вы имеете возмож- ность посылки почтового сообщения каждому зарегистрированному пользо- вателю. Вам задается вопрос о том, действительно ли вы хотите сделать это. Больше от вас ничего не требуется. Этот режим выбирает опция -m. Последний режим - это режим записи на терминал. Режим записи (оп- ция -w) показывает информацию о файле терминала для каждого зарегист- рированного терминального устройства. Эта информация полезна, если вы хотите использовать команду UNIX'а write. Посмотрев на права доступа к файлу устройства пользователя, вы можете сказать, имеется ли у вас возможность записать текст на его экран. Некоторые пользователи, кото- рые не хотят, чтобы их прерывали, закрывают право записи на их терми- нал, выполняя команду "mesg n". Вопрос о праве записи касается любого способа посылки текста в другой файл, а не только с использованием ко- манды write. Право записи также защищает от таких вещей, как "echo hello > /dev/tty00". Способ обработки аргументов в командной строке приводит к несколько странному обращению с данной утилитой. Каждый аргумент про- веряется по порядку и устанавливает внутренние флаги. Если вы постави- те в конце списка какую-либо опцию, меняющую флаг, установленный одной из предыдущих опций, то вы получите действие последней опции. (Другими словами, некоторые опции взаимно исключают друг друга. Лучший способ изучить это - внимательно прочитать исходный текст командного файла и выполнить несколько экспериментов с различными наборами опций.) Например, мы хотим указать каждого пользователя. Мы используем опцию -f. Опция -f устанавливает в качестве режима отображения режим указания. Если мы поместим опцию -w справа от -f, как в команде "whox -f -w", то установится режим записи на терминал. Команда whox будет считать, что вы вообще не указывали опцию -f. На самом деле это не представляет большую проблему, если вы знаете, что делает каждая оп- ция. Случайно смешивая их в одной команде, вы можете получить несколь- ко странные выходные данные. ПРИМЕРЫ 1. $ sh -x whox -x Запуск интерпретатора shell в отладочном режиме выполнения, пода- ча ему командного файла whox в качестве данных, передача опции -x для whox. Отладочный режим показывает присвоение значений переменным и вы- зовы команд. (Мы видели это ранее.) 2. $ whox -n -x Печать выходных данных команды who, отсортированных по именам и выдача дополнительных данных. ПОЯСНЕНИЯ В строках 4-10 выполняется инициализация переменных. Переменная XTRA устанавливается в значение "no". Эта переменная используется для построения командной строки при использовании опции -x. Установка XTRA в значение "no" означает, что действие по умолчанию - не получать до- полнительную информацию. Умолчанием для сортировки является сортировка по времени регист- рации. Это указывается сортировкой по колонке, стоящей после второй (+2) и игнорированием ведущих пробелов (-b) в строке 5. Такой же син- таксис применен ниже с опцией -t. Опция -t здесь лишняя, но она делает более понятной командную строку. Опция -n также изменяет синтаксис сортировки, чтобы просматривать первую колонку, которая является списком имен. Строка 6 инициализирует обычный режим отображения, которым явля- ется печать стандартного выхода команды who. Если присутствуют другие опции, соответственно изменяется переменная DISPLAY. Строки 8,9 и 10 инициализируют некоторые переменные для команд вырезки, что делает более компактными последующие команды. Если неко- торые командные строки оказываются слишком длинными, вы можете по- местить нужный текст в переменные и подставить их при необходимости. Переменная CUT1 выглядит так, как будто она должна работать, но она не работает в моей системе. Почему она не работает, объясняется ниже. Просто запомните, что эта строка никогда не используется в данной программе. Мы пока оставляем ее, чтобы поговорить о ней позже. Вы мо- жете убрать ее, если хотите. Строки 12-35 обрабатывают аргументы командной строки. Цикл for подставляет в переменную ARG каждый параметр по порядку и выполняет оператор case по значению ARG. Если опцией является -f, строка 15 изменяет переменную DISPLAY в режим указания. Она также подставляет в переменную COMMAND команду finger, которая выполнится в следующем цикле. Причина, по которой нам нужно иметь переменную, содержащую команду, заключается в том, что данный цикл является общим циклом, применяемым для двух разных целей. Для того чтобы один и тот же цикл справился с двумя различными задача- ми, мы помещаем эти задачи в переменную и выполняем эту переменную. Это значительно сокращает общее количество текста, хотя привносит не- которую дополнительную работу. Обратите внимание в строке 16, что сим- вол $ экранирован в операторе присваивания. Это необходимо, ведь мы хотим, чтобы переменная COMMAND содержала символьную строку $NAME, а не значение, которое имеет переменная NAME после присваивания. Значе- ние NAME расшифровывается в цикле во время его выполнения. Строка 17 обрабатывает опцию -n. Все, что здесь требуется - изме- нить способ сортировки выхода команды who, чтобы отразить порядок по именам. Поскольку имя находится в первой колонке выхода команды who, а команда sort сортирует по умолчанию по первой колонке, то используется команда sort без опций. Строка 18 обрабатывает опцию -m для передачи почтовых сообщений. Здесь мы должны изменить режим отображения на почтовый. Все, что нужно для этого, находится в цикле mail, и не требуется инициализировать ни- какие переменные. Строки 19 и 20 справляются с опцией -p. Опция паролей изменяет режим отображения на парольный режим и устанавливает команду, которую мы вызываем, находясь в общем цикле. В данном случае мы используем ко- манду grep для получения парольной записи из файла /etc/passwd. Обра- тите внимание, что в строке 20 мы используем внутренние двойные кавыч- ки. Для этого мы вынуждены экранировать их символами обратной косой черты. Напомним, что обратная косая черта используется для отмены спе- циального значения особых shell-символов. Строка 21 управляет опцией -t. Как уже упоминалось ранее, опция -t в действительности не требуется в данной программе. Поскольку она является умолчанием, требуемые для нее действия уже были предприняты в начале программы - была выполнена точно такая же инициализация. Син- таксис команды sort точно такой же, как и в строке 5. Строка 22 обрабатывает опцию -w для показа возможности записи в файлы терминалов. Единственное, что нужно здесь сделать - изменить ре- жим работы терминала. Строка 23 управляет опцией -x. Поскольку для получения дополни- тельной информации требуется довольно сложная инициализация, то мы только устанавливаем в этом месте флаг XTRA, показывающий, что мы хо- тим выполнить эту инициализацию позже. Строка 24 - это улавливатель для обработки ошибок. Символ * соот- ветствует любому символу, который не был распознан ранее. Печатается сообщение об ошибке и синтаксическая подсказка, и whox завершается. Строки 37-43 устанавливают переменные, используемые в опции до- полнительной информации. Строка 37 проверяет, установлена ли перемен- ная XTRA в состояние "yes", что имеет место только тогда, когда в ко- мандной строке имелась опция -x. Если это так, то в переменную EXTRA заносится много всяких вещей, которые мы рассмотрим позже. В противном случае в переменную EXTRA заносится пустая строка, так что она никак не проявляется на стадии фактического выполнения. Переменная EXTRA здесь очень важна и на самом деле делает неболь- шой фокус. Мы собираемся поместить в переменную некоторый код, требуе- мый для обработки опции -x. Поскольку дополнительная информация, кото- рую мы хотим получить, требует предварительно некоторой обработки, то мы помещаем в переменную некоторый командный текст. Как только эта пе- ременная начинает выполняться, выполняется и этот командный текст. Это похоже на макрокоманду, только ее текст находится фактически в испол- няемой программе. Строки 38-41 вставлены внутрь переменной EXTRA. Это сделано путем взятия в двойные кавычки всех четырех строк. Все специальные символы, которые должны быть частью данных в этой переменной, должны быть экра- нированы символами обратной косой черты. В строке 38 в переменную EXTRA заносится символ конвейера (|) и начало цикла while. В конце строки 38 имеется символ косой черты, указывающий интерпретатору shell, что присваивание продолжается после символа конца строки (возв- рата каретки или перевода строки). Строка 39 присваивает переменной NAME значение поля, вырезанного из данных, читаемых в цикле while. Напомним, что весь данный оператор помещается внутрь переменной EXTRA. Когда я выше упоминал, что в стро- ке с переменной CUT1 есть проблемы, то как одно из таких проблемных мест я имел в виду именно это. Когда я попытался использовать перемен- ную CUT1 в этом операторе вместо указания команды cut, shell не смог правильно распознать этот оператор. Одинарные кавычки, отмечающие сим- вол-разделитель для вырезки, не были распознаны. В результате команда cut считала, что символом-разделителем является символ ' и после этого аварийно завершалась, поскольку второй символ ' представлял собой не- допустимое описание списка для опции -f. Строка с опцией -f шла позже, но команда cut этого никогда не узнавала, поскольку аварийно заверша- лась до этого. Когда я заменил переменную CUT1 просто командой cut, эта проблема исчезла. Давайте рассмотрим, как я отлаживал эту часть. Я использовал shell с опцией -x, поэтому я мог следить за тем, что происходит. Как вы можете видеть, когда переменная CUT1 была инициализирована, одинар- ные кавычки находились все еще в операторе, но когда выполнялась настоящая команда cut, одинарные кавычки уходили при синтаксическом расширении. Для генерации такого списка данных я выполнил следующий вызов: sh -x whox -x. Вот что я увидел: XTRA=no SORT=sort -b +2 DISPLAY=norm CUT1=cut -d' ' -f1 <- Одинарные кавычки все еще здесь. Основная проблема. CUT5=cut -d: -f5 CUT6=cut -d: -f6 XTRA=yes + who + read LINE + sort -b +2 + echo russ console Jun 20 14:11 + cut -d -f1 <- Теперь выполняется правильно. Кавычек нет. Это сокращенная распечатка. Она показывает, что когда выполнялась команда cut, она не имела одинарных кавычек. Когда же запускалась пе- ременная CUT1, она имела одинарные кавычки. Я не мог представить, как избавиться от кавычек, поэтому я просто вставил вызов самой команды cut обратно на это место. Может быть какой-нибудь молодой растущий мастер сможет себе это представить. Во всяком случае, вы можете видеть полезность отладки. Цикл, выполняющий такое же присваивание, имеет такой вид при обычном стиле записи на языке shell: | while read LINE do NAME=`echo $LINE | cut -d' ' -f1` ENTRY=`grep "^$NAME:" /etc/passwd` echo "$LINE\t\`echo $ENTRY|$CUT6\`\t\`echo $ENTRY|$CUT5\`\" done Для того чтобы поместить такой же цикл в переменную, мы должны экранировать в этом тексте все специальные символы. Строки 45-60 - это оператор case, который реализует различные ре- жимы отображения. Строка 46 выполняет обычный режим отображения коман- ды who. Поскольку в обычном режиме имеется возможность использовать переменную EXTRA, нам необходимо произвести повторный разбор командной строки командой eval, чтобы эта переменная приняла свое истинное зна- чение во время исполнения. Обратите внимание, что в команде eval име- ются кавычки, заключающие всю командную строку. Это необходимо потому, что вся строка является одним набором входных данных для команды eval. Без кавычек команда eval не работала бы. Переменная EXTRA не подверга- ется повторному разбору. Строки 47-50 управляют режимами указания пользователя и выдачи информации из файла паролей. Оба эти режима используют один и тот же цикл. Цикл for использован для установки переменной NAME в значение первого поля каждой строки, полученной от команды who. Для каждого имени, вырезанного из результата работы команды who, выполняется пов- торный синтаксический разбор командой eval переменной COMMAND (которая была установлена в операторе case, выполнявшем разбор аргументов). Тем самым повторно анализируются и выполняются команды, находящиеся в пе- ременной COMMAND. Для режима указания пользователя переменная COMMAND содержит команду finger, а для режима паролей в COMMAND хранится ко- манда grep. Строки 51-58 похожи на режим указания пользователя. Этот цикл то- же требует имена от команды who, но вместо использования оператора for мы используем метод прямой пересылки по конвейеру. Результат работы команды who по конвейеру передается команде cut (переменная CUT1 и здесь бы не работала), которая по конвейеру передает данные в цикл чтения while. Обратите внимание, что в этом месте нет никакой сорти- ровки. По умолчанию результат команды who выводится в порядке номеров терминальных устройств. Я не думаю, однако, что порядок вывода этих данных имеет большое значение. Для каждого имени пользователя выводится запрос о том, хотите ли вы передать ему почтовое сообщение. При чтении ответа в строке 54 должна быть использована команда UNIX'а line. Почему? Потому что весь цикл использует оператор read для чтения имен. Оператор read читает только со стандартного ввода, который в данном случае привязан к кон- вейеру. Для получения входных данных с клавиатуры мы должны использо- вать команду line, которая получает их из файла /dev/tty. Это расп- ространенный способ чтения данных с клавиатуры из переадресованного цикла. Строка 55 проверяет, является ли ответом символ y. Если да, вызы- вается команда UNIX'а mail, и снова ввод переадресовывается из файла /dev/tty (поскольку строки почтового сообщения мы должны вводить с клавиатуры.) В данном случае мы фактически переадресовываем стандарт- ный ввод для вызова подчиненного shell-процесса, выполняющего команду mail. Без выполнения переадресации команда mail читает из файла /dev/null, что нарушает выполнение всего цикла whox. Строка 59 управляет режимом показа возможности записи на терми- нал. Цель здесь такова - использовать одну команду ls и, применяя под- чиненный процесс, извлечь файлы терминальных устройств из выходных данных команды who. Эти файлы являются вторым полем результата команды who. Сначала запускается команда who, которая по конвейеру передает свои данные команде sed. Затем sed использует команду подстановки для отбрасывания всего, кроме того, что ограничено символами \( и \). Последующая часть коман- ды подстановки ссылается на этот ограниченный участок с помощью обоз- начения \1. Используя символ . как соответствующий любому символу распечатки, мы должны всего лишь посчитать столбцы, которые нам нужно вырезать. Кроме того, имена устройств в команде who не имеют префикса /dev/, который нам необходим. Команда sed вставляет его перед текстом, вырезанным из команды who. В результате команде ls дается список пол- ных маршрутных имен ко всем файлам устройств зарегистрированных поль- зователей. Затем это выводится на экран. ЗАЩИТА ЛИЧНОЙ ИНФОРМАЦИИ Другой стороной осведомленности пользователя является обеспечение разумной степени безопасности. Ваша потребность в защите информации зависит от рода выполняемой вами работы и от чувствительности информа- ции, которой вы управляете. Однако все хотят секретности и чувства бе- зопасности, которое появляется вместе с обоснованной уверенностью в том, что они не могут стать жертвой нарушения защиты информации. Так же, как вы можете изготавливать приспособления, помогающие сделать ваш дом менее привлекательным для грабителей, так и каждый пользователь может сделать приспособления, которые помогают поддерживать секретность и безопасность его работы. Подобно полиции, системные администраторы обычно имеют дело только с нарушением защиты уже после свершившегося факта. Вы можете использовать средства, представленные нами в данном разделе, чтобы помочь уберечь ваши файлы от несанкционированного просмотра или разрушения. Любая компьютерная система требует некоторого рода защиты. Уровни защиты включают в себя физическую защиту (центрального процессора, дисков и терминалов), защиту файлов, защиту процессов и всей работаю- щей системы. В многопользовательской среде еще более важно усиливать защиту. Каждый пользователь имеет право засекречивать и защищать свою среду и свои файлы. Ни один компьютер не имеет стопроцентной защиты. Ваша среда лишь настолько защищена, насколько вы сделали ее таковой. Защитные мероприятия могут достигать такой степени, что начинают ме- шать свободному обмену идеями и затруднять использование гибкости системы или исследование ее новых аспектов. Лично я считаю, что поль- зователи должны иметь свободу делать все, что они хотят, пока это не вредит системе или другим пользователям. Большинство информации, при- веденной в данной книге, подтверждает это убеждение. Тип защиты, который обсуждается в данной главе, касается личного аспекта защиты пользователя системы UNIX. Я хочу, чтобы место, где фи- зически находятся мои разработки, мой регистрационный каталог и любые процессы, которые я запускаю в системе, были защищены. Для полного комфорта мне нужно знать, что никто не влезет в мои вещи (посредством обычных методов доступа или взлома) и не будет заглядывать мне через плечо, чтобы наблюдать, чем я занимаюсь. В данном разделе мы рассмотрим инструментальные средства acme, inuse и lock. Acme - это препроцессор к команде UNIX'а acctcom. Acctcom выполняет довольно неплохую работу по отображению учетной ин- формации, но некоторые опции требуется указывать все время. Acme уста- навливает их для нас. Напомним, что учетные записи хранятся в виде структуры, а не в текстовом виде, и поэтому мы заставляем acctcom по- казывать их для нас. Следующее средство, inuse, позволяет вам установить ваш терминал как "занятый", когда вы куда-нибудь уходите. Это эффективно блокирует его и не позволяет никому использовать его. Для реализации такого бло- кирования представлены программы как на языке Си, так и на языке ин- терпретатора shell. Последнее средство, lock, используется для блокирования и разбло- кирования прав доступа к файлу и является на самом деле простым интер- фейсом с командой chmod. ------------------------------------------------------------ ИМЯ: acme ------------------------------------------------------------ acme Отображение учетной информации обо мне НАЗНАЧЕНИЕ Генерирует опции, необходимые для вывода на экран информации обо мне, которая хранится в учетном файле. ФОРМАТ ВЫЗОВА acme [-l] [-u] ПРИМЕР ВЫЗОВА acme -u Выводит всю учетную информацию о пользователе с именем $LOGNAME ТЕКСТ ПРОГРАММЫ 1 : 2 # @(#) acme v1.0 Give accounting info on me Author: Russ Sage 2а Дать учетную информацию обо мне 3 4 if [ "$1" != "-l" -a "$1" != "-u" ] 5 then echo "usage: acme [-l] [-u]" >&2 6 echo " -l for ttyline" >&2 7 echo " -u for user name" >&2 8 exit 0 9 fi 10 11 OPT="" 12 for ARG in $* 13 do 14 case $ARG in 15 -l) OPT="$OPT -l `basename \`tty\``";; 16 -u) OPT="$OPT -u $LOGNAME";; 17 *) OPT="$OPT $ARG";; 18 esac 19 done 20 21 echo "acctcom $OPT" 22 acctcom $OPT ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ ARG Каждое значение, указанное в командной строке LOGNAME Переменная среды, содержащая мое регистрационное имя OPT Объединенный список всех опций и их аргументов ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ acme? Большинство больших систем UNIX запускают стандартное программное обеспечение для сбора учетной информации об использовании системы. Ре- зультаты учетных транзакций передаются обычно в файл /usr/adm/pacct. Фактически сбор учетной информации выполняется ядром системы. Каждый раз при завершении процесса программы сбора учетной информации в ядре производят одну запись. Переключателем, который включает и выключает эту операцию, является acct(2). Команды пользовательского уровня также взаимодействуют с системным вызовом (accton(1M)) и печатают результаты сбора учетной информации (acctcom(1)). Теперь, когда мы знаем, где находятся учетные записи и как они туда попадают, нам нужно напечатать эту информацию. Acctcom может пе- чатать таблицы с информацией, но вам необходимо знать, какой использо- вать индекс. Просмотр может производится по номеру терминальной линии (это полезно, если идентификатор процесса был изменен командой setuid), по имени пользователя, по группе, по времени и т.д. Я наибо- лее часто использую опции поиска информации по номеру линии терминала и по имени пользователя. С их помощью вы можете получить список всех основных данных, имеющих отношение к вам. Когда вы вызываете acctcom с этими опциями, вам необходимо указать дополнительную информацию, такую как имя вашего терминала и ваше пользовательское имя. Было бы хорошо, если бы мы могли уменьшить количество нажатий на клавиши и объем вы- числений, требуемых для получения информации. Для этого и предназначен acme. ЧТО ДЕЛАЕТ acme? Acme - это интерфейсный процессор для команды acctcom(1). Он слу- жит для выдачи информации, которая требуется согласно указанным опци- ям. Вы должны только дать командному файлу acme опции в сокращенном виде, а все остальное он сделает сам. Если acme вызывается без аргу- ментов, программа acctcom выведет по умолчанию все записи. Команда acctcom имеет много опций. В действительности мы исполь- зуем одну или две, но зато используем их часто. Двумя опциями, которые понимает acme, являются -l и -u. Когда указана опция -l, acme получает имя вашего терминала и помещает его в командную строку. Если указана опция -u, acme получает ваше пользовательское имя и тоже вставляет его в командную строку. Время от времени используются другие опции для бавления специфической информации или небольшого изменения выходного формата. Для того чтобы была возможность использовать другие опции ко- манды acctcom, acme включает в командную строку, формируемую для fcctcom, любые дополнительные корректные опции acctcom, переданные в командной строке для acme. Таким образом, acme поддерживает базовые возможности, а кроме того позволяет вам подгонять команду под ваш вкус. Перед тем, как начнется выполнение команды acctcom, на экран вы- водится расширенный вид командной строки, так что вы можете видеть ко- мандную строку, сгенерированную acme. Без этого может получиться пута- ница, поскольку вы не будете знать, что собирается делать программа. ПРИМЕРЫ 1. $ acme Выдача ВСЕХ моих учетных данных. Это последовательный список всех команд, которые были запущены начиная с момента загрузки по настоящее время. Счастливого чтения! 2. $ acme -u -b Печать в обратном порядке всех учетных записей с моим пользова- тельским именем. Обратный порядок означает - от самой последней из предыдущих команд до моей первой команды. 3. $ acme -l Вывод всех учетных записей для терминальной линии, которую я сей- час занимаю. Сюда могут быть включены фоновые процессы, оставшиеся в системе от предыдущих пользователей моей терминальной линии или даже процессы, имеющие другие идентификационные номера пользователей (из-за программ типа setuid, которые я мог запустить), которые запущены с этой же терминальной линии. ПОЯСНЕНИЯ В строках 4-9 выполняется проверка на наличие ошибок. Если пер- вый позиционный параметр не -l и не -u, то это ошибка. Выводится сооб- щение об этом и программа завершается. В строке 11 переменная OPT инициализируется пустой строкой. Эта переменная содержит все дополнительные опции acctcom и их аргументы. Строки 12-19 представляют собой цикл for, который повторяется по всем позиционным параметрам. Каждый аргумент сверяется в операторе case с допустимыми опциями. Если опцией является -l (строка 15), в пе- ременную OPT заносится то значение, которое она уже имеет, опция -l и добавляется имя терминального устройства, полученное от команды UNIX'а tty. Команда tty выводит и префикс /dev, который не нужен. Для того чтобы избавиться от этого префикса, мы берем из этой символьной строки только основное имя. Если указана опция -u, в переменную OPT добавляется -u и наше ре- гистрационное имя. Если аргументом являются любые другие данные, то они просто добавляются в переменную OPT. Поступая таким образом, мы можем передать в командной строке acme другие опции команде acctcom. Обратите внимание, что здесь не выполняется проверка на ошибки в аргу- ментах командной строки. Вы можете ввести неверное значение, которое нарушит работу команды acctcom. Однако это та цена, которую мы вынуж- дены платить за гибкость при передаче аргументов в командной строке, иначе нам придется значительно увеличивать текст командного файла. После того обработки всех опций строка 21 выводит на экран ко- мандную строку, которая должна быть выполнена, так что мы знаем, что мы задавали. В строке 22 выполняется сама команда acctcom. Выходной результат соответствует описанию acctcom(1). ------------------------------------------------------------- ИМЯ: inuse ------------------------------------------------------------- inuse Запретить использование терминала НАЗНАЧЕНИЕ Блокирует ваш терминал путем перевода в состояние занятости. Если кто-либо попытается вторгнуться, вы это заметите. ФОРМАТ ВЫЗОВА inuse ПРИМЕРЫ ВЫЗОВА inuse Перевод терминала в состояние занятости mypasswd Вводится мой пароль, но не отображается на экран ТЕКСТ ПРОГРАММЫ 1 : 2 # @(#) inuse v1.0 Disable terminal and alert if used Author: Russ Sage 2а Запретить использование терминала и сообщить о попытке использования 3 4 trap "echo you\'re BUSTED!!; stty echo; kill $$" 2 15 5 6 PATH=/bin:/usr/bin 7 SECRET="secret" 8 9 stty -echo 10 echo "Lock string: \c" 11 read BUF1 12 echo 13 14 while : 15 do 16 BUF2=`line < /dev/tty` 17 if [ "$BUF2" = "$BUF1" ] 18 then break 19 elif [ "$BUF2" = "$SECRET" ] 20 then break 21 fi 22 echo "^G\c" 23 done 24 stty echo ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ inuse? Рабочий день всегда загружен: много людей, еще больше бумаг, вся- кие поручения и так далее и так далее. Когда вы покидаете свое рабочее место, что вы собираетесь делать со своим зарегистрированным термина- лом? Каждый раз входить в систему и выходить из нее слишком долго, но вы не хотите оставлять вашу работу открытой для всех. Вам необходима программа, которую вы можете запустить на время вашего отсутствия и которая не позволит другим людям использовать то, что вы делаете. Конечно, не достаточно иметь некий процесс, который выполняется в фоновом режиме, создавая впечатление, что терминал используется. Мы должны перехватывать прерывания в случае, если кто-нибудь введет сим- волы "прерывания" или "выхода" с вашей клавиатуры. Единственным спосо- бом разблокирования терминала должен быть ввод пароля или заранее оп- ределенного слова, которое в любом случае разблокирует его. ЧТО ДЕЛАЕТ inuse? Inuse переводит ваш терминал в режим вечной работы. Это означает, что терминал не отвечает на ваши запросы или запросы кого-то другого. Когда вы готовы разблокировать ваш терминал, введите секретный пароль или пароль, который вы придумали. Когда вы первый раз вызываете inuse, у вас запрашивается пароль. Эхо-отображение на терминал отключено, поэтому пароль не выводится на экран. Для гарантии того, что никто не пытается переадресовать команд- ному файлу inuse какой-либо файл данных, все операции чтения произво- дятся непосредственно из файла терминального устройства /dev/tty, а не через файловый дескриптор стандартного ввода. Это позволяет защититься от попыток других пользователей вторгнуться в работу терминала путем посылки на стандартный ввод файла большого размера, содержащего разные слова. После чтения вашего пароля inuse попадает в бесконечный цикл, ко- торый читает символы с клавиатуры и сравнивает полученные входные дан- ные с двумя паролями. Каждый раз, когда кто-то вводит что-то некор- ректное, выдается звуковой сигнал. Когда же введен один из допустимых паролей, программа останавливается и терминал разблокируется. Мы предлагаем две реализации inuse: командный файл интерпретатора shell, приведенный выше, и программу на языке Си. Они выполняют одну и ту же работу довольно похожими способами. Они демонстрируют, насколько похожи эти два подхода, а их небольшие отличия рассматриваются ниже. Сначала обсуждается командный файл, а затем Си-программа. ПОЯСНЕНИЯ Строка 4 инициализирует оператор trap. При активизации обработчи- ка trap выполняются три команды. Целью применения ловушки trap является реагирование на любую по- пытку прервать работу командного файла и прорваться на ваш терминал. Первая команда выдает предупреждение о том, что вы вторгаетесь. Вторая команда переключает терминал обратно в режим эхо-отображения, так что все, что будет впоследствии введено с клавиатуры, отобразится на экра- не. и последняя команда заставляет программу совершить самоубийство. Как мы увидим позже, это самоубийство является особым родом прекраще- ния работы программы. Обращение к "самому себе" в операторе 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"; 2 3 #include 4 #include 5 #include 6 7 #define SSIZ 7 8 #define BSIZ 512 9 #define BELL "\07" 10 #define LF "\n" 11 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; 18 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'; 26 27 buf1[0] = buf2[0] = '\0'; 28 if ((fd = open("/dev/tty",O_RDONLY)) == -1) 29 exit(1); 30 31 for (sig = 2; sig <= 15; sig++) 32 signal(sig, SIG_IGN); 33 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); 40 41 write(1,"Lock string: ",13); 42 read(fd, buf1, BSIZ); 43 write(1, LF, 1); 44 45 for (;;) { 46 n = read(fd, buf2, BSIZ); 47 buf2[n] = '\0'; 48 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а Блокирование и разблокирование файлов 3 4 if [ $# -eq 0 ] 5 then echo "lock: incorrect argument count" >&2 6 echo "usage: lock [-u] file [...]" >&2 7 exit 1 8 fi 9 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 15 16 MODE1="go-rw" 17 MODE2="u-w" 18 19 if [ "$1" = "-u" ] 20 then shift 21 MODE1="go+r" 22 MODE2="u+w" 23 fi 24 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 и пе- ременные режима инициализируются для разблокирования файлов. Строка 21 разрешает возможность чтения группе пользователей и другим. Строка 22 разрешает мне возможность записи. Обратите внимание, что в командном файле lock не происходит модификации битов x, s или t. Это сделано на- меренно, поскольку бит x должен быть установлен только в случае, если файл может быть исполняемым. Для каталогов бит x должен быть установ- лен только в случае, если вы хотите, чтобы другие пользователи могли заходить в этот каталог. Мы также никогда не устанавливаем возможность записи для группы пользователей и для других пользователей, но мы отк- лючаем ее при блокировании файлов. Это дополнительная мера предосто- рожности на случай, если файл имеет установленными такие права доступа к нему, которые мы по каким-то причинам не желаем оставлять. В строках 25 и 26 выполняется команда chmod. Параметр $@ исполь- зован как обозначение всех имен файлов, указанных в командной строке. Такой способ позволяет вам вызывать lock с несколькими именами файлов. Из последних шести глав вы должны приобрести множество идей о том, как можно обезопасить среду, в которой вы работаете, как иметь дело с другими пользователями и вообще более продуктивно использовать ваш рабочий день. Мы готовы двигаться дальше в понимании системы UNIX и исследовании внутренней работы файловой системы, устройств и комму- никаций.