Contents
Managing handles
Handles are managed by using functions of the Handle Manager and Notification Subsystem.
The Handle Manager is provided for the user in the following files:
coresrv/handle/handle_api.h
is a header file of thelibkos
library.services/handle/Handle.idl
is an IDL description of the Handle Manager's IPC interface.
The Notification Subsystem is provided for the user in the following files:
coresrv/handle/notice_api.h
is a header file of thelibkos
library.services/handle/Notice.idl
is an IDL description of the IPC interface of the Notification Subsystem.
Handle permissions mask
A handle permissions mask has a size of 32 bits and consists of a general part and a specialized part. The general part describes the general rights that are not specific to any particular resource (the flags of these rights are defined in the services/ocap.h
header file). For example, the general part contains the OCAP_HANDLE_TRANSFER
flag, which defines the permission to transfer the handle. The specialized part describes the rights that are specific to the particular user resource or system resource. The flags of the specialized part's permissions for system resources are defined in the services/ocap.h
header file. The structure of the specialized part for user resources is defined by the resource provider by using the OCAP_HANDLE_SPEC()
macro that is defined in the services/ocap.h
header file. The resource provider must export the public header files describing the structure of the specialized part.
When the handle of a system resource is created, the permissions mask is defined by the KasperskyOS kernel, which applies permissions masks from the services/ocap.h
header file. It applies permissions masks with names such as OCAP_*_FULL
(for example, OCAP_IOPORT_FULL
, OCAP_TASK_FULL
, OCAP_FILE_FULL
) and OCAP_IPC_*
(for example, OCAP_IPC_SERVER
, OCAP_IPC_LISTENER
, OCAP_IPC_CLIENT
).
When the handle of a user resource is created, the permissions mask is defined by the user.
When a handle is transferred, the permissions mask is defined by the user but the transferred access rights cannot be elevated above the access rights of the process.
Page topCreating handles
The handles of user resources are created by the providers of the resources. The KnHandleCreateUserObject()
function declared in the coresrv/handle/handle_api.h
header file is used to create handles of user resources.
handle_api.h (fragment)
/**
* Creates the specified handle of the specified type with the rights permissions mask.
* The "type" parameter can take values ranging from HANDLE_TYPE_USER_FIRST to
* HANDLE_TYPE_USER_LAST. The HANDLE_TYPE_USER_FIRST and HANDLE_TYPE_USER_LAST macros
* are defined in the handletype.h header file. The "context" parameter defines the context
* of the user resource.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode KnHandleCreateUserObject(rtl_uint32_t type, rtl_uint32_t rights,
void *context, Handle *handle);
The user resource context is the data that allows the resource provider to identify the resource and its state when access to the resource is requested by other programs. This normally consists of a data set with various types of data (structure). For example, the context of a file may include the name, path, and cursor position. The user resource context is used as the resource transfer context or is used together with multiple resource transfer contexts.
The type
parameter of the KnHandleCreateUserObject()
function is reserved for potential future use and does not affect the behavior of the function, but it must take a value from the interval specified in the function comments.
For details about a handle permissions mask, see "Handle permissions mask".
Page topTransferring handles
Overview
Handles are transferred between programs so that clients (programs that utilize resources) can obtain access to required resources. Due to the specific locality of handles, a handle transfer initiates the creation of a handle from the handle space of the recipient program. This handle is registered as a descendant of the transferred handle and identifies the same resource.
One handle can be transferred multiple times by one or multiple programs. Each transfer initiates the creation of a new descendant of the transferred handle on the recipient program side. A program can transfer the handles that it received from other programs or from the KasperskyOS kernel (when creating handles of system resources). For this reason, a handle may have multiple generations of descendants. The generation hierarchy of handles for each resource is stored in the KasperskyOS kernel in the form of a handle inheritance tree.
A program can transfer handles for user resources and system resources if the access rights of these handles permit such a transfer. A descendant may have less access rights than an ancestor. For example, a transferring program with read-and-write permissions for a file can transfer read-only permissions. The transferring program can also prohibit the recipient program from further transferring the handle. Access rights are defined in the transferred permissions mask for the handle.
Conditions for transferring handles
For programs to transfer handles to other programs, the following conditions must be met:
- An IPC channel is created between the programs.
- The solution security policy (
security.psl
) allows interaction between the programs. - Interface methods are implemented for transferring handles.
- The client program received the endpoint ID (RIID) of the server program that has methods for transferring handles.
Interface methods for transferring handles are declared in the IDL language with input (in
) and/or output (out
) parameters of the Handle
type. Methods with input parameters of the Handle
type are intended for transferring handles from the client program to the server program. Methods with output parameters of the Handle
type are intended for transferring handles from the server program to the client program. No more than seven input and seven output parameters of the Handle
type can be declared for one method.
Example IDL description containing declarations of interface methods for transferring handles:
package IpcTransfer
interface {
PublishResource1(in Handle handle, out UInt32 result);
PublishResource7(in Handle handle1, in Handle handle2,
in Handle handle3, in Handle handle4,
in Handle handle5, in Handle handle6,
in Handle handle7, out UInt32 result);
OpenResource(in UInt32 ID, out Handle handle);
}
For each parameter of the Handle
type, the NK compiler generates a field in the *_req
request structure and/or *_res
response structure of the nk_handle_desc_t
type (hereinafter also referred to as the transport container of the handle). This type is declared in the nk/types.h
header file and comprises a structure consisting of the following three fields: handle
field for the handle, rights
field for the handle permissions mask, and the badge
field for the resource transfer context.
Resource transfer context
The resource transfer context is the data that allows the server program to identify the resource and its state when access to the resource is requested via descendants of the transferred handle. This normally consists of a data set with various types of data (structure). For example, the transfer context of a file may include the name, path, and cursor position. A server program receives a pointer to the resource transfer context when dereferencing a handle.
Regardless of whether or not a server program is the resource provider, it can associate each handle transfer with a separate resource transfer context. This resource transfer context is bound only to the handle descendants (handle inheritance subtree) that were generated as a result of a specific transfer of the handle. This lets you define the state of a resource in relation to a separate transfer of the handle of this resource. For example, for cases when one file may be accessed multiple times, the file transfer context lets you define which specific opening of this file corresponds to a received request.
If the server program is the resource provider, each transfer of the handle of this resource is associated with the user resource context by default. In other words, the user resource context is used as the resource transfer context for each handle transfer if the particular transfer is not associated with a separate resource transfer context.
A server program that is the resource provider can use the user resource context and the resource transfer context together. For example, the name, path and size of a file is stored in the user resource context while the cursor position can be stored in multiple resource transfer contexts because each client can work with different parts of the file. Technically, joint use of the user resource context and resource transfer contexts is possible because the resource transfer contexts store a pointer to the user resource context.
If the client program uses multiple various-type resources of the server program, the resource transfer contexts (or contexts of user resources if they are used as resource transfer contexts) must be specialized objects of the KosObject
type. This is necessary so that the server program can verify that the client program using a resource has sent the interface method the handle of the specific resource that corresponds to this method. This verification is required because the client program could mistakenly send the interface method a resource handle that does not correspond to this method. For example, a client program receives a file handle and sends it to an interface method for working with volumes.
To associate a handle transfer with a resource transfer context, the server program puts the handle of the resource transfer context object into the badge
field of the nk_handle_desc_t
structure. The resource transfer context object is the object that stores the pointer to the resource transfer context. The resource transfer context object is created by the KnHandleCreateBadge()
function, which is declared in the coresrv/handle/handle_api.h
header file. This function is bound to the Notification Subsystem regarding the state of resources because a server program needs to know when a resource transfer context object will be closed and terminated. The server program needs this information to free up or re-use memory that was allotted for storing the resource transfer context.
The resource transfer context object will be closed when deleting or revoking the handle descendants (see Deleting handles, Revoking handles) that were generated during its transfer in association with this object. (A transferred handle may be deleted intentionally or unintentionally, such as when a recipient client program is unexpectedly terminated.) After receiving a notification regarding the closure of a resource transfer context object, the server program deletes the handle of this object. After this, the resource transfer context object is terminated. After receiving a notification regarding the termination of the resource transfer context object, the server program frees up or re-uses the memory that was allotted for storing the resource transfer context.
One resource transfer context object can be associated with only one handle transfer.
handle_api.h (fragment)
/**
* Creates a resource transfer context object for
* the resource transfer "context" and configures the
* notification receiver "notice" to receive notifications about
* this object. The notification receiver is configured to
* receive notifications about events that match the
* event mask flags OBJECT_DESTROYED and EVENT_BADGE_CLOSED.
* Input parameter eventId defines the identifier of the
* "resource–event mask" entry in the notification receiver.
* Output parameter handle contains the handle of the
* resource transfer context.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode KnHandleCreateBadge(Notice notice, rtl_uintptr_t eventId,
void *context, Handle *handle);
Packaging data into the transport container of a handle
The nk_handle_desc()
macro declared in the nk/types.h
header file is used to package a handle, handle permissions mask and resource transfer context object handle into a handle transport container. This macro receives a variable number of arguments.
If no argument is passed to the macro, the NK_INVALID_HANDLE
value will be written in the handle
field of the nk_handle_desc_t
structure.
If one argument is passed to the macro, this argument is interpreted as the handle.
If two arguments are passed to the macro, the first argument is interpreted as the handle and the second argument is interpreted as the handle permissions mask.
If three arguments are passed to the macro, the first argument is interpreted as the handle, the second argument is interpreted as the handle permissions mask, and the third argument is interpreted as the resource transfer context object handle.
Extracting data from the transport container of a handle
The nk_get_handle()
, nk_get_rights()
and nk_get_badge_op()
(or nk_get_badge()
) functions that are declared in the nk/types.h
header file are used to extract the handle, handle permissions mask, and pointer to the resource transfer context, respectively, from the transport container of a handle. The nk_get_badge_op()
and nk_get_badge()
functions are used only when dereferencing handles.
Handle transfer scenarios
A scenario for transferring handles from a client program to the server program includes the following steps:
- The transferring client program packages the handles and handle permissions masks into the fields of the
*_req
requests structure of thenk_handle_desc_t
type. - The transferring client program calls the interface method for transferring handles to the server program. This method executes the
Call()
system call. - The recipient server program receives the request by executing the
Recv()
system call. - The dispatcher on the recipient server program side calls the method corresponding to the request. This method extracts the handles and handle permissions masks from the fields of the
*_req
request structure of thenk_handle_desc_t
type.
A scenario for transferring handles from the server program to a client program includes the following steps:
- The recipient client program calls the interface method for receiving handles from the server program. This method executes the
Call()
system call. - The transferring server program receives the request by executing the
Recv()
system call. - The dispatcher on the transferring server program side calls the method corresponding to the request. This method packages the handles, handle permissions masks and resource transfer context object handles into the fields of the
*_res
response structure of thenk_handle_desc_t
type. - The transferring server program responds to the request by executing the
Reply()
system call. - On the recipient client program side, the interface method returns control. After this, the recipient client program extracts the handles and handle permissions masks from the fields of the
*_res
response structure of thenk_handle_desc_t
type.
If the transferring program defines more access rights in the transferred handle permissions mask than the access rights defined for the transferred handle (which it owns), the transfer is not completed. In this case, the Call()
system call made by the transferring or recipient client program or the Reply()
system call made by the transferring server program ends with the rcSecurityDisallow
error.
Dereferencing handles
When dereferencing a handle, the client program sends the server program the handle, and the server program receives a pointer to the resource transfer context, the permissions mask of the sent handle, and the ancestor of the handle sent by the client program and already owned by the server program. Dereferencing occurs when a client program that called methods for working with a resource (such as read/write or access closure) sends the server program the handle that was received from this server program when access to the resource was opened.
Dereferencing handles requires fulfillment of the same conditions and utilizes the same mechanisms and data types as when transferring handles. A handle dereferencing scenario includes the following steps:
- The client program packages the handle into a field of the
*_req
request structure of thenk_handle_desc_t
type. - The client program calls the interface method for sending the handle to the server program for the purpose of performing operations with the resource. This method executes the
Call()
system call. - The server program receives the request by executing the
Recv()
system call. - The dispatcher on the server program side calls the method corresponding to the request. This method verifies that the dereferencing operation was specifically executed instead of a handle transfer. Then the called method has the option to verify that the access rights of the dereferenced handle (that was sent by the client program) permit the requested actions with the resource, and extracts the pointer to the resource transfer context from the field of the
*_req
request structure of thenk_handle_desc_t
type.
To perform verifications, the server program utilizes the nk_is_handle_dereferenced()
and nk_get_badge_op()
functions that are declared in the nk/types.h
header file.
types.h (fragment)
/**
* Returns a value different from null if
* the handle in the transport container of
* "desc" is received as a result of dereferencing
* the handle. Returns null if the handle
* in the transport container of "desc" is received
* as a result of a handle transfer.
*/
static inline
nk_bool_t nk_is_handle_dereferenced(const nk_handle_desc_t *desc)
/**
* Extracts the pointer to the resource transfer context
* "badge" from the transport container of "desc"
* if the permissions mask that was put in the transport
* container of the desc handle has the operation flags set.
* If successful, the function returns NK_EOK, otherwise it returns an error code.
*/
static inline
nk_err_t nk_get_badge_op(const nk_handle_desc_t *desc,
nk_rights_t operation,
nk_badge_t *badge)
Generally, the server program does not require the handle that was received from dereferencing because the server program normally retains the handles that it owns, for example, within the contexts of user resources. However, the server program can extract this handle from the handle transport container if necessary.
Page topRevoking handles
A program can revoke descendants of a handle that it owns. Handles are revoked according to the handle inheritance tree.
Revoked handles are not deleted. However, you cannot query resources via revoked handles. Any function that accepts a handle will end with the rcHandleRevoked
error if this function is called with a revoked handle.
Handles are revoked by using the KnHandleRevoke()
and KnHandleRevokeSubtree()
functions declared in the coresrv/handle/handle_api.h
header file. The KnHandleRevokeSubtree()
function uses the resource transfer context object that is created when transferring handles.
handle_api.h (fragment)
/**
* Deletes the handle and revokes all of its descendants.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode KnHandleRevoke(Handle handle);
/**
* Revokes handles that form the
* inheritance subtree of the handle. The root node of the inheritance subtree
* is the handle that is generated by transferring
* the handle associated with the object of the
* "badge" resource transfer context.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode KnHandleRevokeSubtree(Handle handle, Handle badge);
Notifying about the state of resources
Programs can track events that occur with resources (system resources as well as user resources), and inform other programs about events involving user resources.
Functions of the Notification Subsystem are declared in the coresrv/handle/notice_api.h
header file. The Notification Subsystem provides for the use of event masks.
An event mask is a value whose bits are interpreted as events that should be tracked or that have already occurred. An event mask has a size of 32 bits and consists of a general part and a specialized part. The general part describes the general events that are not specific to any particular resource (the flags of these events are defined in the handle/event_descr.h
header file). For example, the general part contains the EVENT_OBJECT_DESTROYED
flag, which defines the "resource termination" event. The specialized part describes the events that are specific to a particular user resource. The structure of the specialized part is defined by the resource provider by using the OBJECT_EVENT_SPEC()
macro that is defined in the handle/event_descr.h
header file. The resource provider must export the public header files describing the structure of the specialized part.
The scenario for receiving notifications about events that occur with a resource consists of the following steps:
- The
KnNoticeCreate()
function creates a notification receiver (object that stores notifications). - The
KnNoticeSubscribeToObject()
function adds "resource–event mask" entries to the notification receiver to configure it to receive notifications about events that occur with relevant resources. The set of tracked events is defined for each resource by an event mask. - The
KnNoticeGetEvent()
function is called to extract notifications from the notification receiver.
The KnNoticeSetObjectEvent()
function is used to notify a program about events that occur with a user resource. A call of this function initiates the corresponding notifications in the notification receivers that are configured to track these events that occur with this resource.
notice_api.h (fragment)
/**
* Creates the notification receiver named "notice".
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode Kn
NoticeCreate(Notice *notice);
/**
* Adds a "resource–event mask" entry
* to the "notice" notification receiver so that it will receive notifications about
* events that occur with the "object" resource and that match the
* evMask event mask. Input parameter evId defines the identifier
* of the entry that is assigned by the user and used to
* identify the entry in received notifications.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode Kn
NoticeSubscribeToObject(Notice notice,
Handle object,
rtl_uint32_t evMask,
rtl_uintptr_t evId);
/**
* Extracts notifications from the "notice" notification receiver
* while waiting for events to occur within the specific number of milliseconds.
* Input parameter countMax defines the maximum number
* of notifications that can be extracted. Output parameter
* "events" contains a set of extracted notifications of the EventDesc type.
* Output parameter "count" contains the number of notifications that
* were extracted.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode Kn
NoticeGetEvent(Notice notice,
rtl_uint64_t msec,
rtl_size_t countMax,
EventDesc *events,
rtl_size_t *count);
/* Notification structure */
typedef struct {
/* Identifier of the "resource–event mask" entry
* in the notification receiver */
rtl_uintptr_t eventId;
/* Mask of events that occurred. */
rtl_uint32_t eventMask;
} EventDesc;
/**
* Signals that events from event mask
* evMask occurred with the "object" user resource.
* You cannot set flags of the general part of an event mask
* because events from the general part of an event mask can be
*signaled only by the KasperskyOS kernel.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode Kn
NoticeSetObjectEvent(Handle object, rtl_uint32_t evMask);
Deleting handles
A program can delete the handles that it owns. Deleting a handle does not invalidate its ancestors and descendants (in contrast to revoking a handle, which actually invalidates the descendants of the handle). In other words, the ancestors and descendants of a deleted handle can still be used to provide access to the resource that they identify. Also, deleting a handle does not disrupt the handle inheritance tree associated with the resource identified by the particular handle. The place of a deleted handle is occupied by its ancestor. In other words, the ancestor of a deleted handle becomes the direct ancestor of the descendants of the deleted handle.
Handles are deleted by using the KnHandleClose()
function, which is declared in the coresrv/handle/handle_api.h
header file.
handle_api.h (fragment)
/**
* Deletes the handle.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode KnHandleClose(Handle handle);
OCap usage example
This article describes an OCap usage scenario in which the server program provides the following methods for accessing its resources:
OpenResource()
– opens access to the resource.UseResource()
– uses the resource.CloseResource()
– closes access to the resource.
The client program uses these methods.
IDL description of interface methods:
package SimpleOCap
interface {
OpenResource(in UInt32 ID, out Handle handle);
UseResource(in Handle handle, in UInt8 param, out UInt8 result);
CloseResource(in Handle handle);
}
The scenario includes the following steps:
- The resource provider creates the user resource context and calls the
KnHandleCreateUserObject()
function to create the resource handle. The resource provider saves the resource handle in the user resource context. - The client calls the
OpenResource()
method to open access to the resource.- The resource provider creates the resource transfer context and calls the
KnHandleCreateBadge()
function to create a resource transfer context object and configure the notification receiver to receive notifications regarding the closure or termination of the resource transfer context object. The resource provider saves the handle of the resource transfer context object and the pointer to the user resource context in the resource transfer context. - The resource provider uses the
nk_handle_desc()
macro to package the resource handle, permissions mask of the handle, and pointer to the resource transfer context object into the handle transport container. - The handle is transferred from the resource provider to the client, which means that the client receives a descendant of the handle owned by the resource provider.
- The
OpenResource()
method call completes successfully. The client extracts the handle and permissions mask of the handle from the handle transport container by using thenk_get_handle()
andnk_get_rights()
functions, respectively. The handle permissions mask is not required by the client to query the resource, but is transferred so that the client can find out its permissions for accessing the resource.
- The resource provider creates the resource transfer context and calls the
- The client calls the
UseResource()
method to utilize the resource.- The handle that was received from the resource provider at step 2 is used as an argument of the
UseResource()
method. Before calling this method, the client uses thenk_handle_desc()
macro to package the handle into the handle transport container. - The handle is dereferenced, after which the resource provider receives the pointer to the resource transfer context.
- The resource provider uses the
nk_is_handle_dereferenced()
function to verify that the dereferencing operation was completed instead of a handle transfer. - The resource provider verifies that the access rights of the dereferenced handle (that was sent by the client) allows the requested operation with the resource, and extracts the pointer to the resource transfer context from the handle transport container. To do so, the resource provider uses the
nk_get_badge_op()
function, which extracts the pointer to the resource transfer context from the handle transport container if the received permissions mask has the corresponding flags set for the requested operation. - The resource provider uses the resource transfer context and the user resource context to perform the corresponding operation with the resource as requested by the client. Then the resource provider sends the client the results of this operation.
- The
UseResource()
method call completes successfully. The client receives the results of the operation performed on the resource.
- The handle that was received from the resource provider at step 2 is used as an argument of the
- The client calls the
CloseResource()
method to close access to the resource.- The handle that was received from the resource provider at step 2 is used as an argument of the
CloseResource()
method. Before calling this method, the client uses thenk_handle_desc()
macro to package the handle into the handle transport container. After theCloseResource()
method is called, the client uses theKnHandleClose()
function to delete the handle. - The handle is dereferenced, after which the resource provider receives the pointer to the resource transfer context.
- The resource provider uses the
nk_is_handle_dereferenced()
function to verify that the dereferencing operation was completed instead of a handle transfer. - The resource provider uses the
nk_get_badge()
function to extract the pointer to the resource transfer context from the handle transport container. - The resource provider uses the
KnHandleRevokeSubtree()
function to revoke the handle owned by the client. The resource handle owned by the resource provider and the handle of the resource transfer context object are used as arguments of this function. The resource provider obtains access to these handles through the pointer to the resource transfer context. (Technically, the handle owned by the client does not have to be revoked because the client already deleted it. However, the revoke operation is performed in case the resource provider is not sure if the client actually deleted the handle). - The
CloseResource()
method call completes successfully.
- The handle that was received from the resource provider at step 2 is used as an argument of the
- The resource provider frees up the memory that was allocated for the resource transfer context and the user resource context.
- The resource provider calls the
KnNoticeGetEvent()
function to receive a notification that the resource transfer context object was closed, and uses theKnHandleClose()
function to delete the handle of the resource transfer context object. - The resource provider calls the
KnNoticeGetEvent()
function to receive a notification that the resource transfer context object has been terminated, and frees up the memory that was allocated for the resource transfer context. - The resource provider uses the
KnHandleClose()
function to delete the resource handle and to free up the memory that was allocated for the user resource context.
- The resource provider calls the