MsgKeyData(), MsgKeyData_r()

Передать данные через общего клиента

Прототип:

#include <sys/neutrino.h>
int MsgKeyData( int rcvid,
int op,
uint32_t key,
uint32_t *key2,
const iov_t *msg,
int parts );
int MsgKeyData_r( int rcvid,
int op,
uint32_t key,
uint32_t *key2,
const iov_t *msg,
int parts );

Аргументы:

rcvid
Идентификатор сообщения, возвращаемый потоку-серверу функцией MsgReceivev*() при приеме сообщения.
op
Одна из следующих операций, которую нужно выполнить:
_NTO_KEYDATA_CALCULATE
вычислить новый key.
_NTO_KEYDATA_VERIFY
верифицировать key.
key
Приватное значение для ключа (может быть получено как значение, возвращаемое функцией rand()).
key2
Указатель на ключ. То, что функция сохраняет по этому указателю, зависит от аргумента op:
_NTO_KEYDATA_CALCULATE
новый ключ.
_NTO_KEYDATA_VERIFY
ноль, если не было вмешательства.
msg
Указатель на порцию данных для ответа, для которых требуется защита с помощью ключа.
parts
Количество частей в составном сообщении msg.

Библиотека:

libc

Описание:

Функции MsgKeyData() и MsgKeyData_r() kernel позволяют двум привилегированным процессам передавать данные через общего клиента и верифицировать, что клиент не модифицировал эти данные. Использование этой функции иллюстрируется в примере ниже.

Данные функции идентичны за исключением способа возврата ошибок.

Программа вызывает open(), указав имя файла. Функция open() шлёт сообщение менеджеру процессов, который управляет пространством имен.

msgkey1.png
Рисунок 1. MsgSendv() от клиента к менеджеру процессов

Менеджер процессов определяет полное сетевое имя и идентификатор (pid) процесса, к которому нужно направить запрос open(). Эта информация возвращается клиенту.

msgkey2.png
Рисунок 2. MsgReplyv() от менеджера процессов к клиенту.

Далее клиент посылает сообщение процессу, имеющему указанный в ответе менеджера процессов pid. В качестве параметра передаётся полученное полное сетевое имя.

msgkey3.png
Рисунок 3. MsgSendv() от клиента к менеджеру файловой системы

Следует обратить внимание, что клиент может изменить путевое имя перед отправкой сообщения менеджеру файловой системы. На самом деле он вообще может не обращаться к менеджеру процессов и сформировать какое угодно путевое имя. Менеджер файловой системы всегда проверяет права доступа клиента. Поэтому возможные запросы произвольных путевых имен не является проблемой, за исключением одного случая: функция chroot() позволяет задать префикс, который будет добавляться ко всем путевым именам.

В рассмотренном выше примере клиент мог вызвать chroot() с именем /net/node2/home/dan. В результате процесс будет работать только с файлами в каталоге /net/node2/home/dan. Например:

Путь пользователя мапируется на путь chroot()
/bin/ls /net/node2/home/dan/bin/ls
/ /net/node2/home/dan

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


Note: В ЗОСРВ «Нейтрино» обработку запросов chroot() выполняет менеджер процессов. Менеджеры ресурсов выполняются в отдельных адресных пространствах. Более того, они могут выполняться на разных ЭВМ.

Для решения этой задачи используется MsgKeyData(). Когда менеджер процессов получает сообщение, отправленное функцией open(), он формирует данные для ответа. Прежде чем отвечать, менеджер процессов вызывает MsgKeyData() со следующими аргументами:

rcvid
Идентификатор сообщения, возвращаемый потоку-серверу функцией MsgReceivev*() при приеме сообщения.
op
_NTO_KEYDATA_CALCULATE
key
Приватное значение для ключа (может быть получено как значение, возвращаемое функцией rand()).
key2
Указатель на новый ключ, который следует вернуть клиенту в незащищенной ключом области сообщения.
msg
Указатель на порцию данных, которую нужно закрыть ключом.
parts
Количество частей сообщения msg.

Затем клиент посылает сообщение файловому менеджеру. При получении сообщения файловый менеджер вызывает MsgKeyData() с теми же аргументами, как выше, за исключением:

op
_NTO_KEYDATA_VERIFY
key
Ключ, полученный в сообщении.

Если целостность сообщения не нарушена, то MsgKeyData() устанавливает значение ключа по ссылке key2 в ноль.

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

Состояния блокировки:

Отсутствует.

Возвращаемое значение:

MsgKeyData()
Если возникла ошибка функция возвращает -1, код ошибки записывается в errno. Любое другое возвращенное значение считается успешным завершением.
MsgKeyData_r()
EOK возвращается при успешном завершении. Функция НЕ устанавливает errno. При возникновении ошибки функция возвращает один из представленных ниже кодов.

Коды ошибок:

ESRCH
Поток-клиент, ассоциированный с rcvid не существует.
EFAULT
Ошибка при попытке ядра получить доступ к предоставленным буферам.

Примеры:

/*
* Эта программа демонстрирует использование MsgKeyData() при
* пересылке клиентом данных от исходного сервера (назовем его "source server"
* на целевой сервер ("destination server"). Если клиент искажает данные, то
* целевой сервер будет об этом знать.
*/
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/neutrino.h>
typedef struct {
int public_key;
char text[10];
} IPC_t;
int chid_src, chid_dst;
void * server_src_thread( void* parm );
void * server_dst_thread( void* parm );
int main()
{
pthread_t tid[2];
IPC_t msg;
int coid;
int status;
pthread_create( &tid[0], NULL, server_src_thread, NULL );
pthread_create( &tid[1], NULL, server_dst_thread, NULL );
sleep( 3 );
/* Дадим время на сорздание каналов. Это не элегантный способ, но простой */
/*
* Послать некоторые данные целевому серверу server_src_thread.
* Данные будут включать некоторый текст и публичный ключ
* для контроля целостности этого текста.
*/
coid = ConnectAttach( 0, 0, chid_src, 0, 0 );
MsgSend( coid, NULL, 0, &msg, sizeof( msg ) );
ConnectDetach( coid );
/*
* Затем отправляем целевому серверу server_dst_thread ответ,
* полученный от server_src_thread. Не модифицируем 'text', поэтому
* должен прийти ответ об успешной пересылке текста. Обратите внимание,
* что мы включили в сообщение публичный ключ.
*/
coid = ConnectAttach( 0, 0, chid_dst, 0, 0 );
status = MsgSend( coid, &msg, sizeof( msg ), &msg, sizeof( msg ) );
printf( "Sent unmodified text to server_dst_thread.
Replied with %s\n", status == EOK ? "EOK" : "EINVAL" );
/*
* Теперь модифицируем данные 'text' (чего нам делать не позволено)
* и снова отправляем на сервер server_dst_thread вместе с публичным ключом.
* Поскольку данные 'text' искажены сервер server_dst_thread должен
* вернуть ошибку.
*/
strcpy( msg.text, "NEWDATA" );
status = MsgSend( coid, &msg, sizeof( msg ), &msg, sizeof( msg ) );
printf( "Sent modified text to server_dst_thread.
Replied with %s\n", status == EOK ? "EOK" : "EINVAL" );
return (0);
}
void * server_src_thread( void *parm )
{
int rcvid;
int private_key; /* the kernel keeps this */
iov_t keyed_area_iov;
IPC_t msg;
struct timespec t;
chid_src = ChannelCreate( 0 );
while ( 1 )
{
rcvid = MsgReceive( chid_src, &msg, sizeof( msg ), NULL );
/*
* Передаем в MsgKeyData() приватный ключ, при этом функция сгенерирует
* публичный ключ для поля 'text' сообщения. Ядро сохранит приватный ключ
* и мы ответим отправим в ответ публичный ключ.
* Обратите внимание, что мы используем количество наносекунд последней секунды
* для получения 32-разрядного псевдослучайного числа для приватного ключа.
*/
clock_gettime( CLOCK_REALTIME, &t );
private_key = t.tv_nsec; /* nanoseconds since last second */
strcpy( msg.text, "OKDATA" );
SETIOV( &keyed_area_iov, &msg.text, sizeof( msg.text ) );
MsgKeyData( rcvid, _NTO_KEYDATA_CALCULATE, private_key,
&msg.public_key, &keyed_area_iov, 1 );
MsgReply( rcvid, 0, &msg, sizeof( msg ) );
}
return (NULL);
}
void * server_dst_thread( void *parm )
{
int rcvid, tampered, status;
iov_t keyed_area_iov;
IPC_t msg;
chid_dst = ChannelCreate( 0 );
while ( 1 )
{
rcvid = MsgReceive( chid_dst, &msg, sizeof( msg ), NULL );
/*
* Проверяем целостность данных с помощью публичного ключа.
*/
SETIOV( &keyed_area_iov, &msg.text, sizeof( msg.text ) );
MsgKeyData( rcvid, _NTO_KEYDATA_VERIFY, msg.public_key,
&tampered, &keyed_area_iov, 1 );
if ( tampered )
status = EINVAL; /* ответ: 'text' изменён */
else
status = EOK; /* ответ: 'text' в порядке */
MsgReply( rcvid, status, &msg, sizeof( msg ) );
}
return (NULL);
}

Классификация:

ЗОСРВ «Нейтрино»

Безопасность использования
Точка остановки потока
Нет
Обработчик прерываний
Нет
Обработчик сигналов
Да
В потоке
Да

Тематические ссылки:

iov_t, chroot(), MsgReceive(), MsgReceivev(), open(), rand()




Предыдущий раздел: Описание API системной библиотеки