6.1. Объекты, коды объектов и права доступа
6.2. Просмотр базы объектов
Файл-сервер Novell NetWare содержит базу данных объектов, в которой
есть сведения о ресурсах, доступных в сети (файл-серверы, серверы
печати и т. п.), о пользователях и группах пользователей и т.
д. Эта база данных называется Bindery. Физически она находится
в двух скрытых файлах с именами net$bind.sys и net$bval.sys, расположенных
в каталоге SYS:SYSTEM.
Каждый объект, хранящийся в базе данных, имеет свое имя, уникальный
для данного файл-сервера идентификатор, тип, байт доступа. Кроме
того, объект может быть статическим или динамическим.
Например, каждый пользователь, добавленный супервизором сети или
администратором, становится объектом, имеющим имя и идентификатор.
Тип такого объекта соответствует значению 1, и это статический
объект. Статический объект хранится в базе до тех пор, пока он
не будет удален явным образом.
Если в сети имеются несколько файл-серверов, то для каждого сервера
в базе Bindery имеются объекты, соответствующие всем серверам,
активным в сети. Это временные объекты, которые удаляются автоматически,
когда соответствующий файл-сервер прекращает свою работу. Вы можете
воспользоваться этим обстоятельством для получения списка активных
серверов, имеющихся в сети.
Программы пользователя могут не только считывать содержимое базы
объектов, но и добавлять свои собственные записи, а также редактировать
имеющиеся. Для сокращения объема книги мы рассмотрим только способы
извлечения информации из базы Bindery. Полностью работа с базой
данных объектов описана в документации, поставляющейся вместе
с библиотекой Novell NetWare C Interface.
База данных Bindery хранит объекты, которые имеют такие атрибуты,
как идентификатор, имя, тип, флаг (статический или динамический
объект), байт доступа.
Идентификатор объекта - число размером 4 байта, которое является
уникальным для данного файл-сервера. Никакие два объекта, сведения
о которых хранятся в одной базе данных на одном сервере, не могут
иметь одинаковые идентификаторы.
Имя объекта представляет собой текстовую строку размером не более
47 байт. В операциях поиска объектов в базе данных по имени в
поле имени допускается указывать символы "*" и "?",
которые интерпретируются обычным способом, как и в MS-DOS.
Библиотека NetWare C Interface имеет функции, позволяющие просматривать
список всех объектов базы данных, искать объекты определенного
типа или по имени с использованием шаблона и символов "*"
и "?". По идентификатору объекта вы можете легко получить
его имя и наоборот, по имени можно узнать идентификатор объекта.
Тип объекта определяет сетевой ресурс и может принимать следующие
значения:
Значение | Описание |
0 | Неклассифицируемый (неизвестный) объект
|
1 | Пользователь |
2 | Группа пользователей |
3 | Очередь печати |
4 | Файл-сервер |
5 | Сервер заданий |
6 | Шлюз |
7 | Сервер печати |
8 | Очередь для архивирования
|
9 | Сервер для архивирования
|
A | Очередь заданий |
B | Администратор |
24 | Сервер удаленного моста
|
Флаг объекта характеризует время жизни объекта. Если этот флаг
равен нулю, объект статический и для его уничтожения необходимо
вызывать специальную функцию. Если флаг равен единице, то это
динамический объект, который удаляется автоматически, как только
исчезает соответствующий сетевой ресурс.
Например, если в сети имеется несколько файл-серверов, в базе
каждого сервера имеются объекты, описывающие все активные серверы
сети. Как только один из серверов прекращает свою работу, из всех
баз данных, расположенных на остальных файл-серверах, удаляются
объекты, описывающие завершивший работу файл-сервер.
Байт доступа используется для определения прав, необходимых для
поиска, чтения, создания, редактирования или удаления объекта.
Две тетрады байта отвечают за доступ на чтение и доступ на запись.
Младшие четыре бита байта доступа отвечают за чтение, старшие
- за запись.
Для тетрад определены значения от 0 до 4 - уровни доступа. Для
того чтобы пользователь или другой объект получили доступ, тетрады
его собственного байта доступа должны иметь значения, равные или
превышающие значения в байте доступа объекта, к которому запрашивается
доступ.
Приведем список возможных значений для уровней доступа:
0 | Anyone | Объект не подключен к файл-серверу
|
1 | Logged | Объект подключен к файл-серверу
|
2 | Object | Объект подключен к файл-серверу с именем и паролем
|
3 | Supervisor | Объект имеет права супервизора
|
4 | NetWare | Объект имеет права операционной системы Novell NetWare
|
Для определения собственного уровня доступа и идентификатора пользователя
программа может воспользоваться функцией GetBinderyAccessLevel():
int GetBinderyAccessLevel(BYTE *SecurityAccessLevel,
long *ObjectID);
Первый параметр этой функции указывает на слово, в которое будет
записан уровень доступа, второй - на двойное слово, в которое
будет записан идентификатор пользователя.
Вместо функции GetBinderyAccessLevel() можно использовать функцию
E3h прерывания INT 21h:
На входе: | AH | =
| E3h; |
| DS:SI | = |
Адрес буфера запроса; |
| ES:DI | = |
Адрес буфера ответа. |
На выходе: | AL | =
| Код ошибки или 0, если операция завершилась без ошибок.
|
Буфер запроса имеет следующий формат:
struct REQUEST {
WORD PacketLength;// размер пакета запроса
BYTE Function; // должно быть равно 70
};
Буфер ответа имеет следующий формат:
struct REPLAY {
WORD PacketLength;// размер пакета
BYTE SecurityAccessLevel; // уровень доступа
long ObjectID; // идентификатор объекта
};
По идентификатору объекта вы можете получить его имя и тип с помощью
функции GetBinderyObjectName():
int GetBinderyObjectName(long ObjectID,
char *ObjectName, WORD *ObjectType);
Для объекта, идентификатор которого задан первым параметром, функция
возвращает имя объекта и его тип, записывая их в области памяти,
указанные при помощи второго и третьего параметров.
Функция возвращает 0 при успешном завершении или код ошибки:
Код ошибки | Значение |
0x96 | Мало памяти на сервере
|
0xFC | В базе нет объекта с указанным идентификатором
|
0xFE | База данных Bindery заблокирована
|
0xFF | Сбой базы данных Bindery
|
Вместо функции GetBinderyObjectName() вы также можете использовать
функцию E3h прерывания INT 21h. При этом необходимо использовать
форматы буфера запроса и буфера ответа, приведенные ниже.
Буфер запроса:
struct REQUEST {
WORD PacketLength;// размер пакета запроса
BYTE Function; // должно быть равно 54
long ObjectID; // идентификатор объекта
};
Буфер ответа:
struct REPLAY {
WORD PacketLength;// размер пакета
long ObjectID; // идентификатор объекта
WORD ObjectType; // тип объекта
BYTE ObjectName[48]; // имя объекта
};
Для получения идентификатора объекта по его имени и типу вы можете
воспользоваться функцией GetBinderyObjectID():
int GetBinderyObjectID(char *ObjectName,WORD ObjectType,
long *ObjectID);
Имя и тип объекта задаются первым и вторым параметрами, идентификатор
записывается по адресу, заданному третьим параметром.
Функция возвращает 0 при успешном завершении или код ошибки:
Код ошибки | Значение |
0x96 | Мало памяти на сервере
|
0xEF | Имя объекта указано неправильно
|
0xF0 | Не допускаются символы шаблона "*", "?"
|
0xFC | В базе нет объекта с указанным идентификатором
|
0xFE | База данных Bindery заблокирована
|
0xFF | Сбой базы данных Bindery
|
Вместо функции GetBinderyObjectID() можно использовать функцию
E3h прерывания INT 21h. Приведем форматы буфера запроса и
буфера ответа.
Буфер запроса:
struct REQUEST {
WORD PacketLength;// размер пакета запроса
BYTE Function; // должно быть равно 53
long ObjectType; // тип объекта
BYTE ObjectNameLength; // длина имени объекта
BYTE ObjectName[ObjectNameLength]; // имя объекта
};
Буфер ответа:
struct REPLAY {
WORD PacketLength;// размер пакета
long ObjectID; // идентификатор объекта
WORD ObjectType; // тип объекта
BYTE ObjectName[48]; // имя объекта
};
6.1.1. Программа BACCESS
Приведем текст программы BACCESS (листинг 26), которая определяет
и выводит на экран имя, идентификатор, тип объекта, а также его
уровень доступа. В программе используются описанные выше функции.
// ===================================================
// Листинг 26. Программа для просмотра уровня
// доступа рабочей станции
// Файл baccess\baccess.cpp
//
// (C) A. Frolov, 1993
// ===================================================
#include <stdlib.h>
#include <stdio.h>
#define WORD unsigned int
#define BYTE unsigned char
extern "C" int GetNetWareShellVersion(char *,char *, char *);
extern "C" int GetBinderyAccessLevel(BYTE *, long *);
extern "C" int GetBinderyObjectName(long, char*, WORD*);
void main(void) {
char MajorVersion=0;
char MinorVersion=0;
char Revision=0;
int ccode;
BYTE SecurityAccessLevel;
long ObjectID;
char ObjectName[48];
WORD ObjectType;
printf("\n*BACCESS* (C) Frolov A., 1993\n");
// Проверяем присутствие сетевой оболочки
asm push si
GetNetWareShellVersion(&MajorVersion,
&MinorVersion, &Revision);
asm pop si
if(MajorVersion == 0) {
printf("\nОболочка NetWare не загружена\n");
return;
}
// Получаем свой идентификатор и уровень доступа
GetBinderyAccessLevel(&SecurityAccessLevel, &ObjectID);
// По идентификатору определяем свое имя
ccode = GetBinderyObjectName(ObjectID, ObjectName, &ObjectType);
// Если пользователь подключился к файл-серверу,
// выводим его имя, идентификатор и тип
if(!ccode) {
printf("Пользователь %s, ID = %lX, Type = %d\n",
ObjectName, ObjectID, ObjectType);
}
// Выводим права доступа на чтение
printf("Права доступа на чтение:\t");
switch(SecurityAccessLevel & 0x0f) {
case 0:
printf("Anyone\t(не подключен к файл-серверу)\n");
break;
case 1:
printf("Logged\t(подключен к файл-серверу)\n");
break;
case 2:
printf("Object\t(подключен к файл-серверу "
"с именем и паролем)\n");
break;
case 3:
printf("Supervisor\t(права супервизора)\n");
break;
case 4:
printf("NetWare\t(права Novell NetWare)\n");
break;
}
// Выводим права доступа на запись
printf("Права доступа на запись:\t");
switch((SecurityAccessLevel >> 4) & 0x0f) {
case 0:
printf("Anyone\t(не подключен к файл-серверу)\n");
break;
case 1:
printf("Logged\t(подключен к файл-серверу)\n");
break;
case 2:
printf("Object\t(подключен к файл-серверу "
"с именем и паролем)\n");
break;
case 3:
printf("Supervisor\t(права супервизора)\n");
break;
case 4:
printf("NetWare\t(права Novell NetWare)\n");
break;
}
}
В этом разделе мы рассмотрим задачу сканирования базы данных Bindery
с целью получения списка имеющихся в ней объектов. Например, вам
может потребоваться список активных серверов в сети, список пользователей
или список рабочих групп.
Для получения списка объектов, сведения о которых хранятся в базе
данных Bindery, предназначена функция ScanBinderyObject():
int ScanBinderyObject(char *SearchObjectName,
WORD SearchObjectType, long *ObjectID, char *ObjectName,
WORD *ObjectType, char *ObjectHasProperties,
char *ObjectFlag, char *ObjectSecurity);
Эта функция должна использоваться в цикле. При первом вызове в
переменную, на которую указывает параметр ObjectID, необходимо
записать значение -1. В дальнейшем в эту переменную будет записываться
идентификатор найденного объекта.
Для поиска следует указать имя объекта (параметр SearchObjectName)
и тип объекта (параметр SearchObjectType). В качестве имени объекта
можно использовать шаблон с символами "*" и "?".
Тип объекта может быть задан конкретно, либо можно указать значение
-1. В последнем случае функция будет искать объекты всех типов.
Для того чтобы найти все объекты всех типов, в качестве имени
надо указать строку "*", в качестве типа задать значение
-1.
Для найденных объектов в соответствующие переменные, указанные
параметрами функции, будут записаны имя объекта (параметр ObjectName),
тип объекта (параметр ObjectType), флаг (параметр ObjectFlag),
байт доступа (параметр ObjectSecurity). Кроме того, в переменную,
на которую указывает параметр ObjectHasProperties, записывается
значение 0xFF, если объект имеет дополнительную связанную с ним
информацию (Properties), которую можно извлечь специально предназначенными
для этого функциями.
Функция возвращает 0 при успешном завершении или код ошибки:
Код ошибки | Значение |
0x96 | Мало памяти на сервере
|
0xEF | Имя объекта указано неправильно
|
0xFC | В базе нет объекта с указанным идентификатором
|
0xFE | База данных Bindery заблокирована
|
0xFF | Сбой базы данных Bindery
|
Ваша программа должна вызывать функцию ScanBinderyObject() в цикле
до тех пор, пока она не возвратит код ошибки, отличный от нуля.
Вместо функции ScanBinderyObject() можно использовать функцию
E3h прерывания INT 21h:
На входе: | AH | =
| E3h; |
| DS:SI | = |
Адрес буфера запроса; |
| ES:DI | = |
Адрес буфера ответа. |
На выходе: | AL | =
| Код ошибки или 0, если операция завершилась без ошибок.
|
Буфер запроса имеет следующий формат:
struct REQUEST {
WORD PacketLength;// размер пакета запроса
BYTE Function; // должно быть равно 55
BYTE ObjectID; // идентификатор объекта
WORD SearchObjectType; // тип объекта
BYTE NameLength; // длина имени образца для
// поиска объекта
BYTE SearchObjectName[NameLength]; // имя образца для
// поиска объекта
};
Буфер ответа имеет следующий формат:
struct REPLAY {
WORD PacketLength;// размер пакета
long ObjectID; // идентификатор объекта
WORD ObjectType; // тип объекта
BYTE ObjectName[48]; // имя объекта
BYTE ObjectFlag; // флаг объекта
BYTE SecurityAccessLevel; // уровень доступа
BYTE ObjectHasProperties; // есть записи
};
В базе данных объектов Bindery с каждым объектом может быть связано
несколько дополнительных записей, содержащих данные (property).
Каждая такая запись имеет свое имя, флаг и байт доступа. Если
объект имеет записи, вы можете получить список их имен и других
атрибутов при помощи функции ScanProperty():
int ScanProperty(char *ObjectName, WORD ObjectType,
char *SearchPropertyName, long *SequenceNumber,
char *PropertyName, char *PropertyFlag,
char char *PropertySecurity,
char *PropertyHasValue, char *MoreProperties);
Функция должна вызываться в цикле. При первом вызове переменная,
на которую указывает параметр SequenceNumber, должна содержать
значение -1. При последующих вызовах содержимое этой переменной
будет изменяться автоматически.
Для считывания полей функции необходимо указать имя сканируемого
объекта (параметр ObjectName), тип объекта (параметр ObjectType),
а также имя записи или шаблон имени записи (параметр SearchPropertyName).
В шаблоне можно использовать символы "*" и "?".
Для найденных записей в соответствующие переменные, указанные
параметрами функции, будут записаны имя записи (параметр PropertyName),
флаг записи (параметр PropertyFlag), байт доступа (параметр PropertySecurity),
признак того, что запись имеет значения (параметр PropertyHasValue),
признак того, что в объекте есть еще и другие записи (MoreProperties).
Функция возвращает 0 при успешном завершении или код ошибки:
Код ошибки | Значение |
0x96 | Мало памяти на сервере
|
0xF1 | Неправильный код доступа
|
0xFB | Указанная запись не найдена
|
0xFC | В базе нет объекта с указанным идентификатором
|
0xFE | База данных Bindery заблокирована
|
0xFF | Сбой базы данных Bindery
|
В Novell NetWare для каждого типа объекта существует определенный
набор записей, которые могут быть связаны с этим объектом. Например,
с объектом типа 1 (обычный пользователь) связаны такие записи,
как PASSWORD (пароль) и SECURITY_EQUALS (эквивалентность прав
доступа). Содержимое записей можно считать при помощи функции
ReadPropertyValue(), которая описана в документации по библиотеке
NetWare C Interface. Для этого пользователь, запустивший программу,
должен обладать достаточным уровнем доступа.
Приведем некоторые имена полей, определенных в NetWare:
Имя записи | Тип объекта |
Доступ, запись/чтение |
BLOCKS_READ | Файл-сервер |
3/1 |
BLOCKS_WRITTEN | Файл-сервер
| 3/1 |
CONNECT_TIME | Файл-сервер |
3/1 |
GROUP_MEMBERS | Группа пользователей
| 3/1 |
GROUPS_I'M_IN | Пользователь
| 3/1 |
IDENTIFICATION | Пользователь
| 3/1 |
NET_ADDRESS | Файл-сервер |
4/0 |
OLD_PASSWORDS | Пользователь
| 3/3 |
OPERATORS | Файл-сервер |
3/3 |
PASSWORDS | Пользователь |
4/4 |
SECURITY_EQUALS | Пользователь
| 3/2 |
Полный список полей и подробное их описание вы найдете в документации
по библиотеке NetWare C Interface.
6.2.1. Программа BSCAN
Приведем исходный текст программы BSCAN (листинг 27), которая
просматривает базу данных объектов. Для каждого найденного объекта
программа выводит имя и расшифрованный тип объекта, флаг и уровень
доступа. Если объект имеет дополнительные записи (properties),
вызывается функция, которая выводит имена найденных записей.
// ===================================================
// Листинг 27. Программа для просмотра содержимого
// базы данных объектов
// Файл bscan\bscan.cpp
//
// (C) A. Frolov, 1993
// ===================================================
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define WORD unsigned int
#define BYTE unsigned char
extern "C" int GetNetWareShellVersion(char *,char *, char *);
extern "C" int ScanBinderyObject(char *, WORD, long *,
char *, WORD *, char *, char *, char *);
extern "C" int ScanProperty(char *, WORD, char *, long *,
char *, char *, char *, char *, char *);
void Property(char *ObjectName, WORD ObjectType);
void main(void) {
char MajorVersion=0;
char MinorVersion=0;
char Revision=0;
int ccode;
BYTE ObjectSecurity;
long ObjectID;
char SearchObjectName[48];
char ObjectName[48];
WORD SearchObjectType;
WORD ObjectType;
char ObjectHasProperties;
char ObjectFlag;
printf("\n*BSCAN* (C) Frolov A., 1993\n");
// Проверяем присутствие сетевой оболочки
asm push si
GetNetWareShellVersion(&MajorVersion,
&MinorVersion, &Revision);
asm pop si
if(MajorVersion == 0) {
printf("\nОболочка NetWare не загружена\n");
return;
}
// Просматриваем в цикле содержимое базы объектов,
// ищем объекты всех типов
SearchObjectType = -1;
// Маска для поиска всех объектов
strcpy(SearchObjectName, "*");
for(ObjectID = -1;;) {
// Получаем очередной объект
ccode = ScanBinderyObject(SearchObjectName,
SearchObjectType, &ObjectID, ObjectName, &ObjectType,
&ObjectHasProperties, &ObjectFlag, &ObjectSecurity);
// Если больше нет объектов или произошла ошибка, завершаем цикл
if(ccode) break;
// Выводим имя и тип объекта
printf("\n%-18s\t", ObjectName);
switch(ObjectType) {
case 0:
printf("??? "); break;
case 1:
printf("Пользователь ");break;
case 2:
printf("Группа "); break;
case 3:
printf("Очередь на печать "); break;
case 4:
printf("Файл-сервер "); break;
case 5:
printf("Сервер заданий "); break;
case 6:
printf("Шлюз "); break;
case 7:
printf("Сервер печати "); break;
case 8:
printf("Очередь архивирования "); break;
case 9:
printf("Сервер для архивирования"); break;
case 0xA:
printf("Очередь заданий "); break;
case 0xb:
printf("Администратор "); break;
case 0x26:
printf("Сервер удаленного моста "); break;
default:
printf("Объект 0x%04.4X ", ObjectType); break;
}
// Выводим флаг объекта, который может иметь два значения:
// 0 для постоянных объектов и 1 для временных
if(ObjectFlag) printf("Временный ");
else printf("Постоянный");
// Выводим байт прав, необходимых для получения доступа к объекту
printf(" Доступ %02.2X", ObjectSecurity);
// Если для объекта имеются дополнительные записи,
// выводим их названия
if(ObjectHasProperties) Property(ObjectName,
ObjectType);
}
}
// =================================================================
// Функция Property выводит названия дополнительных записей объектов
// =================================================================
void Property(char *ObjectName, WORD ObjectType) {
int ccode;
BYTE PropertySecurity;
long ObjectID;
char SearchPropertyName[16];
char PropertyName[16];
WORD SearchObjectType;
char PropertyFlag;
long SequenceNumber;
char PropertyHasValue;
char MoreProperties;
// Маска для поиска всех записей
strcpy(SearchPropertyName, "*");
for(SequenceNumber=-1;;) {
// Получаем запись
ccode = ScanProperty(ObjectName, ObjectType,
SearchPropertyName, &SequenceNumber,
PropertyName, &PropertyFlag, &PropertySecurity,
&PropertyHasValue, &MoreProperties);
// Если записей больше нет, завершаем цикл
if(ccode) break;
// Выводим название записи
printf("\n\tProperty %s", PropertyName);
}
}
|