Contents
Managing handles (handle_api.h)
The API is defined in the sysroot-*-kos/include/coresrv/handle/handle_api.h
header file from the KasperskyOS SDK.
The API is intended for performing operations with handles. Handles have the Handle
type, which is defined in the header file sysroot-*-kos/include/handle/handletype.h
from the KasperskyOS SDK.
Locality of handles
Each process receives handles from its own handle space irrespective of other processes. The handle spaces of different processes are absolutely identical in that they consist of the same set of values. Therefore, a handle is unique (has a unique value) only within the handle space of the single process that owns the particular handle. In other words, different processes may have identical handles that identify different resources, or may have different handles that identify the same resource.
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 header file sysroot-*-kos/include/services/ocap.h
from the KasperskyOS SDK). 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 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 ocap.h
header file. The resource provider must export the public header files describing the flags 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 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
Creating handles of system resources
Handles of system resources are created when these resources are created. For example, handles are created when an interrupt or MMIO memory region is registered, and when a DMA buffer, thread, or process is created.
Creating handles of user resources
Handles of user resources are created by the providers of these resources by using the KnHandleCreateUserObject()
or KnHandleCreateUserObjectEx()
function.
The context of a user resource must be defined through the context
parameter. The user resource context consists of data that allows the resource provider to identify the resource and its state when access to the resource is requested by other processes. 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.
You must use the rights
parameter to define the handle permissions mask.
Creating IPC handles
An IPC handle is a handle that identifies an IPC channel. IPC handles are used to execute system calls. A client IPC handle is necessary for executing a Call()
system call. A server IPC handle is necessary for executing the Recv()
and Reply()
system calls. A listener handle is a server IPC handle that has extended rights allowing it to add IPC channels to the set of IPC channels identified by this handle. A callable handle is a client IPC handle that simultaneously identifies the IPC channel to a server and an endpoint of this server.
A server creates a callable handle and passes it to a client so that the client can use the server endpoint. The client initializes IPC transport by using the callable handle that it received. In addition, the client specifies the INVALID_RIID
value as the endpoint ID (RIID) in the proxy object initialization function. To create a callable handle, call the KnHandleCreateUserObjectEx()
function and specify the server IPC handle and the endpoint ID (RIID) in the ipcChannel
and riid
parameters, respectively. Use the context
parameter to specify the data to be associated with the callable handle. The server will be able to receive the pointer to this data when dereferencing the callable handle. (Even though the callable handle is an IPC handle, the kernel puts it into the base_.self
field of the constant part of an IPC request.)
To create the client IPC handle, server IPC handle, and listener IPC handle and associate them with each other, call the KnHandleConnect()
or KnHandleConnectEx()
function. These functions are used to statically create IPC channels. The KnHandleConnect()
function creates IPC handles from the handle space of the calling process. However, the client IPC handle can be transferred to another process. The KnHandleConnectEx()
function can create IPC handles from the handle space of the calling process or from the handle spaces of other processes, such as the client and server.
When calling the KnHandleConnect()
or KnHandleConnectEx()
function with the INVALID_HANDLE
value in the parameter that defines the listener handle, a new listener handle is created. However, the server IPC handle and listener IPC handle in the output parameters are the same handle. If a listener handle is specified when calling the KnHandleConnect()
or KnHandleConnectEx()
function, the created server IPC handle will provide the capability to receive IPC requests over all IPC channels associated with this listener handle. In this case, the server IPC handle and listener IPC handle in the output parameters are different handles. (The first IPC channel associated with the listener handle is created when calling the KnHandleConnect()
or KnHandleConnectEx()
function with the INVALID_HANDLE
value in the parameter that defines the listener handle. The second and subsequent IPC channels associated with the listener handle are created during the second and subsequent calls of the KnHandleConnect()
or KnHandleConnectEx()
function specifying the listener handle that was obtained during the first call.)
To call a listener handle that is not associated with a client IPC handle and server IPC handle, call the KnHandleCreateListener()
function. (The KnHandleConnect()
and KnHandleConnectEx()
functions create a listener handle associated with a client IPC handle and server IPC handle.) The KnHandleCreateListener()
function is convenient for creating a listener handle that will be subsequently bound to callable handles.
To create a client IPC handle for querying the Kaspersky Security Module through the security interface, call the KnHandleSecurityConnect()
function. This function is called by the libkos
library when initializing IPC transport for querying the security module.
Information about API functions
handle_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates a handle. Parameters
Returned values If successful, the function returns |
|
Purpose Creates a handle. Parameters
Returned values If successful, the function returns |
|
Purpose Creates and connects the client, server, and listener IPC handles. Parameters
Returned values If successful, the function returns |
|
Purpose Creates and connects the client, server, and listener IPC handles. Parameters
Returned values If successful, the function returns |
|
Purpose Creates a client IPC handle for querying the Kaspersky Security Module through the security interface. Parameters
Returned values If successful, the function returns |
|
Purpose Creates a listener handle that is not associated with a client IPC handle and server IPC handle. Parameters
Returned values If successful, the function returns |
Transferring handles
Overview
Handles are transferred between processes so that resource consumers can gain 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 process. This handle is registered as a descendant of the transferred handle and identifies the same resource.
One handle can be transferred multiple times to one or more processes. Each transfer initiates the creation of a new descendant of the transferred handle on the recipient process side. A process can transfer handles that it received from other processes or the KasperskyOS kernel. 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 process can transfer handles for user resources and system resources if the access rights of these handles permit such a transfer (the OCAP_HANDLE_TRANSFER
flag is set in the permissions mask). A descendant may have less access rights than an ancestor. For example, a transferring process with read-and-write permissions for a file can transfer read-only permissions. The transferring process can also prohibit the recipient process from further transferring the handle. Access rights are defined in the transferred permissions mask for the handle.
Conditions for transferring handles
To enable processes to transfer handles to other processes, the following conditions must be met:
- An IPC channel is created between the processes.
- The solution security policy (
security.psl
) allows interaction between process classes. - Interface methods are implemented for transferring handles.
The API task.h enables a parent process to pass handles to a child process that is not yet running.
In an IDL description, signatures of interface methods for transferring handles have input (in
) and/or output (out
) parameters of the Handle
type or array
type with elements of the Handle
type. Up to 255 handles can be passed through the input parameters of one method. This same number of handles can be received through output parameters.
Example IDL description that defines the signatures of interface methods for transferring handles:
For each parameter of the Handle
type, the NK compiler generates a field of the nk_handle_desc_t
type (hereinafter also referred to as the transport container of the handle) in the *_req
IPC request structure and/or *_res
IPC response structure. This type is declared in the header file sysroot-*-kos/include/nk/types.h
from the KasperskyOS SDK 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 consists of data that allows the server 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. The server receives a pointer to the resource transfer context when dereferencing a handle.
Regardless of whether or not the server is the resource provider, the server 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 IPC request.
If the server 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 that is the resource provider can use both 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 uses multiple various-type resources of the server, 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 can verify that the client 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 could mistakenly send the interface method a resource handle that does not correspond to this method. For example, a client may have received a file handle and sent it to an interface method for working with volumes.
To associate a handle transfer with a resource transfer context, the server 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 kernel object that stores the pointer to the resource transfer context. To create a resource transfer context object, call the KnHandleCreateBadge()
function. This function is bound to the notification mechanism because a server needs to know when a resource transfer context object will be closed and deleted. The server 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 upon the closure or revocation of the handle descendants that comprise the handle inheritance subtree whose root node was generated by the transfer of this handle in association with this object. (A transferred handle may be closed intentionally or unintentionally, such as when a recipient client is unexpectedly terminated.) After receiving a notification regarding the closure of a resource transfer context object, the server closes the handle of this object. After this, the resource transfer context object will be deleted. After receiving a notification regarding the deletion of the resource transfer context object, the server 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.
Packaging data into the transport container of a handle
To package a handle, handle permissions mask, and resource transfer context object handle into a handle transport container, use the nk_handle_desc()
macro that is defined in the header file sysroot-*-kos/include/nk/types.h
from the KasperskyOS SDK. This macro receives a variable number of parameters.
If no parameter is passed to the macro, the NK_INVALID_HANDLE
value will be written to the handle
field of the nk_handle_desc_t
structure. If one parameter is passed to the macro, this parameter is interpreted as the handle. If two parameters are passed to the macro, the first parameter is interpreted as the handle and the second parameter is interpreted as the handle permissions mask. If three parameters are passed to the macro, the first parameter is interpreted as the handle, the second parameter is interpreted as the handle permissions mask, and the third parameter is interpreted as the resource transfer context object handle.
Extracting data from the transport container of a handle
To extract the handle, handle permissions mask, and pointer to the resource transfer context from the transport container of a handle, use the nk_get_handle()
, nk_get_rights()
and nk_get_badge_op()
(or nk_get_badge()
) functions, respectively, which are declared in the header file sysroot-*-kos/include/nk/types.h
from the KasperskyOS SDK. The nk_get_badge_op()
and nk_get_badge()
functions should be used only when dereferencing handles.
Handle transfer scenarios
The scenario for transferring handles from a client to the server includes the following steps:
- The client packages the handles and handle permissions masks into fields of the
*_req
IPC requests structure of thenk_handle_desc_t
type. - The client calls the interface method for transferring handles to the server. The
Call()
system call is executed when this method is called. - The server receives an IPC request by executing the
Recv()
system call. - The dispatcher on the server side calls the method corresponding to the IPC request. This method extracts the handles and handle permissions masks from fields of the
*_req
IPC request structure of thenk_handle_desc_t
type.
The scenario for transferring handles from the server to a client includes the following steps:
- The client calls the interface method for receiving handles from the server. The
Call()
system call is executed when this method is called. - The server receives an IPC request by executing the
Recv()
system call. - The dispatcher on the server side calls the method corresponding to the IPC request. This method packages the handles, handle permissions masks and resource transfer context object handles into fields of the
*_res
IPC response structure of thenk_handle_desc_t
type. - The server responds to the IPC request by executing the
Reply()
system call. - On the client side, the interface method returns control. After this, the client extracts the handles and handle permissions masks from fields of the
*_res
IPC response structure of thenk_handle_desc_t
type.
If the transferring process 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 executed by the transferring or recipient client or the Reply()
system call executed by the transferring server ends with the rcSecurityDisallow
error.
Information about API functions
handle_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates a resource transfer context object and configures a notification mechanism for monitoring the life cycle of this object. Parameters
Returned values If successful, the function returns Additional information The notification receiver is configured to receive notifications about events that match the |
Duplicating handles
Handle duplication is similar to a handle transfer, but duplication is performed within a process. A handle descendant is created in the same process and from the same handle space. The rights of the handle descendant may be less than or equal to the rights of the original handle. Handle duplication can be associated with a resource transfer context object. This lets you use the notification mechanism to track the closure or revocation of all handle descendants that form the handle inheritance subtree whose root node was generated by the duplication operation. It also provides the capability to revoke these descendants.
To duplicate a handle, call the KnHandleCopy()
function. To do so, the OCAP_HANDLE_COPY
flag must be set in the handle permissions mask.
Information about API functions is provided in the table below.
handle_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Duplicates a handle. As a result of duplication, the calling process receives the handle descendant. Parameters
Returned values If successful, the function returns |
Dereferencing handles
When dereferencing a handle, the client sends the handle to the server, and the server 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 and already owned by the server. Dereferencing occurs when a resource consumer that called methods for working with a resource (such as read/write or access closure) sends the resource provider the handle that was received from this resource provider 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 packages the handle into a field of the
*_req
IPC request structure of thenk_handle_desc_t
type. - The client calls the interface method for sending the handle to the server for the purpose of performing operations with the resource. The
Call()
system call is executed when this method is called. - The server receives the IPC request by executing the
Recv()
system call. - The dispatcher on the server side calls the method corresponding to the IPC 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) 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 verification, the server uses the nk_is_handle_dereferenced()
and nk_get_badge_op()
functions that are declared in the header file sysroot-*-kos/include/nk/types.h
from the KasperskyOS SDK.
types.h (fragment)
Generally, the server does not require the handle that was received from dereferencing because the server normally retains the handles that it owns, for example, within the contexts of user resources. However, the server can extract this handle from the handle transport container if necessary.
Page topRevoking handles
A process can revoke descendants of a handle that it owns. Handles are revoked according to the handle inheritance tree.
Revoked handles are not closed. However, you cannot query resources via revoked handles. Any function that receives the handle will end with the rcHandleRevoked
error if the function is called with a revoked handle.
To revoke handle descendants, call the KnHandleRevoke()
or KnHandleRevokeSubtree()
function. The KnHandleRevokeSubtree()
function uses the resource transfer context object that is created when transferring handles.
If each handle of a system resource in all processes that own these handles are closed (see "Closing handles") or revoked, this system resource will be deleted.
Information about API functions is provided in the table below.
handle_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Closes a handle and revokes its descendants. Parameters
Returned values If successful, the function returns |
|
Purpose Revokes the handles that make up the inheritance subtree of the specified handle. Parameters
Returned values If successful, the function returns |
Closing handles
A process can close the handles that it owns. Closing a handle terminates the association between an ID and a resource, thereby releasing the ID. Closing 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 closed handle can still be used to provide access to the resource that they identify. Also, closing a handle does not disrupt the handle inheritance tree associated with the resource identified by the particular handle. The place of a closed handle is occupied by its ancestor. In other words, the ancestor of a closed handle becomes the direct ancestor of the descendants of the closed handle.
To close the handle, call the KnHandleClose()
function.
If each handle of a system resource in all processes that own these handles are revoked (see "Revoking handles") or closed, this system resource will be deleted.
Information about API functions is provided in the table below.
handle_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Closes a handle. Parameters
Returned values If successful, the function returns |
Getting a security ID (SID)
By getting the SID values for different handles, you can determine whether these handles identify different resources or the same resource.
To get an SID for a handle, call the KnHandleGetSidByHandle()
function. To do so, the OCAP_HANDLE_GET_SID
flag must be set in the handle permissions mask.
Information about API functions is provided in the table below.
handle_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Receives a security ID (SID) based on a handle. Parameters
Returned values If successful, the function returns |
OCap usage example
This example describes an OCap usage scenario in which the resource provider 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 resource consumer uses these methods.
IDL description:
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 resource consumer 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 and deletion 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 resource consumer, which means that the resource consumer receives a descendant of the handle owned by the resource provider.
- The
OpenResource()
method call completes successfully. The resource consumer 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 resource consumer to query the resource, but is transferred so that the resource consumer can find out its permissions for accessing the resource.
- The resource provider creates the resource transfer context and calls the
- The resource consumer calls the
UseResource()
method to utilize the resource.- The handle that was received from the resource provider at step 2 is used as a parameter of the
UseResource()
method. Before calling this method, the resource consumer 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 resource consumer) 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 resource consumer. Then the resource provider sends the results of this operation to the resource consumer.
- The
UseResource()
method call completes successfully. The resource consumer receives the results of the operation performed with the resource.
- The handle that was received from the resource provider at step 2 is used as a parameter of the
- The resource consumer 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 a parameter of the
CloseResource()
method. Before calling this method, the resource consumer uses thenk_handle_desc()
macro to package the handle into the handle transport container. After theCloseResource()
method is called, the resource consumer uses theKnHandleClose()
function to close 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 resource consumer. The resource handle owned by the resource provider and the handle of the resource transfer context object are used as parameters 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 resource consumer does not have to be revoked because the resource consumer already closed it. However, the revoke operation is performed in case the resource provider is not sure if the resource consumer actually closed the handle). - The
CloseResource()
method call completes successfully.
- The handle that was received from the resource provider at step 2 is used as a parameter of the
- The resource provider frees up the memory that was allocated for the resource transfer context and 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 close 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 was deleted, and frees up the memory that was allocated for the resource transfer context. - The resource provider uses the
KnHandleClose()
function to close the resource handle and free up the memory that was allocated for the user resource context.
- The resource provider calls the