Contents
- What's new
- About KasperskyOS Community Edition
- Overview of KasperskyOS
- Getting started
- Development for KasperskyOS
- Starting processes
- File systems and network
- Contents of the VFS component
- Creating an IPC channel to VFS
- Including VFS functionality in a program
- Overview: startup parameters and environment variables of VFS
- Mounting file systems when VFS starts
- Using VFS backends to separate data streams
- Creating a VFS backend
- Dynamically configuring the network stack
- IPC and transport
- KasperskyOS API
- Return codes
- libkos library
- Managing handles (handle_api.h)
- Allocating and freeing memory (alloc.h)
- Using DMA (dma.h)
- Managing interrupt processing (irq.h)
- Initializing IPC transport for interprocess communication and managing IPC request processing (transport-kos.h, transport-kos-dispatch.h)
- Initializing IPC transport for querying the security module (transport-kos-security.h)
- Generating random numbers (random_api.h)
- Getting and changing time values (time_api.h)
- Using notifications (notice_api.h)
- Dynamically creating IPC channels (cm_api.h, ns_api.h)
- Using synchronization primitives (event.h, mutex.h, rwlock.h, semaphore.h, condvar.h)
- Managing I/O memory isolation (iommu_api.h)
- Using queues (queue.h)
- Using memory barriers (barriers.h)
- Executing system calls (syscalls.h)
- IPC interrupt (ipc_api.h)
- POSIX support
- Obtaining statistical data on the system
- MessageBus component
- ExecutionManager component
- Building a KasperskyOS-based solution
- Developing security policies
- Formal specifications of KasperskyOS-based solution components
- Description of a security policy for a KasperskyOS-based solution
- General information about a KasperskyOS-based solution security policy description
- PSL language syntax
- Setting the global parameters of a KasperskyOS-based solution security policy
- Including PSL files in a KasperskyOS-based solution security policy description
- Including EDL files in a KasperskyOS-based solution security policy description
- Creating security model objects
- Binding methods of security models to security events
- Creating security audit profiles
- Creating and performing tests for a KasperskyOS-based solution security policy
- PSL data types
- Examples of binding security model methods to security events
- Example descriptions of basic security policies for KasperskyOS-based solutions
- Examples of security audit profiles
- Examples of tests for KasperskyOS-based solution security policies
- KasperskyOS Security models
- Pred security model
- Bool security model
- Math security model
- Struct security model
- Base security model
- Regex security model
- HashSet security model
- StaticMap security model
- StaticMap security model object
- StaticMap security model init rule
- StaticMap security model fini rule
- StaticMap security model set rule
- StaticMap security model commit rule
- StaticMap security model rollback rule
- StaticMap security model get expression
- StaticMap security model get_uncommitted expression
- Flow security model
- Mic security model
- Mic security model object
- Mic security model create rule
- Mic security model delete rule
- Mic security model execute rule
- Mic security model upgrade rule
- Mic security model call rule
- Mic security model invoke rule
- Mic security model read rule
- Mic security model write rule
- Mic security model query_level expression
- Methods of KasperskyOS core endpoints
- Virtual memory endpoint
- I/O endpoint
- Threads endpoint
- Handles endpoint
- Processes endpoint
- Synchronization endpoint
- File system endpoints
- Time endpoint
- Hardware abstraction layer endpoint
- XHCI controller management endpoint
- Audit endpoint
- Profiling endpoint
- I/O memory isolation management endpoint
- Connections endpoint
- Power management endpoint
- Notifications endpoint
- Hypervisor endpoint
- Trusted Execution Environment endpoints
- IPC interrupt endpoint
- CPU frequency management endpoint
- Using the system programs Klog and KlogStorage to perform a security audit
- Security patterns for development under KasperskyOS
- Appendices
- Additional examples
- hello example
- echo example
- ping example
- net_with_separate_vfs example
- net2_with_separate_vfs example
- embedded_vfs example
- vfs_extfs example
- multi_vfs_ntpd example
- multi_vfs_dns_client example
- multi_vfs_dhcpcd example
- mqtt_publisher (Mosquitto) example
- mqtt_subscriber (Mosquitto) example
- gpio_input example
- gpio_output example
- gpio_interrupt example
- gpio_echo example
- koslogger example
- pcre example
- messagebus example
- I2c_ds1307_rtc example
- iperf_separate_vfs example
- Uart example
- spi_check_regs example
- barcode_scanner example
- perfcnt example
- watchdog_system_reset example
- shared_libs example
- Information about certain limits set in the system
- Additional examples
- Licensing
- Data provision
- Glossary
- Application
- Arena chunk descriptor
- Arena descriptor
- Callable handle
- Capability
- CDL
- Client
- Client library of the solution component
- Client Process
- Conditional variable
- Constant part of an IPC message
- Critical section
- Description of a security policy for a KasperskyOS-based solution
- Direct memory access
- DMA
- DMA buffer
- EDL
- Endpoint
- Endpoint ID
- Endpoint Interface
- Endpoint method
- Endpoint Method ID
- Event
- Event mask
- Execute interface
- Formal specification of the KasperskyOS-based solution component
- Handle
- Handle dereferencing
- Handle inheritance tree
- Handle permissions mask
- Handle transport container
- Hardware interrupt
- IDL
- Init description
- Initializing program
- Interface Method
- Interprocess communication
- IPC
- IPC channel
- IPC handle
- IPC message
- IPC message arena
- IPC request
- IPC response
- IPC transport
- KasperskyOS
- KasperskyOS Security Model
- KasperskyOS-based solution
- KasperskyOS-based solution component
- KSM
- KSS
- Listener handle
- Memory barrier
- Message signaled interrupt (MSI)
- MID
- Mutex
- Notification receiver
- OCap
- Operating Performance Point
- OPP
- PAL
- Process
- Program
- PSL
- Read-write lock
- Recursive mutex
- Resource
- Resource consumer
- Resource integrity level
- Resource provider
- Resource transfer context
- Resource transfer context object
- RIID
- Security audit
- Security audit configuration
- Security audit data
- Security audit profile
- Security audit runtime-level
- Security context
- Security event
- Security ID
- Security interface
- Security model expression
- Security model method
- Security model object
- Security model rule
- Security module decision
- Security pattern
- Security pattern system
- Security policy for a KasperskyOS-based solution
- Security template
- Seed
- Semaphore
- Server
- Server library of the solution component
- Server process
- SID
- Subject integrity level
- System program
- System resource
- Thread
- Transport code
- Transport library
- User resource
- User resource context
- Information about third-party code
- Trademark notices
What's new
KasperskyOS Community Edition 1.2 has the following new capabilities and refinements:
Due to modifications made to SDK components, you must make changes to application code that was developed using KasperskyOS Community Edition version 1.1.1 before using that code with KasperskyOS Community Edition version 1.2. For more details, refer to Migrating application code from version 1.1.1 to version 1.2.
- Updated system requirements: the Ubuntu GNU/Linux 22.04 "Jammy Jellyfish" operating system is required for SDK installation.
- Added capability to use dynamic libraries.
- Added capability to use a hardware watchdog on the Raspberry Pi 4 Model B.
- Added ExecutionManager component designed for creating, starting, and stopping processes.
- Added script for automatically setting environment variables used by SDK tools.
- Added data transmission to Kaspersky servers when starting a build of examples from the SDK. Data is transmitted to account for the number of users of KasperskyOS Community Edition and to obtain information about the distribution and use of KasperskyOS Community Edition. You can disable this functionality.
- Updated Developer's Guide, including:
- Added section titled "Working with an IPC message arena".
- Added section titled "Information about certain limits set in the system".
- Added descriptions of scenarios for working with libkos library interfaces.
- Updated instructions on building and running solution security policy tests.
- Added glossary.
- Added the following third-party libraries and applications:
- Guidelines Support Library (GSL) (2.1.0)
- json_scheme_validator (2.1.0)
- libpcap (1.10.4)
- libunwind (1.6.2)
- Updated the following third-party libraries and applications:
- libxml2
- Mbedtls
- Mosquitto
- OpenSSL
- spdlog
- sqlite
- fmt
- zlib
- flex
- bison
- QEMU
- Excluded the following third-party libraries and applications from the SDK:
- ffmpeg
- opencv
- libjpeg-turbo
- libpng
- protobuf
KasperskyOS Community Edition 1.1.1 has the following new capabilities and refinements:
- Updated the following third-party libraries and applications:
- FFmpeg
- libxml2
- Eclipse Mosquitto
- opencv
- OpenSSL
- protobuf
- sqlite
- usb
- Added support for the Raspberry Pi 4 Model B hardware platform (Revision 1.5).
KasperskyOS Community Edition 1.1 has the following new capabilities and refinements:
- Added support for working with an I2C bus in master device mode.
- Added support for working with an SPI bus in master device mode.
- Added support for USB HID devices.
- Added support for Symmetric Multiprocessing (SMP).
- Expanded capabilities for device profiling: added iperf library and counters that track system parameters.
- Added PCRE library and usage example.
- Added SPDLOG library and usage example.
- Added MessageBus component and usage example.
- Added dynamic code analysis tools (ASAN, UBSAN).
KasperskyOS Community Edition 1.0 has the following new capabilities and refinements:
- Added support for the Raspberry Pi 4 Model B hardware platform.
- Added SD card support for the Raspberry Pi 4 Model B hardware platform.
- Added Ethernet support for the Raspberry Pi 4 Model B hardware platform.
- Added GPIO port support for the Raspberry Pi 4 Model B hardware platform.
- Added network services for DHCP, DNS, and NTP and usage examples.
- Added library for working with the MQTT protocol and usage examples.
About KasperskyOS Community Edition
KasperskyOS Community Edition (CE) is a publicly available version of KasperskyOS that is designed to help you master the main principles of application development under KasperskyOS. KasperskyOS Community Edition will let you see how the concepts rooted in KasperskyOS actually work in practical applications. KasperskyOS Community Edition includes sample applications with source code, detailed explanations, and instructions and tools for building applications.
KasperskyOS Community Edition will help you:
- Learn the principles and techniques of "secure by design" development based on practical examples.
- Explore KasperskyOS as a potential platform for implementing your own projects.
- Make prototypes of solutions (primarily Embedded/IoT) based on KasperskyOS.
- Port applications/components to KasperskyOS.
- Explore security issues in software development.
KasperskyOS Community Edition lets you develop applications in the C and C++ languages. For more details about setting up the development environment, see "Configuring the development environment".
You can download KasperskyOS Community Edition here.
In addition to this documentation, we also recommend that you explore the materials provided in the specific KasperskyOS website section for developers.
Page top
About this Guide
The KasperskyOS Community Edition Developer's Guide is intended for specialists involved in the development of secure solutions based on KasperskyOS.
The Guide is designed for specialists who know the C/C++ programming languages, have experience developing for POSIX-compatible systems, and are familiar with GNU Binary Utilities (binutils).
You can use the information in this Guide to:
- Install and remove KasperskyOS Community Edition.
- Use KasperskyOS Community Edition.
Distribution kit
The KasperskyOS SDK is a set of software tools for creating KasperskyOS-based solutions.
The distribution kit of KasperskyOS Community Edition includes the following:
- DEB package for installation of KasperskyOS Community Edition, including:
- Image of the KasperskyOS kernel
- Development tools (GCC compiler, LD linker, binutils toolset, QEMU emulator, and accompanying tools)
- Utilities and scripts (for example, source code generators,
makekss
script for creating the Kaspersky Security Module, andmakeimg
script for creating the solution image) - A set of libraries that provide partial compatibility with the POSIX standard
- Drivers
- System programs (for example, virtual file system)
- Usage examples for components of KasperskyOS Community Edition
- End User License Agreement
- Information about third-party code (Legal Notices)
- KasperskyOS Community Edition Developer's Guide (Online Help)
- Release Notes
The KasperskyOS SDK is installed to a computer running the Ubuntu GNU/Linux operating system.
The following components included in the KasperskyOS Community Edition distribution kit are the Runtime Components as defined by the terms of the License Agreement:
- Image of the KasperskyOS kernel.
All the other components of the distribution kit are not the Runtime Components. Terms and conditions of the use of each component can be additionally defined in the section "Information about third-party code".
Page top
System requirements
To install KasperskyOS Community Edition and run examples on QEMU, the following is required:
- Operating system: Ubuntu GNU/Linux 22.04 (Jammy Jellyfish). A Docker container can be used.
- Processor: x86-64 architecture (support for hardware virtualization is required for higher performance).
- RAM: it is recommended to have at least 4 GB of RAM for convenient use of the build tools.
- Disk space: at least 3 GB of free space in the
/opt
folder (depending on the solution being developed).
To run examples on the Raspberry Pi hardware platform, the following is required:
- Raspberry Pi 4 Model B (Revision 1.1, 1.2, 1.4, 1.5) with 2, 4, or 8 GB of RAM
- MicroSD card with at least 2 GB
- USB-UART converter
Included third-party libraries and applications
To simplify the application development process, KasperskyOS Community Edition also includes the following third-party libraries and applications:
- flex (v.2.6.2) is a lexical analyzer generator.
Documentation: https://github.com/westes/flex
- pkg-config-lite (v.0.28) is a tool that provides an interface for getting information about the libraries installed in the system (such as the version and parameters for the C/C ++ compiler and linker).
Documentation: https://sourceforge.net/projects/pkgconfiglite
- CMake (v.3.25.0) is a cross-platform software tool that automatically builds software from source code.
Documentation: https://cmake.org/documentation
- autoconf-archive (v.2022.09.03) is a set of macros for the Autoconf tool, which creates configuration scripts for automatically configuring and building software from source code.
Documentation: https://www.gnu.org/software/autoconf-archive
- Automake (v.1.13 and v.1.16.4) is a tool that generates standard
Makefile.in
files for automatically configuring and building software from source code.Documentation: https://www.gnu.org/software/automake
- Autoconf (v.2.69) is a tool that generates
configure
scripts for automatically configuring and building software from source code.Documentation: https://www.gnu.org/software/autoconf
- autotools-wrappers (v.am-10) is a wrapper for the Autoconf and Automake tools that determines which installed version of a tool is suitable for automatically configuring and building software.
Documentation: https://gitweb.gentoo.org/proj/autotools-wrappers.git/tree
- Libtool (v.2.4.2) is a generic library support script that conceals the complexity of using shared libraries behind a consistent, portable interface.
Documentation: https://www.gnu.org/software/libtool
- Binutils (v.2.38) is a set of tools for working with binary files that includes an assembler, linker, archiver, and other tools.
Documentation: https://www.gnu.org/software/binutils
- Bison (v.3.5.4) is a general-purpose syntax analyzer generator that converts an annotated context-free grammar into an LR or GLR parser employing LALR(1) parser tables.
Documentation: https://www.gnu.org/software/bison
- GNU Compiler Collection (GCC) (v.9.2.1) is a set of compilers for various programming languages, including C and C ++.
Documentation: https://gcc.gnu.org/onlinedocs
- QEMU (v.8.1.3) is a program that emulates hardware of various platforms.
Documentation: https://www.qemu.org/docs/master
- Automated Testing Framework (ATF) (v.0.20) is a set of libraries for writing tests for programs in C, C++ and POSIX shell.
Documentation: https://github.com/jmmv/atf
- Boost (v.1.78.0) is a set of class libraries that utilize C++ language functionality and provide a convenient cross-platform, high-level interface for concise coding of various everyday programming subtasks (such as working with data, algorithms, files, threads, and more).
Documentation: https://www.boost.org/doc
- nlohmann_json (v.3.9.1) is the library for working with JSON format.
Documentation: https://github.com/nlohmann/json
- Civetweb (v.1.11) is an easy-to-use, powerful, embeddable web server based on C/C++ with additional support for CGI, SSL and Lua.
Documentation: http://civetweb.github.io/civetweb/UserManual.html
- fmt (v.9.1.0) is an open-source formatting library.
Documentation: https://fmt.dev/latest/index.html
- Guidelines Support Library (GSL) (v.2.1.0) is a library containing functions and types that are suggested for use by the C++ Core Guidelines maintained by the Standard C++ Foundation.
Documentation: https://github.com/microsoft/gsl
- GoogleTest (v.1.10.0) is a C++ code testing library.
Documentation: https://google.github.io/googletest
- iperf (v.3.10.1) is a network performance testing library.
Documentation: https://software.es.net/iperf
- json-schema-validator (v.2.1.0) is a library designed for validating data in JSON format according to defined JSON schemas.
Documentation: https://github.com/pboettch/json-schema-validator
- libffi (v.3.2.1) is a library providing a C interface for calling previously compiled code.
Documentation: https://github.com/libffi/libffi
- jsoncpp (v.1.9.4) is a library for working with JSON format.
Documentation: https://github.com/open-source-parsers/jsoncpp
- libpcap (v.1.10.4) is a library for developing programs that can capture, filter, and analyze network traffic in UNIX-like systems.
Documentation: https://www.tcpdump.org/index.html#documentation
- libunwind (v.1.6.2) is a library for handling exceptional situations and implementing a mechanism for backtracing the stack of function calls when a process crashes.
Documentation: https://www.nongnu.org/libunwind/docs.html
- libxml2 (v.2.10.4) is a library for working with XML.
Documentation: http://xmlsoft.org
- Mbed TLS (v.3.3.0) is a library that implements cryptographic protocols such as TLS/SSL and DTLS, and algorithms for encryption, hashing, and authentication.
Documentation: https://mbed-tls.readthedocs.io/en/latest
- Eclipse Mosquitto (v2.0.18) is a message broker that implements the MQTT protocol.
Documentation: https://mosquitto.org/documentation
- jsoncpp (v.4.2.8P15) is a library for working with the NTP time protocol.
Documentation: http://www.ntp.org/documentation.html
- OpenSSL (v.1.1.1t) is a full-fledged open-source encryption library.
Documentation: https://www.openssl.org/docs/
- pcre (v.8.44) is a library for working with regular expressions.
Documentation: https://www.pcre.org/current/doc/html
- spdlog (v.1.11.0) is a logging library.
Documentation: https://github.com/gabime/spdlog
- sqlite (v.3.41.2) is a library for working with databases.
Documentation: https://www.sqlite.org/docs.html
- Zlib (v.1.2.13) is the data compression library.
Documentation: https://zlib.net/manual.html
- usb (v.13.0.0) is a library for working with USB devices.
Documentation: https://github.com/freebsd/freebsd-src/tree/release/13.0.0/sys/dev/usb
- libevdev (v.1.6.0) is a library for working with evdev peripheral devices.
Documentation: https://www.freedesktop.org/software/libevdev/doc/latest
- dhcpcd (v.9.4.1) is a DHCP/DHCPv6 client intended for automatic configuration of network settings on the client side.
Documentation: https://github.com/NetworkConfiguration/dhcpcd
- Lwext4 (v.1.0.0) is a library for working with the ext2/3/4 file systems.
Documentation: https://github.com/gkostka/lwext4.git
See also Information about third-party code.
Page top
Limitations and known issues
Because the KasperskyOS Community Edition is intended for educational purposes only, it includes several limitations:
- The maximum supported number of running programs is 32.
- When a program is terminated through any method (for example, "return" from the main thread), the resources allocated by the program are not released, and the program goes to sleep. Programs cannot be started repeatedly.
- You cannot start two or more programs that have the same EDL description.
- The system stops if no running programs remain, or if one of the driver program threads has been terminated, whether normally or abnormally.
- When running examples on the Raspberry Pi 4 Model B hardware platform, the maximum size of the solution image (kos-image file) must not exceed 248 MB.
Migrating application code from SDK version 1.1.1 to SDK version 1.2
Due to modifications made to SDK components in version 1.2, you must make changes to application code that was developed using KasperskyOS Community Edition version 1.1.1 before using that code with KasperskyOS Community Edition version 1.2.
Required changes:
- The SDK now includes a driver for working with the VideoCore (VC6) coprocessor via mailbox technology:
kl.drivers.Bcm2711MboxArmToVc
. Thekl.drivers.DNetSrv
andkl.drivers.USB
drivers require access to this new driver.- If the
init.yaml.in
template is used to create the solution init description (init.yaml
file) and the@INIT_ProgramName_ENTITY_CONNECTIONS+@
or@INIT_ProgramName_ENTITY_CONNECTIONS@
macros were used for thekl.drivers.DNetSrv
andkl.drivers.USB
processes, no changes to the init description are required.Otherwise, if IPC channels for the
kl.drivers.DNetSrv
andkl.drivers.USB
processes are manually specified, you must add thekl.drivers.Bcm2711MboxArmToVc
process to the init description and define the IPC channels between it and thekl.drivers.DNetSrv
andkl.drivers.USB
processes:
- name: kl.drivers.Bcm2711MboxArmToVc path: bcm2711_mbox_arm2vc_h - name: kl.drivers.USB path: usb connections: ... - target: kl.drivers.Bcm2711MboxArmToVc id: kl.drivers.Bcm2711MboxArmToVc - name: kl.drivers.DNetSrv path: dnet_entity connections: ... - target: kl.drivers.Bcm2711MboxArmToVc id: kl.drivers.Bcm2711MboxArmToVc- You must add the
kl.drivers.Bcm2711MboxArmToVc
process to thesecurity.psl
file and allow thekl.drivers.DNetSrv
andkl.drivers.USB
processes and the kernel to interact with it:
... use kl.drivers.Bcm2711MboxArmToVc._ ... execute src = Einit dst = kl.drivers.Bcm2711MboxArmToVc { grant () } request src = kl.drivers.Bcm2711MboxArmToVc dst = kl.core.Core { grant () } response src = kl.core.Core dst = kl.drivers.Bcm2711MboxArmToVc { grant () } request src = kl.drivers.DNetSrv dst = kl.drivers.Bcm2711MboxArmToVc { grant () } response src = kl.drivers.Bcm2711MboxArmToVc dst = kl.drivers.DNetSrv { grant () } request src = kl.drivers.USB dst = kl.drivers.Bcm2711MboxArmToVc { grant () } response src = kl.drivers.Bcm2711MboxArmToVc dst = kl.drivers.USB{ grant () } - If the
- All implementations of the VFS component now require access to the
kl.EntropyEntity
program.- If the
init.yaml.in
template is used to create the solution init description (init.yaml
file) and the@INIT_ProgramName_ENTITY_CONNECTIONS+@
or@INIT_ProgramName_ENTITY_CONNECTIONS@
macros were used for processes that use the VFS component (thekl.VfsNet
,kl.VfsRamFs
, andkl.VfsSdCardFs
processes as well as the processes that statically include VFS), no changes to the init description are required.Otherwise, if IPC channels for processes that use the VFS component are manually specified, you must add the
kl.EntropyEntity
process to the init description and define the IPC channels between it and the processes that use the VFS component:
- name: kl.VfsSdCardFs path: VfsSdCardFs connections: ... - target: kl.EntropyEntity id: kl.EntropyEntity - name: kl.VfsNet path: VfsNet connections: ... - target: kl.EntropyEntity id: kl.EntropyEntity - name: kl.ProgramWithEmbeddedVfs path: ProgramWithEmbedVfs connections: ... - target: kl.EntropyEntity id: kl.EntropyEntity - name: kl.EntropyEntity path: Entropy- You must add the
kl.EntropyEntity
process to thesecurity.psl
file and allow the kernel and processes that use the VFS component to interact with it:
... use kl.EntropyEntity._ ... execute src = Einit dst = kl.drivers.EntropyEntity { grant () } ... request src = kl.EntropyEntity dst = kl.core.Core { grant () } response src = kl.core.Core dst = kl.EntropyEntity { grant () } request src = kl.VfsNet dst = kl.EntropyEntity { grant () } response src = kl.EntropyEntity dst = kl.VfsNet { grant () } request src = kl.VfsSdCardFs dst = kl.EntropyEntity { grant () } response src = kl.EntropyEntity dst = kl.VfsSdCardFs { grant () } request src = kl.ProgramWithEmbeddedVfs dst = kl.EntropyEntity { grant () } response src = kl.EntropyEntity dst = kl.ProgramWithEmbeddedVfs { grant () } - If the
- The
kl.drivers.USB
driver now requires access to thekl.core.NameServer
program.- If the
init.yaml.in
template is used to create the solution init description (init.yaml
file) and the@INIT_ProgramName_ENTITY_CONNECTIONS+@
or@INIT_ProgramName_ENTITY_CONNECTIONS@
macros were used for thekl.drivers.USB
process, no changes to the init description are required.Otherwise, if IPC channels for the
kl.drivers.USB
process are manually specified, you must add thekl.core.NameServer
process to the init description and define the IPC channels between it and thekl.drivers.USB
process:
- name: kl.core.NameServer path: ns - name: kl.drivers.USB path: usb connections: ... - target: kl.core.NameServer id: kl.core.NameServer- You must add the
kl.core.NameServer
process to thesecurity.psl
file and allow thekl.drivers.USB
process and the kernel to interact with it:
... use kl.core.NameServer ... execute src = Einit dst = kl.core.NameServer { grant () } ... request src = kl.core.NameServer dst = kl.core.Core { grant () } response src = kl.core.Core dst = kl.core.NameServer { grant () } request src = kl.drivers.USB dst = kl.core.NameServer { grant () } response src = core.NameServer dst = kl.drivers.USB { grant () } - If the
- The capability to use dynamic libraries has been added to the SDK. Now all solutions are built using dynamic linking by default. This may affect the build of solutions containing libraries that have both static and dynamic variants.
- To include enforced static linking of executable files, replace
initialize_platform()
withinitialize_platform (FORCE_STATIC)
in the root CMakeLists.txt of the project. - To switch from static linking to dynamic linking, you must complete additional steps as described in the article titled Using dynamic libraries.
- To use dynamic libraries, your solution must include the system program BlobContainer.
- You must add the
kl.bc.BlobContainer
process to thesecurity.psl
file and allow processes that use dynamic libraries to interact with it:
... use kl.bc.BlobContainer ... execute src = Einit dst = kl.bc.BlobContainer { grant () } request { /* Allows tasks with the kl.bc.BlobContainer class to send requests to specified tasks. */ match src = kl.bc.BlobContainer { match dst = kl.core.Core { grant () } match dst = kl.VfsSdCardFs { grant () } } /* Allows task with the kl.bc.BlobContainer class to recive request from any task. */ match dst = kl.bc.BlobContainer { grant () } } response { /* Allows tasks with the kl.bc.BlobContainer class to get responses from specified tasks. */ match dst = kl.bc.BlobContainer { match src = kl.core.Core { grant () } match src = kl.VfsSdCardFs { grant () } } /* Allows task with the kl.bc.BlobContainer class to send response to any task. */ match src = kl.bc.BlobContainer { grant () } }You can move the permissions for
kl.bc.BlobContainer
operations to a separate PSL file and include this file in the solution. (see the secure_logger example in the SDK). - To include enforced static linking of executable files, replace
- The romfs file system can now be mounted only in read-only mode.
- When mounting romfs in C/C++ code using the
mount()
function, you must pass theMS_RDONLY
flag. - You must also make changes to the command-line arguments of the VFS program in the init description or in the CMakeLists.txt file for building the Einit program.
Example of mounting the romfs file system in the init.yaml file:
- name: kl.VfsSdCardFs path: VfsSdCardFs connections: - target: kl.drivers.SDCard id: kl.drivers.SDCard - target: kl.EntropyEntity id: kl.EntropyEntity args: - -l - nodev /tmp ramfs 0 - -l - romfs /etc romfs ro env: ROOTFS: mmc0,0 / fat32 0 VFS_FILESYSTEM_BACKEND: server:kl.VfsSdCardFsExample of mounting the romfs file system in the CMakeLists.txt file:
set (VFS_NET_ARGS " - -l - devfs /dev devfs 0 - -l - romfs /etc romfs ro") set_target_properties (${precompiled_vfsVfsNet} PROPERTIES EXTRA_ARGS ${VFS_NET_ARGS}) - When mounting romfs in C/C++ code using the
Overview of KasperskyOS
KasperskyOS is a specialized operating system based on a separation microkernel and security monitor.
See also:
Overview
Microkernel
KasperskyOS is a microkernel operating system. The kernel provides minimal functionality, including scheduling of program execution, management of memory and input/output. The code of device drivers, file systems, network protocols and other system software is executed in user mode (outside of the kernel context).
Processes and endpoints
Software managed by KasperskyOS is executed as processes. A process is a running program that has the following distinguishing characteristics:
- It can provide endpoints to other processes and/or use the endpoints of other processes via the IPC mechanism.
- It uses core endpoints via the IPC mechanism.
- It is associated with security rules that regulate the interactions of the process with other processes and with the kernel.
An endpoint is a set of logically related methods available via the IPC mechanism (for example, an endpoint for receiving and transmitting data over the network, or an endpoint for handling interrupts).
Implementation of the MILS and FLASK architectural approaches
When developing a KasperskyOS-based system, software is designed as a set of components (programs) whose interactions are regulated by security mechanisms. In terms of security, the degree of trust in each component may be high or low. In other words, the system software includes trusted and untrusted components. Interactions between different components (and between components and the kernel) are controlled by the kernel (see the figure below), which has a high level of trust. This type of system design is based on the architectural approach known as MILS (Multiple Independent Levels of Security), which is employed when developing critical information systems.
A decision on whether to allow or deny a specific interaction is made by the Kaspersky Security Module. (This decision is referred to as the security module decision.) The security module is a kernel module whose trust level is high like the trust level of the kernel. The kernel executes the security module decision. This type of division of interaction management functions is based on the architectural approach known as FLASK (Flux Advanced Security Kernel), which is used in operating systems for flexible application of security policies.
Interaction between different processes and between processes and the kernel in KasperskyOS
KasperskyOS-based solution
A KasperskyOS-based solution (hereinafter also referred to as the solution) consists of system software (including the KasperskyOS kernel and Kaspersky Security Module) and applications integrated to work as part of the software/hardware system. The programs included in a KasperskyOS-based solution are considered to be components of the KasperskyOS-based solution (hereinafter referred to as solution components). Each instance of a solution component is executed in the context of a separate process.
Security policy for a KasperskyOS-based solution
Interactions between the various processes and between processes and the KasperskyOS kernel are allowed or denied according to the KasperskyOS-based solution security policy (hereinafter referred to as the solution security policy or simply the policy). The solution security policy is stored in the Kaspersky Security Module and is used by this module whenever it makes decisions on whether to allow or deny interactions.
The solution security policy can also define the logic for handling queries sent by a process to the security module via the security interface. A process can use the security interface to send some data to the security module (for example, to influence future decisions made by the security module) or to receive a security module decision that is needed by the process to determine its own further actions.
Kaspersky Security System technology
Kaspersky Security System technology lets you implement diverse security policies for solutions. You can also combine multiple security mechanisms and flexibly regulate the interactions between different processes and between processes and the KasperskyOS kernel. A Kaspersky Security Module to be used in a specific solution is created based on the solution security policy description.
Source code generators
Some of the source code of a KasperskyOS-based solution is created by source code generators. Specialized programs generate the source code in C from declarative descriptions. They generate source code of the Kaspersky Security Module, source code of the initializing program (which starts all other programs in the solution and statically defines the topology of interaction between them), and the source code of the methods and types for carrying out IPC (transport code).
The transport code is generated by the nk-gen-c
compiler based on the formal specifications of solution components.
The source code of the Kaspersky Security Module is generated by the nk-psl-gen-c
compiler from the solution security policy description and formal specifications of solution components.
The source code of the initializing program is generated by the einit
tool from the init description and formal specifications of solution components.
KasperskyOS architecture
The KasperskyOS architecture is presented in the figure below:
KasperskyOS architecture
In KasperskyOS, applications and drivers interact with each other and with the kernel by using the libkos
library, which provides the interfaces for querying core endpoints. (In KasperskyOS, a driver generally operates with the same level of privileges as the application.) The libkos
library queries the kernel by executing only three system calls: Call()
, Recv()
and Reply()
. These calls are implemented by the IPC mechanism. Core endpoints are supported by kernel subsystems whose purposes are presented in the table below. Kernel subsystems interact with hardware through the hardware abstraction layer (HAL), which makes it easier to port KasperskyOS to various platforms.
Kernel subsystems and their purpose
Designation |
Name |
Purpose |
---|---|---|
HAL |
Hardware abstraction subsystem |
Basic hardware support: timers, interrupt controllers, memory management unit (MMU). This subsystem includes UART drivers and low-level means for power management. |
IO |
I/O manager |
Registration and deallocation of hardware platform resources required for the operation of drivers, such as Interrupt ReQuest (IRQ), Memory-Mapped Input-Output (MMIO), I/O ports, and DMA buffers. If the hardware platform has an input–output memory management unit (IOMMU), this subsystem guarantees the allocation of memory used by devices. |
MM |
Physical memory manager |
Allocation and deallocation of physical memory pages, distribution of physically contiguous page areas. |
VMM |
Virtual memory manager |
Management of physical and virtual memory: reserving, committing, and releasing memory. Working with memory page tables for insulating the address spaces of processes. |
THREAD |
Thread manager |
Management of threads: creating, terminating, locking, and resuming threads. |
TIME |
Real-time clock subsystem |
Getting the time and setting the system clock. Using clocks provided by hardware. |
SCHED |
Scheduler |
Scheduling of threads: standard threads, real-time threads, and idle threads. |
SYNC |
Synchronization primitive support subsystem |
Implementation of basic synchronization primitives: spinlocks, mutexes, and events. The kernel supports only one primitive—futex. All other primitives are implemented based on a futex in the user space. |
IPC |
Interprocess communication subsystem |
Implementation of a synchronous IPC mechanism based on the rendezvous principle. |
KSMS |
Security module interaction subsystem |
This subsystem is used for working with the security module. It provides all messages relayed via IPC to the security module so that these messages can be checked. |
OBJ |
Object manager |
Management of the general behavior of all KasperskyOS resources: tracking their life cycle and assigning unique security IDs (for details, see "Resource Access Control"). This subsystem is closely linked to the capability-based access control mechanism (OCap). |
ROMFS |
Immutable file system image startup subsystem |
Operations with files from ROMFS: opening and closing, receiving a list of files and their descriptions, and receiving file characteristics (name, size). |
TASK |
Process management subsystem |
Management of processes: creating, starting, and terminating processes. Receiving information about running processes (such as names and paths) and their exit codes. |
ELF |
Executable file loading subsystem |
Loading executable ELF files from ROMFS into RAM, parsing headers of ELF files. |
DBG |
Debug support subsystem |
Debugging mechanism based on GDB (GNU Debugger). The availability of this subsystem in the kernel is optional. |
PM |
Power manager |
Power management: restart and shutdown. |
IPC mechanism
Exchanging IPC messages
In KasperskyOS, processes interact with each other by exchanging IPC messages (IPC request and IPC response). In an interaction between processes, there are two separate roles: client (the process that initiates the interaction) and server (the process that handles the request). Additionally, a process that acts as a client in one interaction can act as a server in another.
To exchange IPC messages, the client and server use three system calls: Call()
, Recv()
and Reply()
(see the figure below):
- The client sends an IPC request to the server. To do so, one of the client's threads makes the
Call()
system call and is locked until an IPC response is received from the server. - The server thread that has made the
Recv()
system call waits for IPC requests. When an IPC request is received, this thread is unlocked and handles the request, then sends an IPC response by making theReply()
system call. - When an IPC response is received, the client thread is unlocked and continues execution.
Exchanging IPC messages between a client and a server
Calling methods of server endpoints
IPC requests are sent to the server when the client calls endpoint methods of the server (hereinafter also referred to as interface methods) (see the figure below). The IPC request contains input parameters for the called method, as well as the endpoint ID (RIID) and the called method ID (MID). Upon receiving a request, the server uses these identifiers to find the method's implementation. The server calls the method's implementation while passing in the input parameters from the IPC request. After handling the request, the server sends the client an IPC response that contains the output parameters of the method.
Calling a server endpoint method
IPC channels
To enable two processes to exchange IPC messages, an IPC channel must be established between them. An IPC channel has a client side and a server side. One process can use multiple IPC channels at the same time. A process may act as a server for some IPC channels while acting as a client for other IPC channels.
KasperskyOS has two mechanisms for creating IPC channels:
- The static mechanism allows the parent process to create an IPC channel between child processes. Static creation of IPC channels is normally performed by the initializing program.
- The dynamic mechanism allows already running processes to create IPC channels between each other.
IPC control
The Kaspersky Security Module is integrated into the IPC implementation mechanism. The security module is aware of the structure of IPC messages for all possible interactions because IDL, CDL and EDL descriptions are used to generate the source code of this module. This enables the security module to verify that the interactions between processes comply with the solution security policy.
The KasperskyOS kernel queries the security module each time a process sends an IPC message to another process. The security module operating scenario includes the following steps:
- The security module verifies that the IPC message complies with the called method of the endpoint (the size of the IPC message is verified along with the size and location of certain structural elements).
- If the IPC message is incorrect, the security module makes the "deny" decision and the next step of the scenario is not carried out. If the IPC message is correct, the next step of the scenario is carried out.
- The security module checks whether the security rules allow the requested action. If allowed, the security module makes the "granted" decision. Otherwise it makes the "denied" decision.
The kernel executes the security module decision. In other words, it either delivers the IPC message to the recipient process or rejects its delivery. If delivery of an IPC message is rejected, the sender process receives an error code via the return code of the Call()
or Reply()
system call.
The security module checks IPC requests as well as IPC responses. The figure below depicts the controlled exchange of IPC messages between a client and a server.
Controlled exchange of IPC messages between a client and a server
Page top
Transport code for IPC
Implementation of interprocess interaction requires transport code, which is responsible for generating, sending, receiving, and processing IPC messages. However, developers of KasperskyOS-based solutions do not have to write their own transport code. Instead, you can use special tools and libraries included in the KasperskyOS SDK.
Transport code for developed components of a solution
A developer of a KasperskyOS-based solution component can generate transport code based on IDL, CDL and EDL descriptions related to this component. The KasperskyOS SDK includes the nk-gen-c
compiler for this purpose. The nk-gen-c
compiler generates transport methods and types for use by both a client and a server.
Transport code for supplied components of a solution
Most components included in the KasperskyOS SDK may be used in a solution both locally (through static linking with other components) as well as via IPC.
To use a supplied component via IPC, the KasperskyOS SDK provides the following transport libraries:
- Solution component's client library, which converts local calls into IPC requests.
- Solution component's server library, which converts IPC requests into local calls.
The client library is linked to the client code (the component code that will use the supplied component). The server library is linked to the implementation of the supplied component (see the figure below).
Using a supplied solution component via IPC
Page top
IPC between a process and the kernel
The IPC mechanism is used for interaction between processes and the KasperskyOS kernel. In other words, processes exchange IPC messages with the kernel. The kernel provides endpoints, and processes use those endpoints. Processes query core endpoints by calling functions of the libkos
library (directly or via other libraries). The client transport code for interaction between a process and the kernel is included in this library.
A solution developer is not required to create IPC channels between processes and the kernel because these channels are created automatically when processes are created. (To set up interaction between processes, the solution developer has to create IPC channels between them.)
The Kaspersky Security Module makes decisions regarding interaction between processes and the kernel the same way it makes decisions regarding interaction between a process and other processes. (The KasperskyOS SDK has IDL, CDL and EDL descriptions for the kernel that are used to generate source code of the security module.)
Page top
Resource Access Control
Types of resources
KasperskyOS has two types of resources:
- System resources, which are managed by the kernel. Some examples of these include processes, memory regions, and interrupts.
- User resources, which are managed by processes. Examples of user resources: files, input-output devices, data storage.
Handles
Both system resources and user resources are identified by handles. Processes (and the KasperskyOS kernel) can transfer handles to other processes. By receiving a handle, a process obtains access to the resource that is identified by this handle. In other words, the process that receives a handle can request operations to be performed on a resource by specifying its received handle in the request. The same resource can be identified by multiple handles used by different processes.
Security identifiers (SID)
The KasperskyOS kernel assigns security identifiers to system resources and user resources. A security identifier (SID) is a global unique ID of a resource (in other words, a resource can have only one SID but can have multiple handles). The Kaspersky Security Module identifies resources based on their SID.
When transmitting an IPC message containing handles, the kernel modifies the message so that it contains SID values instead of handles when the message is checked by the security module. When the IPC message is delivered to its recipient, it will contain the handles.
The kernel also has an SID like other resources.
Security context
Kaspersky Security System technology lets you employ security mechanisms that receive SID values as inputs. When employing these mechanisms, the Kaspersky Security Module distinguishes resources (and the KasperskyOS kernel) and binds security contexts to them. A security context consists of data that is associated with an SID and used by the security module to make decisions.
The contents of a security context depend on the security mechanisms being used. For example, a security context may contain the state of a resource and the levels of integrity of access subjects and/or access objects. If a security context stores the state of a resource, this lets you allow certain operations to be performed on a resource only if the resource is in a specific state, for example.
The security module can modify a security context when it makes a decision. For example, it can modify information about the state of a resource (the security module used the security context to verify that a file is in the "not in use" state and allowed the file to be opened for write access and wrote a new state called "opened for write access" into the security context of this file).
Resource access control by the KasperskyOS kernel
The KasperskyOS kernel controls access to resources by using two mutually complementary methods at the same time: executing the decisions of the Kaspersky Security Module and implementing a security mechanism based on object capabilities (OCap).
Each handle is associated with access rights to the resource identified by this handle, which means it is a capability in OCap terms. By receiving a handle, a process obtains the access rights to the resource that is identified by this handle. For example, these access rights may consist of read permissions, write permissions, and/or permissions to allow another process to perform operations on the resource (handle transfer permission).
Processes that use the resources provided by the kernel or other processes are referred to as resource consumers. When a resource consumer opens a system resource, the kernel sends the consumer the handle associated with the access rights to this resource. These access rights are assigned by the kernel. Before an operation is performed on a system resource requested by a consumer, the kernel verifies that the consumer has sufficient rights. If the consumer does not have sufficient rights, the kernel rejects the request of the consumer.
In an IPC message, a handle is sent together with its permissions mask. The handle permissions mask is a value whose bits are interpreted as access rights to the resource identified by the handle. A resource consumer can find out their access rights to a system resource from the handle permissions mask of this resource. The kernel uses the handle permissions mask to verify that the consumer is allowed to request the operations to be performed on the system resource.
The security module can verify the permissions masks of handles and use these verifications to either allow or deny interactions between different processes and between processes and the kernel when such interactions are related to resource access.
The kernel prohibits the expansion of access rights when handles are transferred among processes (when a handle is transferred, access rights can only be restricted).
Resource access control by resource providers
Processes that control user resources and access to those resources for other processes are referred to as resource providers. For example, drivers are resource providers. Resource providers control access to resources by using two mutually complementary methods: executing the decisions of the Kaspersky Security Module and using the OCap mechanism that is provided by the KasperskyOS kernel.
If a resource is queried by its name (for example, to open it), the security module cannot be used to control access to the resource without the involvement of the resource provider. This is because the security module identifies a resource by its SID, not by its name. In such cases, the resource provider finds the resource handle based on the resource name and forwards this handle (together with other data, such as the required state of the resource) to the security module via the security interface (the security module receives the SID corresponding to the transferred handle). The security module makes a decision and returns it to the resource provider. The resource provider implements the decision of the security module.
When a resource consumer opens a user resource, the resource provider sends the consumer the handle associated with the access rights to this resource. In addition, the resource provider decides which specific rights for accessing the resource will be granted to the resource consumer. Before an operation is performed on a user resource as requested by a consumer, the resource provider verifies that the consumer has sufficient rights. If the consumer does not have sufficient rights, the resource provider rejects the request of the consumer.
A resource consumer can find out their access rights to a user resource from the permissions mask of the handle of this resource. The resource provider uses the handle permissions mask to verify that the consumer is allowed to request the operations to be performed on the user resource.
Page top
Structure and startup of a KasperskyOS-based solution
Structure of a solution
The image of the KasperskyOS-based solution loaded into hardware contains the following files:
- Image of the KasperskyOS kernel
- File containing the executable code of the Kaspersky Security Module
- Executable file of the initializing program
- Executable files of all other solution components (for example, applications and drivers)
- Files used by programs (for example, dynamic libraries, files containing settings, fonts, graphical and audio data)
The ROMFS file system is used to save files in the solution image.
Starting a solution
A KasperskyOS-based solution is started as follows:
- The bootloader starts the KasperskyOS kernel.
- The kernel finds and loads the security module (as a kernel module).
- The kernel starts the initializing program.
- The initializing program starts the programs included in the solution (one, several, or all).
Getting started
This section tells you what you need to know to start working with KasperskyOS Community Edition.
Using a Docker container
To install and use KasperskyOS Community Edition, you can use a Docker container in which an image of one of the supported operating systems is deployed.
To use a Docker container for installing KasperskyOS Community Edition:
- Make sure that the Docker software is installed and running.
- To download the official Docker image of the Ubuntu GNU/Linux 22.04 (Jammy Jellyfish) operating system from the public Docker Hub repository, run the following command:
docker pull ubuntu:22.04
- To run the image, run the following command:
docker run --net=host --user root --privileged -it --rm ubuntu:22.04 bash
- Copy the DEB package for installation of KasperskyOS Community Edition into the container.
- Install KasperskyOS Community Edition.
- To ensure correct operation of some examples, you must add the
/usr/sbin
directory to thePATH
environment variable within the container by running the following command:export PATH=/usr/sbin:$PATH
Installation and removal
Installation
KasperskyOS Community Edition is distributed as a DEB package. It is recommended to use the apt
package installer to install KasperskyOS Community Edition.
To deploy the package using apt
, run the following command:
The package will be installed in /opt/KasperskyOS-Community-Edition-<version>
.
To conveniently work with tools provided in the KasperskyOS Community Edition SDK, the path to the executable files of these tools in /opt/KasperskyOS-Community-Edition-<version>/toolchain/bin
must be added to the PATH
environment variable. To avoid having to do this each time you log in to a user session, run the script /opt/KasperskyOS-Community-Edition-<version>/set_env.sh
, log out and log in to a session again.
Syntax of the command for calling the set_env.sh
script:
Parameters:
-d
Cancels the action of the script.
-h
,--help
Displays the Help text.
In addition to changing the PATH
environment variable, the script defines the KOSCEVER
and KOSCEDIR
environment variables that contain the version and absolute path to the KasperskyOS Community Edition SDK, respectively. Use of these environment variables allows the build system to determine the SDK installation path at startup, and to verify that the solution version matches the SDK version.
Removal
Prior to removing KasperskyOS Community Edition, cancel the action of the set_env.sh
script if you ran this script after installing the SDK.
To remove KasperskyOS Community Edition, run the following command:
After running this command, all installed files in the /opt/KasperskyOS-Community-Edition-<version>
directory will be deleted.
Configuring the development environment
This section provides brief instructions on configuring the development environment and adding the header files included in KasperskyOS Community Edition to a development project.
Configuring the code editor
Before getting started, you should do the following to simplify your development of solutions based on KasperskyOS:
- Install code editor extensions and plugins for your programming language (C and/or C++).
- Add the header files included in KasperskyOS Community Edition to the development project.
The header files are located in the directory:
/opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include
.
Example of how to configure Visual Studio Code
For example, during KasperskyOS development, you can work with source code in Visual Studio Code.
To more conveniently navigate the project code, including the system API:
- Create a new workspace or open an existing workspace in Visual Studio Code.
A workspace can be opened implicitly by using the
File
>Open folder
menu options. - Make sure the C/C++ for Visual Studio Code extension is installed.
- In the
View
menu, select theCommand Palette
item. - Select the
C/C++: Edit Configurations (UI)
item. - In the
Include path
field, enter/opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include
. - Close the
C/C++ Configurations
window.
Building the examples
The examples are built using the CMake
build system that is included in KasperskyOS Community Edition.
The code of the examples and build scripts are available at the following path:
/opt/KasperskyOS-Community-Edition-<version>/examples
You need to build the examples in a directory in which you have write access, such as the home directory.
Building the examples to run on QEMU
To build an example, go to the directory with the example and run this command:
Running the cross-build.sh
script creates a KasperskyOS-based solution image that includes the example, and initiates startup of the example in QEMU. The kos-qemu-image
solution image is located in the <name of example>/build/einit
directory.
Building the examples to run on Raspberry Pi 4 B
To build an example, go to the directory with the example and run this command:
The type of image that is created by the cross-build.sh
script depends on the value that you choose for the target
parameter:
kos-image
This creates a KasperskyOS-based solution image that includes the example. The
kos-image
solution image is located in the<name of example>/build/einit
directory.sd-image
This creates a file system image for a bootable SD card. The following is loaded into the file system image:
kos-image
, U-Boot bootloader that starts the example, and the firmware for Raspberry Pi 4 B. The source code for the U-Boot bootloader and firmware can be downloaded from the website https://github.com. The file system image filerpi4kos.img
is saved in the directory<example name>/build
.
Running examples on QEMU
Running examples on QEMU on Linux with a graphical shell
An example is run on QEMU on Linux with a graphical shell using the cross-build.sh
script, which also builds the example. To run the script, go to the folder with the example and run the command:
Running examples on QEMU on Linux without a graphical shell
To run an example on QEMU on Linux without a graphical shell, go to the directory with the example, build the example and run the following commands:
Preparing Raspberry Pi 4 B to run examples
Connecting a computer and Raspberry Pi 4 B
To see the output from Raspberry Pi 4 B on a computer, do the following:
- Connect the pins of the FT232 USB-UART converter to the corresponding GPIO pins of the Raspberry Pi 4 B (see the figure below).
Diagram for connecting the USB-UART converter and Raspberry Pi 4 B
- Connect the computer's USB port to the USB-UART converter.
- Install PuTTY or another equivalent program. Configure the settings as follows:
bps = 115200
,data bits = 8
,stop bits = 1
,parity = none
,flow control = none
. Define the USB port connected to the USB-UART converter used for receiving output from Raspberry Pi 4 B.
To allow a computer and Raspberry Pi 4 B to interact through Ethernet:
- Connect the network cards of the computer and Raspberry Pi 4 B to a switch or to each other.
- Configure the computer's network card so that its IP address is in the same subnet as the IP address of the Raspberry Pi 4 B network card (the settings of the Raspberry Pi 4 B network card are defined in the
dhcpcd.conf
file, which is found at the path<example name>/resources/...
).
Preparing a bootable SD card for Raspberry Pi 4 B
If the rpi4kos.img
image was created when building the example, all you have to do is write the resulting image to the SD card. To do this, connect the SD card to the computer and run the following command:
If kos-image
was created when building the example, the SD card requires additional preparations before you can write the image to it. A bootable SD card for Raspberry Pi 4 B can be prepared automatically or manually.
To automatically prepare the bootable SD card, connect the SD card to the computer and run the following commands:
To manually prepare the bootable SD card:
- Build the U-Boot bootloader for ARMv8, which will automatically run the example. To do this, run the following commands:$ sudo apt install git build-essential libssl-dev bison flex unzip parted gcc-aarch64-linux-gnu pv -y $ git clone --depth 1 --branch v2022.01 https://github.com/u-boot/u-boot.git u-boot-armv8 $ cd u-boot-armv8 $ make ARCH=arm CROSS_COMPILE=aarch64-linux-gnu- rpi_4_defconfig $ echo 'CONFIG_SERIAL_PROBE_ALL=y' > ./.custom_config $ echo 'CONFIG_BOOTCOMMAND="fatload mmc 0 ${loadaddr} kos-image; bootelf ${loadaddr} ${fdt_addr}"' >> ./.custom_config $ echo 'CONFIG_PREBOOT="pci enum;"' >> ./.custom_config $ ./scripts/kconfig/merge_config.sh '.config' '.custom_config' $ make ARCH=arm CROSS_COMPILE=aarch64-linux-gnu- u-boot.bin
- Prepare the image containing the file system for the SD card.# Image will contain a boot partition of 1 GB in fat32 and three partitions of 350 MB each in ext2, ext3 and ext4, respectively. $ fs_image_name=sdcard.img $ dd if=/dev/zero of=${fs_image_name} bs=1024k count=2048 $ sudo parted ${fs_image_name} mklabel msdos $ loop_device=$(sudo losetup --find --show --partscan ${fs_image_name}) $ sudo parted ${loop_device} mkpart primary fat32 8192s 50% $ sudo parted ${loop_device} mkpart extended 50% 100% $ sudo parted ${loop_device} mkpart logical ext2 50% 67% $ sudo parted ${loop_device} mkpart logical ext3 67% 84% $ sudo parted ${loop_device} mkpart logical ext4 84% 100% $ sudo parted ${loop_device} set 1 boot on $ sudo mkfs.vfat ${loop_device}p1 $ sudo mkfs.ext2 ${loop_device}p5 $ sudo mkfs.ext3 ${loop_device}p6 $ sudo mkfs.ext4 -O ^64bit,^extent ${loop_device}p7
- Copy the U-Boot bootloader and embedded software (firmware) for Raspberry Pi 4 B to the received file system image by running the following commands:# In the following commands, the path ~/mnt/fat32 is just an example. # You can use a different path. $ mount_temp_dir=~/mnt/fat32 $ mkdir -p ${mount_temp_dir} $ sudo mount ${loop_device}p1 ${mount_temp_dir} $ git clone --depth 1 --branch 1.20220331 https://github.com/raspberrypi/firmware.git firmware $ sudo cp u-boot.bin ${mount_temp_dir}/u-boot.bin $ sudo cp -r firmware/boot/. ${mount_temp_dir}
- Fill in the configuration file for the U-Boot bootloader in the image by using the following commands:$ sudo sh -c "echo '[all]' > ${mount_temp_dir}/config.txt" $ sudo sh -c "echo 'arm_64bit=1' >> ${mount_temp_dir}/config.txt" $ sudo sh -c "echo 'enable_uart=1' >> ${mount_temp_dir}/config.txt" $ sudo sh -c "echo 'kernel=u-boot.bin' >> ${mount_temp_dir}/config.txt" $ sudo sh -c "echo 'dtparam=i2c_arm=on' >> ${mount_temp_dir}/config.txt" $ sudo sh -c "echo 'dtparam=i2c=on' >> ${mount_temp_dir}/config.txt" $ sudo sh -c "echo 'dtparam=spi=on' >> ${mount_temp_dir}/config.txt" $ sudo sh -c "echo 'device_tree_address=0x2eff5b00' >> ${mount_temp_dir}/config.txt" $ sudo sh -c "echo 'device_tree_end=0x2f0f5b00' >> ${mount_temp_dir}/config.txt" $ sudo sh -c "echo 'dtoverlay=uart5' >> ${mount_temp_dir}/config.txt" $ sudo umount ${mount_temp_dir} $ sudo losetup -d ${loop_device}
- Write the resulting image to an SD card. To do this, connect the SD card to the computer and run the following command:# In the following command, [X] is the last symbol in the name of the block device for the SD card. $ sudo pv -L 32M ${fs_image_name} | sudo dd bs=64k of=/dev/sd[X] conv=fsync
Running examples on Raspberry Pi 4 B
To run an example on a Raspberry Pi 4 B:
- Go to the directory with the example and build the example.
- Make sure that Raspberry Pi 4 B and the bootable SD card are prepared to run examples.
- Connect the bootable SD card to the Raspberry Pi 4 B.
- Supply power to the Raspberry Pi 4 B and wait for the example to run.
The output displayed on the computer connected to Raspberry Pi 4 B indicates that the example started.
Overview: Einit and init.yaml
Einit initializing program
When a solution is started, the KasperskyOS kernel finds the executable file named Einit
(initializing program) in the solution image and runs this executable file. The initializing program performs the following operations:
- Creates and starts processes when a solution is started.
- Creates IPC channels between processes when a solution is started (statically creates IPC channels).
A process of the initializing program belongs to the Einit
class.
Generating the source code of the initializing program
The KasperskyOS SDK includes the einit
tool, which generates the C-language source code of the initializing program. The standard way to use the einit
tool is to integrate an einit call into one of the steps of the build script, which generates the einit.c
file containing the source code of the initializing program. In one of the following steps of the build script, you must compile the einit.c
file into the executable file of Einit
and include it into the solution image.
You are not required to create formal specification files for the initializing program. These files are provided in the KasperskyOS SDK and are automatically applied during a solution build. However, the Einit
process class must be specified in the security.psl
file.
The einit
tool generates the source code of the initializing program based on the init description, which consists of a text file that is usually named init.yaml
.
Syntax of init.yaml
An init description contains data in YAML format. This data identifies the following:
- Processes that are started when the solution starts.
- IPC channels that are created when the solution starts and are used by processes to interact with each other (not with the kernel).
This data consists of a dictionary with the entities
key containing a list of dictionaries of processes. Process dictionary keys are presented in the table below.
Process dictionary keys in an init description
Key |
Required |
Value |
---|---|---|
|
Yes |
Process class name (from the EDL description). |
|
No |
Process name. If this name is not specified, the process class name will be used. Each process must have a unique name. You can start multiple processes of the same class if they have different names. |
|
No |
Name of the executable file in ROMFS (in the solution image). If this name is not specified, the process class name (without prefixes and dots) will be used. For example, processes of the You can start multiple processes from the same executable file. |
|
No |
Process IPC channel dictionaries list. This list defines the statically created IPC channels whose client IPC handles will be owned by the process. The list is empty by default. (In addition to statically created IPC channels, processes can also use dynamically created IPC channels.) |
|
No |
List of program startup parameters ( |
|
No |
Dictionary of program environment variables. The keys in this dictionary are the names of environment variables. The maximum size of an environment variable value is 1024 bytes. |
Process IPC channel dictionary keys are presented in the table below.
IPC channel dictionary keys in an init description
Key |
Required |
Value |
---|---|---|
|
Yes |
IPC channel name, which can be defined as a specific value or as a link such as
|
|
Yes |
Name of the process that will own the server handle of the IPC channel. |
Example init descriptions
This section provides examples of init descriptions that demonstrate various aspects of starting processes.
The build system can automatically create an init description based on the init.yaml.in
template.
Starting a client and server and creating an IPC channel between them
In this example, a process of the Client
class and a process of the Server
class are started. The names of the processes are not specified, so they will match the names of their respective process classes. The names of the executable files are not specified, so they will also match the names of their respective process classes. The processes will be connected by an IPC channel named server_connection
.
init.yaml
Starting processes from defined executable files
This example will start a Client
-class process from the executable file named cl
, a ClientServer
-class process from the executable file named csr
, and a MainServer
-class process from the executable file named msr
. The names of the processes are not specified, so they will match the names of their respective process classes.
init.yaml
Starting two processes from the same executable file
This example will start a Client
-class process from the executable file named Client
, and two processes of the MainServer
and BkServer
classes from the executable file named srv
. The names of the processes are not specified, so they will match the names of their respective process classes.
init.yaml
Starting two servers of the same class and a client, and creating IPC channels between the client and servers
This example will start a Client
-class process (named Client
) and two Server
-class processes named UserServer
and PrivilegedServer
. The client will be connected to the servers via IPC channels named server_connection_us
and server_connection_ps
. The names of the executable files are not specified, so they will match the names of their respective process classes.
init.yaml
Setting the startup parameters and environment variables of programs
This example will start a VfsFirst
-class process (named VfsFirst
) and a VfsSecond
-class process (named VfsSecond
). The program that will run in the context of the VfsFirst
process will be started with the parameter -f /etc/fstab
, and will receive the ROOTFS
environment variable with the value ramdisk0,0 / ext2 0
and the UNMAP_ROMFS
environment variable with the value 1
. The program that will run in the context of the VfsSecond
process will be started with the -l devfs /dev devfs 0
parameter. The names of the executable files are not specified, so they will match the names of their respective process classes.
init.yaml
Starting processes using the system program ExecutionManager
The ExecutionManager component provides a C++ interface for creating, starting and stopping processes in solutions that are based on KasperskyOS.
The interface of the ExecutionManager component is not suitable for use in code that is written in C. To manage processes in the C language, use the task.h interface of the libkos library.
The ExecutionManager component API is an add-on over IPC that helps simplify the program development process. ExecutionManager is a separate system program that is accessed through IPC. However, developers are provided with a client library that eliminates the necessity of directly using IPC calls.
The programming interface of the ExecutionManager component is described in the article titled "ExecutionManager component".
ExecutionManager component usage scenario
Hereinafter "the client" refers to the application that uses the ExecutionManager component API to manage other applications.
The typical usage scenario for the ExecutionManager component includes the following steps:
- Add the ExecutionManager program to a solution. To add ExecutionManager to a solution:
- Add the following commands to the CMakeLists.txt root file:
find_package (execution_manager REQUIRED) include_directories (${execution_manager_INCLUDE}) add_subdirectory (execution_manager)The
BlobContainer
program is required for the ExecutionManager program to work properly. This program is automatically added to a solution when adding ExecutionManager.- The ExecutionManager component is provided in the SDK as a set of static libraries and header files, and is built for a specific solution by using the CMake command
create_execution_manager_entity()
from the CMake libraryexecution_manager
.To build the ExecutionManager program, create a directory named
execution_manager
in the root directory of the project. In the new directory, create aCMakeLists.txt
file containing thecreate_execution_manager_entity()
command.The CMake command
create_execution_manager_entity()
takes the following parameters:Mandatory
ENTITY
parameter that specifies the name of the executable file for the ExecutionManager program.Optional parameters:
DEPENDS
– additional dependencies for building the ExecutionManager program.MAIN_CONN_NAME
– name of the IPC channel for connecting to the ExecutionManager process. It must match the value of themainConnection
variable when calling the ExecutionManager API in the client code.ROOT_PATH
– path to the root directory for service files of the ExecutionManager program. The default root path is"/ROOT"
.VFS_CLIENT_LIB
– name of the client transport library used to connect the ExecutionManager program to theVFS
program.
include (execution_manager/create_execution_manager_entity) create_execution_manager_entity( ENTITY ExecMgrEntity MAIN_CONN_NAME ${ENTITY_NAME} ROOT_PATH "/root" VFS_CLIENT_LIB ${vfs_CLIENT_LIB})- When building a solution (CMakeLists.txt file for the Einit program), add the following executable files to the solution image:
- Executable file of the ExecutionManager program
- Executable file of the
BlobContainer
program
- Link the client executable file to the client proxy library of ExecutionManager by adding the following command to the
CMakeLists.txt
file for building the client:target_link_libraries (<name of the CMake target for building the client> ${execution_manager_EXECMGR_PROXY}) - Add permissions for the necessary events to the solution security policy description:
- To enable the ExecutionManager program to run other processes, the solution security policy must allow the following interactions for the
execution_manager.ExecMgrEntity
process class:- Security events of the
execute
type for all classes of processes that will be run. - Access to all endpoints of the
VFS
program. - Access to all endpoints of the
BlobContainer
program. - Access to the core endpoints
Sync
,Task
,VMM
,Thread
,HAL
,Handle
,FS
,Notice
,CM
andProfiler
(their descriptions are located in the directorysysroot-*-kos/include/kl/core
from the SDK).
- Security events of the
- To enable a client to call the ExecutionManager program, the solution security policy must allow the following interactions for the client process class:
- Access to the appropriate endpoints of the ExecutionManager program (their descriptions are located in the directory
sysroot-*-kos/include/kl/execution_manager
from the SDK).
- Access to the appropriate endpoints of the ExecutionManager program (their descriptions are located in the directory
- To enable the ExecutionManager program to run other processes, the solution security policy must allow the following interactions for the
- Use of the ExecutionManager program API in the client code.
Use the header file
component/package_manager/kos_ipc/package_manager_proxy.h
for this. For more details, refer to "ExecutionManager component".
Overview: Env program
The system program Env
is intended for setting startup parameters and environment variables of programs. If the Env
program is included in a solution, the processes connected via IPC channel to the Env
process will automatically send IPC requests to this program and receive startup parameters and environment variables when these processes are started.
Use of the Env
system program is an outdated way of setting startup parameters and environment variables of programs. Instead, you must set the startup parameters and environment variables of programs via the init.yaml.in
or init.yaml
file.
If the value of a startup parameter or environment variable of a program is defined through the Env
program and via the init.yaml.in
or init.yaml
file, the value defined through the Env
program will be applied.
To use the Env
program in a solution, you need to do the following:
- Develop the code of the
Env
program using the macros and functions from the header filesysroot-*-kos/include/env/env.h
from the KasperskyOS SDK. - Build the executable file of the
Env
program by linking it to theenv_server
library from the KasperskyOS SDK. - In the init description, indicate that the
Env
process must be started and connected to other processes (Env
acts as a server in this case). The name of the IPC channel is assigned by theENV_SERVICE_NAME
macro defined in the header fileenv.h
. - Include the
Env
executable file in the solution image.
Source code of the Env program
The source code of the Env
program utilizes the following macros and functions from the header file env.h
:
ENV_REGISTER_ARGS(name,argarr)
sets theargarr
startup parameters for the program that will run in the context of thename
process.ENV_REGISTER_VARS(name,envarr)
sets theenvarr
environment variables for the program that will run in the context of thename
process.ENV_REGISTER_PROGRAM_ENVIRONMENT(name,argarr,envarr)
sets theargarr
startup parameters andenvarr
environment variables for the program that will run in the context of thename
process.envServerRun()
initializes the server part of theEnv
program so that it can respond to IPC requests.
Examples of using Env to set the startup parameters and environment variables of programs
Use of the Env
system program is an outdated way of setting startup parameters and environment variables of programs. Instead, you must set the startup parameters and environment variables of programs via the init.yaml.in
or init.yaml
file.
If the value of a startup parameter or environment variable of a program is defined through the Env
program and via the init.yaml.in
or init.yaml
file, the value defined through the Env
program will be applied.
Example of setting startup parameters of a program
Source code of the Env
program is presented below. When the process named NetVfs
starts, the program passes the following two program startup parameters to this process: -l devfs /dev devfs 0
and -l romfs /etc romfs ro
:
env.c
Example of setting environment variables of a program
Source code of the Env
program is presented below. When the process named Vfs3
starts, the program passes the following two program environment variables to this process: ROOTFS=ramdisk0,0 / ext2 0
and UNMAP_ROMFS=1
:
env.c
File systems and network
In KasperskyOS, operations with file systems and the network are executed via a separate system program that implements a virtual file system (VFS).
In the SDK, the VFS component consists of a set of executable files, libraries, formal specification files, and header files. For more details, see the Contents of the VFS component section.
The main scenario of interaction with the VFS system program includes the following:
- An application connects via IPC channel to the VFS system program and then links to the client library of the VFS component during the build.
- In the application code, POSIX calls for working with file systems and the network are converted into client library function calls.
Input and output to file handles for standard I/O streams (stdin, stdout and stderr) are also converted into queries to the VFS. If the application is not linked to the client library of the VFS component, printing to stdout is not possible. If this is the case, you can only print to the standard error stream (stderr), which in this case is performed via special methods of the KasperskyOS kernel without using VFS.
- The client library makes IPC requests to the VFS system program.
- The VFS system program receives an IPC requests and calls the corresponding file system implementations (which, in turn, may make IPC requests to device drivers) or network drivers.
- After the request is handled, the VFS system program responds to the IPC requests of the application.
Using multiple VFS programs
Multiple copies of the VFS system program can be added to a solution for the purpose of separating the data streams of different system programs and applications. You can also separate the data streams within one application. For more details, refer to Using VFS backends to separate data streams.
Adding VFS functionality to an application
The complete functionality of the VFS component can be included in an application, thereby avoiding the need to pass each request via IPC. For more details, refer to Including VFS functionality in a program.
However, use of VFS functionality via IPC enables the solution developer to do the following:
- Use a solution security policy to control method calls for working with the network and file systems.
- Connect multiple client programs to one VFS program.
- Connect one client program to two VFS programs to separately work with the network and file systems.
Contents of the VFS component
The VFS component implements the virtual file system. In the KasperskyOS SDK, the VFS component consists of a set of executable files, libraries, formal specification files and header files that enable the use of file systems and/or a network stack.
VFS libraries
The vfs
CMake package contains the following libraries:
vfs_fs
contains implementations of the devfs, ramfs and ROMFS file systems, and adds implementations of other file systems to VFS.vfs_net
contains the implementation of the devfs file system and the network stack.vfs_imp
contains thevfs_fs
andvfs_net
libraries.vfs_remote
is the client transport library that converts local calls into IPC requests to VFS and receives IPC responses.vfs_server
is the VFS server transport library that receives IPC requests, converts them into local calls, and sends IPC responses.vfs_local
is used to include VFS functionality in a program.
VFS executable files
The precompiled_vfs
CMake package contains the following executable files:
VfsRamFs
VfsSdCardFs
VfsNet
The VfsRamFs
and VfsSdCardFs
executable files include the vfs_server
, vfs_fs
, vfat
and lwext4
libraries. The VfsNet
executable file includes the vfs_server
, vfs_imp
libraries.
Each of these executable files has its own default values for startup parameters and environment variables.
Formal specification files and header files of VFS
The sysroot-*-kos/include/kl
directory from the KasperskyOS SDK contains the following VFS files:
- Formal specification files
VfsRamFs.edl
,VfsSdCardFs.edl
,VfsNet.edl
andVfsEntity.edl
, and the header files generated from them. - Formal specification file
Vfs.cdl
and the header fileVfs.cdl.h
generated from it. - Formal specification files
Vfs*.idl
and the header files generated from them.
Libc library API supported by VFS
VFS functionality is available to programs through the API provided by the libc
library.
The functions implemented by the vfs_fs
and vfs_net
libraries are presented in the table below. The *
character denotes the functions that are optionally included in the vfs_fs
library (depending on the library build parameters).
Functions implemented by the vfs_fs library
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Functions implemented by the vfs_net library
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
If there is no implementation of a called function in VFS, the EIO
error code is returned.
Creating an IPC channel to VFS
In this example, the Client
process uses the file systems and network stack, and the VfsFsnet
process handles the IPC requests of the Client
process related to the use of file systems and the network stack. This approach is utilized when there is no need to separate data streams related to file systems and the network stack.
The IPC channel name must be assigned by the _VFS_CONNECTION_ID
macro defined in the header file sysroot-*-kos/include/vfs/defs.h
from the KasperskyOS SDK.
Init description of the example:
init.yaml
Including VFS functionality in a program
In this example, the Client
program includes the VFS program functionality for working with the network stack (see the figure below).
VFS component libraries in a program
The client.c
implementation file is compiled and the vfs_local
, vfs_implementation
and dnet_implementation
libraries are linked:
CMakeLists.txt
In case the Client
program uses file systems, you must link the vfs_local
and vfs_fs
libraries, and the libraries for implementing these file systems. In this case, you must also add a block device driver to the solution.
Overview: startup parameters and environment variables of VFS
VFS program startup parameters
-l <entry in fstab format>
The startup parameter
-l
mounts the defined file system.-f <path to fstab file>
The parameter
-f
mounts the file systems specified in thefstab
file. If theUNMAP_ROMFS
environment variable is not defined, thefstab
file will be sought in the ROMFS image. If theUNMAP_ROMFS
environment variable is defined, thefstab
file will be sought in the file system defined through theROOTFS
environment variable.
Examples of using VFS program startup parameters
Environment variables of the VFS program
UNMAP_ROMFS
If the
UNMAP_ROMFS
environment variable is defined, the ROMFS image will be deleted from memory. This helps conserve memory. When using the startup parameter-f
, it also provides the capability to search for thefstab
file in the file system defined through theROOTFS
environment variable instead of searching the ROMFS image.ROOTFS = <entry in fstab format>
The
ROOTFS
environment variable mounts the defined file system to the root directory. When using the startup parameter-f
, a combination of theROOTFS
andUNMAP_ROMFS
environment variables provides the capability to search for thefstab
file in the file system defined through theROOTFS
environment variable instead of searching the ROMFS image.VFS_CLIENT_MAX_THREADS
The
VFS_CLIENT_MAX_THREADS
environment variable redefines the SDK configuration parameterVFS_CLIENT_MAX_THREADS
._VFS_NETWORK_BACKEND=<VFS backend name>:<name of the IPC channel to the VFS process>
The
_VFS_NETWORK_BACKEND
environment variable defines the VFS backend for working with the network stack. You can specify the name of the standard VFS backend:client
(for a program that runs in the context of a client process),server
(for a VFS program that runs in the context of a server process) orlocal
, and the name of a custom VFS backend. If thelocal
VFS backend is used, the name of the IPC channel is not specified (_VFS_NETWORK_BACKEND=local:
). You can specify more than one IPC channel by separating them with a comma._VFS_FILESYSTEM_BACKEND=<VFS backend name>:<name of the IPC channel to the VFS process>
The
_VFS_FILESYSTEM_BACKEND
environment variable defines the VFS backend for working with file systems. The name of the VFS backend and the name of the IPC channel to the VFS process are defined the same way as they are defined in the_VFS_NETWORK_BACKEND
environment variable.
Default values for startup parameters and environment variables of VFS
For the VfsRamFs
executable file:
For the VfsSdCardFs
executable file:
For the VfsNet
executable file:
Mounting file systems when VFS starts
When the VFS program starts, only the RAMFS file system is mounted to the root directory by default. If you need to mount other file systems, this can be done not only by calling the mount()
function but also by setting the startup parameters and environment variables of the VFS program.
The ROMFS
and squashfs
file systems are intended for read-only operations. For this reason, you must specify the ro
parameter to mount these file systems.
Using the startup parameter -l
One way to mount a file system is to set the startup parameter -l <entry in fstab format>
for the VFS program.
In these examples, the devfs and ROMFS file systems will be mounted when the VFS program is started:
init.yaml.(in)
CMakeLists.txt
Using the fstab file from the ROMFS image
When building a solution, you can add the fstab
file to the ROMFS image. This file can be used to mount file systems by setting the startup parameter -f <path to the fstab file>
for the VFS program.
In these examples, the file systems defined via the fstab
file that was added to the ROMFS image during the solution build will be mounted when the VFS program is started:
init.yaml.(in)
CMakeLists.txt
Using an "external" fstab file
If the fstab
file resides in another file system instead of in the ROMFS image, you must set the following startup parameters and environment variables for the VFS program to enable use of this file:
ROOTFS
. This environment variable mounts the file system containing thefstab
file to the root directory.UNMAP_ROMFS
. If this environment variable is defined, thefstab
file will be sought in the file system defined through theROOTFS
environment variable.-f
. This startup parameter is used to mount the file systems specified in thefstab
file.
In these examples, the ext2 file system that should contain the fstab
file at the path /etc/fstab
will be mounted to the root directory when the VFS program starts:
init.yaml.(in)
CMakeLists.txt
Using VFS backends to separate data streams
This example employs a secure development pattern that separates data streams related to file system use from data streams related to the use of a network stack.
The Client
process uses file systems and the network stack. The VfsFirst
process works with file systems, and the VfsSecond
process provides the capability to work with the network stack. The environment variables of programs that run in the contexts of the Client
, VfsFirst
and VfsSecond
processes are used to define the VFS backends that ensure the segregated use of file systems and the network stack. As a result, IPC requests of the Client
process that are related to the use of file systems are handled by the VfsFirst
process, and IPC requests of the Client
process that are related to network stack use are handled by the VfsSecond
process (see the figure below).
Process interaction scenario
Init description of the example:
init.yaml
Creating a VFS backend
This example demonstrates how to create and use a custom VFS backend.
The Client
process uses the fat32 and ext4 file systems. The VfsFirst
process works with the fat32 file system, and the VfsSecond
process provides the capability to work with the ext4 file system. The environment variables of programs that run in the contexts of the Client
, VfsFirst
and VfsSecond
processes are used to define the VFS backends ensuring that IPC requests of the Client
process are handled by the VfsFirst
or VfsSecond
process depending on the specific file system being used by the Client
process. As a result, IPC requests of the Client
process related to use of the fat32 file system are handled by the VfsFirst
process, and IPC requests of the Client
process related to use of the ext4 file system are handled by the VfsSecond
process (see the figure below).
On the VfsFirst
process side, the fat32 file system is mounted to the directory /mnt1
. On the VfsSecond
process side, the ext4 file system is mounted to the directory /mnt2
. The custom VFS backend custom_client
used on the Client
process side sends IPC requests over the IPC channel VFS1
or VFS2
depending on whether or not the file path begins with /mnt1
. The custom VFS backend uses the standard VFS backend client
as an intermediary.
Process interaction scenario
Source code of the VFS backend
This implementation file contains the source code of the VFS backend custom_client
, which uses the standard client
VFS backends:
backend.c
Linking the Client program
Creating a static VFS backend library:
CMakeLists.txt
Linking the Client
program to the static VFS backend library:
CMakeLists.txt
Setting the startup parameters and environment variables of programs
Init description of the example:
init.yaml
Dynamically configuring the network stack
To change the default network stack parameters, use the sysctl()
function or sysctlbyname()
function that are declared in the header file sysroot-*-kos/include/sys/sysctl.h
from the KasperskyOS SDK. The parameters that can be changed are presented in the table below.
Configurable network stack parameters
Parameter name |
Parameter description |
---|---|
|
Maximum time to live (TTL) of sent IP packets. It does not affect the ICMP protocol. |
|
If its value is set to |
|
MSS value (in bytes) that is applied if only the communicating side failed to provide this value when opening the TCP connection, or if "Path MTU Discovery" mode (RFC 1191) is not enabled. This MSS value is also forwarded to the communicating side when opening a TCP connection. |
|
Minimum MSS value, in bytes. |
|
If its value is set to |
|
Number of times to send test messages (Keep-Alive Probes, or KA) without receiving a response before the TCP connection will be considered closed. If its value is set to |
|
TCP connection idle period, after which keep-alive probes begin. This is defined in conditional units, which can be converted into seconds via division by the |
|
Time interval between recurring keep-alive probes when no response is received. This is defined in conditional units, which can be converted into seconds via division by the |
|
Size of the buffer (in bytes) for data received over the TCP protocol. |
|
Size of the buffer (in bytes) for data sent over the TCP protocol. |
|
Size of the buffer (in bytes) for data received over the UDP protocol. |
|
Size of the buffer (in bytes) for data sent over the UDP protocol. |
MSS configuration example:
Creating IPC channels
There are two methods for creating IPC channels: static and dynamic.
Static creation of IPC channels is simpler to implement because you can use the init description for this purpose.
Dynamic creation of IPC channels lets you change the topology of interaction between processes on the fly. This is necessary if it is unknown which specific server provides the endpoint required by the client. For example, you may not know which specific drive you will need to write data to.
Statically creating an IPC channel
Static creation of IPC channels has the following characteristics:
- The client and server are not yet running when the IPC channel is created.
- Creation of an IPC channel is performed by the parent process that starts the client and server (this is normally Einit).
- A deleted IPC channel cannot be restored.
- To get the IPC handle and the endpoint ID (riid) after an IPC channel is created, the client and server must use the API defined in the header file
sysroot-*-kos/include/coresrv/sl/sl_api.h
from the KasperskyOS SDK.
The IPC channels defined in the init description are statically created.
Dynamically creating an IPC channel
Dynamic creation of IPC channels has the following characteristics:
- The client and server are already running when the IPC channel is created.
- The IPC channel is jointly created by the client and server.
- A new IPC channel may be created in place of a deleted one.
- The client and server get the IPC handle and endpoint ID (riid) immediately after the IPC channel is successfully created.
Adding an endpoint from KasperskyOS Community Edition to a solution
To ensure that a Client
program can use some specific functionality via the IPC mechanism, the following is required:
- In KasperskyOS Community Edition, find the executable file (we'll call it
Server
) that implements the necessary functionality. (The term "functionality" used here refers to one or more endpoints that have their own IPC interfaces.) - Include the CMake package containing the
Server
file and its client library. - Add the
Server
executable file to the solution image. - Edit the init description so that when the solution starts, the
Einit
program starts a new server process from theServer
executable file and connects it, using an IPC channel, to the process started from theClient
file.You must indicate the correct name of the IPC channel so that the transport libraries can identify this channel and find its IPC handles. The correct name of the IPC channel normally matches the name of the server process class. VFS is an exception in this case.
- Edit the PSL description to allow startup of the server process and IPC interaction between the client and the server.
- In the source code of the
Client
program, include the server methods header file. - Link the
Client
program with the client library.
Example of adding a GPIO driver to a solution
KasperskyOS Community Edition includes a gpio_hw
file that implements GPIO driver functionality.
The following commands connect the gpio CMake package:
.\CMakeLists.txt
The gpio_hw
executable file is added to a solution image by using the gpio_HW_ENTITY
variable, whose name can be found in the configuration file of the package at /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/lib/cmake/gpio/gpio-config.cmake:
einit\CMakeLists.txt
The following strings need to be added to the init description:
init.yaml.in
The following strings need to be added to the PSL description:
security.psl.in
In the code of the Client
program, you need to include the header file in which the GPIO driver methods are declared:
client.c
Finally, you need to link the Client
program with the GPIO client library:
client\CMakeLists.txt
To ensure correct operation of the GPIO driver, you may need to add the BSP component to the solution. To avoid overcomplicating this example, BSP is not examined here. For more details, see the gpio_output example: /opt/KasperskyOS-Community-Edition-<version>/examples/gpio_output
Overview: IPC message structure
In KasperskyOS, all interactions between processes have statically defined types. The permissible structures of an IPC message are defined by the IDL descriptions of servers.
An IPC message (request and response) contains a constant part and an (optional) arena.
Constant part of an IPC message
The constant part of an IPC message contains the RIID, MID, and (optionally) fixed-size parameters of interface methods.
Fixed-size parameters are parameters that have IDL types of a fixed size.
The RIID and MID identify the interface and method being called:
- The RIID (Runtime Implementation ID) is the sequence number of the utilized endpoint within the set of server endpoints (starting at zero).
- The MID (Method ID) is the sequence number of the called method within the set of methods of the utilized endpoint (starting at zero).
The type of the constant part of the IPC message is generated by the NK compiler based on the IDL description of the interface. A separate structure is generated for each interface method. Union
types are also generated for storing any request to a process, component or interface. For more details, refer to Example generation of transport methods and types.
IPC message arena
An IPC message arena (hereinafter also referred to as an arena) contains variable-size parameters of interface methods (and/or elements of these parameters).
Variable-size parameters are parameters that have IDL types of a variable size.
For more details, refer to "Working with an IPC message arena".
Maximum IPC message size
The maximum size of an IPC message is determined by the KasperskyOS kernel parameters. On most hardware platforms supported by KasperskyOS, the cumulative size of the constant part and arena of an IPC message cannot exceed 4, 8, or 16 MB.
IPC message structure verification by the security module
Prior to querying IPC message-related rules, the Kaspersky Security Module verifies that the sent IPC message is correct. Requests and responses are both validated. If the IPC message has an incorrect structure, it will be rejected without calling the security model methods associated with it.
Implementation of IPC interaction
To make it easier for a developer to implement IPC interaction, KasperskyOS Community Edition provides the following:
- NK compiler that generates transport methods and types.
Libkos
library that provides the API for working with IPC transport.
Implementation of simple IPC interaction is demonstrated in the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
Getting an IPC handle
The client and server IPC handles must be obtained if there are no ready-to-use transport libraries for the utilized endpoint (for example, if you wrote your own endpoint). To independently work with IPC transport, you need to first initialize it by using the NkKosTransport_Init()
method and pass the IPC handle of the utilized channel as the second argument.
For more details, see the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
You do not need to get an IPC handle to utilize endpoints that are implemented in executable files provided in KasperskyOS Community Edition. The provided transport libraries are used to perform all transport operations, including obtaining IPC handles.
See the gpio_*, net_*, net2_* and multi_vfs_* examples (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
Getting an IPC handle when statically creating a channel
When statically creating an IPC channel, both the client and server can obtain their IPC handles immediately after startup by using the ServiceLocatorRegister()
and ServiceLocatorConnect()
methods and specifying the name of the created IPC channel.
For example, if the IPC channel is named server_connection
, the following must be called on the client side:
The following must be called on the server side:
For more details, see the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/
), and the header file /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/sl/sl_api.h
.
Closing an obtained IPC handle will cause the IPC channel to become unavailable. After an IPC handle is closed, it is impossible to obtain it again or restore access to the IPC channel.
Getting an IPC handle when dynamically creating a channel
Both the client and server receive their own IPC handles immediately after dynamic creation of an IPC channel is successful.
The client IPC handle is one of the output (out
) arguments of the KnCmConnect()
method. The server IPC handle is an output argument of the KnCmAccept()
method. For more details, see the header file /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/cm/cm_api.h
.
If the dynamically created IPC channel is no longer required, its client and server handles should be closed. The IPC channel can be created again if necessary.
Page top
Getting an endpoint ID (riid)
The endpoint ID (riid) must be obtained on the client side if there are no ready-to-use transport libraries for the utilized endpoint (for example, if you wrote your own endpoint). To call methods of the server, you must first call the proxy object initialization method on the client side and pass the endpoint ID as the third parameter. For example, for the Filesystem
interface:
For more details, see the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
You do not need to get the endpoint ID to utilize endpoints that are implemented in executable files provided in KasperskyOS Community Edition. The provided transport libraries are used to perform all transport operations.
See the gpio_*, net_*, net2_* and multi_vfs_* examples (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
Getting an endpoint ID when statically creating a channel
When statically creating an IPC channel, the client can obtain the ID of the necessary endpoint by using the ServiceLocatorGetRiid()
method and specifying the IPC channel handle and the qualified name of the endpoint. For example, if the OpsComp
component instance provides the FS
endpoint, the following must be called on the client side:
For more details, see the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/
), and the header file /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/sl/sl_api.h
.
Getting an endpoint ID when dynamically creating a channel
The client receives the endpoint ID immediately after dynamic creation of an IPC channel is successful. The client IPC handle is one of the output (out
) arguments of the KnCmConnect()
method. For more details, see the header file /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/cm/cm_api.h
.
Example generation of transport methods and types
When building a solution, the NK compiler uses the EDL, CDL and IDL descriptions to generate a set of special methods and types that simplify the creation, forwarding, receipt and processing of IPC messages.
As an example, we will examine the Server
process class that provides the FS
endpoint, which contains a single Open()
method:
Server.edl
Operations.cdl
Filesystem.idl
These descriptions will be used to generate the files named Server.edl.h
, Operations.cdl.h
, and Filesystem.idl.h
, which contain the following methods and types:
Methods and types that are common to the client and server
- Abstract interfaces containing the pointers to the implementations of the methods included in them.
In our example, one abstract interface (
Filesystem
) will be generated:typedef struct Filesystem { const struct Filesystem_ops *ops; } Filesystem; typedef nk_err_t Filesystem_Open_fn(struct Filesystem *, const struct Filesystem_Open_req *, const struct nk_arena *, struct Filesystem_Open_res *, struct nk_arena *); typedef struct Filesystem_ops { Filesystem_Open_fn *Open; } Filesystem_ops; - Set of interface methods.
When calling an interface method, the corresponding values of the RIID and MID are automatically inserted into the request.
In our example, a single
Filesystem_Open
interface method will be generated:nk_err_t Filesystem_Open(struct Filesystem *self, struct Filesystem_Open_req *req, const struct nk_arena *req_arena, struct Filesystem_Open_res *res, struct nk_arena *res_arena)
Methods and types used only on the client
- Types of proxy objects.
A proxy object is used as an argument in an interface method. In our example, a single
Filesystem_proxy
proxy object type will be generated:typedef struct Filesystem_proxy { struct Filesystem base; struct nk_transport *transport; nk_iid_t iid; } Filesystem_proxy; - Functions for initializing proxy objects.
In our example, the single initializing function
Filesystem_proxy_init
will be generated:void Filesystem_proxy_init(struct Filesystem_proxy *self, struct nk_transport *transport, nk_iid_t iid) - Types that define the structure of the constant part of a message for each specific method.
In our example, two such types will be generated:
Filesystem_Open_req
(for a request) andFilesystem_Open_res
(for a response).typedef struct __nk_packed Filesystem_Open_req { __nk_alignas(8) struct nk_message base_; __nk_alignas(4) nk_ptr_t name; } Filesystem_Open_req; typedef struct Filesystem_Open_res { union { struct { __nk_alignas(8) struct nk_message base_; __nk_alignas(4) nk_uint32_t h; }; struct { __nk_alignas(8) struct nk_message base_; __nk_alignas(4) nk_uint32_t h; } res_; struct Filesystem_Open_err err_; }; } Filesystem_Open_res;
Methods and types used only on the server
- Type containing all endpoints of a component, and the initializing function. (For each server component.)
If there are embedded components, this type also contains their instances, and the initializing function takes their corresponding initialized structures. Therefore, if embedded components are present, their initialization must begin with the most deeply embedded component.
In our example, the
Operations_component
structure andOperations_component_init
function will be generated:typedef struct Operations_component { struct Filesystem *FS; }; void Operations_component_init(struct Operations_component *self, struct Filesystem *FS) - Type containing all endpoints provided directly by the server; all instances of components included in the server; and the initializing function.
In our example, the
Server_entity
structure andServer_entity_init
function will be generated:typedef struct Server_component { struct : Operations_component *OpsComp; } Server_component; void Server_entity_init(struct Server_entity *self, struct Operations_component *OpsComp) - Types that define the structure of the constant part of a message for any method of a specific interface.
In our example, two such types will be generated:
Filesystem_req
(for a request) andFilesystem_res
(for a response).typedef union Filesystem_req { struct nk_message base_; struct Filesystem_Open_req Open; }; typedef union Filesystem_res { struct nk_message base_; struct Filesystem_Open_res Open; }; - Types that define the structure of the constant part of a message for any method of any endpoint of a specific component.
If embedded components are present, these types also contain structures of the constant part of a message for any method of any endpoint included in all embedded components.
In our example, two such types will be generated:
Operations_component_req
(for a request) andOperations_component_res
(for a response).typedef union Operations_component_req { struct nk_message base_; Filesystem_req FS; } Operations_component_req; typedef union Operations_component_res { struct nk_message base_; Filesystem_res FS; } Operations_component_res; - Types that define the structure of the constant part of a message for any method of any endpoint of a specific component whose instance is included in the server.
If embedded components are present, these types also contain structures of the constant part of a message for any method of any endpoint included in all embedded components.
In our example, two such types will be generated:
Server_entity_req
(for a request) andServer_entity_res
(for a response).typedef union Server_component_req { struct nk_message base_; Filesystem_req OpsComp_FS; } Server_component_req; typedef union Server_component_res { struct nk_message base_; Filesystem_res OpsComp_FS; } Server_component_res; - Dispatch methods (dispatchers) for a separate interface, component, or process class.
Dispatchers analyze the received query (the RIID and MID values), call the implementation of the corresponding method, and then save the response in the buffer. In our example, three dispatchers will be generated:
Filesystem_interface_dispatch
,Operations_component_dispatch
, andServer_entity_dispatch
.The process class dispatcher handles the request and calls the methods implemented by this class. If the request contains an incorrect RIID (for example, an RIID for a different endpoint that this process class does not have) or an incorrect MID, the dispatcher returns
NK_EOK
orNK_ENOENT
.nk_err_t Server_entity_dispatch(struct Server_entity *self, const struct nk_message *req, const struct nk_arena *req_arena, struct nk_message *res, struct nk_arena *res_arena)In special cases, you can use dispatchers of the interface and the component. They take an additional argument: interface implementation ID (
nk_iid_t
). The request will be handled only if the passed argument and RIID from the request match, and if the MID is correct. Otherwise, the dispatchers returnNK_EOK
orNK_ENOENT
.nk_err_t Operations_component_dispatch(struct Operations_component *self, nk_iid_t iidOffset, const struct nk_message *req, const struct nk_arena *req_arena, struct nk_message *res, struct nk_arena *res_arena) nk_err_t Filesystem_interface_dispatch(struct Filesystem *impl, nk_iid_t iid, const struct nk_message *req, const struct nk_arena *req_arena, struct nk_message *res, struct nk_arena *res_arena)
Working with an IPC message arena
Arena overview
From the perspective of a developer of KasperskyOS-based solutions, an IPC message arena is a byte buffer in the memory of a process intended for storing variable-size data transmitted over IPC. This variable-size data includes input parameters, output parameters, and error parameters of interface methods (and/or elements of these parameters) that have variable-size IDL types. An arena is also used when querying the Kaspersky Security Module to store input parameters of security interface methods (and/or elements of these parameters) that have variable-size IDL types. (Parameters of fixed-size interface methods are stored in the constant part of an IPC message.) Arenas are used on the client side and on the server side. One arena is intended either for transmitting or for receiving variable-size data through IPC, but not for both transmitting and receiving this data at the same time. In other words, arenas can be divided into IPC request arenas (containing input parameters of interface methods) and IPC response arenas (containing output parameters and error parameters of interface methods).
Only the utilized part of an arena that is occupied by data is transmitted over IPC. (If it has no data, the arena is not transmitted.) The utilized part of an arena includes one or more segments. One segment of an arena stores an array of same-type objects, such as an array of single-byte objects or an array of structures. Arrays of different types of objects may be stored in different segments of an arena. The starting address of an arena must be equal to the boundary of a 2^N-byte sequence, where 2^N is a value that is greater than or equal to the size of the largest primitive type in the arena (for example, the largest field of a primitive type in a structure). The address of an arena chunk must also be equal to the boundary of a 2^N-byte sequence, where 2^N is a value that is greater than or equal to the size of the largest primitive type in the arena chunk.
An arena must have multiple segments if the interface method has multiple variable-size input, output, or error parameters, or if multiple elements of input, output, or error parameters of the interface method have a variable size. For example, if an interface method has an input parameter of the sequence
IDL type and an input parameter of the bytes
IDL type, the IPC request arena will have at least two segments. In this case, it may even require additional segments if a parameter of the sequence
IDL type consists of elements of a variable-size IDL type (for example, if elements of a sequence are string
buffers). As another example, if an interface method has one output parameter of the struct
IDL type that contains two fields of the bytes
and string
type, the IPC response arena will have two segments.
Due to the alignment of arena chunk addresses, there may be unused intervals between these chunks. Therefore, the size of the utilized part of an arena may exceed the size of the data it contains.
API for working with an arena
The set of functions and macros for working with an arena is defined in the header file sysroot-*-kos/include/nk/arena.h
from the KasperskyOS SDK. In addition, the function for copying a string to an arena is declared in the header file sysroot-*-kos/include/coresrv/nk/transport-kos.h
from the KasperskyOS SDK.
Information on the functions and macros defined in the header file sysroot-*-kos/include/nk/arena.h
is provided in the table below. In these functions and macros, an arena and arena chunk are identified by an arena descriptor (the nk_arena
type) and an arena chunk descriptor (the nk_ptr_t
type), respectively. An arena descriptor is a structure containing three pointers: one pointer to the start of the arena, one pointer to the start of the unused part of the arena, and one pointer to the end of the arena. An arena chunk descriptor is a structure containing the offset of the arena chunk in bytes (relative to the start of the arena) and the size of the arena chunk in bytes. (The arena chunk descriptor type is defined in the header file sysroot-*-kos/include/nk/types.h
from the KasperskyOS SDK.)
Creating an arena
To pass variable-size parameters of interface methods over IPC, you must create arenas on the client side and on the server side. (When IPC requests are handled on the server side using the NkKosDoDispatch()
function defined in the header file sysroot-*-kos/include/coresrv/nk/transport-kos-dispatch.h
from the KasperskyOS SDK, the IPC request arena and IPC response arena are created automatically.)
To create an arena, you must create a buffer (in the stack or heap) and initialize the arena descriptor.
The address of the buffer must be aligned to comply with the maximum size of a primitive type that can be put into this buffer. The address of a dynamically created buffer usually has adequate alignment to hold the maximum amount of data of the primitive type. To ensure the required alignment of the address of a statically created buffer, you can use the alignas
specifier.
To initialize an arena descriptor using the pointer to an already created buffer, you must use an API function or macro:
NK_ARENA_INITIALIZER()
macronk_arena_init()
functionnk_arena_create()
functionNK_ARENA_FINAL()
macronk_arena_init_final()
macro
The type of pointer makes no difference because this pointer is converted into a pointer to a single-byte object in the code of API functions and macros.
The NK_ARENA_INITIALIZER()
macro and the nk_arena_init()
and nk_arena_create()
functions initialize arena descriptor that may contain one or more segments. The NK_ARENA_FINAL()
and nk_arena_init_final()
macros initialize arena descriptor that contains only one segment spanning the entire arena throughout its entire life cycle.
To create a buffer in the stack and initialize the handle in one step, use the NK_ARENA_AUTO()
macro. This macro creates an arena that may contain one or more segments, and the address of the buffer created by this macro has adequate alignment to hold the maximum amount of data of the primitive type.
The size of an arena must be sufficient to hold variable-size parameters for IPC requests or IPC responses of one interface method or a set of interface methods corresponding to one interface, component, or process class while accounting for the alignment of segment addresses. Automatically generated transport code (the header files *.idl.h
, *.cdl.h
, and *.edl.h
) contain *_arena_size
constants whose values are guaranteed to comply with sufficient sizes of arenas in bytes.
The header files *.idl.h
, *.cdl.h
, and *.edl.h
contain the following *_arena_size
constants:
<interface name>_<interface method name>_req_arena_size
– size of an IPC request arena for the specified interface method of the specified interface<interface name>_<interface method name>_res_arena_size
– size of an IPC response arena for the specified interface method of the specified interface<interface name>_req_arena_size
– size of an IPC request arena for any interface method of the specified interface<interface name>_res_arena_size
– size of an IPC response arena for any interface method of the specified interface
The header files *.cdl.h
and *.edl.h
also contain the following *_arena_size
constants:
<component name>_component_req_arena_size
– size of an IPC request arena for any interface method of the specified component<component name>_component_res_arena_size
– size of an IPC response arena for any interface method of the specified component
The *.edl.h
header files also contain the following *_arena_size
constants:
<process class name>_entity_req_arena_size
– size of an IPC request arena for any interface method of the specified process class<process class name>_entity_res_arena_size
– size of an IPC response arena for any interface method of the specified process class
Constants containing the size of an IPC request arena or IPC response arena for one interface method (<interface name>_<interface method name>_req_arena_size
and <interface name>_<interface method name>_res_arena_size
) are intended for use on the client side. All other constants can be used on the client side and on the server side.
Examples of creating an arena:
Adding data to an arena before transmission over IPC
Before transmitting an IPC request on the client side or an IPC response on the server side, data must be added to the arena. If the NK_ARENA_FINAL()
macro or the nk_arena_init_final()
macro is used to create an arena, you do not need to reserve an arena chunk. Instead, you only need to add data to this chunk. If the NK_ARENA_INITIALIZER()
or NK_ARENA_AUTO()
macro, or the nk_arena_init()
or nk_arena_create()
function is used to create an arena, one or multiple segments in the arena must be reserved to hold data. To reserve an arena chunk, you must use an API function or macro:
__nk_arena_alloc()
functionnk_arena_store()
macro__nk_arena_store()
functionnk_arena_alloc()
macroNkKosCopyStringToArena()
function
The arena chunk descriptor, which is passed through the output parameter of these functions and macros and through the output parameter of the NK_ARENA_FINAL()
and nk_arena_init_final()
macros, must be put into the constant part or into the arena of an IPC message. If an interface method has a variable-size parameter, the constant part of IPC messages contains arena chunk descriptor containing the parameter instead of the actual parameter. If an interface method has a fixed-size parameter with variable-size elements, the constant part of IPC messages contains arena chunk descriptors containing the parameter elements instead of the actual parameter elements. If an interface method has a variable-size parameter containing variable-size elements, the constant part of IPC messages contains arena chunk descriptor containing the descriptors of other arena chunks that contain these parameter elements.
The nk_arena_store()
macro and the __nk_arena_store()
and NkKosCopyStringToArena()
functions not only reserve an arena chunk, but also copy data to this chunk.
The nk_arena_alloc()
macro gets the address of a reserved arena chunk. An arena chunk address can also be received by using the __nk_arena_get()
function or the nk_arena_get()
macro, which additionally pass the arena size through the output parameter.
A reserved arena chunk can be reduced. To do so, use the nk_arena_shrink()
macro or the _nk_arena_shrink()
function.
To undo a current reservation of arena chunks so that new chunks can be reserved for other data (after sending an IPC message), call the nk_arena_reset()
function. If the NK_ARENA_FINAL()
macro or nk_arena_init_final()
macro is used to create an arena, you do not need to undo a segment reservation because the arena contains one segment spanning the entire arena throughout its entire life cycle.
Examples of adding data to an arena:
Retrieving data from an arena after receipt over IPC
Prior to receiving an IPC request on the server side or an IPC response on the client side for an arena that will store the data received over IPC, you must undo the current reservation of segments by calling the nk_arena_reset()
function. This must be done even if the NK_ARENA_FINAL()
macro or the nk_arena_init_final()
macro is used to create the arena. (The NK_ARENA_INITIALIZER()
and NK_ARENA_AUTO()
macros, and the nk_arena_init()
and nk_arena_create()
functions create an arena without reserved segments. You do not need to call the nk_arena_reset()
function if this arena will only be used once to save data received over IPC.)
To receive pointers to arena chunks and the sizes of these chunks, you must use the __nk_arena_get()
function or the nk_arena_get()
macro while using the input parameter to pass the corresponding arena chunk descriptors received from the constant part and arena of the IPC message.
Example of receiving data from an arena:
Additional capabilities of the API
To get the arena size, call the nk_arena_capacity()
function.
To get the size of the utilized part of the arena, call the nk_arena_allocated_size()
function.
To verify that the arena chunk descriptor is correct, use the nk_arena_validate()
macro or the __nk_arena_validate()
function.
Information about API functions and macros
Functions and macros of arena.h
Function/Macro |
Information about the function/macro |
---|---|
|
Purpose Initializes the arena descriptor. Parameters
Macro values Arena descriptor initialization code. |
|
Purpose Initializes the arena descriptor. Parameters
Returned values N/A |
|
Purpose Creates and initializes the arena descriptor. Parameters
Returned values Arena descriptor. |
|
Purpose Creates a buffer in the stack, and creates and initializes the arena descriptor. Parameters
Macro values Arena descriptor. |
|
Purpose Initializes the arena descriptor containing only one segment. Parameters
Macro values Arena descriptor. |
|
Purpose Resets the reservation of arena chunks. Parameters
Returned values N/A |
|
Purpose Reserves an arena chunk with a specific size and a specific alignment. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the size of an arena. Parameters
Returned values Size of the arena in bytes. Additional information If the parameter has the |
|
Purpose Verifies that the arena chunk descriptor is correct. Parameters
Macro values It has a value of
It has a value of
It has a value of |
|
Purpose Verifies that the arena chunk descriptor is correct. Parameters
Returned values Returns
Returns
Returns |
|
Purpose Gets the pointer to the arena chunk and the size of this chunk. Parameters
Returned values Pointer to the arena chunk or |
|
Purpose Gets the size of the utilized part of the arena. Parameters
Returned values Size of the utilized part of the arena, in bytes. Additional information If the parameter has the |
|
Purpose Reserves an arena chunk for the specified number of objects of the defined type and copies these objects to the reserved chunk. Parameters
Macro values It has a value of |
|
Purpose Reserves an arena chunk with a defined alignment for data of a specific size and copies this data to the reserved segment. Parameters
Returned values Returns |
|
Purpose Initializes the arena descriptor containing only one segment. Parameters
Macro values N/A |
|
Purpose Reserves an arena chunk for the defined number of objects of the specified type. Parameters
Macro values It has the address of the reserved arena chunk if successful, otherwise |
|
Purpose Gets the address of an arena chunk and the number of objects of the specified type that can be put into this chunk. Parameters
Macro values It has the address of the arena chunk if successful, otherwise Additional information If the size of the arena chunk is not a multiple of the size of the type of objects for which this chunk is intended, it has the |
|
Purpose Reduces the size of an arena chunk. Parameters
Macro values It has the address of the reduced arena chunk if successful, otherwise Additional information If the required size of the arena chunk exceeds the current size, it has the If the alignment of the arena chunk that needs to be reduced does not match the type of objects for which the reduced chunk is intended, it has the If the arena chunk that needs to be reduced is the last chunk in the arena, the freed part of this chunk will become available for reservation of subsequent chunks. |
|
Purpose Reduces the size of an arena chunk. Parameters
Returned values Returns the address of the reduced arena chunk if successful, otherwise returns Additional information If the required size of the arena chunk exceeds the current size, it returns If the alignment of the arena chunk that needs to be reduced does not match the specified alignment, it returns If the arena chunk that needs to be reduced is the last chunk in the arena, the freed part of this chunk will become available for reservation of subsequent chunks. |
Transport code in C++
Before reading this section, you should review the information on the IPC mechanism in KasperskyOS and the IDL, CDL, and EDL descriptions.
Implementation of interprocess interaction requires transport code, which is responsible for generating, sending, receiving, and processing IPC messages.
However, a developer of a KasperskyOS-based solution does not have to write their own transport code. Instead, you can use the special tools and libraries included in the KasperskyOS SDK. These libraries enable a solution component developer to generate transport code based on IDL, CDL and EDL descriptions related to this component.
Transport code
The KasperskyOS SDK includes the nkppmeta
compiler for generating transport code in C++.
The nkppmeta
compiler lets you generate transport C++ proxy objects and stubs for use by both a client and a server.
Proxy objects are used by the client to pack the parameters of the called method into an IPC request, execute the IPC request, and unpack the IPC response.
Stubs are used by the server to unpack the parameters from the IPC request, dispatch the call to the appropriate method implementation, and pack the IPC response.
Generating transport code for development in C++
The CMake
commands add_nk_idl(), add_nk_cdl() and add_nk_edl() are used to generate transport proxy objects and stubs using the nkppmeta
compiler when building a solution.
С++ types in the *.idl.cpp.h file
Each interface is defined in an IDL description. This description defines the interface name, signatures of interface methods, and data types for the parameters of interface methods.
The CMake
command add_nk_idl() is used to generate transport code when building a solution. This command creates a CMake
target for generating header files for the defined IDL file when using the nkppmeta
compiler.
The generated header files contain a C++ representation for the interface and data types described in the IDL file, and the methods required for use of proxy objects and stubs.
The mapping of data types declared in an IDL file to C++ types are presented in the table below.
Mapping IDL types to C++ types
IDL type |
C++ type |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Working with transport code in C++
The scenarios for developing a client and server that exchange IPC messages are presented in the sections titled Statically creating IPC channels for C++ development and Dynamically creating IPC channels for C++ development.
Page top
Statically creating IPC channels for C++ development
To implement a client program that calls a method of an endpoint provided by a server program:
- Include the generated header file (
*.edl.cpp.h
) of the client program description. - Include the generated header files of the descriptions of the utilized interfaces (
*.idl.cpp.h
). - Include the header files:
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/application.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/api.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/connect_static_channel.h
- Initialize the application object by calling the
kosipc::MakeApplicationAutodetect()
function. (You can also use thekosipc::MakeApplication()
andkosipc::MakeApplicationPureClient()
functions.) - Get the client IPC handle of the channel and the endpoint ID (
riid
) by calling thekosipc::ConnectStaticChannel()
function.This function gets the name of the IPC channel (from the init.yaml file) and the qualified name of the endpoint (from the CDL and EDL descriptions of the solution component).
- Initialize the proxy object for the utilized endpoint by calling the
MakeProxy()
function.
Example
To implement a server program that provides endpoints to other programs:
- Include the generated header file
*.edl.cpp.h
containing a description of the component structure of the program, including all provided endpoints. - Include the header files:
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/event_loop.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/api.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/serve_static_channel.h
- Create classes containing the implementations of interfaces that this program and its components provide as endpoints.
- Initialize the application object by calling the
kosipc::MakeApplicationAutodetect()
function. - Initialize the
kosipc::components::Root
structure, which describes the component structure of the program and describes the interfaces of all endpoints provided by the program. - Bind fields of the
kosipc::components::Root
structure to the objects that implement the corresponding endpoints.Fields of the
Root
structure replicate the hierarchy of components and endpoints that are collectively defined by the CDL and EDL files. - Get the server IPC handle of the channel by calling the
ServeStaticChannel()
function.This function gets the name of the IPC channel (from the init.yaml file) and the structure created at step 5.
- Create the
kosipc::EventLoop
object by calling theMakeEventLoop()
function. - Start the loop for dispatching incoming IPC messages by calling the
Run()
method of thekosipc::EventLoop
object.
Example
Dynamically creating IPC channels for C++ development
Dynamic creation of an IPC channel on the client side includes the following steps:
- Include the generated description header file (
*.edl.cpp.h
) in the client program. - Include the generated header files of the descriptions of the utilized interfaces (
*.idl.cpp.h
). - Include the header files:
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/application.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/make_application.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/connect_dynamic_channel.h
- Get the pointers to the server name and the qualified name of the endpoint by using a name server, which is a special kernel service provided by the
NameServer
program. To do so, you must connect to the name server by calling theNsCreate()
function and find the server that provides the required endpoint by using theNsEnumServices()
function. For more details, refer to Dynamically creating IPC channels (cm_api.h, ns_api.h). - Create an application object by calling the
kosipc::MakeApplicationAutodetect()
function. (You can also use thekosipc::MakeApplication()
andkosipc::MakeApplicationPureClient()
functions.) - Create a proxy object for the required endpoint by calling the
MakeProxy()
function. Use thekosipc::ConnectDynamicChannel()
function call as the input parameter of theMakeProxy()
function. Pass the pointers for the server name and qualified name of the endpoint obtained at step 4 to thekosipc::ConnectDynamicChannel()
function.
After successful initialization of the proxy object, the client can call methods of the required endpoint.
Example
Dynamic creation of an IPC channel on the server side includes the following steps:
- Include the generated header file (
*.edl.cpp.h
) containing a description of the component structure of the server, including all provided endpoints, in the server program. - Include the header files:
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/application.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/event_loop.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/make_application.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/root_component.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/serve_dynamic_channel.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/simple_connection_acceptor.h
- Create classes containing the implementations of interfaces that the server provides as endpoints. Create and initialize the objects of these classes.
- Create an application object by calling the
kosipc::MakeApplicationAutodetect()
function. - Create and initialize the
kosipc::components::Root
class object that describes the structure of components and endpoints of the server. This structure is generated from the descriptions in the CDL and EDL files. - Bind the
kosipc::components::Root
class object to the class objects created at step 3. - Create and initialize the
kosipc::EventLoop
class object that implements a loop for dispatching incoming IPC messages by calling theMakeEventLoop()
function. Use theServeDynamicChannel()
function call as an input parameter of theMakeEventLoop()
function. Pass thekosipc::components::Root
class object created at step 5 to theServeDynamicChannel()
function. - Start the loop for dispatching incoming IPC messages in a separate thread by calling the
Run()
method of thekosipc::EventLoop
object. - Create and initialize the object that implements the handler for receiving incoming requests to dynamically create an IPC channel.
When creating an object, you can use the
kosipc::SimpleConnectionAcceptor
class, which is the standard implementation of thekosipc::IConnectionAcceptor
interface. (Thekosipc::IConnectionAcceptor
interface is defined in the file named/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/connection_acceptor.h
.) In this case, the handler will implement the following logic: if the endpoint requested by the client was published on the server, the request from the client will be accepted. Otherwise, it will be rejected.If you need to create your own handler, you should implement your own request handling logic in the
OnConnectionRequest()
method inherited from thekosipc::IConnectionAcceptor
interface. This method will be called by the server when it receives a request for dynamic IPC channel creation from the client. - Create a
kosipc::EventLoop
class object that implements a loop for receiving incoming requests to dynamically create an IPC channel by calling theMakeEventLoop()
function. Use theServeConnectionRequests()
function call as an input parameter of theMakeEventLoop()
function. Pass the object created at step 9 to theServeConnectionRequests()
function.There can only be one loop for receiving incoming requests to dynamically create an IPC channel. The loop must work in one thread. The loop for receiving incoming requests to dynamically create an IPC channel must be created after the loop for dispatching incoming IPC channels is created (see step 7).
- Start the loop for receiving incoming requests for a dynamic connection in the current thread by calling the
Run()
method of thekosipc::EventLoop
object.
Example
If necessary, you can create and initialize multiple kosipc::components::Root
class objects combined into a list of objects of the ServiceList
type using the AddServices()
method. For example, use of multiple objects enables you to separate components and endpoints of the server into groups or publish endpoints under different names.
Example
Return codes
Overview
In a KasperskyOS-based solution, the return codes of functions of various APIs (for example, APIs of the libkos
and kdf
libraries, drivers, transport code, and application software) are 32-bit signed integers. This type is defined in the sysroot-*-kos/include/rtl/retcode.h
header file from the KasperskyOS SDK as follows:
The set of return codes consists of a success code with a value of 0
and error codes. An error code is interpreted as a data structure whose format is described in the sysroot-*-kos/include/rtl/retcode.h
header file from the KasperskyOS SDK. This format provides for multiple fields that contain not only information about the results of a function call, but also the following additional information:
- Flag in the
Customer
field indicating that the error code was defined by the developers of the KasperskyOS-based solution and not by the developers of software from the KasperskyOS SDK.Thanks to the flag in the
Customer
field, developers of a KasperskyOS-based solution and developers of software from the KasperskyOS SDK can define error codes from non-overlapping sets. - Global ID of the error code in the
Space
field.Global IDs let you define non-overlapping sets of error codes. Error codes can be generic or specific. Generic error codes can be used in the APIs of any solution components and in the APIs of any constituent parts of solution components (for example, a driver or VFS may be a constituent part of a solution component). Specific error codes are used in the APIs of one or more solution components or in the APIs of one or more constituent parts of solution components.
For example, the
RC_SPACE_GENERAL
ID corresponds to generic errors, theRC_SPACE_KERNEL
ID corresponds to error codes of the kernel, and theRC_SPACE_DRIVERS
ID corresponds to error codes of drivers. - Local ID of the error code in the
Facility
field.Local IDs let you define non-overlapping subsets of error codes within the set of error codes corresponding to one global ID. For example, the set of error codes with the global ID
RC_SPACE_DRIVERS
includes non-overlapping subsets of error codes with the local IDsRC_FACILITY_I2C
,RC_FACILITY_USB
, andRC_FACILITY_BLKDEV
.
The global and local IDs of specific error codes are assigned by the developers of a KasperskyOS-based solution and by the developers of software from the KasperskyOS SDK independently of each other. In other words, two sets of global IDs are generated. Each global ID has a unique meaning within one set. Each local ID has a unique meaning within a set of local IDs related to one global ID. Generic error codes can be used in any API.
This type of centralized approach helps avoid situations in which the same error codes have various meanings within a KasperskyOS-based solution. This is necessary to eliminate a potential problem transmitting error codes through different APIs. For example, this problem occurs when drivers call kdf
library functions, receive error codes, and return these codes through their own APIs. If error codes are generated without a centralized approach, the same error code can have different meanings for the kdf
library and for the driver. Under these conditions, drivers return correct error codes only if the error codes of the kdf
library are converted into error codes of each driver. In other words, error codes in a KasperskyOS-based solution are assigned in such way that does not require conversion of these codes during their transit through various APIs.
The information about return codes provided here does not apply to functions of a POSIX interface or the APIs of third-party software used in KasperskyOS-based solutions.
Generic return codes
Return codes that are generic for APIs of all solution components and their constituent parts are defined in the sysroot-*-kos/include/rtl/retcode.h
header file from the KasperskyOS SDK. Descriptions of generic return codes are provided in the table below.
Generic return codes
Return code |
Description |
---|---|
|
The function completed successfully. |
|
Invalid function parameter. |
|
No connection between the client and server sides of interaction. For example, there is no server IPC handle. |
|
Insufficient memory to perform the operation. |
|
Insufficient buffer size. |
|
The function ended with an internal error related to incorrect logic. Some examples of internal errors include values outside of the permissible limits, and null indicators and values where they are not permitted. |
|
Error sending an IPC message. |
|
Error receiving an IPC message. |
|
IPC message was not transmitted due to the IPC message source. |
|
IPC message was not transmitted due to the IPC message recipient. |
|
IPC was interrupted by another process thread. |
|
Indicates that the function needs to be called again. |
|
The function ended with an error. |
|
The operation cannot be performed on the resource. |
|
Initialization failed. |
|
The function was not implemented. |
|
Large buffer size. |
|
Resource temporarily unavailable. |
|
Resource not found. |
|
Timed out. |
|
The operation was denied by security mechanisms. |
|
The operation will result in a block. |
|
The operation was aborted. |
|
Invalid function called in the interrupt handler. |
|
Set of elements already contains the element being added. |
|
Operation cannot be completed. |
|
Resource access rights were revoked. |
|
Resource quota exceeded. |
|
Device not found. |
|
An overflow occurred. |
|
Operation has already been completed. |
Defining error codes
To define an error code, the developer of a KasperskyOS-based solution needs to use the MAKE_RETCODE()
macro defined in the sysroot-*-kos/include/rtl/retcode.h
header file from the KasperskyOS SDK. The developer must also use the customer
parameter to pass the symbolic constant RC_CUSTOMER_TRUE
.
Example:
An error description that is passed via the desc
parameter is not used by the MAKE_RETCODE()
macro. This description is needed to create a database of error codes when building a KasperskyOS-based solution. At present, a mechanism for creating and using such a database has not been implemented.
Reading error code structure fields
The RC_GET_CUSTOMER()
, RC_GET_SPACE()
, RC_GET_FACILITY()
and RC_GET_CODE()
macros defined in the sysroot-*-kos/include/rtl/retcode.h
header file from the KasperskyOS SDK let you read error code structure fields.
The RETCODE_HR_PARAMS()
and RETCODE_HR_FMT()
macros defined in the sysroot-*-kos/include/rtl/retcode_hr.h
header file from the KasperskyOS SDK are used for formatted display of error details.
libkos library
The libkos
library is the basic KasperskyOS library that provides the set of APIs that allow programs and other libraries (for example, libc
and kdf
) to use core endpoints. The APIs provided by the libkos
library enable solution developers to do the following:
- Manage processes, threads, and virtual memory.
- Control access to resources.
- Perform input/output operations.
- Create IPC channels.
- Manage power.
- Obtain statistical data on the system.
- Use other capabilities supported by core endpoints.
This section contains detailed descriptions for working with some libkos
library interfaces. Descriptions of other interfaces can be found in corresponding header files.
The header files that define the libkos
library API are located in the following directories:
sysroot-*-kos/include/coresrv/
sysroot-*-kos/include/kos/
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 top
Creating 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 top
Revoking 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
Allocating and freeing memory (alloc.h)
The API is defined in the header file sysroot-*-kos/include/kos/alloc.h
from the KasperskyOS SDK.
The API is intended for allocating and freeing memory. Allocated memory is a committed virtual memory region that can be accessed for read-and-write operations.
Information about API functions is provided in the table below.
alloc.h functions
Function |
Information about the function |
---|---|
|
Purpose Allocates memory. Parameters
Returned values If successful, the function returns the pointer to the allocated memory, otherwise it returns |
|
Purpose Allocates memory. Parameters
Returned values If successful, the function returns the pointer to the allocated memory, otherwise it returns |
|
Purpose Allocates memory and initializes it with zeros. Parameters
Returned values If successful, the function returns the pointer to the allocated memory, otherwise it returns |
|
Purpose Deallocates memory. Parameters
Returned values N/A |
|
Purpose Gets the actual size of allocated memory. The actual size of allocated memory exceeds the requested size because it includes the size of service data and also may be increased due to alignment when the Parameters
Returned values Actual size of allocated memory (in bytes). |
|
Purpose Gets the size of memory that was requested when it is allocated. The actual size of allocated memory exceeds the requested size because it includes the size of service data and also may be increased due to alignment when the Parameters
Returned values Size (in bytes) of memory that was requested when it is allocated. |
Using DMA (dma.h)
The API is defined in the header file sysroot-*-kos/include/coresrv/io/dma.h
from the KasperskyOS SDK.
The API is designed to set up data exchange between devices and RAM in direct memory access (DMA) mode in which the processor is not used.
Information about API functions is provided in the table below.
Using the API
The standard scenario for API usage includes the following steps:
- Creating a DMA buffer.
DMA buffer consists of one or more physical memory regions (blocks) that are used for DMA. A DMA buffer consisting of multiple blocks can be used if the device supports "scatter/gather DMA" mode. A DMA buffer consisting of one block can be used only if the device supports "scatter/gather DMA" or "continuous DMA" mode. The likelihood of creating a DMA buffer consisting of one large block is lower than the likelihood of creating a DMA buffer consisting of multiple small blocks. This is especially relevant when physical memory is highly fragmented.
If the device supports only "continuous DMA" mode, you must use a DMA buffer consisting of one block even if IOMMU is enabled.
To complete this step, call the
KnIoDmaCreate()
orKnIoDmaCreateContinuous()
function. TheKnIoDmaCreateContinuous()
function creates a DMA buffer consisting of one block. TheKnIoDmaCreate()
function creates a DMA buffer consisting of one block if the 2^order value is equal to the memory page size value, or if the 2^order value is the next largest value of the memory page size in the ascending ordered set {2^(order-1);memory page size;2^order}. If the value of the memory page size is greater than the 2^order value, theKnIoDmaCreate()
function can create a DMA buffer consisting of multiple blocks.The DMA buffer handle can be transferred to another process via IPC.
- Mapping the DMA buffer to the memory of processes.
One DMA buffer can be mapped to multiple virtual memory regions of one or more processes that own the handle of this DMA buffer. Mapping allows processes to receive read-and/or-write access to the DMA buffer.
To reserve a virtual memory region and map the DMA buffer to it, call the
KnIoDmaMap()
function.A handle received when calling the
KnIoDmaMap()
function cannot be transferred to another process via IPC. - Opening access to the DMA buffer for a device via the
KnIoDmaBegin()
function call.The
KnIoDmaBegin()
function must be called to create a kernel object containing the addresses and sizes of blocks comprising the DMA buffer. A device needs this information to use the DMA buffer. A device can work with physical addresses and/or virtual addresses depending on whether IOMMU is enabled. If IOMMU is enabled, an object contains virtual addresses of blocks. Otherwise, an object contains physical addresses of blocks.A handle received when calling the
KnIoDmaBegin()
function cannot be transferred to another process via IPC. - Information about the DMA buffer is received.
At this step, get the addresses and sizes of blocks from the kernel object that was created by calling the
KnIoDmaBegin()
function. The received addresses and sizes will need to be passed to the device by using MMIO, for example. After receiving this information, the device can write to the DMA buffer and/or read from it (if IOMMU is enabled, a device on the PCIe bus must be attached to the IOMMU domain).To complete this step, you need to call the
KnIoDmaGetInfo()
orKnIoDmaContinuousGetDmaAddr()
function. TheKnIoDmaGetInfo()
function gets the memory page number (frame
) and theorder
for each block. (The memory page number multiplied by the memory page size results in the block address. The 2^order value is the block size in memory pages.) TheKnIoDmaContinuousGetDmaAddr()
function can be used if the DMA buffer consists of one block. This function gets the block address. (The accepted block size should be the DMA buffer size that was defined when this buffer was created.)
Closing access to the DMA buffer for a device
If you delete the kernel object that was created when the KnIoDmaBegin()
function was called and IOMMU is enabled, the device will be denied access to the DMA buffer. To delete this object, call the KnHandleClose()
function and specify the handle that was received when the KnIoDmaBegin()
function was called. (The KnHandleClose()
function is declared in the header file sysroot-*-kos/include/coresrv/handle/handle_api.h
from the KasperskyOS SDK.)
Deleting a DMA buffer
To delete a DMA buffer, complete the following steps:
- Free the virtual memory regions that were reserved during
KnIoDmaMap()
function calls.To complete this step, use the
KnHandleClose()
function and specify the handles that were received fromKnIoDmaMap()
function calls. (KnHandleClose()
function is declared in the header filesysroot-*-kos/include/coresrv/handle/handle_api.h
from the KasperskyOS SDK.)This step must be completed for all processes whose memory is mapped to the DMA buffer.
- Delete the kernel object that was created by the
KnIoDmaBegin()
function call.To complete this step, call the
KnHandleClose()
function and specify the handle that was received when theKnIoDmaBegin()
function was called. - Close or revoke each DMA buffer handle in all processes that own these handles.
To complete this step, use the
KnHandleClose()
and/orKnHandleRevoke()
functions that are declared in the header filesysroot-*-kos/include/coresrv/handle/handle_api.h
from the KasperskyOS SDK.
Information about API functions
dma.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates a DMA buffer. Parameters
Returned values If successful, the function returns Additional information In the
|
|
Purpose Creates a DMA buffer consisting of one block. Parameters
Returned values If successful, the function returns Additional information In the
|
|
Purpose Reserves a virtual memory region and maps the DMA buffer to it. Parameters
Returned values If successful, the function returns Additional information In the
|
|
Purpose Modifies the DMA buffer cache settings. Parameters
Returned values If successful, the function returns Additional information This function can be used if the following conditions are fulfilled:
In the
|
|
Purpose Gets information about a DMA buffer. This information includes the addresses and sizes of blocks. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the block address for a DMA buffer consisting of one block. Parameters
Returned values If successful, the function returns |
|
Purpose Opens access to a DMA buffer for a device. Parameters
Returned values If successful, the function returns |
Managing interrupt processing (irq.h)
The API is defined in the header file sysroot-*-kos/include/coresrv/io/irq.h
from the KasperskyOS SDK.
The API manages the handling of hardware interrupts. A hardware interrupt is a signal sent from a device to direct the processor to immediately pause execution of the current program and instead handle an event related to this device. For example, pressing a key on the keyboard invokes a hardware interrupt that ensures the required response to this pressed key (for example, input of a character).
A hardware interrupt occurs when the device queries the interrupt controller. This query can be transmitted through a hardware interrupt line between the device and the interrupt controller or through MMIO memory. In the second case, the device writes to MMIO memory by calling the Message Signaled Interrupt (MSI).
At present, no functions for managing the handling of MSI interrupts have been implemented.
Each hardware interrupt line corresponds to one interrupt with a unique number.
Information about API functions is provided in the table below.
Using the API
To attach an interrupt to its handler, complete the following steps:
- Registering an interrupt by calling the
KnRegisterIrq()
function.One interrupt can be registered multiple times in one or more processes.
The handle of an interrupt can be transferred to another process via IPC.
- Attaching a thread to an interrupt by calling the
KnIoAttachIrq()
function.This step is performed by the thread in whose context the interrupt will be handled.
When using the handle received from the
KnRegisterIrq()
function call, you can attach only one thread to an interrupt. To attach multiple threads in one or more processes to an interrupt, use different handles for this interrupt received from separateKnRegisterIrq()
function calls. In this case, theKnIoAttachIrq()
function must be called with the same flags in theflags
parameter.A handle received when calling the
KnIoAttachIrq()
function cannot be transferred to another process via IPC.
To deny (mask) an interrupt, call the KnIoDisableIrq()
function. To allow (unmask) an interrupt, call the KnIoEnableIrq()
function. Even though these functions receive an interrupt handle that is used to attach only one thread to the interrupt, their action is applied to all threads that are attached to this interrupt. These functions must be called outside of the threads attached to the interrupt. After an interrupt is registered and a thread is attached to it, this interrupt does not require unmasking.
To initiate detachment of a thread from an interrupt, call the KnIoDetachIrq()
function outside of the thread that is attached to the interrupt. Detachment is performed by the thread attached to the interrupt by calling the KnThreadDetachIrq()
function declared in the header file sysroot-*-kos/include/coresrv/thread/thread_api.h
from the KasperskyOS SDK.
Handling an interrupt
After attaching to an interrupt, a thread calls the Call()
function declared in the header file sysroot-*-kos/include/coresrv/syscalls.h
from the KasperskyOS SDK. The thread is locked as a result of this call. When an interrupt occurs or the KnIoDetachIrq()
function is called, the KasperskyOS kernel sends an IPC message to the process that contains this thread. This IPC message contains a request to handle the interrupt or a request to detach the thread from the interrupt. When a process receives an IPC message, the Call()
function in the thread attached to the interrupt returns control and provides the contents of the IPC message to the thread. The thread extracts the request from the IPC message and either processes the interrupt or detaches from the interrupt. If the interrupt is processed, information about its failure or success upon completion is added to the response IPC message that is sent to the kernel by the next Call()
function call in the loop.
When processing an interrupt, use the IoGetIrqRequest()
and IoSetIrqAnswer()
functions that are declared in the header file sysroot-*-kos/include/io/io_irq.h
from the KasperskyOS SDK. These functions let you extract data from IPC messages and add data to IPC messages for data exchange between the kernel and the thread attached to the interrupt.
The standard interrupt processing loop includes the following steps:
- Adding information about the failure or success of interrupt processing to an IPC message by calling the
IoSetIrqAnswer()
function. - Sending the IPC message to the kernel and receiving an IPC message from the kernel.
To complete this step, call the
Call()
functions. In thehandle
parameter, you must specify the handle that was received when theKnIoAttachIrq()
function was called. You must use themsgOut
parameter to define the IPC message that will be sent to the kernel, and use themsgIn
parameter to define the IPC message that will be received from the kernel. - Extracting a request from the IPC message received from the kernel by calling the
IoGetIrqRequest()
function. - Processing the interrupt or detaching from the interrupt depending on the request.
If the request requires detachment from the interrupt, exit the interrupt processing loop and call the
KnThreadDetachIrq()
function.
Deregistering an interrupt
To deregister an interrupt, complete the following steps:
- Detach the thread from the interrupt.
To complete this step, call the
KnThreadDetachIrq()
function. - Close the handle that was received when the
KnIoAttachIrq()
function was called.To complete this step, call the
KnHandleClose()
function. (TheKnHandleClose()
function is declared in the header filesysroot-*-kos/include/coresrv/handle/handle_api.h
from the KasperskyOS SDK.) - Close or revoke each interrupt handle in all processes that own these handles.
To complete this step, use the
KnHandleClose()
and/orKnHandleRevoke()
functions that are declared in the header filesysroot-*-kos/include/coresrv/handle/handle_api.h
from the KasperskyOS SDK.
One interrupt can be registered multiple times, but completion of these steps cancels only one registration. The other registrations will remain active. Each registration of one interrupt must be canceled separately.
Information about API functions
irq.h functions
Function |
Information about the function |
---|---|
|
Purpose Registers an interrupt. Parameters
Returned values If successful, the function returns |
|
Purpose Attaches the calling thread to an interrupt. Parameters
Returned values If successful, the function returns Additional information In the
|
|
Purpose Sends a request to a thread. When this request is fulfilled, the thread must detach from the interrupt. Parameters
Returned values If successful, the function returns |
|
Purpose Allows (unmasks) an interrupt. Parameters
Returned values If successful, the function returns |
|
Purpose Denies (masks) an interrupt. Parameters
Returned values If successful, the function returns |
Initializing IPC transport for interprocess communication and managing IPC request processing (transport-kos.h, transport-kos-dispatch.h)
APIs are defined in the header files transport-kos.h
and transport-kos-dispatch.h
from the KasperskyOS SDK that are located at the path sysroot-*-kos/include/coresrv/nk
.
API capabilities:
- Initialize IPC transport on the client side and on the server side.
IPC transport is an add-on that works on top of system calls for sending and receiving IPC messages and works separately with the constant part and arena of IPC messages. Transport code works on top of this add-on.
- Start the loop for processing IPC requests on the server side.
- Copy data to the IPC message arena.
Information about API functions is provided in the tables below.
This section contains API usage examples. In these examples, programs acting as servers have the following formal specification:
FsDriver.edl
Operations.cdl
FileIface.idl
Initializing IPC transport for interprocess communication
To initialize IPC transport for interaction with other processes, call the NkKosTransport_Init()
or NkKosTransportSync_Init()
function declared in the header file transport-kos.h
.
Example use of the NkKosTransport_Init()
function on the client side:
If a client needs to use several endpoints, the same number of proxy objects must be initialized. When initializing each proxy object, you need to specify the IPC transport that is associated through the client IPC handle with the relevant server. When initializing multiple proxy objects pertaining to the endpoints of one server, you can specify the same IPC transport that is associated with this server.
Example use of the NkKosTransport_Init()
function on the server side:
If a server processes IPC requests received through multiple IPC channels, the following special considerations should be taken into account:
- If a listener handle is associated with all IPC channels, IPC interaction with all clients can use the same IPC transport associated with this listener handle.
- If IPC channels are associated with different listener handles, IPC interaction with each group of clients corresponding to the same listener handle must use a separate IPC transport associated with this listener handle. In this case, IPC requests can be processed in parallel threads if you are using a thread-safe implementation of endpoint methods.
The NkKosTransportSync_Init()
function initializes IPC transport with support for interrupting the Call()
and Recv()
locking system calls. (For example, an interrupt of these calls may be required for correct termination of the process that is executing them.) To interrupt the Call()
and Recv()
system calls, use the API ipc_api.h.
The NkKosSetTransportTimeouts()
function declared in the header file transport-kos.h
defines the maximum lockout duration for Call()
and Recv()
system calls used for IPC transport.
Starting the IPC request processing loop
The IPC request processing loop on a server includes the following steps:
- Receive an IPC request.
- Process the IPC request.
- Send an IPC response.
Each step of this loop can be completed separately by sequentially calling the nk_transport_recv()
, dispatcher, and nk_transport_reply()
functions. (The nk_transport_recv()
and nk_transport_reply()
functions are declared in the header file sysroot-*-kos/include/nk/transport.h
from the KasperskyOS SDK.) You can also call the NkKosTransport_Dispatch()
or NkKosDoDispatch()
function in which this loop is completed in its entirety. (The NkKosTransport_Dispatch()
and NkKosDoDispatch()
functions are declared in the header files transport-kos.h
and transport-kos-dispatch.h
, respectively.) It is more convenient to use the NkKosDoDispatch()
function because it requires fewer preparatory operations (for example, you do not need to initialize IPC transport).
You can initialize the structure passed to the NkKosDoDispatch()
function through the info
parameter by using the macros defined in the header file transport-kos-dispatch.h
.
The NkKosTransport_Dispatch()
and NkKosDoDispatch()
functions can be called from parallel threads if you are using a thread-safe implementation of endpoint methods.
Example use of the NkKosDoDispatch()
function:
Example use of the NkKosTransport_Dispatch()
function:
Copying data to the IPC message arena
To copy a string to the IPC message arena, call the NkKosCopyStringToArena()
function declared in the header file transport-kos.h
. This function reserves a segment of the arena and copies a string to this segment.
Example use of the NkKosCopyStringToArena()
function:
Information about API functions
transport-kos.h functions
Function |
Information about the function |
---|---|
|
Purpose Initializes IPC transport. Parameters
Returned values N/A |
|
Purpose Initializes IPC transport with support for interrupting the Parameters
Returned values N/A |
|
Purpose Defines the maximum lockout duration for Parameters
Returned values N/A |
|
Purpose Starts the IPC request processing loop. Parameters
Returned values If unsuccessful, it returns an error code. |
|
Purpose Reserves a segment of the arena and copies a string to this segment. Parameters
Returned values If successful, the function returns |
transport-kos-dispatch.h functions
Function |
Information about the function |
---|---|
|
Purpose Starts the IPC request processing loop. Parameters
Returned values N/A |
Initializing IPC transport for querying the security module (transport-kos-security.h)
The API is defined in the header file sysroot-*-kos/include/coresrv/nk/transport-kos-security.h
from the KasperskyOS SDK.
The API initializes IPC transport for querying the Kaspersky Security Module via the security interface. Transport code works on top of IPC transport.
Information about API functions is provided in the table below.
This section contains an API usage example. In this example, the program that queries the security module has the following formal specification:
Verifier.edl
Approve.idl
Fragment of the policy description in the example:
security.psl
Using the API
To initialize IPC transport for querying the security module, call the NkKosSecurityTransport_Init()
function.
Example use of the NkKosSecurityTransport_Init()
function:
If a process needs to use several security interfaces, the same number of proxy objects must be initialized by specifying the same IPC transport and the unique IDs of the security interfaces.
Information about API functions
transport-kos-security.h functions
Function |
Information about the function |
---|---|
|
Purpose Initializes IPC transport for querying the Kaspersky Security Module through the security interface. Parameters
Returned values If successful, the function returns |
Generating random numbers (random_api.h)
The API is defined in the header file sysroot-*-kos/include/kos/random/random_api.h
from the KasperskyOS SDK.
The API generates random numbers and includes functions that can be used to ensure high entropy (high level of unpredictability) of the seed value of the random number generator. The start number of the random number generator (seed) determines the sequence of the generated random numbers. In other words, if the same seed value is set, the generator creates identical sequences of random numbers. (The entropy of these numbers is fully determined by the entropy of the seed value, which means that these numbers are not entirely random, but pseudorandom.)
The random number generator of one process does not depend on the random number generators of other processes.
Information about API functions is provided in the table below.
Using the API
To generate a sequence of random byte values, call the KosRandomGenerate()
or KosRandomGenerateEx()
function.
Example use of the KosRandomGenerate()
function:
The KosRandomGenerateEx()
function gets the quality level of the generated random values through the output parameter quality
. The quality level can be high or low. High-quality random values are random values that are generated while fulfilling all of the following conditions:
- When the seed value is changed, at least one entropy source is registered and data is successfully received from all registered entropy sources.
To register the entropy source, call the
KosRandomRegisterSrc()
function. This function uses thecallback
parameter to receive the pointer to a callback function of the following type:typedef Retcode (*KosRandomSeedMethod)(void *context, rtl_size_t size, void *output);Using the
context
parameters, this callback function receives data with the specifiedsize
of bytes from the entropy source and writes this data to theoutput
buffer. If the function returnsrcOk
, it is assumed that data was successfully received from the entropy source. An entropy source can be a digitalized signal of a sensor or a hardware-based random number generator, for example.To deregister an entropy source, call the
KosRandomUnregisterSrc()
function. - The random number generator is initialized if the quality level was low before changing the seed value while fulfilling condition 1.
If the quality level is low, it cannot be changed to high without initializing the random number generator.
To initialize a random number generator, call the
KosRandomInitSeed()
function. The entropy of data passed through theseed
parameter must be guaranteed by the calling process. - The counter of random byte values that were generated after changing the seed value while fulfilling condition 1 does not exceed the system-defined limit.
- The counter of time that has elapsed since changing the seed value while fulfilling condition 1 does not exceed the system-defined limit.
If at least one of these conditions is not fulfilled, the generated random values are deemed low-quality values.
When a process is started, the seed value is automatically defined by the system. Then the seed value is modified when doing the following:
- Generating a sequence of random values.
Each successful call of the
KosRandomGenerateEx()
function with thesize
parameter greater than zero and each successful call of theKosRandomGenerate()
function results in a change of the seed value of the random number generator, but not each seed change results in the receipt of data from registered entropy sources. The registered entropy sources are used only when condition 3 or 4 is not fulfilled. If at least one entropy source is registered, the time counter for the change in the seed value is reset. If data from at least one entropy source is successfully received, the counter of generated random byte values is also reset.The quality level may change from high to low.
- Initializing a random number generator.
Each successful call of the
KosRandomInitSeed()
function changes the seed value by using the data that is passed through theseed
parameter and received from registered entropy sources. If at least one registered entropy source is available, the counters for generated byte values and the time of a change in the seed value are reset. Otherwise, only the counter of generated random byte values is reset.The quality level may change from high to low, and vice versa.
- Registering an entropy source.
Each successful call of the
KosRandomRegisterSrc()
function changes the seed value by using the data only from the registered entropy source. The counter of the generated random byte values is also reset.The quality level may not change.
A possible scenario for generating high-quality random values includes the following steps:
- Register at least one source of entropy by calling the
KosRandomRegisterSrc()
function. - Generate a sequence of random values by calling the
KosRandomGenerateEx()
function. - Check the quality level of the random values.
If the quality level is low, initialize the random number generator by calling the
KosRandomInitSeed()
function, and proceed to step 2.If the quality level is high, proceed to step 4.
- Use random values.
To get the quality level without generating random values, call the KosRandomGenerateEx()
function with the values 0
and RTL_NULL
in the size
and output
parameters, respectively.
Information about API functions
random_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Initializes the random number generator. Parameters
Returned values If successful, the function returns |
|
Purpose Generates a sequence of random byte values. Parameters
Returned values If successful, the function returns |
|
Purpose Generates a sequence of random byte values. Parameters
Returned values If successful, the function returns |
|
Purpose Registers an entropy source. Parameters
Returned values If successful, the function returns |
|
Purpose Deregisters the source of entropy. Parameters
Returned values If successful, the function returns |
Getting and changing time values (time_api.h)
The API is defined in the header file sysroot-*-kos/include/coresrv/time/time_api.h
from the KasperskyOS SDK.
Main capabilities of the API:
- Get and modify the system time
- Get the monotonic time that has elapsed since the moment the KasperskyOS kernel was started
- Get the resolution of the sources of system time and monotonic time
Information about API functions is provided in the table below.
time_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Gets the resolution of the system time source. Parameters
Returned values If successful, the function returns |
|
Purpose Sets the system time. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the system time. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the resolution of the source of monotonic time that has elapsed since the KasperskyOS kernel was started. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the monotonic time that has elapsed since the moment the KasperskyOS kernel was started. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the system time. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the monotonic time that has elapsed since the moment the KasperskyOS kernel was started. Parameters N/A Returned values Monotonic time (in milliseconds) that has elapsed since the KasperskyOS kernel was started. |
|
Purpose Starts gradual adjustment of the system time. Parameters
Returned values If successful, the function returns Additional information If a new adjustment is started before a previously running adjustment is finished, the previously running adjustment is interrupted. |
Using notifications (notice_api.h)
The API is defined in the sysroot-*-kos/include/coresrv/handle/notice_api.h
header file from the KasperskyOS SDK.
The API can track events that occur to (both system and user) resources, and inform other processes and threads about events involving user resources.
Information about API functions is provided in the table below.
Using the API
The notification mechanism uses an event mask. 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 common part describes events that are not specific to any resources. The specialized part describes events that are specific to certain resources. Specialized part flags for system resources and common part flags are defined in the sysroot-*-kos/include/handle/event_descr.h
header file from KasperskyOS SDK. (For example, the common part flag EVENT_OBJECT_DESTROYED
signifies resource termination, and the specialized part flag EVENT_TASK_COMPLETED
signifies process termination.) Specialized part flags for a user resource are defined by the resource provider with the help of the OBJECT_EVENT_SPEC()
and OBJECT_EVENT_USER()
macros, which are defined in the sysroot-*-kos/include/handle/event_descr.h
header file from the KasperskyOS SDK. The resource provider must export the public header files describing the flags of the specialized part.
The standard scenario for receiving notifications about events occurring to resources consists of the following steps:
- Creating a notification receiver (KasperskyOS kernel object that collects notifications) by calling the
KnNoticeCreate()
function. - Adding "resource—event mask" entries to the notification receiver to configure it to get notifications about events that occur to relevant resources.
To add a "resource—event mask" entry to the notification receiver, you need to call the
KnNoticeSubscribeToObject()
function. (TheOCAP_HANDLE_GET_EVENT
flag should be set in the handle permissions mask of the resource stated in theobject
parameter.) Several "resource—event mask" entries can be added for one resource, and the entry identifiers do not need to be unique. Tracked events for each "resource—event mask" entry should be defined with an event mask that may match one or several events."Resource—event mask" entries added to the notification receiver can be fully or partially removed to prevent the receiver from getting notifications that match these entries. To remove all "resource—event mask" entries from the receiver, you need to call the
KnNoticeDropAndWake()
function. To remove from the receiver "resource—event mask" entries that refer to the same resource, you need to call theKnNoticeUnsubscribeFromObject()
function. To remove from the receiver a "resource—event mask" entry with a specific identifier, you need to call theKnNoticeUnsubscribeFromEvent()
function."Resource—event mask" entries can be added to, or removed from, the notification receiver throughout its life cycle.
- Extracting notifications from the receiver with the
KnNoticeGetEvent()
function.You can set the time-out for notifications to appear in the receiver when calling the
KnNoticeGetEvent()
function. Threads that are locked while waiting for notifications to appear in the receiver will resume when notifications appear, even if these notifications match "resource—event mask" entries added after wait start.Threads that are locked while waiting for notifications to appear in the receiver will resume if all "resource—event mask" entries are removed from the receiver by calling the
KnNoticeDropAndWake()
function. If you add at least one "resource—event mask" entry to the notification receiver after calling theKnNoticeDropAndWake()
function, threads that get notifications from that receiver will be locked again when calling theKnNoticeGetEvent()
function for the specified time-out duration as long as there are no notifications. If all "resource—event mask" entries are removed from the notification receiver with theKnNoticeUnsubscribeFromObject()
and/orKnNoticeUnsubscribeFromEvent()
functions, threads waiting for notifications to appear in the receiver will not resume until the time-out elapses. - Removing a notification receiver by calling the
KnNoticeRelease()
function.Threads that are locked while waiting for notifications to appear in the receiver will resume when the receiver is removed by calling the
KnNoticeRelease()
function.
To notify other processes and/or threads about events that occurred to the user resource, you need to call the KnNoticeSetObjectEvent()
function. Calling the function results in notifications appearing in receivers configured to get events defined with the evMask
parameter that occur to the user resource defined with the object
parameter. You cannot set flags of the general part of an event mask in the evMask
parameter, because only the kernel can signal about events that match the general part of an event mask. If the process calling the KnNoticeSetObjectEvent()
function created the user resource handle stated in the object
parameter, you can set flags defined by the OBJECT_EVENT_SPEC()
and OBJECT_EVENT_USER()
macros in the evMask
parameter. If the process calling the KnNoticeSetObjectEvent()
function received the user resource handle stated in the object
parameter from another process, you can set only those flags defined by the OBJECT_EVENT_USER()
macro in the evMask
parameter, while the permissions mask of the resulting handle must have a OCAP_HANDLE_SET_EVENT
flag set.
Information about API functions
notice_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates a notification receiver. Parameters
Returned values If successful, the function returns |
|
Purpose Adds a "resource–event mask" entry to the notification receiver so that it can receive notifications about events that occur with the defined resource and match the defined event mask. Parameters
Returned values If successful, the function returns |
|
Purpose Extracts notifications from the receiver. Parameters
Returned values If successful, the function returns If the time-out for notifications to appear in the receiver has elapsed, returns If the time-out for notifications appear in the receiver is interrupted by a call to the |
|
Purpose Removes from the notification receiver "resource—event mask" entries that match the specified resource to prevent the receiver from getting notifications about events that match these entries. Parameters
Returned values If successful, the function returns Additional information Notifications that correspond to the removed "resource—event mask" entries will be removed from the receiver. |
|
Purpose Removes from the notification receiver "resource—event mask" entries with the specified identifier to prevent the receiver from getting notifications about events that match these entries. Parameters
Returned values If successful, the function returns Additional information Notifications that correspond to the removed "resource—event mask" entries will be removed from the receiver. |
|
Purpose Removes all "resource—event mask" entries from the specified notification receiver and resumes all threads that are waiting for notifications to appear in the specified receiver. Parameters
Returned values If successful, the function returns |
|
Purpose Removes the specified notification receiver and resumes all threads that are waiting for notifications to appear in the specified receiver. Parameters
Returned values If successful, the function returns |
|
Purpose Signals that events matching the specified event mask occurred to the specified user resource. Parameters
Returned values If successful, the function returns |
Dynamically creating IPC channels (cm_api.h, ns_api.h)
APIs are defined in the header files from the KasperskyOS SDK:
sysroot-*-kos/include/coresrv/cm/cm_api.h
sysroot-*-kos/include/coresrv/ns/ns_api.h
The API dynamically creates IPC channels.
Information about API functions is provided in the tables below.
Using the API
To ensure that servers can notify clients about the endpoints that they provide, the solution should include a name server, which is provided by the NameServer
system program (executable file sysroot-*-kos/bin/ns
from the KasperskyOS SDK). IPC channels from clients and servers to a name server can be statically created (these IPC channels must be named kl.core.NameServer
). If this is not done, attempts will be made to dynamically create these IPC channels whenever clients and servers call the NsCreate()
function. A name server does not have to be included in a solution if the clients initially already have information about the names of servers and the endpoints provided by these servers.
The names of endpoints and interfaces should be defined according to the formal specifications of solution components. (For information about the qualified name of an endpoint, see "Binding methods of security models to security events") Instead of the qualified name of an endpoint, you can use any conditional name of this endpoint. The names of clients and servers are defined in the init description. You can also get a process name by calling the KnTaskGetName()
function from the API task_api.h.
Dynamic creation of an IPC channel on the server side includes the following steps:
- Connect to the name server by calling the
NsCreate()
function. - Publish the provided endpoints on the name server by using the
NsPublishService()
function.To unpublish an endpoint, call the
NsUnPublishService()
function. - Receive a client request to create an IPC channel by calling the
KnCmListen()
function.The
KnCmListen()
function gets the first request in the queue without deleting this request but instead putting it at the end of the queue. If there is only one request in the queue, multiple consecutive calls of theKnCmListen()
function will provide the same result. A request is deleted from the queue when theKnCmAccept()
orKnCmDrop()
function is called. - You can accept a client request to create an IPC channel by calling the
KnCmAccept()
function.To decline a client request, call the
KnCmDrop()
function.A listener handle is created when the
KnCmAccept()
function is called with theINVALID_HANDLE
value in thelistener
parameter. If a listener handle is specified, the created server IPC handle will provide the capability to receive IPC requests over all IPC channels associated with this listener handle. (The first IPC channel associated with the listener handle is created when theKnCmAccept()
function is called with theINVALID_HANDLE
value in thelistener
parameter. The second and subsequent IPC channels associated with the listener handle are created during the second and subsequent calls of theKnCmAccept()
function specifying the handle that was obtained during the first call.) In thelistener
parameter of theKnCmAccept()
function, you can specify the listener handle received using theKnHandleConnect()
,KnHandleConnectEx()
andKnHandleCreateListener()
functions from the API handle_api.h, and theServiceLocatorRegister()
function declared in the header filesysroot-*-kos/include/coresrv/sl/sl_api.h
from the KasperskyOS SDK.
Dynamic creation of an IPC channel on the client side includes the following steps:
- Connect to the name server by calling the
NsCreate()
function. - Find the server providing the required endpoint by using the
NsEnumServices()
function.To get a full list of endpoints with a defined interface, call the function several times while incrementing the index until you receive the
rcResourceNotFound
error. - Fulfill the request to create an IPC channel with the necessary server by calling the
KnCmConnect()
function.
You can connect multiple clients and servers to the name server. Each client and server can create multiple connections to the name server. A server can unpublish an endpoint that was published by another server.
Deleting dynamically created IPC channels
A dynamically created IPC channel will be deleted when its client IPC handle and server IPC handle are closed.
Information about API functions
ns_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates a connection to a name server. Parameters
Returned values If successful, the function returns |
|
Purpose Publishes an endpoint on a name server. Parameters
Returned values If successful, the function returns |
|
Purpose Unpublishes an endpoint on a name server. Parameters
Returned values If successful, the function returns |
|
Purpose Enumerates the endpoints published on a name server. Parameters
Returned values If successful, the function returns |
cm_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Requests to create an IPC channel with a server for use of the defined endpoint. Parameters
Returned values If successful, the function returns |
|
Purpose Receives a client request to create an IPC channel for use of an endpoint. Parameters
Returned values If successful, the function returns |
|
Purpose Rejects a client request to create an IPC channel for use of the defined endpoint. Parameters
Returned values If successful, the function returns |
|
Purpose Accepts a client request to create an IPC channel for use of the defined endpoint. Parameters
Returned values If successful, the function returns |
Using synchronization primitives (event.h, mutex.h, rwlock.h, semaphore.h, condvar.h)
The libkos
library provides APIs that enable use of the following synchronization primitives:
- Events (
event.h
) - Mutexes (
mutex.h
) - Read-write locks (
rwlock.h
) - Semaphores (
semaphore.h
) - Conditional variables (
condvar.h
)
The header files are located in the KasperskyOS SDK at sysroot-*-kos/include/kos
.
The APIs are intended for synchronizing threads that belong to the same process.
Events
An event is a synchronization primitive that is used to notify one or more threads about the fulfillment of a condition required by these threads. The notified thread waits for the event to switch from a non-signaling state to a signaling state, and the notifying thread changes the state of this event.
The standard API usage scenario for working with events includes the following steps:
- An event is initialized via the
KosEventInit()
function call. - The event is used by threads:
- The notified threads wait for the event to switch from non-signaling state to signaling state via the
KosEventWait()
orKosEventWaitTimeout()
function call. - The notifying threads change the state of the event via the
KosEventSet()
andKosEventReset()
function calls.
- The notified threads wait for the event to switch from non-signaling state to signaling state via the
Information about the API event functions is provided in the table below.
event.h functions
Function |
Information about the function |
---|---|
|
Purpose Initializes an event. The event is in a non-signaling state after it is initialized. Parameters
Returned values N/A |
|
Purpose Sets the event state to signaling. Parameters
Returned values N/A |
|
Purpose Sets the event state to non-signaling. Parameters
Returned values N/A |
|
Purpose Waits for the event to change its state from non-signaling to signaling. Parameters
Returned values N/A |
|
Purpose Waits on the event to change its state from non-signaling to signaling for a period that does not exceed the specified time. Parameters
Returned values If successful, the function returns Returns |
Mutexes
A mutex is a synchronization primitive that ensures mutually exclusive execution of critical sections (areas of code where resources shared between threads are queried). One thread captures the mutex and executes a critical section. Meanwhile, other threads wait for the mutex to be freed and attempt to capture this mutex to execute other critical sections. A mutex can be freed only by the specific thread that captured it. You can use a recursive mutex, which can be captured by the same thread multiple times.
The standard API usage scenario for working with mutexes includes the following steps:
- A mutex is initialized via the
KosMutexInit()
orKosMutexInitEx()
function call. - The mutex is used by threads:
- The mutex is captured via the
KosMutexTryLock()
,KosMutexLock()
orKosMutexLockTimeout()
function call. - The mutex is freed via the
KosMutexUnlock()
function call.
- The mutex is captured via the
Information about the API mutex functions is provided in the table below.
mutex.h functions
Function |
Information about the function |
---|---|
|
Purpose Initializes a non-recursive mutex. Parameters
Returned values N/A |
|
Purpose Initializes a mutex. Parameters
Returned values N/A |
|
Purpose Acquires the mutex. If the mutex is already acquired, returns control rather than waits for the mutex to be released. Parameters
Returned values If successful, the function returns If the mutex is already acquired, returns |
|
Purpose Acquires the mutex. If the mutex is already acquired, waits indefinitely for it to be released. Parameters
Returned values N/A |
|
Purpose Releases the mutex. Parameters
Returned values N/A |
|
Purpose Acquires the mutex. If the mutex is already acquired, waits for it to be released for a period that does not exceed the specified time. Parameters
Returned values If successful, the function returns Returns |
Read-write locks
A read-write lock is a synchronization primitive used to allow access to resources shared between threads: write access for one thread or read access for multiple threads at the same time.
The standard API usage scenario for working with read-write locks includes the following steps:
- A read-write lock is initialized by the
KosRWLockInit()
function call. - The read-write lock is used by threads:
- The read-write lock is captured for write operations (via the
KosRWLockWrite()
orKosRWLockTryWrite()
function call) or for read operations (via theKosRWLockRead()
orKosRWLockTryRead()
function call). - The read-write lock is freed via the
KosRWLockUnlock()
function call.
- The read-write lock is captured for write operations (via the
Information about the API read-write lock functions is provided in the table below.
rwlock.h functions
Function |
Information about the function |
---|---|
|
Purpose Initializes a read-write lock. Parameters
Returned values N/A |
|
Purpose Acquires a read-write lock for reading. If the read-write lock is already acquired for writing, or if there are threads waiting on the lock to be acquired for writing, waits indefinitely for the lock to be released. Parameters
Returned values N/A |
|
Purpose Acquires the read-write lock for reading. If the read-write lock is already acquired for writing, or if there are threads waiting on the lock to be acquired for writing, returns control, rather than waits for the lock to be released. Parameters
Returned values If successful, the function returns |
|
Purpose Acquires the read-write lock for writing. If the read-write lock is already acquired for writing or reading, waits indefinitely for the lock to be released. Parameters
Returned values N/A |
|
Purpose Acquires the read-write lock for writing. If the read-write lock is already acquired for writing or reading, returns control, rather than waits for the lock to be released. Parameters
Returned values If successful, the function returns |
|
Purpose Releases the read-write lock. Parameters
Returned values N/A Additional information If the read-write lock is acquired for reading, it remains acquired for reading until released by every reading thread. |
Semaphores
A semaphore is a synchronization primitive that is based on a counter whose value can be atomically modified. The value of the counter normally reflects the number of available resources shared between threads. To execute a critical section, the thread waits until the counter value becomes greater than zero. If the counter value is greater than zero, it is decremented by one and the thread executes the critical section. After the critical section is executed, the thread signals the semaphore and the counter value is increased.
The standard API usage scenario for working with semaphores includes the following steps:
- A semaphore is initialized via the
KosSemaphoreInit()
function call. - The semaphore is used by threads:
- They wait for the semaphore via the
KosSemaphoreWait()
,KosSemaphoreWaitTimeout()
orKosSemaphoreTryWait()
function call. - The semaphore is signaled via the
KosSemaphoreSignal()
orKosSemaphoreSignalN()
function call.
- They wait for the semaphore via the
- Deallocating semaphore resources by calling the
KosSemaphoreDeinit()
function.
Information about the API semaphore functions is provided in the table below.
semaphore.h functions
Function |
Information about the function |
---|---|
|
Purpose Initializes a semaphore. Parameters
Returned values If successful, the function returns If the value of the |
|
Purpose Deallocates semaphore resources. Parameters
Returned values If successful, the function returns If there are threads waiting on the semaphore, returns |
|
Purpose Signals the semaphore and increases the counter by one. Parameters
Returned values If successful, the function returns |
|
Purpose Signals the semaphore and increases the counter by the specified number. Parameters
Returned values If successful, the function returns |
|
Purpose Waits on the semaphore for a period that does not exceed the specified time. Parameters
Returned values If successful, the function returns Returns |
|
Purpose Waits on the semaphore indefinitely. Parameters
Returned values If successful, the function returns |
|
Purpose Waits on the semaphore. If the semaphore counter has a zero value, returns control, rather than waits for the semaphore counter to increase. Parameters
Returned values If successful, the function returns If the semaphore counter has a zero value, returns |
Conditional variables
A conditional variable is a synchronization primitive that is used to notify one or more threads about the fulfillment of a condition required by these threads. A conditional variable is used together with a mutex. The notifying and notified threads capture a mutex to execute critical sections. During execution of a critical section, the notified thread verifies that its required condition was fulfilled (for example, the data has been prepared by the notifying thread). If the condition is fulfilled, the notified thread executes the critical section and frees the mutex. If the condition is not fulfilled, the notified thread is locked at the conditional variable and waits for the condition to be fulfilled. When this happens, the mutex is automatically freed. During execution of a critical section, the notifying thread verifies fulfillment of the condition required by the notified thread. If the condition is fulfilled, the notifying thread signals this fulfillment through the conditional variable and frees the mutex. The notified thread that was locked and waiting for the fulfillment of its required condition resumes execution of the critical section while automatically capturing the mutex. After the critical section is executed, the notified thread frees the mutex.
The standard API usage scenario for working with conditional variables includes the following steps:
- The conditional variable and mutex are initialized.
To initialize a conditional variable, you need to call the
KosCondvarInit()
function. - The conditional variable and mutex are used by threads.
Use of a conditional variable and mutex by notified threads includes the following steps:
- The mutex is captured.
- Condition fulfillment is verified.
- The
KosCondvarWait()
orKosCondvarWaitTimeout()
function is called to wait for condition fulfillment.After the
KosCondvarWait()
orKosCondvarWaitTimeout()
function is returned, you normally need to re-verify that the condition is fulfilled because another notified thread also received the signal and may have voided this condition again. (For example, another thread could have extracted the data prepared by the notifying thread). To do so, you need to use the following construct:while(<condition>) <call of KosCondvarWait() or KosCondvarWaitTimeout()> - The mutex is freed.
Use of a conditional variable and mutex by notifying threads includes the following steps:
- The mutex is captured.
- Condition fulfillment is verified.
- Fulfillment of the condition is signaled via the
KosCondvarSignal()
orKosCondvarBroadcast()
function call. - The mutex is freed.
Information about the API conditional variable functions is provided in the table below.
condvar.h functions
Function |
Information about the function |
---|---|
|
Purpose Initializes a conditional variable. Parameters
Returned values N/A |
|
Purpose Waits for condition fulfillment for a period that does not exceed the specified time. Parameters
Returned values Returns Returns |
|
Purpose Waits indefinitely for condition fulfillment. Parameters
Returned values If successful, the function returns |
|
Purpose Signals condition fulfillment to one of the threads waiting for it. Parameters
Returned values N/A |
|
Purpose Signals condition fulfillment to all threads waiting for it. Parameters
Returned values N/A |
Managing I/O memory isolation (iommu_api.h)
The API is defined in the sysroot-*-kos/include/coresrv/iommu/iommu_api.h
header file from the KasperskyOS SDK.
The API is intended for managing the isolation of physical memory regions used by devices on a PCIe bus for DMA. (Isolation is provided by the IOMMU.)
Information about API functions is provided in the table below.
Using the API
A device on the PCIe bus cannot use DMA unless the device is attached to the IOMMU domain. After a device is attached to the IOMMU domain, the device can access all DMA buffers that are associated with this IOMMU domain. A device can be attached to only one IOMMU domain at a time, but multiple devices can be attached to the same IOMMU domain. A DMA buffer can be associated with multiple IOMMU domains at the same time. Each process is associated with a separate IOMMU domain.
The API attaches devices on the PCIe bus to an IOMMU domain associated with the calling process, and performs the inverse operation. A device is normally attached to an IOMMU domain when its driver is initialized. A device is usually detached from an IOMMU domain when errors are encountered during driver initialization or driver finalization.
A DMA buffer is associated with an IOMMU domain when calling the KnIoDmaBegin()
function that is included in the API dma.h.
Information about API functions
iommu_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Attaches a device on a PCIe bus to the IOMMU domain associated with the calling process. Parameters
Returned values If successful, the function returns Additional information If IOMMU is not enabled, |
|
Purpose Detaches a device on a PCIe bus from the IOMMU domain associated with the calling process. Parameters
Returned values If successful, the function returns Additional information If IOMMU is not enabled, |
Using queues (queue.h)
The API is defined in the header file sysroot-*-kos/include/kos/queue.h
from the KasperskyOS SDK.
The API sets up data exchange between threads owned by one process via a queuing mechanism that does not lock threads. In other words, you can add or extract elements of a queue without locking other threads that add or extract elements of this queue.
Information about API functions is provided in the table below.
Using the API
The standard scenario for API usage includes the following steps:
- Create a queue abstraction.
A queue abstraction consists of a structure containing queue metadata and a queue buffer intended for storing elements of the queue. A queue buffer is logically divided into equal segments, each of which is intended for an individual element of the queue. The number of segments in a queue buffer matches the maximum number of elements in the queue. The alignment of segment addresses corresponds to the data types of elements in the queue.
To complete this step, call the
KosQueueCreate()
function. This function can allocate memory for the queue buffer or use already allocated memory. The size of the already allocated memory must be sufficient to accommodate the maximum number of elements in the queue. Also take into account that the size of a segment in the queue buffer is rounded to the next largest multiple of the alignment value defined through theobjAlign
parameter. The initial address of the already allocated memory must also be aligned to correspond to the data types of queue elements. If the memory address alignment specified in thebuffer
parameter is less than the value defined through theobjAlign
parameter, the function returnsRTL_NULL
. - Exchange data between threads by adding and extracting elements of the queue.
To add one element to the end of the queue, you must reserve a segment in the queue buffer by calling the
KosQueueAlloc()
function, copy this element to the reserved segment, and call theKosQueuePush()
function.To add a sequence of elements to the end of the queue, you must reserve the necessary number of segments in the queue buffer via
KosQueueAlloc()
function calls, copy the elements of this sequence to the reserved segments, and call theKosQueuePushArray()
function. The order of elements in a sequence is not changed after this sequence is added to the queue. In other words, elements are added to the queue in the same order in which the pointers to reserved segments in the queue buffer are put into the array that is passed through theobjs
parameter of theKosQueuePushArray()
function.To extract the first element of the queue, you must call the
KosQueuePop()
function. This function returns the pointer to the reserved segment in the queue buffer that contains the first element of the queue. After using an extracted element (for example, after checking or saving the value of an element), you must free the queue buffer segment occupied by this element. To do so, call theKosQueueFree()
function.To clear the queue and free all registered segments in the queue buffer, you must call the
KosQueueFlush()
function. - Delete the queue abstraction.
To complete this step, call the
KosQueueDestroy()
function. This function deletes the queue buffer if only the memory for this buffer was allocated by theKosQueueCreate()
function. Otherwise, you must separately delete the queue buffer.
Information about API functions
queue.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates a queue abstraction. Parameters
Returned values If successful, the function returns the queue abstraction ID, otherwise it returns |
|
Purpose Deletes a queue abstraction. Parameters
Returned values N/A |
|
Purpose Reserves a segment in the queue buffer. Parameters
Returned values If successful, the function returns the pointer to the reserved segment in the queue buffer, otherwise it returns |
|
Purpose Resets the reservation of the defined segment in the queue buffer. Parameters
Returned values N/A |
|
Purpose Adds an element to the end of the queue. Parameters
Returned values N/A |
|
Purpose Adds a sequence of elements to the end of the queue. Parameters
Returned values N/A |
|
Purpose Extracts the first element of the queue. Parameters
Returned values If successful, this function returns the pointer to the reserved segment in the queue buffer containing the first element of the queue. Otherwise it returns |
|
Purpose Clears the queue and resets the reservation of all registered segments in the queue buffer. Parameters
Returned values N/A |
Using memory barriers (barriers.h)
This API is defined in the header file sysroot-*-kos/include/coresrv/io/barriers.h
from the KasperskyOS SDK.
The API sets barriers for reading from memory and/or writing to memory. A memory barrier is an instruction for a compiler or processor that guarantees that memory access operations specified in source code before setting a barrier will be executed before the memory access operations specified in source code after setting a barrier. Use of memory barriers is required if the specific order of memory write and memory read operations is important. Otherwise, the optimization mechanisms of a compiler and/or processor could cause these operations to be executed in a different order than the order specified in the source code.
Information about API functions is provided in the table below.
barriers.h functions
Function |
Information about the function |
---|---|
|
Purpose Sets a barrier for reading from memory. Parameters N/A Returned values N/A |
|
Purpose Sets a barrier for writing to memory. Parameters N/A Returned values N/A |
|
Purpose Sets a barrier for writing to memory and reading from memory. Parameters N/A Returned values N/A |
Executing system calls (syscalls.h)
This API is defined in the header file sysroot-*-kos/include/coresrv/syscalls.h
from the KasperskyOS SDK.
The API allows execution of the Call()
, Recv()
, and Reply()
system calls for sending and receiving IPC messages.
Information about API functions is provided in the table below.
Using the API
Pointers to buffers containing the constant part and arena of IPC messages are passed to API functions by using an IPC message header whose type is defined in the header file sysroot-*-kos/include/ipc/if_rend.h
from the KasperskyOS SDK. Prior to API function calls, the headers of IPC messages must be bound to buffers containing the constant part and arena of IPC messages. To do so, use the PackInMsg()
and PackOutMsg()
functions that are declared in the header file sysroot-*-kos/include/services/rtl/nk_msg.h
from the KasperskyOS SDK.
The Call()
, CallEx()
, Recv()
, and RecvEx()
functions lock execution of the calling thread while waiting for the system calls to complete. The CallEx()
and RecvEx()
functions let you define the timeout for completion of a system call. When this timeout is reached, an uncompleted system call is interrupted and the thread that is waiting for its completion resumes execution. A system call is also interrupted if an error occurs during its execution (such as an error due to termination of a server process). If a thread waiting on the completion of a system call is terminated externally, this system call is also interrupted. A system call executed by the CallEx()
or RecvEx()
function can be interrupted (for example, for correct termination of a process) by using the API ipc_api.h.
If a system call was interrupted using the API ipc_api.h, the CallEx()
and RecvEx()
functions return the error code rcIpcInterrupt
. If IPC message transmission is prohibited by security mechanisms (such as the Kaspersky Security Module or a capability-based security mechanism implemented by the KasperskyOS kernel), the Call()
, CallEx()
, and Reply()
functions return the error code rcSecurityDisallow
.
Information about API functions
syscalls.h functions
Function |
Information about the function |
---|---|
|
Purpose Executes the Parameters
Returned values If successful, the function returns |
|
Purpose Executes the Parameters
Returned values If successful, the function returns |
|
Purpose Executes the Parameters
Returned values If successful, the function returns |
|
Purpose Executes the Parameters
Returned values If successful, the function returns |
|
Purpose Executes the Parameters
Returned values If successful, the function returns |
IPC interrupt (ipc_api.h)
This API is defined in the header file sysroot-*-kos/include/coresrv/ipc/ipc_api.h
from the KasperskyOS SDK.
The API interrupts the Call()
and Recv()
system calls if one or more process threads are locked while waiting for these system calls to complete. For example, you may need to interrupt these system calls to correctly terminate a process so that threads waiting for the completion of these system calls can resume execution.
Information about API functions is provided in the table below.
Using the API
The API interrupts system calls in process threads that were locked after the CallEx()
or RecvEx()
function was called from the API syscalls.h if these functions were called while specifying the IPC synchronization object handle in the syncHandle
parameter. To create an IPC synchronization object, call the KnIpcCreateSyncObject()
function. (The handle of an IPC synchronization object cannot be transferred to another process because the necessary flag for this operation is not set in the permissions mask of this handle.)
The KnIpcSetInterrupt()
function switches an IPC synchronization object to a state that allows interruption of the system calls in those process threads that have been locked after a CallEx()
or RecvEx()
function call specifying the handle of this IPC synchronization object in the syncHandle
parameter. A system call can be interrupted only during certain stages of its execution. A system call that is executed by the CallEx()
function can be interrupted only when the server has not yet received a Recv()
or RecvEx()
function call for the IPC channel whose client IPC handle was specified during the CallEx()
function call. A system call executed by the RecvEx()
function can be interrupted only while waiting for an IPC request from a client.
The KnIpcClearInterrupt()
function cancels the action of the KnIpcSetInterrupt()
function.
To delete an IPC synchronization object, close its handle by calling the KnHandleClose()
function that is declared in the header file sysroot-*-kos/include/coresrv/handle/handle_api.h
from the KasperskyOS SDK.
Information about API functions
ipc_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates an IPC synchronization object. Parameters
Returned values If successful, the function returns |
|
Purpose Switches the defined IPC synchronization object to a state in which the Parameters
Returned values If successful, the function returns |
|
Purpose Switches the defined IPC synchronization object to a state in which the Parameters
Returned values If successful, the function returns |
POSIX support limitations
KasperskyOS has a limited implementation of POSIX oriented toward the POSIX.1-2008 standard. These limitations are primarily due to security precautions.
There is no XSI support or optional functionality.
Limitations affect the following:
- Interaction between processes
- Interaction between threads via signals
- Asynchronous input/output
- Use of robust mutexes
- Terminal operations
- Shell operations
- File handle management
- Clock usage
- Getting system parameters
Limitations include:
- Unimplemented interfaces
- Interfaces that are implemented with deviations from the POSIX.1-2008 standard
- Stub interfaces that do not perform any operations except assign the
ENOSYS
value to theerrno
variable and return the value-1
In KasperskyOS, signals cannot interrupt the Call()
, Recv()
, and Reply()
system calls that support the operation of libraries that implement the POSIX interface.
The KasperskyOS kernel does not transmit signals.
Limitations on interaction between processes
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Create a new (child) process. |
Not implemented |
|
|
Register the handlers that are called before and after the child process is created. |
Not implemented |
|
|
Wait for the child process to stop or complete. |
Stub |
|
|
Wait for the state of the child process to change. |
Not implemented |
|
|
Wait for the child process to stop or complete. |
Stub |
|
|
Run the executable file. |
Stub |
|
|
Run the executable file. |
Stub |
|
|
Run the executable file. |
Stub |
|
|
Run the executable file. |
Not implemented |
|
|
Run the executable file. |
Not implemented |
|
|
Run the executable file. |
Stub |
|
|
Run the executable file. |
Stub |
|
|
Move the process to another group or create a group. |
Stub |
|
|
Create a session. |
Stub |
|
|
Get the group ID of the calling process. |
Stub |
|
|
Get the group ID. |
Stub |
|
|
Get the ID of the parent process. |
Stub |
|
|
Get the session ID. |
Stub |
|
|
Get the time values for the process and its descendants. |
Stub |
|
|
Send a signal to the process or group of processes. |
Only the |
|
|
Wait for a signal. |
Stub |
|
|
Check for received blocked signals. |
Not implemented |
|
|
Send a signal to the process. |
Not implemented |
|
|
Wait for a signal from the defined set of signals. |
Not implemented |
|
|
Wait for a signal from the defined set of signals. |
Not implemented |
|
|
Create an unnamed semaphore. |
You cannot create an unnamed semaphore for synchronization between processes. If a non-zero value is passed through the |
|
|
Create/open a named semaphore. |
You cannot open a named semaphore that was created by another process. Named semaphores (like unnamed semaphores) are local, which means that they are accessible only to the process that created them. |
|
|
Create a spin lock. |
You cannot create a spin lock for synchronization between processes. If the |
|
|
Map to memory. |
You cannot perform memory mapping for interaction between processes. If the |
|
|
Define the memory access permissions. |
For security purposes, some configurations of the KasperskyOS kernel prohibit granting simultaneous write-and-execute access to virtual memory regions. If this type of kernel configuration is in use and you pass the |
|
|
Create an unnamed channel. |
You cannot use an unnamed channel for data transfer between processes. Unnamed channels are local, which means that they are accessible only to the process that created them. |
|
|
Create a special FIFO file (named channel). |
Stub |
|
|
Create a special FIFO file (named channel). |
Not implemented |
|
Limitations on interaction between threads via signals
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Send a signal to a thread. |
You cannot send a signal to a thread. If a signal number is passed through the |
|
|
Restore the state of the control thread and the signals mask. |
Not implemented |
|
|
Save the state of the control thread and the signals mask. |
Not implemented |
|
Asynchronous input/output limitations
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Cancel input/output requests that are waiting to be handled. |
Not implemented |
|
|
Receive an error from an asynchronous input/output operation. |
Not implemented |
|
|
Request the execution of input/output operations. |
Not implemented |
|
|
Request a file read operation. |
Not implemented |
|
|
Get the status of an asynchronous input/output operation. |
Not implemented |
|
|
Wait for the completion of asynchronous input/output operations. |
Not implemented |
|
|
Request a file write operation. |
Not implemented |
|
|
Request execution of a set of input/output operations. |
Not implemented |
|
Limitations on the use of robust mutexes
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Return a robust mutex to a consistent state. |
Not implemented |
|
|
Get a robust mutex attribute. |
Not implemented |
|
|
Define a robust mutex attribute. |
Not implemented |
|
Terminal operation limitations
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Get the path to the file of the control terminal. |
Only returns or passes an empty string through the |
|
|
Define the terminal settings. |
The input speed, output speed, and other settings specific to hardware terminals are ignored. |
|
|
Wait for output completion. |
Returns only the value |
|
|
Suspend or resume receipt or transmission of data. |
Suspending output and resuming suspended output are not supported. |
|
|
Clear the input queue or output queue, or both of these queues. |
Returns only the value |
|
|
Break the connection with the terminal for a set time. |
Returns only the value |
|
|
Get the path to the terminal file. |
Returns only a null pointer. |
|
|
Get the path to the terminal file. |
Returns only an error value. |
|
|
Get the ID of a group of processes using the terminal. |
Returns only the value |
|
|
Define the ID for a group of processes using the terminal. |
Returns only the value |
|
|
Get the ID of a group of processes for the leader of the session connected to the terminal. |
Returns only the value |
|
Shell operation limitations
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Create a child process for command execution and an unnamed channel with this process. |
Only assigns the |
|
|
Close the unnamed channel with the child process created by |
Cannot be used because |
|
|
Create a child process for command execution. |
Stub |
|
|
Perform a shell-like expansion of the string. |
Not implemented |
|
|
Free up the memory allocated for the results from calling |
Not implemented |
|
Limitations on file handle management
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Make a copy of the handle of an opened file. |
Handles of regular files, standard I/O streams, sockets and channels are supported. There is no guarantee that the lowest available handle will be received. |
|
|
Make a copy of the handle of an opened file. |
Handles of regular files, standard I/O streams, sockets and channels are supported. The handle of an opened file needs to be passed through the |
|
Limitations on clock usage
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Get the time value. |
If the |
|
|
Get the CPU time spent on execution of the calling process. |
Returns the amount of time (in milliseconds) that has elapsed since the KasperskyOS kernel was started. |
|
Getting system parameters
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Get a system parameter. |
Stub |
|
POSIX implementation specifics
In KasperskyOS, the specific implementation of some POSIX interfaces not entirely defined by the POSIX.1-2008 standard differs from the implementation of these interfaces in Linux and other UNIX-like operating systems. Information about these interfaces is provided in the table below.
POSIX interfaces and their implementation specifics
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Assign a name to a socket. |
When using a VFS version that supports only network operations, files of sockets in the |
|
|
Map to memory. |
Mapping more than 4 GB is not supported on hardware platforms running an AArch64 (ARM64) processor architecture. |
|
|
Read from a file. |
If the size of the |
|
Concurrently using POSIX and the libkos API
In a thread that is created using Pthreads, you cannot use the following libkos
APIs:
- event.h, mutex.h, rwlock.h, semaphore.h, condvar.h
- thread.h, thread_api.h
- dma.h
- ports.h
- mmio.h
- irq.h
The following libkos
APIs can be used together with Pthreads (and other POSIX APIs):
- handle_api.h
- notice_api.h
- task.h, task_api.h
- cm_api.h, ns_api.h
- queue.h
POSIX interfaces cannot be used in threads that were created using the thread.h and thread_api.h APIs.
The syscalls.h API can be used in any threads that were created using Pthreads or the thread.h and thread_api.h APIs.
Page top
Obtaining statistical data on the system
The libkos
and libc
libraries provide APIs for obtaining statistical data on the system. This data includes the following information:
- CPU time usage by the system and by an individual process
- Memory usage by the system and by an individual process
- Info on processes and threads
- Info on file systems and network interfaces
Obtaining statistical data on the system through the libkos library API
The libkos
library provides an API that obtains statistical data on CPU time and memory usage, and info on processes and threads. This API is defined in the header file sysroot-*-kos/include/coresrv/stat/stat_api.h
from the KasperskyOS SDK.
The API defined in the header file sysroot-*-kos/include/coresrv/stat/stat_api.h
from the KasperskyOS SDK includes functions that "wrap" the KnProfilerGetCounters()
function declared in the header file sysroot-*-kos/include/coresrv/profiler/profiler_api.h
from the KasperskyOS SDK. This function requests the values of performance counters. To get this statistical data, you need to build a solution with a KasperskyOS kernel version that supports performance counters (for details, see "Image library").
Receiving information about CPU time
Uptime of CPUs (processor cores) is counted from the startup of the KasperskyOS kernel.
To obtain information about CPU time usage, use the KnGroupStatGetParam()
, KnTaskStatGetParam()
and KnCpuStatGetParam()
functions. The values provided in the table below need to be passed in the param
parameter of these functions.
Information about CPU time
Function |
Value of the |
Obtained value |
---|---|---|
|
|
Uptime of all processors in kernel mode |
|
|
Uptime of all processors in user mode |
|
|
Uptime of all processors in idle mode |
|
|
Uptime of all processors used for execution of the defined process |
|
|
Uptime of all processors used for execution of the defined process in user mode |
|
|
Uptime of the defined processor in idle mode |
|
|
Uptime of the defined processor in user mode |
|
|
Uptime of the defined processor in kernel mode |
The CPU time obtained by calling the KnGroupStatGetParam()
, KnTaskStatGetParam()
or KnCpuStatGetParam()
function is presented in nanoseconds.
The CPU index (enumeration starts with zero) is the input parameter of the KnCpuStatGetParam()
function. To get the total number of processors on a hardware platform, use the KnHalGetCpuCount()
function declared in the header file sysroot-*-kos/include/coresrv/hal/hal_api.h
from the KasperskyOS SDK.
Receiving information about memory usage
To receive information about memory usage, you need to use the KnGroupStatGetParam()
and KnTaskStatGetParam()
functions. The values provided in the table below need to be passed in the param
parameter of these functions.
Information about memory usage
Function |
Value of the |
Obtained value |
---|---|---|
|
|
Size of all installed physical memory |
|
|
Size of free physical memory |
|
|
Size of physical memory with the "execution access" attribute |
|
|
Size of physical memory used as shared memory |
|
|
Size of physical memory used by the defined process |
|
|
Size of virtual memory of the defined process |
|
|
Size of virtual memory of the defined process mapped to shared physical memory |
|
|
Size of the page table of the defined process |
The memory size obtained by calling the KnGroupStatGetParam()
or KnTaskStatGetParam()
function is presented as the number of memory pages. The size of a memory page is 4 KB for all hardware platforms supported by KasperskyOS.
The size of physical memory used by a process refers only to the memory allocated directly for this process. For example, if the memory of a process is mapped to an MDL buffer created by another process, the size of this buffer is not included in this value.
Obtaining information on processes and threads
In addition to information about CPU time and memory usage, the KnGroupStatGetParam()
and KnTaskStatGetParam()
functions also let you obtain information on processes and threads. To receive this information, the values provided in the table below need to be passed through the param
parameter of these functions.
Information on processes and threads
Function |
Value of the |
Obtained value |
---|---|---|
|
|
Number of user processes (not counting the kernel process) |
|
|
Total number of threads (including kernel threads) |
|
|
ID of the parent process of the defined process (PPID) |
|
|
Priority of the initial thread of the defined process |
|
|
State of the defined process (according to the list of |
|
|
Size of the memory-loaded image of the program running in the context of the defined process, in bytes |
|
|
Time (in nanoseconds) between startup of the kernel and startup of the defined process |
|
|
Number of handles owned by the defined process |
|
|
Number of threads in the defined process |
In addition to the KnGroupStatGetParam()
and KnTaskStatGetParam()
functions, information on processes and threads can also be obtained by using the following functions:
KnTaskStatGetName()
– gets the name of the defined process.KnTaskStatGetPath()
– gets the name of the executable file in ROMFS that was used to start the defined process.This function can be used only if the process was started from an executable file in ROMFS. Otherwise, the function call will result in an empty string.
KnTaskStatGetId()
– gets the ID of the defined process (PID).KnProfilerGetCounters()
– gets the values of performance counters.For example, to get the number of kernel threads and the total number of handles, pass the
kl.core.Core.threads
andhandles.total
values through thenames
parameter.
Obtaining information on CPU time and memory usage by each process
To get information about CPU time and memory usage by each process, do the following:
- Get the list of processes by calling the
KnGroupStatGetTaskList()
function. - Get the number of items on the list of processes by calling the
KnTaskStatGetTasksCount()
function. - Iterate through the list of processes, repeating the following steps:
- Get an item from the list of processes by calling the
KnTaskStatEnumTaskList()
function. - Get the process name by calling the
KnTaskStatGetName()
function.This is necessary to identify the process for which the information about CPU time and memory usage will be received.
- Get information about CPU time and memory usage by calling the
KnTaskStatGetParam()
function. - Verify that the process was not terminated. If the process has terminated, discard the obtained information about the CPU time and memory used by this process.
To verify that the process was not terminated, you need to call the
KnTaskStatGetParam()
function, using theparam
parameter to pass theTASK_PARAM_STATE
value. A value other thanTaskStateTerminated
should be received. - Finish working with the item on the list of processes by calling the
KnTaskStatCloseTask()
function.
- Get an item from the list of processes by calling the
- Finish working with the list of processes by calling the
KnTaskStatCloseTaskList()
function.
Calculating CPU load
Indicators of load on CPUs (processor cores) may be the following values:
- Percent load of all processors
- Percent load of all processors by each process
- Percent load of each processor
These indicators are calculated for a specific time interval, at the start and end of which the information about CPU time utilization was received. (For example, CPU load can be monitored with periodic receipt of information about CPU time utilization.) The values obtained at the start of the interval need to be subtracted from the values obtained at the end of the interval. In other words, the following increments need to be obtained for the interval:
- TK – uptime of all processors in kernel mode.
- TKi [i=1,2,...,n] – uptime of the ith processor in kernel mode.
- TU – uptime of all processors in user mode.
- TUi [i=1,2,...,n] – uptime of the ith processor in user mode.
- TIDLE – uptime of all processors in idle mode.
- TIDLEi [i=1,2,...,n] – uptime of the ith processor in idle mode.
- Tj [j=1,2,...,m] – CPU time spent on execution of the jth process.
The percent load of all processors is calculated as follows:
(TK+TU)/(TK+TU+TIDLE).
The percent load of the ith processor is calculated as follows:
(TKi+TUi)/(TKi+TUi+TIDLEi).
The percent load of all processors caused by the jth process is calculated as follows:
Tj/(TK+TU+TIDLE).
Page top
Obtaining statistical data on the system through the libc library API
The libkos
library provides APIs that let you obtain statistical data on file systems and network interfaces managed by VFS. The functions of these APIs are presented in the table below.
Information on file systems and network interfaces
Function |
Header file from the KasperskyOS SDK |
Obtained information |
---|---|---|
|
|
File system information, such as the block size, number of blocks, and number of available blocks |
|
|
The information on all mounted file systems is identical to the information provided by the |
|
|
Information on network interfaces, such as their name, IP address, and subnet mask |
MessageBus component
The MessageBus
component implements the message bus that ensures receipt, distribution and delivery of messages between programs running KasperskyOS. This bus is based on the publisher-subscriber model. Use of a message bus lets you avoid having to create a large number of IPC channels to connect each subscriber program to each publisher program.
Messages transmitted through the MessageBus
cannot contain data. These messages can be used only to notify subscribers about events.
The MessageBus
component provides an additional level of abstraction over KasperskyOS IPC that helps simplify the development and expansion of your programs. MessageBus
is a separate program that is accessed through IPC. However, developers are provided with a MessageBus
access library that lets you avoid direct use of IPC calls.
The API of the access library provides the following interfaces:
IProviderFactory
- Provides factory methods for obtaining access to instances of all other interfaces.
IProviderControl
- Interface for registering and deregistering a publisher and subscriber in the bus.
IProvider (MessageBus component)
- Interface for transferring a message to the bus.
ISubscriber
- Callback interface for sending a message to a subscriber.
IWaiter
- Interface for waiting for a callback when the corresponding message appears.
Message structure
Each message contains two parameters:
topic
- Identifier of the message subject.
id
- Additional parameter that identifies a particular message.
The topic
and id
parameters are unique for each message. The interpretation of topic
+id
is determined by the contract between the publisher and subscriber. For example, if there are changes to the configuration data used by the publisher and subscriber, the publisher forwards a message regarding the modified data and the id
of the specific entry containing the new data. The subscriber uses mechanisms outside of the MessageBus
to receive the new data based on the id
key.
IProviderFactory interface
The IProviderFactory
interface provides factory methods for receiving the interfaces necessary for working with the MessageBus
component.
A description of the IProviderFactory
interface is provided in the file named messagebus/i_messagebus_control.h
.
An instance of the IProviderFactory
interface is obtained by using the free InitConnection()
function, which receives the name of the IPC channel between your application and the MessageBus
program. The connection name is defined in the init.yaml.in
file when describing the solution configuration. If the connection is successful, the output parameter contains a pointer to the IProviderFactory
interface.
- The interface for registering and deregistering (see "IProviderControl interface") publishers and subscribers in the message bus is obtained by using the
IProviderFactory::CreateBusControl()
method. - The interface containing the methods enabling the publisher to send messages to the bus (see "IProvider interface (MessageBus component)") is obtained by using the
IProviderFactory::CreateBus()
method. - The interfaces containing the methods enabling the subscriber to receive messages from the bus (see "ISubscriber, IWaiter and ISubscriberRunner interfaces") are obtained by using the
IProviderFactory::CreateCallbackWaiter
andIProviderFactory::CreateSubscriberRunner()
methods.It is not recommended to use the
IWaiter
interface, because calling a method of this interface is a locking call.
i_messagebus_control.h (fragment)
IProviderControl interface
The IProviderControl
interface provides the methods for registering and deregistering publishers and subscribers in the message bus.
A description of the IProviderControl
interface is provided in the file named messagebus/i_messagebus_control.h
.
The IProviderFactory
interface is used to obtain an interface instance.
Registering and deregistering a publisher
The IProviderControl::RegisterPublisher()
method is used to register the publisher in the message bus. This method receives the message subject and puts the unique ID of the bus client into the output parameter. If the message subject is already registered in the bus, the call will be declined and the client ID will not be filled.
The IProviderControl::UnregisterPublisher()
method is used to deregister a publisher in the message bus. This method accepts the bus client ID received during registration. If the indicated ID is not registered as a publisher ID, the call will be declined.
i_messagebus_control.h (fragment)
Registering and deregistering a subscriber
The IProviderControl::RegisterSubscriber()
method is used to register the subscriber in the message bus. This method accepts the subscriber name and the list of subjects of messages for the necessary subscription, and puts the unique ID of the bus client into the output parameter.
The IProviderControl::UnregisterSubscriber()
method is used to deregister a subscriber in the message bus. This method accepts the bus client ID received during registration. If the indicated ID is not registered as a subscriber ID, the call will be declined.
i_messagebus_control.h (fragment)
IProvider interface (MessageBus component)
The IProvider
interface provides the methods enabling the publisher to send messages to the bus.
A description of the IProvider
interface is provided in the file named messagebus/i_messagebus.h
.
The IProviderFactory
interface is used to obtain an interface instance.
Sending a message to the bus
The IProvider::Push()
method is used to send a message. This method accepts the bus client ID received during registration and the message ID. If the message queue in the bus is full, the call will be declined.
i_messagebus.h (fragment)
ISubscriber, IWaiter and ISubscriberRunner interfaces
The ISubscriber
, IWaiter
, and ISubscriberRunner
interfaces provide the methods enabling the subscriber to receive messages from the bus and process them.
Descriptions of the ISubscriber
, IWaiter
and ISubscriberRunner
interfaces are provided in the file named messagebus/i_subscriber.h
.
The IProviderFactory
interface is used to obtain instances of the IWaiter
and ISubscriberRunner
interfaces. The implementation of the ISubscriber
callback interface is provided by the subscriber application.
Receiving a message from the bus
You can use the IWaiter::Wait()
or ISubscriberRunner::Run()
method to switch a subscriber to standby mode, waiting for a message from the bus. These methods accept the bus client ID and the pointer to the ISubscriber
callback interface. If the client ID is not registered, the call will be declined.
It is not recommended to use the IWaiter
interface, because calling the IWaiter::Wait()
method is a locking call.
The ISubscriber::OnMessage()
method will be called when a message is received from the bus. This method accepts the message subject and message ID.
i_subscriber.h (fragment)
ExecutionManager component
The API is defined in the header files located in the directory sysroot-*-kos/include/component/execution_manager/
from the SDK.
The ExecutionManager component usage scenario is described in the article titled "Starting a process using the KasperskyOS API".
execution_manager_proxy.h interface
The API is defined in the header file sysroot-*-kos/include/component/execution_manager/kos_ipc/execution_manager_proxy.h
The interface contains the factory method CreateExecutionManager()
for getting the pointer to the instance of the IExecutionManager
interface that is required for working with the ExecutionManager component.
Usage example:
client.cpp
IExecutionManager interface
The API is defined in the header file sysroot-*-kos/include/component/execution_manager/i_execution_manager.h
The IExecutionManager
interface lets you access pointers to the following interfaces:
IApplicationController
– interface for starting and stopping processes.ISystemController
– interface for managing the system.
Usage example:
client.cpp
IApplicationController interface
The API is defined in the header file sysroot-*-kos/include/component/execution_manager/i_application_control.h
The IApplicationController
interface provides the following methods that let you change the state of a process:
StartEntity(
const std::filesystem::path& runPath,
const StartEntityInfo& info,
StartEntityResultInfo& resInfo
) – method for starting a process.RestartEntity(EntityId endId)
– method for restarting a previously started process.ShutdownEntity(EntityId entId)
– method for sending a termination signal to a process.StopEntity(EntityId entId)
– method for immediately stopping execution of a process.
The StartEntity()
method receives the path to the executable file that should be run and the structure containing the run parameters for the StartEntityInfo
process, and returns the structure containing the StartEntityResultInfo
process run results. All fields of the StartEntityInfo
structure are optional for initialization.
All other methods receive the EntityId
structure that identifies the started process.
Usage example:
client.cpp
ISystemController interface
The API is defined in the header file sysroot-*-kos/include/component/execution_manager/i_system_control.h
The ISystemController
interface provides the following method for system management:
StopAllEntities()
method stops all running processes, then terminates the ExecutionManager process, then sends a device shutdown request to the kernel.
Usage example:
client.cpp
Building a KasperskyOS-based solution
This section contains the following information:
- Description of the KasperskyOS-based solution build process.
- Descriptions of the scripts, libraries and build templates provided in KasperskyOS Community Edition.
- Information on how to use dynamic libraries in a KasperskyOS-based solution.
Building a solution image
A KasperskyOS-based solution consists of system software (including the KasperskyOS kernel and Kaspersky Security Module) and application software integrated for operation within a software/hardware system.
For more details, refer to Structure and startup of a KasperskyOS-based solution.
System programs and application software
Programs are divided into two types according to their purpose:
- System programs create the infrastructure for application software. For example, they facilitate hardware operations, support the IPC mechanism, and implement file systems and network protocols. System programs are included in KasperskyOS Community Edition. If necessary, you can develop your own system programs.
- Application software is designed for interaction with a solution user and for performing user tasks. Application software is not included in KasperskyOS Community Edition.
Building programs during the solution build process
During a solution build, programs are divided into the following two types:
- System programs provided as executable files in KasperskyOS Community Edition.
- System programs or application software that requires linking to an executable file.
Programs that require linking are divided into the following types:
- System programs that implement an IPC interface whose ready-to-use transport libraries are provided in KasperskyOS Community Edition.
- Application software that implements its own IPC interface. To build this software, transport methods and types need to be generated by using the NK compiler.
- Client programs that do not provide endpoints.
Building a solution image
KasperskyOS Community Edition provides an image of the KasperskyOS kernel and the executable files of some system programs and driver applications that are ready to use in a solution.
A specialized Einit program intended for starting all other programs, and a Kaspersky Security Module are built for each specific solution and are therefore not already provided in KasperskyOS Community Edition. Instead, the toolchain provided in KasperskyOS Community Edition includes the tools for building these resources.
The general step-by-step build scenario is described in the article titled Build process overview. A solution image can be built as follows:
- [Recommended] Using scripts of the
CMake
build system, which is provided in KasperskyOS Community Edition. - [Without CMake] Using other automated build systems or manually with scripts and compilers provided in KasperskyOS Community Edition.
Build process overview
To build a solution image, the following is required:
- Prepare EDL, CDL and IDL descriptions of applications, an init description file (
init.yaml
by default), and files containing a description of the solution security policy (security.psl
by default).When building with
CMake
, an EDL description can be generated by using thegenerate_edl_file()
command. - Generate *.edl.h files for all programs except the system programs provided in KasperskyOS Community Edition.
- When building with
CMake
, thenk_build_edl_files()
command is used for this purpose. - When building without
CMake
, the NK compiler must be used for this.
- When building with
- For programs that implement their own IPC interface, generate code of the transport methods and types that are used for generating, sending, receiving and processing IPC messages.
- When building with
CMake
, thenk_build_idl_files()
andnk_build_cdl_files()
commands are used for these purposes. - When building without
CMake
, the NK compiler must be used for this.
- When building with
- Build all programs that are part of the solution, and link them to the transport libraries of system programs or applications if necessary. To build applications that implement their own IPC interface, you will need the code containing transport methods and types that was generated at step 3.
- When building with
CMake
, standard build commands are used for this purpose. The necessary cross-compilation configuration is done automatically. - When building without
CMake
, the cross compilers included in KasperskyOS Community Edition must be manually used for this purpose.
- When building with
- Build the Einit initializing program.
- When building with
CMake
, theEinit
program is built during the solution image build process using thebuild_kos_qemu_image()
andbuild_kos_hw_image()
commands. - When building without
CMake
, the einit tool must be used to generate the code of theEinit
program. Then theEinit
application must be built using the cross compiler that is provided in KasperskyOS Community Edition.
- When building with
- Build the Kaspersky Security Module.
- When building with
CMake
, the security module is built during the solution image build process using thebuild_kos_qemu_image()
andbuild_kos_hw_image()
commands. - When building without
CMake
, themakekss
script must be used for this purpose.
- When building with
- Create the solution image.
- When building with
CMake
, thebuild_kos_qemu_image()
andbuild_kos_hw_image()
commands are used for this purpose. - When building without
CMake
, themakeimg
script must be used for this.
- When building with
Example 1
For the basic hello
example included in KasperskyOS Community Edition that contains one application that does not provide any services, the build scenario looks as follows:
Example 2
The echo
example included in KasperskyOS Community Edition describes a basic case of interaction between two programs via an IPC mechanism. To set up this interaction, you will need to implement an interface with the Ping
method on a server and put the Ping
service into a new component (for example, Responder
), and an instance of this component needs to be put into the EDL description of the Server
program.
If a solution contains programs that utilize an IPC mechanism, the build scenario looks as follows:
Using CMake from the contents of KasperskyOS Community Edition
To automate the process of preparing the solution image, you need to configure the CMake
build system. You can base this system on the build system parameters used in the examples from KasperskyOS Community Edition.
CMakeLists.txt
files use the standard CMake
syntax, and commands and macros from libraries provided in KasperskyOS Community Edition.
Recommended structure of project directories
When creating a KasperskyOS-based solution, it is recommended to use the following directory structure in a project:
- In the project root, create a CMakeLists.txt boot file containing the general build instructions for the entire solution.
- The source code of each program being developed should be placed into a separate directory within the
src
subdirectory. - Create CMakeLists.txt files for building each application in the corresponding directories.
- To generate the source code of the
Einit
program, you should create a separateeinit
directory containing thesrc
subdirectory in which you should put the init.yaml.in and security.psl.in templates.Any other files that need to be included in the solution image can also be put into this directory.
- Create a CMakeLists.txt file for building the
Einit
program in theeinit
directory. - The files of EDL, CDL and IDL descriptions should be put into the
resources
directory in the project root.
Example structure of project directories
Building a solution image
To build a solution image, you must use the cmake
tool (the toolchain/bin/cmake
executable file from KasperskyOS Community Edition).
Build script example:
build.sh
CMakeLists.txt root file
The CMakeLists.txt
boot file contains general build instructions for the entire solution.
The CMakeLists.txt
boot file must contain the following commands:
cmake_minimum_required (VERSION 3.25)
indicates the minimum supported version ofCMake
.For a KasperskyOS-based solution build,
CMake
version 3.25 or later is required.The required version of
CMake
is provided in KasperskyOS Community Edition and is used by default.include (platform)
connects theplatform
library ofCMake
.initialize_platform()
initializes theplatform
library.project_header_default("STANDARD_GNU_11:YES" "STRICT_WARNINGS:NO")
sets the flags of the compiler and linker.- [Optional] Connect and configure packages for the provided system programs and drivers that need to be included in the solution:
- A package is connected by using the
find_package()
command. - After connecting a package, you must add the package-related directories to the list of search directories by using the
include_directories()
command. - For some packages, you must also set the values of properties by using the
set_target_properties()
command.
CMake
descriptions of system programs and drivers provided in KasperskyOS Community Edition, and descriptions of their exported variables and properties are located in the corresponding files at/opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/lib/cmake/<program name>/<program name>-config.cmake
- A package is connected by using the
- The
Einit
initializing program must be built using theadd_subdirectory(einit)
command. - All applications to be built must be added by using the
add_subdirectory(<program directory name>)
command.
Example CMakeLists.txt boot file
CMakeLists.txt
CMakeLists.txt files for building applications
The CMakeLists.txt
file for building an application must contain the following commands:
include (platform/nk)
connects theCMake
library for working with the NK compiler.project_header_default ("STANDARD_GNU_11:YES" "STRICT_WARNINGS:NO"
) sets the flags of the compiler and linker.- An EDL description of a process class for a program can be generated by using the
generate_edl_file()
command. - If the program provides endpoints using an IPC mechanism, the following transport code must be generated:
- idl.h files are generated by the
nk_build_idl_files()
command - cdl.h files are generated by the
nk_build_cdl_files()
command - edl.h files are generated by the
nk_build_edl_files()
command
- idl.h files are generated by the
add_executable (<program name> "<path to the file containing the program source code>")
adds the program build target.add_dependencies (<program name> <name of the edl.h file build target>
) adds a program build dependency on edl.h file generation.target_link_libraries (<program name> <list of libraries>)
determines the libraries that need to be linked with the program during the build.For example, if the program uses file I/O or network I/O, it must be linked with the
${vfs_CLIENT_LIB}
transport library.CMake
descriptions of system programs and drivers provided in KasperskyOS Community Edition, and descriptions of their exported variables and properties are located in the corresponding files at/opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/lib/cmake/<program name>/<program name>-config.cmake
- To automatically add descriptions of IPC channels to the
init.yaml
file when building a solution, you must define theEXTRA_CONNECTIONS
property and assign it a value with descriptions of the relevant IPC channels.Please note the indentations at the beginning of strings in the
EXTRA_CONNECTIONS
property. These indentations are necessary to correctly insert values into theinit.yaml
file and must comply with its syntax requirements.Example of creating an IPC channel between a
Client
process and aServer
process:set_target_properties (Client PROPERTIES EXTRA_CONNECTIONS " - target: Server id: server_connection")When building this solution, the description of this IPC channel will be automatically added to the
init.yaml
file when processing macros of the init.yaml.in template. - To automatically add a list of arguments for the
main()
function and a dictionary of environment variables to theinit.yaml
file when building a solution, you must define theEXTRA_ARGS
andEXTRA_ENV
properties and assign the appropriate values to them.Note the indentations at the beginning of strings in the
EXTRA_ARGS
andEXTRA_ENV
properties. These indentations are necessary to correctly insert values into theinit.yaml
file and must comply with its syntax requirements.Example of sending the
Client
program the"-v"
argument of themain()
function and the environment variableVAR1
set toVALUE1
:set_target_properties (Client PROPERTIES EXTRA_ARGS " - \"-v\"" EXTRA_ENV " VAR1: VALUE1")When building this solution, the description of the
main()
function argument and the environment variable value will be automatically added to theinit.yaml
file when processing macros of the init.yaml.in template.
Example CMakeLists.txt file for building a simple application
CMakeLists.txt
CMakeLists.txt file for building the Einit program
The CMakeLists.txt
file for building the Einit
initializing program must contain the following commands:
include (platform/image)
connects theCMake
library that contains the solution image build scripts.project_header_default ("STANDARD_GNU_11:YES" "STRICT_WARNINGS:NO"
) sets the flags of the compiler and linker.- Configure the packages of system programs and drivers that need to be included in the solution.
- A package is connected by using the
find_package ()
command. - For some packages, you must also set the values of properties by using the
set_target_properties ()
command.
CMake
descriptions of system programs and drivers provided in KasperskyOS Community Edition, and descriptions of their exported variables and properties are located in the corresponding files at/opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/lib/cmake/<program name>/<program name>-config.cmake
- A package is connected by using the
- To automatically add descriptions of IPC channels between processes of system programs to the
init.yaml
file when building a solution, you must add these channels to theEXTRA_CONNECTIONS
property for the corresponding programs.Please note the indentations at the beginning of strings in the
EXTRA_CONNECTIONS
property. These indentations are necessary to correctly insert values into theinit.yaml
file and must comply with its syntax requirements.For example, the
VFS
program does not have a channel for connecting to theEnv
program by default. To automatically add a description of this channel to the init.yaml file during a solution build, you must add the following call to theCMakeLists.txt
file for building theEinit
program:set_target_properties (${vfs_ENTITY} PROPERTIES EXTRA_CONNECTIONS " - target: env.Env id: {var: ENV_SERVICE_NAME, include: env/env.h}"When building this solution, the description of this IPC channel will be automatically added to the
init.yaml
file when processing macros of the init.yaml.in template. - To automatically add a list of arguments for the
main()
function and a dictionary of environment variables to theinit.yaml
file when building a solution, you must define theEXTRA_ARGS
andEXTRA_ENV
properties and assign the appropriate values to them.Note the indentations at the beginning of strings in the
EXTRA_ARGS
andEXTRA_ENV
properties. These indentations are necessary to correctly insert values into theinit.yaml
file and must comply with its syntax requirements.
Example of sending the VfsEntity
program the "-f fstab"
argument of the main()
function and the environment variable ROOTFS
set to ramdisk0,0 / ext2 0
:
When building this solution, the description of the main()
function argument and the environment variable value will be automatically added to the init.yaml
file when processing macros of the init.yaml.in template.
set(ENTITIES <full list of programs included in the solution>)
defines theENTITIES
variable containing a list of executable files of all programs included in the solution.- One or both commands for building the solution image:
- build_kos_hw_image() creates the target for building a solution image for the hardware platform.
- build_kos_qemu_image() creates the target for building a solution image for QEMU.
Example CMakeLists.txt file for building the Einit program
CMakeLists.txt
init.yaml.in template
The init.yaml.in
template is used to automatically generate a part of the init.yaml
file prior to building the Einit
program using CMake
tools.
When using the init.yaml.in
template, you do not have to manually add descriptions of system programs and the IPC channels for connecting to them to the init.yaml
file.
The init.yaml.in
template must contain the following data:
- Root
entities
key. - List of all applications included in the solution.
- For applications that use an IPC mechanism, you must specify a list of IPC channels that connect this application to other applications.
The IPC channels that connect this application to other applications are either indicated manually or specified in the CMakeLists.txt file for this application using the
EXTRA_CONNECTIONS
property.To specify a list of IPC channels that connect this application to system programs that are included in KasperskyOS Community Edition, the following macros are used:
@INIT_<program name>_ENTITY_CONNECTIONS@
– during the build, this is replaced with the list of IPC channels containing all system programs that are linked to the application. Thetarget
andid
fields are filled according to theconnect.yaml
files from KasperskyOS Community Edition located in/opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/<system program name>
).This macro needs to be used if the application does not have connections to other applications but instead connects only to system programs. This macro adds the root
connections
key.@INIT_<program name>_ENTITY_CONNECTIONS+@
– during the build, the list of IPC channels containing all system programs that are linked to the application is added to the manually defined list of IPC channels. This macro does not add the rootconnections
key.This macro needs to be used if the application has connections to other applications that were manually indicated in the
init.yaml.in
template.
- The
@INIT_<program name>_ENTITY_CONNECTIONS@
and@INIT_<program name>_ENTITY_CONNECTIONS+@
macros also add the list of connections for each program defined in theEXTRA_CONNECTIONS
property when building this program. - If you need to pass
main()
function arguments defined in theEXTRA_ARGS
property to a program when building this program, you need to use the following macros:@INIT_<program name>_ENTITY_ARGS@
– during the build, this is replaced with the list of arguments of themain()
function defined in theEXTRA_ARGS
property. This macro adds the rootargs
key.@INIT_<program name>_ENTITY_ARGS+@
– during the build, this macro adds the list ofmain()
function arguments defined in theEXTRA_ARGS
property to the list of manually defined arguments. This macro does not add the rootargs
key.
- If you need to pass the values of environment variables defined in the
EXTRA_ENV
property to a program when building this program, you need to use the following macros:@INIT_<program name>_ENTITY_ENV@
– during the build, this is replaced with the dictionary of environment variables and their values defined in theEXTRA_ENV
property. This macro adds the rootenv
key.@INIT_<program name>_ENTITY_ENV+@
– during the build, this macro adds the dictionary of environment variables and their values defined in theEXTRA_ENV
property to the manually defined variables. This macro does not add the rootenv
key.
@INIT_EXTERNAL_ENTITIES@
– during the build, this macro is replaced with the list of system programs linked to the application and their IPC channels,main()
function arguments, and values of environment variables.
Example init.yaml.in template
init.yaml.in
When building the Einit
program from this template, the following init.yaml file will be generated:
init.yaml
security.psl.in template
The security.psl.in
template is used to automatically generate a part of the security.psl
file prior to building the Einit
program using CMake
tools.
The security.psl
file contains part of the solution security policy description.
When using the security.psl.in
template, you do not have to manually add EDL descriptions of system programs to the security.psl
file.
The security.psl.in
template must contain a manually created solution security policy description, including the following declarations:
- Set the global parameters of a solution security policy
- Include PSL files in a solution security policy description
- Include EDL files of application software in a solution security policy description
- Create security model objects
- Bind methods of security models to security events
- Create security audit profiles
To automatically include system programs, the @INIT_EXTERNAL_ENTITIES@
macro must be used.
Example security.psl.in template
security.psl.in
CMake libraries in KasperskyOS Community Edition
This section contains a description of the libraries that are provided in KasperskyOS Community Edition for automatically building a KasperskyOS-based solution.
platform library
The platform
library contains the following commands:
initialize_platform()
is the command for initializing theplatform
library.The
initialize_platform()
command can be called with theFORCE_STATIC
parameter, which enables forced static linking of executable files:- By default, if the toolchain in the KasperskyOS SDK supports dynamic linking, the
initialize_platform()
command causes the-rdynamic
flag to be used automatically for building all executable files defined viaCMake
add_executable()
commands. - When calling
initialize_platform (FORCE_STATIC)
in theCMakeLists.txt
root file, the toolchain supporting dynamic linking performs static linking of executable files.
The
initialize_platform()
command can be called with theNO_NEW_VERSION_CHECK
parameter, which disables the check for SDK updates and transmission of the SDK version to the Kaspersky server.To disable the check for SDK updates and transmission of SDK version data to the Kaspersky server, use the following call during the solution build:
initialize_platform(NO_NEW_VERSION_CHECK)
. For more details about the data provision policy, see Data provision.- By default, if the toolchain in the KasperskyOS SDK supports dynamic linking, the
project_static_executable_header_default()
is the command for enabling forced static linking of executable files defined via subsequentCMake
add_executable()
commands in oneCMakeLists.txt
file. The toolchain that supports dynamic linking performs static linking of these executable files.platform_target_force_static()
is the command for enabling forced static linking of an executable file defined via theCMake
add_executable()
command. The toolchain that supports dynamic linking performs static linking of this executable file. For example, if theCMake
commandsadd_executable(client "src/client.c")
andplatform_target_force_static(client)
are called, static linking is performed for theclient
program.project_header_default()
is the command for setting compile flags.Command parameters are defined in pairs consisting of a compile flag and its value:
"FLAG_1:VALUE_1"
"FLAG_2:VALUE_2"
..."FLAG_N:VALUE_N"
. TheCMake
platform
library converts these pairs into compiler parameters. Frequently used compile flags for C and C++ compilers from the GCC set and the values of these flags are presented in the table below.Compile flags
YES
valueNO
valueDefault value
STANDARD_ANSI
The ISO C90 and 1998 ISO C++ standards are used.
For C and C++ compilers, the value is converted into the parameter
-ansi
.The ISO C90 and 1998 ISO C++ standards are not used.
STANDARD_ANSI:NO
STANDARD_C99
The ISO C99 standard is used.
For a C compiler, the value is converted into the parameter
-std=c99
.The ISO C99 standard is not used.
STANDARD_C99:NO
STANDARD_GNU_C99
The ISO C99 standard with GNU extensions is used.
For a C compiler, the value is converted into the parameter
-std=gnu99
.The ISO C99 standard with GNU extensions is not used.
STANDARD_GNU_C99:NO
STANDARD_11
The ISO C11 and 2011 ISO C++ standards are used.
For a C compiler, the value is converted into the parameter
-std=c11
or-std=c1x
depending on the compiler version.For a C++ compiler, the value is converted into the parameter
-std=c++11
or-std=c++0x
depending on the compiler version.The ISO C11 and 2011 ISO C++ standards are not used.
STANDARD_11:NO
STANDARD_GNU_11
The ISO C11 and 2011 ISO C++ standards with GNU extensions are used.
For a C compiler, the value is converted into the parameter
-std=gnu1x
or-std=gnu11
depending on the compiler version.For a C++ compiler, the value is converted into the parameter
-std=gnu++0x
or-std=gnu++11
depending on the compiler version.The ISO C11 and 2011 ISO C++ standards with GNU extensions are not used.
STANDARD_GNU_11:NO
STANDARD_14
The 2014 ISO C++ standard is used.
For a C++ compiler, the value is converted into the parameter
-std=c++14
.The 2014 ISO C++ standard is not used.
STANDARD_14:NO
STANDARD_GNU_14
The 2014 ISO C++ standard with GNU extensions is used.
For a C++ compiler, the value is converted into the parameter
-std=gnu++14
.The 2014 ISO C++ standard with GNU extensions is not used.
STANDARD_GNU_14:NO
STANDARD_17
The ISO C17 and 2017 ISO C++ standards are used.
For a C compiler, the value is converted into the parameter
-std=c17
.For a C++ compiler, the value is converted into the parameter
-std=c++17
.The ISO C17 and 2017 ISO C++ standards are not used.
STANDARD_17:NO
STANDARD_GNU_17
The ISO C17 and 2017 ISO C++ standards with GNU extensions are used.
For a C compiler, the value is converted into the parameter
-std=gnu17
.For a C++ compiler, the value is converted into the parameter
-std=gnu++17
.The ISO C17 and 2017 ISO C++ standards with GNU extensions are not used.
STANDARD_GNU_17:NO
STRICT_WARNINGS
Warnings are enabled for the detection of potential issues and errors in code written in C and C++.
For C and C++ compilers, the value is converted into the following parameters:
-Wcast-qual
,-Wcast-align
,-Wundef
.For a C compiler, the parameter
-Wmissing-prototypes
is additionally used.Warnings are disabled.
STRICT_WARNINGS:YES
If compiler flags in the format
STANDART_*
are not defined through command parameters, the parameterSTANDARD_GNU_17:YES
is used by default.
When using the initialize_platform(FORCE_STATIC)
, project_static_executable_header_default()
and platform_target_force_static()
commands, you may encounter linking errors if the static variants of the required libraries are missing (for example, if they were not built or are not included in the KasperskyOS SDK). Even if the static variants of the required libraries are available, these errors may still occur because the build system searches for the dynamic variants of required libraries by default instead of the expected static variants when using the initialize_platform(FORCE_STATIC)
, project_static_executable_header_default()
and platform_target_force_static()
commands. To avoid errors, first make sure that the static variants are available. Then configure the build system to search for static libraries (although this capability may not be available for some libraries), or explicitly define linking with static libraries.
Examples of configuring the build system to search for static libraries:
Example that explicitly defines linking with a static library:
For more details about using dynamic libraries, see "Using dynamic libraries".
These commands are used in CMakeLists.txt
files for the Einit program and application software.
nk library
This section contains a description of the commands and macros of the CMake
library for working with the NK compiler.
generate_edl_file()
This command is declared in the file /opt/KasperskyOS-Community-Edition-<version>toolchain/share/cmake/Modules/platform/nk2.cmake
.
This command generates an EDL file containing a description of the process class.
Parameters:
NAME
– name of the EDL file being created. Required parameter.PREFIX
– parameter in which you need to specify the name of the process class, excluding the name of the EDL file. For example, if the name of the process class for which the EDL file is being created is defined askl.core.NameServer
, thePREFIX
parameter must pass the valuekl.core
.EDL_COMPONENTS
– name of the component and its instance that will be included in the EDL file. For example:EDL_COMPONENTS "env: kl.Env"
. To include multiple components, you need to use multipleEDL_COMPONENTS
parameters.SECURITY
– qualified name of the security interface method that will be included in the EDL file.OUTPUT_DIR
– directory in which the EDL file will be created. The default directory is${CMAKE_CURRENT_BINARY_DIR}
.
As a result of this command, the EDL_FILE
variable is exported and contains the path to the generated EDL file.
Example call:
For an example of using this command, see the article titled "CMakeLists.txt files for building application software".
Page top
nk_build_idl_files()
This command is declared in the file /opt/KasperskyOS-Community-Edition-<version>toolchain/share/cmake/Modules/platform/nk2.cmake
.
This command creates a CMake
target for generating .idl.h
files for one or more defined IDL files using the NK compiler.
Parameters:
NAME
– name of theCMake
target for building.idl.h
files. If a target has not yet been created, it will be created by usingadd_library()
with the specified name. Required parameter.NOINSTALL
– if this option is specified, files will only be generated in the working directory and will not be installed in global directories:${CMAKE_BINARY_DIR}/_headers_ ${CMAKE_BINARY_DIR}/_headers_/${PROJECT_NAME}
.NK_MODULE
– parameter in which you need to specify the package name, excluding the name of the IDL file. For example, if the package name in the IDL description is defined askl.core.NameServer
, thekl.core
value must be passed in theNK_MODULE
parameter.WORKING_DIRECTORY
– working directory for calling the NK compiler, which is${CMAKE_CURRENT_BINARY_DIR}
by default.DEPENDS
– additional build targets on which the IDL file depends.To add multiple targets, you need to use multiple
DEPENDS
parameters.IDL
– path to the IDL file for which the idl.h file is being generated. Required parameter.To add multiple IDL files, you need to use multiple
IDL
parameters.If one IDL file imports another IDL file, idl.h files need to be generated in the order necessary for compliance with dependencies (with the most deeply nested first).
NK_FLAGS
– additional flags for the NK compiler.
Example call:
For an example of using this command, see the article titled "CMakeLists.txt files for building application software".
Page top
nk_build_cdl_files()
This command is declared in the file /opt/KasperskyOS-Community-Edition-<version>toolchain/share/cmake/Modules/platform/nk2.cmake
.
This command creates a CMake
target for generating .cdl.h
files for one or more defined CDL files using the NK compiler.
Parameters:
NAME
– name of theCMake
target for building.cdl.h
files. If a target has not yet been created, it will be created by usingadd_library()
with the specified name. Required parameter.NOINSTALL
– if this option is specified, files will only be generated in the working directory and are not installed in global directories:${CMAKE_BINARY_DIR}/_headers_ ${CMAKE_BINARY_DIR}/_headers_/${PROJECT_NAME}
.IDL_TARGET
– target when building.idl.h
files for IDL files containing descriptions of endpoints provided by components described in CDL files.NK_MODULE
– parameter in which you need to specify the component name, excluding the name of the CDL file. For example, if the component name in the CDL description is defined askl.core.NameServer
, thekl.core
value must be passed in theNK_MODULE
parameter.WORKING_DIRECTORY
– working directory for calling the NK compiler, which is${CMAKE_CURRENT_BINARY_DIR}
by default.DEPENDS
– additional build targets on which the CDL file depends.To add multiple targets, you need to use multiple
DEPENDS
parameters.CDL
– path to the CDL file for which the.cdl.h
file is being generated. Required parameter.To add multiple CDL files, you need to use multiple
CDL
parameters.NK_FLAGS
– additional flags for the NK compiler.
Example call:
For an example of using this command, see the article titled "CMakeLists.txt files for building application software".
Page top
nk_build_edl_files()
This command is declared in the file /opt/KasperskyOS-Community-Edition-<version>toolchain/share/cmake/Modules/platform/nk2.cmake
.
This command creates a CMake
target for generating an .edl.h
file for one defined EDL file using the NK compiler.
Parameters:
NAME
– name of theCMake
target for building an.edl.h
file. If a target has not yet been created, it will be created by usingadd_library()
with the specified name. Required parameter.NOINSTALL
– if this option is specified, files will only be generated in the working directory and are not installed in global directories:${CMAKE_BINARY_DIR}/_headers_ ${CMAKE_BINARY_DIR}/_headers_/${PROJECT_NAME}
.CDL_TARGET
– target when building.cdl.h
files for CDL files containing descriptions of components of the EDL file for which the build is being performed.IDL_TARGET
– target when building .idl.h files for IDL files containing descriptions of interfaces of the EDL file for which the build is being performed.NK_MODULE
– parameter in which you need to specify the name of the process class, excluding the name of the EDL file. For example, if the process class name in the EDL description is defined askl.core.NameServer
, thekl.core
value must be passed in theNK_MODULE
parameter.WORKING_DIRECTORY
– working directory for calling the NK compiler, which is${CMAKE_CURRENT_BINARY_DIR}
by default.DEPENDS
– additional build targets on which the EDL file depends.To add multiple targets, you need to use multiple
DEPENDS
parameters.EDL
– path to the EDL file for which the edl.h file is being generated. Required parameter.NK_FLAGS
– additional flags for the NK compiler.
Example calls:
For an example of using this command, see the article titled "CMakeLists.txt files for building application software".
Page top
Generating transport code for development in C++
The CMake
commands add_nk_idl(), add_nk_cdl() and add_nk_edl() are used to generate transport proxy objects and stubs using the nkppmeta
compiler when building a solution.
add_nk_idl()
This command is declared in the file /opt/KasperskyOS-Community-Edition-<version>/toolchain/share/cmake/Modules/platform/nk2.cmake
.
This command creates a CMake
target for generating a *.idl.cpp.h
header file for a defined IDL file using the nkppmeta
compiler. The command also creates a library containing the transport code for the defined interface. To link to this library, use the bind_nk_targets()
command.
The generated header files contain a C++ representation for the interface and data types described in the IDL file, and the methods required for use of proxy objects and stubs.
Parameters:
NAME
— name of theCMake
target. Required parameter.IDL_FILE
— path to the IDL file. Required parameter.NK_MODULE
– parameter in which you need to specify the package name, excluding the name of the IDL file. For example, if the package name in the IDL description is defined askl.core.NameServer
, thekl.core
value must be passed in theNK_MODULE
parameter.LANG
– parameter in which you need to specify theCXX
value.
Example call:
add_nk_cdl()
This command is declared in the file /opt/KasperskyOS-Community-Edition-<version>/toolchain/share/cmake/Modules/platform/nk2.cmake
.
This command creates a CMake
target for generating a *.cdl.cpp.h
file for a defined CDL file using the nkppmeta
compiler. The command also creates a library containing the transport code for the defined component. To link to this library, use the bind_nk_targets()
command.
The *.cdl.cpp.h
file contains the tree of embedded components and endpoints provided by the component described in the CDL file.
Parameters:
NAME
— name of theCMake
target. Required parameter.CDL_FILE
— path to the CDL file. Required parameter.NK_MODULE
– parameter in which you need to specify the component name, excluding the name of the CDL file. For example, if the component name in the CDL description is defined askl.core.NameServer
, thekl.core
value must be passed in theNK_MODULE
parameter.LANG
– parameter in which you need to specify theCXX
value.
Example call:
add_nk_edl()
This command is declared in the file /opt/KasperskyOS-Community-Edition-<version>/toolchain/share/cmake/Modules/platform/nk2.cmake
.
This command creates a CMake
target for generating a *.edl.cpp.h
file for a defined EDL file using the nkppmeta
compiler. The command also creates a library containing the transport code for the server or client program. To link to this library, use the bind_nk_targets()
command.
The *.edl.cpp.h
file contains the tree of embedded components and endpoints provided by the process class described in the EDL file.
Parameters:
NAME
— name of theCMake
target. Required parameter.EDL_FILE
— path to the EDL file. Required parameter.NK_MODULE
– parameter in which you need to specify the name of the process class, excluding the name of the EDL file. For example, if the process class name in the EDL description is defined askl.core.NameServer
, thekl.core
value must be passed in theNK_MODULE
parameter.LANG
– parameter in which you need to specify theCXX
value.
Example call:
image library
This section contains a description of the commands and macros of the CMake
library named image
that is included in KasperskyOS Community Edition and contains solution image build scripts.
build_kos_qemu_image()
This command is declared in the file /opt/KasperskyOS-Community-Edition-<version>toolchain/share/cmake/Modules/platform/image.cmake
.
The command creates a CMake
target for building a solution image for QEMU.
Parameters:
NAME
– name of theCMake
target for building a solution image. Required parameter.PERFCNT_KERNEL
– use the kernel with performance counters if it is available in KasperskyOS Community Edition.EINIT_ENTITY
– name of the executable file that will be used to start theEinit
program.EXTRA_XDL_DIR
– additional directories to include when building theEinit
program.CONNECTIONS_CFG
– path to theinit.yaml
file or init.yaml.in template.SECURITY_PSL
– path to thesecurity.psl
file or security.psl.in template.KLOG_ENTITY
– target for building theKlog
system program, which is responsible for the security audit. If the target is not specified, the audit is not performed.QEMU_FLAGS
– additional flags for running QEMU.IMAGE_BINARY_DIR_BIN
– directory for the final image and other artifacts. It matchesCMAKE_CURRENT_BINARY_DIR
by default.NO_AUTO_BLOB_CONTAINER
– solution image will not include theBlobContainer
program that is required for working with dynamic libraries in shared memory. For more details, refer to "Including the BlobContainer system program in a KasperskyOS-based solution".PACK_DEPS
,PACK_DEPS_COPY_ONLY
,PACK_DEPS_LIBS_PATH
, andPACK_DEPS_COPY_TARGET
– parameters that define the method used to add dynamic libraries to the solution image.IMAGE_FILES
– executable files of applications and system programs (except theEinit
program) and any other files to be added to the ROMFS image.To add multiple applications or files, you can use multiple
IMAGE_FILES
parameters.<path to files>
– free parameters likeIMAGE_FILES
.
Example call:
For an example of using this command, see the article titled "CMakeLists.txt files for building the Einit program".
Page top
build_kos_hw_image()
This command is declared in the file /opt/KasperskyOS-Community-Edition-<version>toolchain/share/cmake/Modules/platform/image.cmake
.
The command creates a CMake
target for building a solution image for the hardware platform.
Parameters:
NAME
– name of theCMake
target for building a solution image. Required parameter.PERFCNT_KERNEL
– use the kernel with performance counters if it is available in KasperskyOS Community Edition.EINIT_ENTITY
– name of the executable file that will be used to start theEinit
program.EXTRA_XDL_DIR
– additional directories to include when building theEinit
program.CONNECTIONS_CFG
– path to the init.yaml file or init.yaml.in template.SECURITY_PSL
– path to the security.psl file or security.psl.in template.KLOG_ENTITY
– target for building theKlog
system program, which is responsible for the security audit. If the target is not specified, the audit is not performed.IMAGE_BINARY_DIR_BIN
– directory for the final image and other artifacts. The default directory isCMAKE_CURRENT_BINARY_DIR
.NO_AUTO_BLOB_CONTAINER
– solution image will not include theBlobContainer
program that is required for working with dynamic libraries in shared memory. For more details, refer to "Including the BlobContainer system program in a KasperskyOS-based solution".PACK_DEPS
,PACK_DEPS_COPY_ONLY
,PACK_DEPS_LIBS_PATH
, andPACK_DEPS_COPY_TARGET
– parameters that define the method used to add dynamic libraries to the solution image.IMAGE_FILES
– executable files of applications and system programs (except theEinit
program) and any other files to be added to the ROMFS image.To add multiple applications or files, you can use multiple
IMAGE_FILES
parameters.<path to files>
– free parameters likeIMAGE_FILES
.
Example call:
For an example of using this command, see the article titled "CMakeLists.txt files for building the Einit program".
Page top
Building without CMake
This section contains a description of the scripts, tools, compilers and build templates provided in KasperskyOS Community Edition.
These tools can be used:
- In other build systems.
- To perform individual steps of the build.
- To analyze the build specifications and write a custom build system.
The general scenario for building a solution image is described in the article titled Build process overview.
Tools for building a solution
This section contains a description of the scripts, tools, compilers and build templates provided in KasperskyOS Community Edition.
Build scripts and tools
KasperskyOS Community Edition includes the following build scripts and tools:
- nk-gen-c
The NK compiler (
nk-gen-c
) generates transport code based on the IDL, CDL, and EDL descriptions. Transport code is needed for generating, sending, receiving, and processing IPC messages. - nk-psl-gen-c
The
nk-psl-gen-c
compiler generates the C-language source code of the Kaspersky Security Module based on the solution security policy description and the IDL, CDL, and EDL descriptions. Thenk-psl-gen-c
compiler also generates the C-language source code of solution security policy tests based on solution security policy tests in PAL. - einit
The
einit
tool automates the creation of code for theEinit
initializing program. This program is the first to start when KasperskyOS is loaded. Then it starts all other programs and creates IPC channels between them. - makekss
The
makekss
script creates the Kaspersky Security Module. - makeimg
The
makeimg
script creates the final boot image of the KasperskyOS-based solution with all programs to be started and the Kaspersky Security Module.
nk-gen-c
The NK compiler (nk-gen-c
) generates transport code based on the IDL, CDL, and EDL descriptions.
The nk-gen-c
compiler receives the IDL, CDL or EDL file and creates the following files:
- A
*.*dl.h
file containing the transport code. - A
*.*dl.nk.d
file that lists the created*.*dl.h
file's dependencies on the IDL and CDL files. The*.*dl.nk.d
file is created for the build system.
Syntax of the shell command for starting the nk-gen-c
compiler:
Basic parameters:
FILE
Path to the IDL, CDL, or EDL file for which you need to generate transport code.
-I
<PATH
>These parameters are used to define the paths to directories containing the auxiliary files required for generating transport code. (The auxiliary files are located in the
sysroot-*-kos/include
directory from the KasperskyOS SDK.) These parameters can also be used to define the paths to directories containing IDL and CDL files that are referenced by the file defined via theFILE
parameter.-o
<PATH
>Path to an existing directory where the created files will be placed. If this parameter is not specified, the created files will be put into the current directory.
-h
|--help
Prints the Help text.
--version
Prints the version of the
nk-gen-c
compiler.--extended-errors
This parameter provides the capability to use interface methods with one or more error parameters of user-defined IDL types. (The client works with error parameters like it works with output parameters.)
If the
--extended-errors
parameter is not specified, you can use interface methods only with onestatus
error parameter of the IDL typeUInt16
whose value is passed to the client via return code of the interface method. This mechanism is obsolete and will no longer be supported in the future, so you are advised to always specify the--extended-errors
parameter.
Selective generation of transport code
To reduce the volume of generated transport code, you can use flags for selective generation of transport code. For example, you can use the --server
flag for programs that implement endpoints, and use the --client
flag for programs that utilize the endpoints.
If no selective generation flag for transport code is specified, the nk-gen-c
compiler generates transport code with all possible methods and types for the defined IDL, CDL, or EDL file.
Flags for selective generation of transport code for an IDL file:
--types
The transport code includes the types corresponding to the IDL types from the defined IDL file, and the types corresponding to this file's imported IDL types that are used in IDL types of the defined IDL file. However, the types corresponding to imported IDL constants and to the aliases of imported IDL types are not included in the
*.idl.h
file. To use the types corresponding to imported IDL constants and to the aliases of imported IDL types, you must separately generate the transport code for the IDL files from which you are importing.--interface
The transport code corresponds to the
--types
flag, and includes the types of structures of the constant part of IPC requests and IPC responses for interface methods whose signatures are specified in the defined IDL file. In addition, the transport code contains constants indicating the sizes of IPC message arenas.--client
The transport code corresponds to the
--interface
flag, and includes the proxy object type, proxy object initialization method, and the interface methods specified in the defined IDL file.--server
The transport code corresponds to the
--interface
flag, and includes the types and dispatcher (dispatch method) used to process IPC requests corresponding to the interface methods specified in the defined IDL file.
Flags for selective generation of transport code for a CDL or EDL file:
--types
The transport code includes the types corresponding to the IDL types that are used in the method parameters of endpoints provided by the component (for the defined CDL file) or process class (for the defined EDL file).
--endpoints
The transport code corresponds to the
--types
flag, and includes the types of structures of the constant part of IPC requests and IPC responses for the methods of endpoints provided by the component (for the defined CDL file) or process class (for the defined EDL file). In addition, the transport code contains constants indicating the sizes of IPC message arenas.--client
The transport code corresponds to the
--types
flag, and includes the types of structures of the constant part of IPC requests and IPC responses for the methods of endpoints provided by the component (for the defined CDL file) or process class (for the defined EDL file). In addition, the transport code contains constants indicating the sizes of IPC message arenas, and the proxy object types, proxy object initialization methods, and methods of endpoints provided by the component (for the defined CDL file) or process class (for the defined EDL file).--server
The transport code corresponds to the
--types
flag, and includes the types and dispatchers (dispatch methods) used to process IPC requests corresponding to the endpoints provided by the component (for the defined CDL file) or process class (for the defined EDL file). In addition, the transport code contains constants indicating the sizes of IPC message arenas, and the stub types, stub initialization methods, and the types of structures of the constant part of IPC requests and IPC responses for the methods of endpoints provided by the component (for the defined CDL file) or process class (for the defined EDL file).
Printing diagnostic information about sending and receiving IPC messages
Transport code can generate diagnostic information about sending and receiving IPC messages and print this data via standard error. To generate transport code with these capabilities, use the following parameters:
--trace-client-ipc
{headers
|dump
}The code for printing diagnostic information is executed directly before the
Call()
system call is executed and immediately after it is executed. If theheaders
value is specified, the diagnostic information includes the endpoint method ID (MID), the endpoint ID (RIID), the size of the constant part of the IPC message (in bytes), the contents of the IPC message arena handle, the size of the IPC message arena (in bytes), and the size of the utilized part of the IPC message arena (in bytes). If thedump
value is specified, the diagnostic information additionally includes the contents of the constant part and arena of the IPC message in hexadecimal format.When using this parameter, you must either specify the selective generation flag for transport code
--client
or refrain from specifying a selective generation flag for transport code.--trace-server-ipc
{headers
|dump
}The code for printing diagnostic information is executed directly before calling the function that implements the interface method, and immediately after the completion of this function. In other words, it is executed when the dispatcher (dispatch method) is called in the interval between execution of the
Recv()
andReply()
system calls. If theheaders
value is specified, the diagnostic information includes the endpoint method ID (MID), the endpoint ID (RIID), the size of the constant part of the IPC message (in bytes), the contents of the IPC message arena handle, the size of the IPC message arena (in bytes), and the size of the utilized part of the IPC message arena (in bytes). If thedump
value is specified, the diagnostic information additionally includes the contents of the constant part and arena of the IPC message in hexadecimal format.When using this parameter, you must either specify the selective generation flag for transport code
--server
or refrain from specifying a selective generation flag for transport code.--ipc-trace-method-filter
<METHOD
>[,METHOD
]...Diagnostic information is printed if only the defined interface methods are called. For the
METHOD
value, you can use the interface method name or the construct <package name
>:
<interface method name
>. The package name and interface method name are specified in the IDL file.If this parameter is not specified, diagnostic information is printed when any interface method is called.
This parameter can be specified multiple times. For example, you can specify all required interface methods in one parameter or specify each required interface method in a separate parameter.
nk-psl-gen-c
The nk-psl-gen-c
compiler generates the C-language source code of the Kaspersky Security Module based on the solution security policy description and the IDL, CDL, and EDL descriptions. This code is used by the makekss script.
The nk-psl-gen-c
compiler can also generate the C-language source code of solution security policy tests based on solution security policy tests in PAL.
Syntax of the shell command for starting the nk-psl-gen-c
compiler:
Parameters:
INPUT
Path to the top-level file of the solution security policy description. This is normally the
security.psl
file.- {
-I
|--include-dir
}DIR
>These parameters are used to define the paths to directories containing IDL, CDL, and EDL files pertaining to the solution, and the paths to directories containing auxiliary files from the KasperskyOS SDK (
common
,sysroot-*-kos/include
,toolchain/include
). - {
-o
|--output
}FILE
>Path to the file that will save the source code of the Kaspersky Security Module and (optionally) the source code of solution security policy tests. The path must include existing directories.
--out-tests
<FILE
>Path to the file that will save the source code of the solution security policy tests.
- {
-t
|--tests
}ARG
>Defines whether the source code of solution security policy tests must be generated.
ARG
can take the following values:skip:
– source code of tests is not generated. This value is used by default if the-t, --tests
<ARG
> parameter is not specified.generate:
– source code of tests is generated. If the source code of tests is generated, you are advised to use the--out-tests
<FILE
> parameter. Otherwise, the source code of tests will be saved in the same file containing the source code of the Kaspersky Security Module, which may lead to errors during the build.
- {
-a
|--audit
}FILE
>Path to the file that will save the C-language source code of the audit decoder.
-h
|--help
Prints the Help text.
--version
Prints the version of the
nk-psl-gen-c
compiler.
einit
The einit
tool automates the creation of code for the Einit initializing program.
The einit
tool receives the solution initialization description (the init.yaml
file by default) and EDL, CDL and IDL descriptions, and creates a file containing the source code of the Einit
initializing program. Then the Einit
program must be built using the C-language cross compiler that is provided in KasperskyOS Community Edition.
Syntax for using the einit
tool:
Parameters:
FILE
Path to the
init.yaml
file.-I PATH
Path to the directory containing the auxiliary files (including EDL, CDL and IDL descriptions) required for generating the initializing program. By default, these files are located in the directory
/opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include
.-o, --out-file PATH
Path to the created .c file containing the code of the initializing program.
-h, --help
Displays the Help text.
makekss
The makekss
script creates the Kaspersky Security Module.
The script calls the nk-psl-gen-c compiler to generate the source code of the security module, then compiles the resulting code by calling the C compiler that is provided in KasperskyOS Community Edition.
The script creates the security module from the solution security policy description.
Syntax for using the makekss
script:
Parameters:
FILE
Path to the top-level file of the solution security policy description.
--target=ARCH
Processor architecture for which the build is intended.
--module=-lPATH
Path to the
ksm_kss
library. This key is passed to the C compiler for linking to this library.--with-nk=PATH
Path to the
nk-psl-gen-c
compiler that will be used to generate the source code of the security module. By default, the compiler is located in/opt/KasperskyOS-Community-Edition-<version>/toolchain/bin/nk-psl-gen-c
.--with-nktype="TYPE"
Indicates the type of NK compiler that will be used. To use the
nk-psl-gen-c
compiler, indicate thepsl
type.--with-nkflags="FLAGS"
Parameters used when calling the
nk-psl-gen-c
compiler.The
nk-psl-gen-c
compiler will require access to all EDL, CDL and IDL descriptions. To enable thenk-psl-gen-c
compiler to find these descriptions, you need to pass the paths to these descriptions in the--with-nkflags
parameter by using the-I
switch of thenk-psl-gen-c
compiler.--output=PATH
Path to the created security module file.
--with-cc=PATH
Path to the C compiler that will be used to build the security module. The compiler provided in KasperskyOS Community Edition is used by default.
--with-cflags=FLAGS
Parameters used when calling the C compiler.
-h, --help
Displays the Help text.
makeimg
The makeimg
script creates the final boot image of the KasperskyOS-based solution with all executable files of programs and the Kaspersky Security Module.
The script receives a list of files, including the executable files of all applications that need to be added to ROMFS of the loaded image, and creates the following files:
- Solution image
- Solution image without character tables (
.stripped
) - Solution image with debug character tables (
.dbg.syms
)
Syntax for using the makeimg script:
Parameters:
FILES
List of paths to files, including the executable files of all applications that need to be added to ROMFS.
The security module (
ksm.module
) must be explicitly specified, or else it will not be included in the solution image. TheEinit
application does not need to be indicated because it will be automatically included in the solution image.--target=ARCH
Architecture for which the build is intended.
--sys-root=PATH
Path to the root directory sysroot. By default, this directory is located in
/opt/KasperskyOS-Community-Edition-version/sysroot-aarch64-kos/
.--with-toolchain=PATH
Path to the set of auxiliary tools required for the solution build. By default, these tools are located in
/opt/KasperskyOS-Community-Edition-<version>/toolchain/
.--ldscript=PATH
Path to the linker script required for the solution build. By default, this script is located in
/opt/KasperskyOS-Community-Edition-<version>/libexec/aarch64-kos/
.--img-src=PATH
Path to the precompiled KasperskyOS kernel. By default, the kernel is located in
/opt/KasperskyOS-Community-Edition-<version>/libexec/aarch64-kos/
.--img-dst=PATH
Path to the created image file.
--with-init=PATH
Path to the executable file of the
Einit
initializing program.--with-extra-asflags=FLAGS
Additional flags for the AS Assembler.
--with-extra-ldflags=FLAGS
Additional flags for the LD Linker.
-h, --help
Displays the Help text.
Cross compilers
The toolchain provided in the KasperskyOS SDK includes one or more GCC compilers. The toolchain/bin
directory contains the following files:
- Executable files of compilers (for example,
x86_64-pc-kos-gcc
,arm-kos-g++
) - Executable files of linkers (for example,
x86_64-pc-kos-ld
,arm-kos-ld
) - Executable files of assemblers (for example,
x86_64-pc-kos-as
,arm-kos-as
)
In addition to standard macros, an additional macro __KOS__=1
is defined in GCC. Use of this macro lets you simplify porting of the software code to KasperskyOS, and also simplifies development of platform-independent programs.
To view the list of standard macros of GCC, run the following command:
Linker operation specifics
When building the executable file of an application, by default the linker links the following libraries in the specified order:
- libc is the standard C library.
- libm is the library that implements the mathematical functions of the standard C language library.
- libvfs_stubs is the library that contains stubs of I/O functions (for example,
open
,socket
,read
,write
). - libkos is the library for accessing the KasperskyOS core endpoints.
- libenv is the library of the subsystem for configuring the environment of applications (environment variables, arguments of the
main
function, and custom configurations). - libsrvtransport-u is the library that supports IPC between processes and the kernel.
Example build without using CMake
Below is an example of a script for building a basic example. This example contains a single application called Hello
, which does not provide any endpoints.
The provided script is intended only for demonstrating the build commands being used.
build.sh
Formal specifications of KasperskyOS-based solution components
Solution development includes the creation of formal specifications for its components that form a global picture for the Kaspersky Security Module. A formal specification of a KasperskyOS-based solution component (hereinafter referred to as the formal specification of the solution component) is comprised of a system of IDL, CDL and EDL descriptions (IDL and CDL descriptions are optional) for this component. These descriptions are used to automatically generate transport code of solution components, and source code of the security module and the initializing program. The formal specifications of solution components are also used as source data for the solution security policy description.
Just like solution components, the KasperskyOS kernel also has a formal specification (for details, see "Methods of KasperskyOS core endpoints").
Each solution component corresponds to an EDL description. In terms of a formal specification, a solution component is a container for components that provide endpoints. Multiple instances of one solution component may be used at the same time, which means that multiple processes can be started from the same executable file. Processes that correspond to the same EDL description are processes of the same class. An EDL description defines the process class name and the top-level component parameters, such as the provided endpoints with one or multiple interfaces, the security interface, and embedded components.
Each embedded component corresponds to a CDL description. This description defines the component name, provided endpoints, security interface, and embedded components. Embedded components can simultaneously provide endpoints, support a security interface, and serve as containers for other components. Each embedded component can provide multiple endpoints with one or more interfaces.
Each interface (including the security interface) is defined in an IDL description. This description defines the interface name, signatures of interface methods, and data types for the parameters of interface methods. The data comprising signatures of interface methods and definitions of data types for parameters of interface methods is referred to as a package.
Processes that do not provide endpoints may only act as clients. Processes that provide endpoints are servers, but they can also act as clients at the same time.
The formal specification of a solution component does not define how this component will be implemented. In other words, the presence of components in a formal specification of a solution component does not mean that these components will be present in the architecture of this solution component.
Names of process classes, components, packages and interfaces
Process classes, components, packages and interfaces are identified by their names in IDL, CDL and EDL descriptions. Within one KasperskyOS-based solution, the names of process classes and the names of components form one set of names, while the names of packages form a different set of names. These two sets may overlap. A set of package names includes a set of interface names.
The name of a process class, component, package or interface is a link to the IDL, CDL or EDL file in which this name is defined. This link is a path to the IDL, CDL or EDL file (without the extension and dot before it) relative to the directory that is included in the set of directories where the source code generators search for IDL, CDL and EDL files. (This set of directories is defined by parameters -I
<path to the directory
>.) A dot is used as a separator in a path description.
For example, the kl.core.NameServer
process class name is a link to the EDL file named NameServer.edl
, which is located in the KasperskyOS SDK at the following path:
sysroot-*-kos/include/kl/core
However, source code generators must be configured to search for IDL, CDL and EDL files in the following directory:
sysroot-*-kos/include
The name of an IDL, CDL or EDL file begins with an uppercase letter and must not contain any underscores _
.
EDL description
EDL descriptions are placed into separate *.edl
files and contain declarations in the Entity Definition Language (EDL):
- Process class name. The following declaration is used:entity <process class name>
- [Optional] List of instances of components. The following declaration is used:components { <component instance name : component name> [...] }
Each component instance is indicated in a separate line. The component instance name must not contain any underscores
_
. The list can contain multiple instances of one component. Each component instance in the list has a unique name. - [Optional] Security interface. The following declaration is used:security <interface name>
- [Optional] List of endpoints. The following declaration is used:endpoints { <endpoint name : interface name> [...] }
Each endpoint is indicated in a separate line. The endpoint name must not contain any underscores
_
. The list can contain multiple endpoints with the same interface. Each endpoint in the list has a unique name.
The EDL language is case sensitive.
Single-line comments and multi-line comments can be used in an EDL description.
A security interface and provided endpoints can be defined in an EDL description and in a CDL description. If solution component development is utilizing already prepared constituent parts (such as libraries) that are accompanied by CDL descriptions, it is advisable to refer to them from the EDL description by using the components
declaration. Otherwise, you can describe all provided endpoints in the EDL description. In addition, you can separately define the security interface in the EDL description and in each CDL description.
Examples of EDL files
Hello.edl
Signald.edl
LIGHTCRAFT.edl
Downloader.edl
CDL description
CDL descriptions are placed into individual *.cdl
files and contain declarations in the Component Definition Language (CDL):
- The name of the component. The following declaration is used:component <component name>
- [Optional] Security interface. The following declaration is used:security <interface name>
- [Optional] List of endpoints. The following declaration is used:endpoints { <endpoint name : interface name> [...] }
Each endpoint is indicated in a separate line. The endpoint name must not contain any underscores
_
. The list can contain multiple endpoints with the same interface. Each endpoint in the list has a unique name. - [Optional] List of instances of embedded components. The following declaration is used:components { <component instance name : component name> [...] }
Each component instance is indicated in a separate line. The component instance name must not contain any underscores
_
. The list can contain multiple instances of one component. Each component instance in the list has a unique name.
The CDL language is case sensitive.
Single-line comments and multi-line comments can be used in a CDL description.
At least one optional declaration is used in a CDL description. If a CDL description does not use at least one optional declaration, this description will correspond to an "empty" component that does not provide endpoints, does not contain embedded components, and does not support a security interface.
Examples of CDL files
KscProductEventsProvider.cdl
KscConnectorComponent.cdl
FsVerifier.cdl
IDL description
IDL descriptions are placed into separate *.idl
files and contain declarations in the Interface Definition Language (IDL):
- Package name. The following declaration is used:package <package name>
- [Optional] Packages from which the data types for interface method parameters are imported. The following declaration is used:import <package name>
- [Optional] Definitions of data types for parameters of interface methods.
- [Optional] Signatures of interface methods. The following declaration is used:interface { <interface method name([parameters])>; [...] }
Each method signature is indicated in a separate line. The method name must not contain any underscores
_
. Each method in the list has a unique name. The parameters of methods are divided into input parameters (in
), output parameters (out
), and parameters for transmitting error information (error
). The order of parameters in the description is important: first input parameters, then output parameters, then error parameters. Methods of the security interface cannot have output parameters and error parameters.Input parameters and output parameters are transmitted in IPC requests and IPC responses, respectively. Error parameters are transmitted in IPC responses if the server cannot correctly handle the corresponding IPC requests.
The server can inform a client about IPC request processing errors via error parameters as well as through output parameters of interface methods. If the server sets the error flag in an IPC response when an error occurs, this IPC response will contain the error parameters without any output parameters. Otherwise this IPC response will contain output parameters just like when requests are correctly processed. (The error flag is set in IPC responses by using the
nk_err_reset()
macro defined in thenk/types.h
header file from the KasperskyOS SDK.)An IPC response sent with the error flag set and an IPC response with the error flag not set are considered to be different types of events for the Kaspersky Security Module. When describing a solution security policy, this difference lets you conveniently distinguish between the processing of events associated with the correct execution of IPC requests and the processing of events associated with incorrect execution of IPC requests. If the server does not set the error flag in IPC responses, the security module must check the values of output parameters indicating errors to properly process events related to the incorrect execution of IPC requests. (A client can check the state of the error flag in an IPC response even if the corresponding interface method does not contain error parameters. To do so, the client uses the
nk_msg_check_err()
macro defined in thenk/types.h
header file from the KasperskyOS SDK.)Signatures of interface methods cannot be imported from other IDL files.
The IDL language is case sensitive.
Single-line comments and multi-line comments can be used in an IDL description.
At least one optional declaration is used in a IDL description. If an IDL description does not use at least one optional declaration, this description will correspond to an "empty" package that does not assign any interface methods or data types (including from other IDL descriptions).
Some IDL files from the KasperskyOS SDK do not describe interface methods, but instead only contain definitions of data types. These IDL files are used only as exporters of data types.
If a package contains a description of interface methods, the interface name matches the package name.
Examples of IDL files
Env.idl
Kpm.idl
MessageBusSubs.idl
WaylandTypes.idl
IDL data types
IDL supports primitive data types as well as composite data types. The set of supported composite types includes unions, structures, arrays, and sequences.
Primitive types
IDL supports the following primitive types:
SInt8
,SInt16
,SInt32
,SInt64
– signed integer.UInt8
,UInt16
,UInt32
,UInt64
– unsigned integer.Handle
– value whose binary representation consists of multiple fields, including a handle field and a handle permissions mask field.bytes<
<size in bytes
>>
– byte buffer consisting of a memory area with a size that does not exceed the defined number of bytes.string<
<size in bytes
>>
– string buffer consisting of a byte buffer whose last byte is a terminating zero. The maximum size of a string buffer is a unit larger than the defined size due to the additional byte with the terminating zero.
Integer literals can be specified in decimal format, hexadecimal format (for example, 0x2f
, 0X2f
, 0x2F
, 0X2F
) or octal format (for example, 0O123
, 0o123
).
You can use the reserved word const
to define the named integer constants by assigning their values using integer literals or integer expressions.
Example definitions of named integer constants:
Named integer constants can be used to avoid problems associated with so-called "magic numbers". For example, if an IDL description defines named integer constants for return codes of an interface method, you can interpret these codes without additional information when describing a policy. Named integer constants and integer expressions can also be applied in definitions of byte buffers, string buffers, and composite types to define the size of data or the number of data elements.
The bytes<
<size in bytes
>>
and string<
<size in bytes
>>
constructs are used in definitions of composite types, signatures of interface methods, and when creating type aliases because they define anonymous types (types without a name).
Unions
A union stores different types of data in one memory area. In an IPC message, a union is provided with an additional tag
field that defines which specific member of the union is used.
The following construct is used to define a union:
Example of a union definition:
Structures
The following construct is used to define a structure:
Example of a structure definition:
Arrays
The following construct is used to define an array:
This construct is used in definitions of other composite types, signatures of interface methods, and when creating type aliases because it defines an anonymous type.
The Handle
type can be used as the type of array elements if this array is not included in another composite data type. However, the total number of handles in an IPC message cannot exceed 255.
Sequences
A sequence is a variable-sized array. When defining a sequence, the maximum number of elements of the sequence is specified.
The following construct is used to define a sequence:
This construct is used in definitions of other composite types, signatures of interface methods, and when creating type aliases because it defines an anonymous type.
The Handle
type cannot be used as the type of sequence elements.
Variable-size and fixed-size types
The bytes
, string
and sequence
types are variable-size types. In other words, the maximum number of elements is assigned when defining these types, but less elements (or none) may actually be used. Data of the bytes
, string
and sequence
types are stored in the IPC message arena. All other types are fixed-size types. Data of fixed-size types are stored in the constant part of IPC messages.
Types based on composite types
Composite types can be used to define other composite types. The definition of an array or sequence can also be included in the definition of another type.
Example definition of a structure with embedded definitions of an array and sequence:
The definition of a union or structure cannot be included in the definition of another type. However, a type definition may include already defined unions and structures. This is done by indicating the names of the included types in the type definition.
Example definition of a structure that includes a union and structure:
Creating aliases of types
Type aliases make it more convenient to work with types. For example, type aliases can be used to assign mnemonic names to types that have abstract names. Assigned aliases for anonymous types also let you receive named types.
The following construct is used to create a type alias:
Example of creating mnemonic aliases:
Example of creating an alias for an array definition:
Example of creating an alias for a sequence definition:
Example of creating an alias for a union definition:
Defining anonymous types in signatures of interface methods
Anonymous types can be defined in signatures of interface methods.
Example of defining a sequence in an interface method signature:
Integer expressions in IDL
Integer expressions in IDL are composed of named integer constants, integer literals, operators (see the table below), and grouping parentheses.
Example use of integer expressions:
If an integer overflow occurs when computing an expression, the source code generator using the IDL file will terminate with an error.
Details on operators of integer expressions in IDL
Syntax |
Operation |
Precedence |
Associativity |
Special considerations |
---|---|---|---|---|
|
Sign change |
1 |
No |
N/A |
|
Bitwise negation |
1 |
No |
N/A |
|
Exponentiation |
2 |
No |
Special considerations:
|
|
Multiplication |
3 |
Left |
N/A |
|
Integer division |
3 |
Left |
Special considerations:
|
|
Modulo |
3 |
Left |
Special considerations:
|
|
Addition |
4 |
Left |
N/A |
|
Subtraction |
4 |
Left |
N/A |
|
Bit shift left |
2* |
No |
Special considerations:
|
|
Bit shift right |
2* |
No |
Special considerations:
|
Describing a security policy for a KasperskyOS-based solution
A KasperskyOS-based solution security policy description (hereinafter also referred to as a solution security policy description or policy description) provides a set of interrelated text files with the psl
extension that contain declarations in the PSL language (Policy Specification Language). Some files reference other files through an inclusion declaration, which results in a hierarchy of files with one top-level file. The top-level file is specific to the solution. Files of lower and intermediate levels contain parts of the solution security policy description that may be specific to the solution or may be re-used in other solutions.
Some of the files of the lower and intermediate levels are provided in the KasperskyOS SDK. These files contain definitions of the basic data types and formal descriptions of KasperskyOS security models. KasperskyOS security models (hereinafter referred to as security models) serve as the framework for implementing security policies for KasperskyOS-based solutions. Files containing formal descriptions of security models reference a file containing definitions of the basic data types that are used in the descriptions of models.
The other files of lower and intermediate levels are created by the policy description developer if any parts of the policy description need to be re-used in other solutions. A policy description developer can also put parts of the policy description into separate files for convenient editing.
The top-level file references files containing definitions of basic data types and descriptions of security models that are applied in the part of the solution security policy that is described in this file. The top-level file also references all files of the lower and intermediate levels that were created by the policy description developer.
The top-level file is normally named security.psl
, but it can have any other name in the *.psl
format.
General information about a KasperskyOS-based solution security policy description
In simplified terms, a KasperskyOS-based solution security policy description consists of bindings that associate descriptions of security events with calls of methods provided by security model objects. A security model object is an instance of a class whose definition is a formal description of a security model (in a PSL file). Formal descriptions of security models contain signatures of methods of security models that determine the permissibility of interactions between different processes and between processes and the KasperskyOS kernel. These methods are divided into two types:
- Security model rules are methods of security models that return a "granted" or "denied" result. Security model rules can change security contexts (for information about a security context, see "Resource Access Control").
- Security model expressions are methods of security models that return values that can be used as input data for other methods of security models.
A security model object provides methods that are specific to one security model and stores the parameters used by these methods (for example, the initial state of a finite-state machine or the size of a container for specific data). The same object can be used to work with multiple resources. (In other words, you do not need to create a separate object for each resource.) However, the security contexts of these resources will be independent of each other. Likewise, multiple objects of one or more different security models can be used to work with the same resource. In this case, different objects will use the security context of the same resource without any reciprocal influence.
Security events serve as signals indicating the initiation of interaction between different processes and between processes and the KasperskyOS kernel. Security events include the following events:
- Clients send IPC requests.
- Servers or the kernel send IPC responses.
- The kernel or processes initialize the startup of processes.
- The kernel starts.
- Processes query the Kaspersky Security Module via the security interface.
Security events are processed by the security module.
Security models
The KasperskyOS SDK provides PSL files that describe the following security models:
- Base – methods that implement basic logic.
- Pred – methods that implement comparison operations.
- Bool – methods that implement logical operations.
- Math – methods that implement integer arithmetic operations.
- Struct – methods that provide access to structural data elements (for example, access to parameters of interface methods transmitted in IPC messages).
- Regex – methods for text data validation based on regular expressions.
- HashSet – methods for working with one-dimensional tables associated with resources.
- StaticMap – methods for working with two-dimensional "key–value" tables associated with resources.
- Flow – methods for working with finite-state machines associated with resources.
- Mic – methods for implementing Mandatory Integrity Control (MIC).
Security event handling by the Kaspersky Security Module
The Kaspersky Security Module calls all methods (rules and expressions) of security models related to an occurring security event. If all rules returned the "granted" result, the security module returns the "granted" decision. If even one rule returned the "denied" result, the security module returns the "denied" decision.
If even one method related to an occurring security event cannot be correctly performed, the security module returns the "denied" decision.
If no rule is related to an occurring security event, the security module returns the "denied" decision. In other words, all interactions between solution components and between those components and the KasperskyOS kernel are denied by default (Default Deny principle) unless those interactions are explicitly allowed by the solution security policy.
Page top
PSL language syntax
Basic rules
- Declarations can be listed in any sequence in a file.
- One declaration can be written to one or multiple lines.
- The PSL language is case sensitive.
- Single-line comments and multi-line comments are supported:/* This is a comment * And this, too */ // Another comment
Types of declarations
The PSL language has declarations for the following purposes:
- Setting the global parameters of a solution security policy
- Including PSL files in a solution security policy description
- Including EDL files in a solution security policy description
- Create security model objects
- Bind methods of security models to security events
- Creating security audit profiles
- Creating solution security policy tests
Setting the global parameters of a KasperskyOS-based solution security policy
Global parameters include the following parameters of a solution security policy:
- Execute interface used by the KasperskyOS kernel when querying the Kaspersky Security Module to notify it about kernel startup or about initiating the startup of a process by the kernel or by other processes. To define this interface, use the following declaration:execute: kl.core.Execute
KasperskyOS currently supports only one execute interface (
Execute
) defined in the file namedkl/core/Execute.idl
. (This interface consists of onemain
method, which has no parameters and does not perform any actions. Themain
method is reserved for potential future use.) - [Optional] Global security audit profile and initial security audit run-time level. (For more details about profiles and the security audit run-time level, see "Creating security audit profiles".) To define these parameters, use the following declaration:audit default = <security audit profile name> <security audit runtime-level>
Example:
audit default = global 0The default global profile is the
empty
security audit profile described in the file namedtoolchain/include/nk/base.psl
from the KasperskyOS SDK, and the default security audit runtime-level is 0. When theempty
security audit profile is applied, a security audit is not conducted.
Including PSL files in a KasperskyOS-based solution security policy description
To include a PSL file in a policy description, use the following declaration:
The link to the PSL file is the file path (without the extension and dot before it) relative to the directory that is included in the set of directories where the nk-psl-gen-c
compiler searches for PSL, IDL, CDL, and EDL files. (This set of directories is defined by the parameters -I
<path to the directory
> when starting the makekss
script or the nk-psl-gen-c
compiler.) A dot is used as a separator in a path description. A declaration is ended by the ._
character sequence.
Example:
This declaration includes the flow_part.psl
file, which is located in the policy_parts
directory. The policy_parts
directory must reside in one of the directories where the nk-psl-gen-c
compiler searches for PSL, IDL, CDL, and EDL files. For example, the policy_parts
directory may reside in the same directory as the PSL file containing this declaration.
Including a PSL file containing a formal description of a security model
To use the methods of a required security model, the policy description must include a PSL file containing a formal description of this model. PSL files containing formal descriptions of security models are located in the KasperskyOS SDK at the following path:
toolchain/include/nk
Example:
Including EDL files in a KasperskyOS-based solution security policy description
To include an EDL file for the KasperskyOS kernel in a policy description, use the following declaration:
To include an EDL file for a program (such as a driver or application) into a policy description, use the following declaration:
The link to the EDL file is the EDL file path (without the extension and dot before it) relative to the directory that is included in the set of directories where the nk-psl-gen-c
compiler searches for PSL, IDL, CDL, and EDL files. (This set of directories is defined by the parameters -I
<path to the directory
> when starting the makekss
script or the nk-psl-gen-c
compiler.) A dot is used as a separator in a path description.
Example:
The nk-psl-gen-c
compiler finds IDL and CDL files through EDL files because EDL files contain links to the corresponding CDL and IDL files, and the CDL files contain links to the corresponding CDL and IDL files.
Creating security model objects
To call the methods of a required security model, create an object for this security model.
To create a security model object, use the following declaration:
The security model object name must begin with a lowercase letter. The parameters of a security model object are specific to the security model. A description of parameters and examples of creating objects of various security models are provided in the "KasperskyOS security models" section.
Page top
Binding methods of security models to security events
To create an attachment between methods of security models and a security event, use the following declaration:
Security event type
To define the security event type, use the following specifiers:
request
– sending IPC requests.response
– sending IPC responses.error
– sending IPC responses containing information about errors.security
– processes querying the Kaspersky Security Module via the security interface.execute
– initializing the startups of processes or the startup of the KasperskyOS kernel.
When processes interact with the security module, they use a mechanism that is different from IPC. However, when describing a policy, queries sent by processes to the security module can be viewed as the transfer of IPC messages because processes actually transmit messages to the security module (the recipient is not indicated in these messages).
The IPC mechanism is not used to start processes. However, when the startup of a process is initiated, the kernel queries the security module and provides information about the initiator of the startup and the started process. For this reason, the policy description developer can consider the startup of a process to be analogous to sending an IPC message from the startup initiator to the started process. When the kernel is started, this is analogous to the kernel sending an IPC message to itself.
Security event selectors
Security event selectors let you clarify the description of the defined type of security event. You can use the following selectors:
src=
<kernel/process class name
> – processes of the defined class or the KasperskyOS kernel are the sources of IPC messages.dst=
<kernel/process class name
> – processes of the defined class or the kernel are the recipients of IPC messages.interface=
<interface name
> – describes the following security events:- Clients attempt to use the endpoints of servers or the kernel with the defined interface.
- Processes query the Kaspersky Security Module via the defined security interface.
- The kernel or servers send clients the results from using the endpoints with the defined interface.
component=
<component name
> – describes the following security events:- Clients attempt to use the core or server endpoints provided by the defined component.
- The kernel or servers send clients the results from using the endpoints provided by the defined component.
endpoint=
<qualified endpoint name
> – describes the following security events:- Clients attempt to use the defined core or server endpoints.
- The kernel or servers send clients the results from using the defined endpoint.
method=
<method name
> – describes the following security events:- Clients attempt to query servers or the kernel by calling the defined method of the endpoint.
- Processes query the security module by calling the defined method of the security interface.
- The kernel or servers send clients the results from calling the defined method of the endpoint.
- The kernel notifies the security module about its startup by calling the defined method of the execute interface.
- The kernel initiates the startup of processes by calling the defined method of the execute interface.
- Processes initiate the startup of other processes, which results in the kernel calling the defined method of the execute interface.
Process classes, components, instances of components, interfaces, endpoints, and methods must be named the same as they are in the IDL, CDL, and EDL descriptions. The kernel must be named kl.core.Core
.
The qualified name of the endpoint has the format <path to endpoint.endpoint name
>. The path to the endpoint is a sequence of component instance names separated by dots. Among these component instances, each subsequent component instance is embedded into the previous one, and the last one provides the endpoint with the defined name.
For security
events, specify the qualified name of the security interface method if you need to use the security interface defined in a CDL description. (If you need to use a security interface defined in an EDL description, it is not necessary to specify the qualified name of the method.) The qualified name of a security interface method is a construct in the format <path to security interface.method name
>. The path to the security interface is a sequence of component instance names separated by dots. Among these component instances, each subsequent component instance is embedded into the previous one, and the last one supports the security interface that includes the method with the defined name.
If selectors are not specified, the participants of a security event may be any process and the kernel (except security
events in which the kernel cannot participate).
You can use combinations of selectors. Selectors can be separated by commas.
There are restrictions on the use of selectors. The interface
, component
, and endpoint
selectors cannot be used for security events of the execute
type. The dst
, component
, and endpoint
selectors cannot be used for security events of the security
type.
There are also restrictions on combinations of selectors. For security events of the request
, response
and error
types, the method
selector can only be used together with one of the endpoint
, interface
, or component
selectors or a combination of them. (The method
, endpoint
, interface
and component
selectors must be coordinated. In other words, the method, endpoint, interface, and component must be interconnected.) For security events of the request
type, the endpoint
selector can be used only together with the dst
selector. For security events of the response
and error
types, the endpoint
selector can be used only together with the src
selector.
The type and selectors of a security event form the security event description. It is recommended to describe security events with maximum precision to allow only the required interactions between different processes and between processes and the kernel. If IPC messages of the same type are always verified when processing the defined event, the description of this event is maximally precise.
To ensure that IPC messages of the same type correspond to a security event description, one of the following conditions must be fulfilled for this description:
- For events of the
request
,response
anderror
type, the "interface method-endpoint-server class or kernel" chain is unequivocally defined. For example, the security event descriptionrequest dst=Server endpoint=net.Net method=Send
corresponds to IPC messages of the same type, and the security event descriptionrequest dst=Server
corresponds to any IPC message sent to theServer
. - For
security
events, the security interface method is specified. - The execute-interface method is indicated for
execute
events.There is currently support for only one fictitious method of the
main
execute-interface. This method is used by default, so it does not have to be defined through themethod
selector. This way, any description of anexecute
security event corresponds to IPC messages of the same type.
Security audit profile
To define a security audit profile, use the following construct:
If a security audit profile is not defined, the global security audit profile is used.
Called security model methods
To call a security model method, use the following construct:
Data of PSL-supported types can be used as the parameter. However, the following special considerations should be taken into account:
- If a security model method does not actually have a parameter, this method formally has a Unit-type parameter designated as
()
. - If a security model method parameter is a dictionary
{name of field 1 : value of field 1[, name of field 2 : value of field 2]...}
, this parameter does not need to be enclosed in parentheses. - If the security model method parameter is not a dictionary and does not have the Unit type, this parameter must be enclosed in parentheses.
You can call one or more methods by using the same or different security model objects. Security model rules can use the parameter to receive values returned by expressions of security models.
When a security event is processed by the Kaspersky Security Module, expressions are called before rules. Therefore, expressions do not receive the changes made by rules. For example, if a declaration of attachment between StaticMap security model methods and security events first specifies the set
rule and then specifies the get_uncommited
expression for the same resource, the get_uncommited
expression will return the key value that was defined before the current security event was processed instead of the key value that is defined by the set
rule when processing the current security event. The key value defined by the set
rule when processing the current security event can be returned by the get_uncommited
expression only when processing subsequent security events if the security module returns the "allowed" decision as a result of processing the current security event. If the security module returns a "denied" decision as a result of processing the current security event, all changes made by rules and expressions invoked during processing of the current security event will be discarded.
A security model method (rule or expression) can use the parameter to receive the parameters of interface methods. (For details about obtaining access to parameters of interface methods, see "Struct security model"). A security model method can also use the parameter to receive the SID values of processes and the KasperskyOS kernel that are defined by the reserved words src_sid
and dst_sid
. The first reserved word refers to the SID of the process (or kernel) that is the source of the IPC message. The second reserved word refers to the SID of the process (or kernel) that is the recipient of the IPC message (dst_sid
cannot be used for queries to the Kaspersky Security Module).
You do not have to indicate the security model object name to call certain security model methods. Also, some of the security model methods must be called using operators instead of the call construct. For details about the methods of security models, see KasperskyOS Security models.
Embedded constructs for binding methods of security models to security events
In one declaration, you can bind methods of security models to different security events of the same type. To do so, use the match sections that consist of the following types of constructs:
Match sections can be embedded into another match section. A match section simultaneously uses its own security event selectors and the security event selectors at the level of the declaration and all match sections in which this match section is "wrapped". By default, a match section applies the security audit profile of its own container (match section of the preceding level or the declaration level), but you can define a separate security audit profile for the match section.
In one declaration, you can define different variants for processing a security event depending on the conditions in which this event occurred (for example, depending on the state of the finite-state machine associated with the resource). To do so, use the conditional sections that are elements of the following construct:
The choice
construct can be used within a match section. A conditional section uses the security event selectors and security audit profile of its own container.
If multiple conditions described in the choice
construct are simultaneously fulfilled when a security event is processed, only the one conditional section corresponding to the first true condition on the list is triggered.
You can verify the fulfillment of conditions in the choice
construct only by using the expressions that are specially intended for this purpose. Some security models contain these expressions (for more details, see KasperskyOS Security models).
Only text and integer literals, logical values and the _
character designating an always true condition can be used as conditions.
Examples of binding security model methods to security events
See "Examples of binding security model methods to security events", "Example descriptions of basic security policies for KasperskyOS-based solutions", and "KasperskyOS security models".
Page top
Creating security audit profiles
A security audit (hereinafter also referred to as an audit) is the following sequence of actions. The Kaspersky Security Module notifies the KasperskyOS kernel about decisions made by this module. Then the kernel forwards this data to the system program Klog
, which decodes this data and forwards it to the system program KlogStorage
(data is transmitted via IPC). The latter sends the received audit data to standard output (or standard error) or writes it to a file.
Security audit data (hereinafter referred to as audit data) refers to information about decisions made by the Kaspersky Security Module, which includes the actual decisions ("granted" or "denied"), descriptions of security events, results from calling methods of security models, and data on incorrect IPC messages. Data on calls of security model expressions is included in the audit data just like data on calls of security model rules.
To perform a security audit, you need to associate security model objects with security audit profile(s). A security audit profile (hereinafter also referred to as an audit profile) combines security audit configurations (hereinafter also referred to as audit configurations), each of which defines the security model objects covered by the audit, and specifies the conditions for conducting the audit. You can define a global audit profile (for more details, see "Setting the global parameters of a KasperskyOS-based solution security policy") and/or assign an audit profile(s) at the level of binding security model methods to security events, and/or assign an audit profile(s) at the level of match sections (for more details, see "Binding methods of security models to security events").
Regardless of whether or not audit profiles are being used, audit data contains information about "denied" decisions that were made by the Kaspersky Security Module when IPC messages were invalid and when handling security events that are not associated with any security model rule.
To create a security audit profile, use the following declaration:
Security audit runtime-level
The security audit runtime-level (hereinafter referred to as the audit runtime-level) is a global parameter of a solution security policy and consists of an unsigned integer that defines the active security audit configuration. (The word "runtime-level" here refers to the configuration variant and does not necessarily involve a hierarchy.) The audit runtime-level can be changed during operation of the Kaspersky Security Module. To do so, use a specialized method of the Base
security model that is called when processes query the security module through the security interface (for more details, see "Base security model"). The initial audit runtime-level is assigned together with the global audit profile (for more details, see "Setting the global parameters of a KasperskyOS-based solution security policy"). An empty
audit profile can be explicitly assigned as the global audit profile.
You can define multiple audit configurations in an audit profile. In different configurations, different security model objects can be covered by the audit and different conditions for conducting the audit can be applied. Audit configurations in a profile correspond to different audit runtime-levels. If a profile does not have an audit configuration corresponding to the current audit runtime-level, the security module will activate the configuration that corresponds to the next-lowest audit runtime-level. If a profile does not have an audit configuration for an audit runtime-level equal to or less than the current level, the security module will not use this profile (in other words, an audit will not be performed for this profile).
The capability to change the audit runtime-level lets you regulate the level of detail of an audit, for example. The higher the audit runtime-level, the higher the level of detail. In other words, a higher audit runtime-level activates audit configurations in which more security model objects are covered by the audit and/or less restrictions are applied in the audit conditions. In addition, you can change the audit runtime-level to switch the audit from one set of logically connected security model objects to another set. For example, a low audit runtime-level activates audit configurations in which the audit covers security model objects related to drivers, a medium audit runtime-level activates audit configurations in which the audit covers security model objects related to the network subsystem, and a high audit runtime-level activates audit configurations in which the audit covers security model objects related to applications.
Name of the security model object
The security model object name is indicated so that the methods provided by this object can be covered by the audit. These methods will be covered by the audit whenever they are called, provided that the conditions for conducting the audit are observed.
Information about the decisions of the Kaspersky Security Module contained in audit data includes the overall decision of the security module as well as the results from calling individual methods of security modules covered by the audit. To ensure that information about a security module decision is included in audit data, at least one method called during security event handling must be covered by the audit.
The names of security model objects and the names of methods provided by these objects are included in the audit data.
Security audit conditions
Security audit conditions must be defined separately for each object of a security model.
To define the audit conditions related to the results from calling security model methods, use the following constructs:
["granted"]
– the audit is performed if the rules return the "granted" result; the expressions are correctly executed.["denied"]
– the audit is performed if the rules return the "denied" result; the expressions are incorrectly executed.["granted", "denied"]
– the audit is performed regardless of the result returned by rules, and regardless of whether or not rules are correctly fulfilled.[]
– the audit is not performed.
Audit conditions specific to security models are defined by constructs specific to these models (for more details, see KasperskyOS Security models). These conditions apply to rules and expressions. For example, one of these conditions can be the state of a finite-state machine.
Security audit profile for a security audit route
A security audit route includes the kernel and the Klog
and KlogStorage
processes, which are connected by IPC channels based on the "kernel – Klog
– KlogStorage
" scheme. Security model methods that are associated with transmission of audit data via this route must not be covered by the audit. Otherwise, this will lead to an avalanche of audit data because any data transmission will give rise to new data.
To "suppress" an audit that was defined by a profile with a wider scope (for example, by a global profile or a profile at the level of binding security model methods to a security event), assign an empty
audit profile at the level of binding security model methods to security events or at the level of the match section.
Examples of security audit profiles
See "Examples of security audit profiles".
Creating and performing tests for a KasperskyOS-based solution security policy
A solution security policy is tested to verify whether or not the policy actually allows what should be allowed and denies what should be denied.
To create a set of tests for a solution security policy, use the following declaration:
You can create multiple sets of tests by using several of these declarations.
A set of tests can optionally include the initial part of the tests and/or the final part of the tests. The execution of each test from the set begins with whatever is described in the initial part of the test and ends with whatever is described in the final part of the test. This lets you describe the repeated initial and/or final parts of tests in each test.
After completing each test, all modifications in the Kaspersky Security Module related to the execution of this test are rolled back.
Each test includes one or more test cases.
Test cases
A test case associates a security event description and values of interface method parameters with an expected decision of the Kaspersky Security Module. If the actual security module decision matches the expected decision, the test case passes. Otherwise it fails.
When a test is run, the test cases are executed in the same sequence in which they are described. In other words, the test demonstrates how the security module handles a sequence of security events.
If all test cases within a test pass, the test passes. If even one test case fails to pass, the test fails. A test is terminated on the first failing test case. Each test from the set is run regardless of whether the previous test passed or failed.
A test case description in the PAL language is comprised of the following construct:
The expected decision of the security module can be indicated as grant
("granted"), deny
("denied") or any
("any decision"). You are not required to indicate the expected decision of the security module. The "granted" decision is expected by default. If the any
value is specified, the security module decision does not have any influence on whether or not the test case passes. In this case, the test case may fail due to errors that occur when the security module processes an IPC message (for example, when the IPC message has an invalid structure).
The name of the test case can be specified if only the expected decision of the security module is specified.
For information about the types and selectors of security events, and about the limitations when using selectors, see Binding methods of security models to security events. Selectors must ensure that the security event description corresponds to IPC messages of the same type. (When security model methods are bound to security events, selectors may not ensure this.)
In security event descriptions, you need to specify the SID instead of the process class name (and the KasperskyOS kernel). However, this requirement does not apply to execute
events for which the SID of the started process (or kernel) is unknown. To save the SID of the process or kernel to a variable, you need to use the <-
operator in the test case description in the following format:
The SID value will be assigned to the variable even if startup of the process of the defined class (or kernel) is denied by the tested policy but the "denied" decision is expected.
The PAL language supports abbreviated forms of security event descriptions:
security
: <Process SID
>!
<qualified name of security interface method
> corresponds tosecurity src=
<process SID
>method=
<qualified name of security interface method
>.request
: <client SID
>~>
<kernel/server SID
>:
<qualified name of endpoint.method name
> corresponds torequest src=
<client SID
>dst=
<kernel/server SID
>endpoint=
<qualified name of endpoint
>method=
<method name
>.response
: <client SID
><~
<kernel/server SID
>:
<qualified name of endpoint.method name
> corresponds toresponse src=
<kernel/server SID
>dst=
<client SID
>endpoint=
<qualified name of endpoint
>method=
<method name
>.
The values of interface method parameters must be defined for all types of security events except execute
. If the interface method has no parameters, specify {}
. You cannot specify {}
for security events of the execute
type.
Interface method parameters and their values must be defined by comma-separated constructs that look as follows:
The names and types of parameters must comply with the IDL description. The sequence order of parameters is not important.
Example definition of parameter values:
In this example, the number is passed through the param1
parameter. The string buffer is passed through the param2
parameter. A structure consisting of two fields is passed through the param3
parameter. The collection
field contains an array or sequence of three numeric elements. The filehandle
field contains the SID. A union or structure containing one field is passed through the param4
parameter. The name
field contains an array or sequence of two string buffers.
Currently, only an SID can be indicated as the value of a Handle
parameter, and there is no capability to indicate the SID together with a handle permissions mask. For this reason, it is not possible to properly test a solution security policy when the permissions masks of handles influence the security module decisions.
The values of parameters (or elements of parameters) do not have to be specified. If they are not specified, the system automatically applies the default values corresponding to the IDL types of parameters (and elements of parameters):
- The default values for numerical types and the
Handle
type are zero. - A zero-sized byte- or string buffer is the default value for byte- or string buffers.
- The default value for sequences is a sequence with zero elements.
- The default value for arrays is an array of elements with the default values.
- The default value for structures is a structure consisting of fields with the default values.
- For unions, the default value of the first member of the union is applied by default.
Example of applying the default value for a parameter and parameter element:
Example tests
See "Examples of tests for KasperskyOS-based solution security policies".
Test procedure
The test procedure includes the following steps:
- Save the tests in one or multiple PSL files (
*.psl
or*.psl.in
)
. - Add the
CMake
commandadd_kss_pal_qemu_tests()
to one of theCMakeLists.txt
files of the project.Use the
PSL_FILES
parameter to define the paths to PSL files containing tests. Use theDEPENDS
parameter to define theCMake
targets whose execution will cause the PSL file-dependent IDL, CDL, and EDL files to be put into the directories where thenk-psl-gen-c
compiler can find them. If*.psl.in
files are utilized, use theENTITIES
parameter to define the names of process classes of system programs. (These system programs are included in a KasperskyOS-based solution that requires security policy testing.)Example use of the
CMake
commandadd_kss_pal_qemu_tests()
in the fileeinit/CMakeLists.txt
:add_kss_pal_qemu_tests ( PSL_FILES src/security.psl.in DEPENDS kos-qemu-image ENTITIES ${ENTITIES}) - Build and run the tests.
You must run the Bash build script
cross-build.sh
with the parameter--target pal-test
<N
> (N is the index of the PSL file in the list of PSL files defined through thePSL_FILES
parameter of theCMake
commandadd_kss_pal_qemu_tests()
at step 2. For example,--target pal-test0
will create a KasperskyOS-based solution image corresponding to the first PSL file defined through thePSL_FILES
parameter of the CMake
commandadd_kss_pal_qemu_tests()
and then run that image in QEMU. (Instead of applications and system programs, this solution will contain the program that runs tests.)Example:
./cross-build.sh --target pal-test0
Example test results:
The test results contain information about whether or not each test passed or failed. If a test failed, information indicating the location of the description of the failed test example will be displayed in the PSL file.
Page top
PSL data types
The data types supported in the PSL language are presented in the table below.
PSL data types
Designations of types |
Description of types |
---|---|
|
Unsigned integer |
|
Signed integer |
|
Boolean type The Boolean type includes two values: |
|
Text type |
|
The |
|
Text literal A text literal includes one immutable text value. Example definitions of text literals:
|
< |
Integer literal An integer literal includes one immutable integer value. Example definitions of integer literals:
|
< |
Variant type A variant type combines two or more types and may perform the role of either of them. Examples of definitions of variant types:
|
|
Dictionary A dictionary consists of one or more types of fields. A dictionary can be empty. Examples of dictionary definitions:
|
|
Tuple A tuple consists of fields of one or more types in the order in which the types are listed. A tuple can be empty. Examples of tuple definitions:
|
|
Set A set includes zero or more unique elements of the same type. Examples of set definitions:
|
|
List A list includes zero or more elements of the same type. Examples of list definitions:
|
|
Associative array An associative array includes zero or more entries of the "key-value" type with unique keys. Example of defining an associative array:
|
|
Array An array includes a defined number of elements of the same type. Example of defining an array:
|
|
Sequence A sequence includes from zero to the defined number of elements of the same type. Example of defining a sequence:
|
Aliases of certain PSL types
The nk/base.psl
file from the KasperskyOS SDK defines the data types that are used as the types of parameters (or structural elements of parameters) and returned values for methods of various security models. Aliases and definitions of these types are presented in the table below.
Aliases and definitions of certain data types in PSL
Type alias |
Type definition |
---|---|
|
Unsigned integer
|
|
Signed integer
|
|
Integer
|
|
Scalar literal
|
|
Literal
|
|
Type of security ID (SID)
|
|
Type of security ID (SID)
|
|
Dictionary containing fields for the SID and handle permissions mask
|
|
Type of data received by expressions of security models called in the
|
|
Type of data defining the conditions for conducting the security audit
|
Mapping IDL types to PSL types
Data types of the IDL language are used to describe the parameters of interface methods. The input data for security model methods have types from the PSL language. The set of data types in the IDL language differs from the set of data types in the PSL language. Parameters of interface methods transmitted in IPC messages can be used as input data for methods of security models, so the policy description developer needs to understand how IDL types are mapped to PSL types.
Integer types of IDL are mapped to integer types of PSL and to variant types of PSL that combine these integer types (including with other types). For example, signed integer types of IDL are mapped to the Signed
type in PSL, and integer types of IDL are mapped to the ScalarLiteral
type in PSL.
The Handle
type in IDL is mapped to the HandleDesc
type in PSL.
Unions and structures of IDL are mapped to PSL dictionaries.
Arrays and sequences of IDL are mapped to arrays and sequences of PSL, respectively.
String buffers in IDL are mapped to the text type in PSL.
Byte buffers in IDL are not currently mapped to PSL types, so the data contained in byte buffers cannot be used as inputs for security model methods.
Page top
Examples of binding security model methods to security events
Before analyzing examples, you need to become familiar with the Base security model.
Processing the initiation of process startups
Handling the startup of the KasperskyOS kernel
Handling IPC request forwarding
Handling IPC response forwarding
Handling the transmission of IPC responses containing error information
Handling queries sent by processes to the Kaspersky Security Module
Using match sections
Setting audit profiles
Example descriptions of basic security policies for KasperskyOS-based solutions
Before analyzing examples, you need to become familiar with the Struct, Base and Flow security models.
Example 1
The solution security policy in this example allows any interaction between different processes of the Client
, Server
and Einit
classes, and between these processes and the KasperskyOS kernel. The "granted" decision will always be received when these processes query the Kaspersky Security Module. This policy can be used only as a stub during the early stages of development of a KasperskyOS-based solution so that the Kaspersky Security Module does not interfere with interactions. It would be unacceptable to apply such a policy in a real-world KasperskyOS-based solution.
security.psl
Example 2
The solution security policy in this example imposes limitations on queries sent from clients of the FsClient
class to servers of the FsDriver
class. When a client opens a resource controlled by a server of the FsDriver
class, a finite-state machine in the unverified
state is associated with this resource. A client of the FsClient
class is allowed to read data from a resource controlled by a server of the FsDriver
class only if the finite-state machine associated with this resource is in the verified
state. To switch a resource-associated finite-state machine from the unverified
state to the verified
state, a process of the FsVerifier
class needs to query the Kaspersky Security Module.
In a real-world KasperskyOS-based solution, this policy cannot be applied because it allows an excessive variety of interactions between different processes and between processes and the KasperskyOS kernel.
security.psl
Examples of security audit profiles
Before analyzing examples, you need to become familiar with the Base, Regex and Flow security models.
Example 1
Example 2
Examples of tests for KasperskyOS-based solution security policies
Example 1
Example 2
Example 3
Pred security model
The Pred security model performs comparison operations.
A PSL file containing a description of the Pred security model is located in the KasperskyOS SDK at the following path:
toolchain/include/nk/basic.psl
Pred security model object
The basic.psl
file contains a declaration that creates a Pred security model object named pred
. Consequently, inclusion of the basic.psl
file into the solution security policy description will create a Pred security model object by default.
A Pred security model object does not have any parameters and cannot be covered by a security audit.
It is not necessary to create additional Pred security model objects.
Pred security model methods
A Pred security model contains expressions that perform comparison operations and return values of the Boolean
type. To call these expressions, use the following comparison operators:
- <
ScalarLiteral
>==
<ScalarLiteral
> – "equals". - <
ScalarLiteral
>!=
<ScalarLiteral
> – "does not equal". - <
Number
><
<Number
> – "is less than". - <
Number
><=
<Number
> – "is less than or equal to". - <
Number
>>
<Number
> – "is greater than". - <
Number
>>=
<Number
> – "is greater than or equal to".
The Pred security model also contains the empty
expression that determines whether data contains its own structural elements. This expression returns values of the Boolean
type. If data does not contain its own structural elements (for example, a set is empty), the expression returns true
, otherwise it returns false
. To call the expression, use the following construct:
Bool security model
The Bool security model performs logical operations.
A PSL file containing a description of the Bool security model is located in the KasperskyOS SDK at the following path:
toolchain/include/nk/basic.psl
Bool security model object
The basic.psl
file contains a declaration that creates a Bool security model object named bool
. Consequently, inclusion of the basic.psl
file into the solution security policy description will create a Bool security model object by default.
A Bool security model object does not have any parameters and cannot be covered by a security audit.
It is not necessary to create additional Bool security model objects.
Bool security model methods
The Bool security model contains expressions that perform logical operations and return values of the Boolean
type. To call these expressions, use the following logical operators:
!
<Boolean
> – "logical NOT".- <
Boolean
>&&
<Boolean
> – "logical AND". - <
Boolean
>||
<Boolean
> – "logical OR". - <
Boolean
>==>
<Boolean
> – "implication" (!
<Boolean
>||
<Boolean
>).
The Bool security model also contains the all
, any
and cond
expressions.
The expression all
performs a "logical AND" for an arbitrary number of values of Boolean
type. It returns values of the Boolean
type. It returns true
if an empty list of values ([]
) is passed via the parameter. To call the expression, use the following construct:
The expression any
performs a "logical OR" for an arbitrary number of values of Boolean
type. It returns values of the Boolean
type. It returns false
if an empty list of values ([]
) is passed via the parameter. To call the expression, use the following construct:
cond
expression performs a ternary conditional operation. Returns values of the ScalarLiteral
type. To call the expression, use the following construct:
In addition to expressions, the Bool security model includes the assert
rule that works the same as the rule of the same name included in the Base security model.
Math security model
The Math security model performs integer arithmetic operations.
A PSL file containing a description of the Math security model is located in the KasperskyOS SDK at the following path:
toolchain/include/nk/basic.psl
Math security model object
The basic.psl
file contains a declaration that creates a Math security model object named math
. Consequently, inclusion of the basic.psl
file into the solution security policy description will create a Math security model object by default.
A Math security model object does not have any parameters and cannot be covered by a security audit.
It is not necessary to create additional Math security model objects.
Math security model methods
The Math security model contains expressions that perform integer arithmetic operations. To call a part of these expressions, use the following arithmetic operators:
- <
Number
>+
<Number
> – "addition". Returns values of theNumber
type. - <
Number
>-
<Number
> – "subtraction". Returns values of theNumber
type. - <
Number
>*
<Number
> – "multiplication". Returns values of theNumber
type.
The other expressions are as follows:
neg (
<Signed
>)
– "change number sign". Returns values of theSigned
type.abs (
<Signed
>)
– "get module of number". Returns values of theSigned
type.sum (
<List<Number>
>)
– "add numbers from list". Returns values of theNumber
type. It returns0
if an empty list of values ([]
) is passed via the parameter.product (
<List<Number>
>)
– "multiple numbers from list". Returns values of theNumber
type. It returns1
if an empty list of values ([]
) is passed via the parameter.
To call these expressions, use the following construct:
Struct security model
The Struct security model obtains access to structural data elements.
A PSL file containing a description of the Struct security model is located in the KasperskyOS SDK at the following path:
toolchain/include/nk/basic.psl
Struct security model object
The basic.psl
file contains a declaration that creates a Struct security model object named struct
. Consequently, inclusion of the basic.psl
file into the solution security policy description will create a Struct security model object by default.
A Struct security model object does not have any parameters and cannot be covered by a security audit.
It is not necessary to create additional Struct security model objects.
Struct security model methods
The Struct security model contains expressions that provide access to structural data elements. To call these expressions, use the following constructs:
- <
dictionary
>.
<field name
> – "get access to dictionary field". The type of returned data corresponds to the type of dictionary field. - <
List | Set | Sequence | Array
>.[
<element number
>]
– "get access to data element". The type of returned data corresponds to the type of elements. The numbering of elements starts with zero. When out of bounds of dataset, the expression terminates with an error and the Kaspersky Security Module returns the "denied" decision. - <
HandleDesc
>.handle
– "get SID". Returns values of theHandle
type. (For details on the correlation between handles and SID values, see "Resource Access Control"). - <
HandleDesc
>.rights
– "get handle permissions mask". Returns values of theUInt32
type.
Parameters of interface methods are saved in a special dictionary named message
. To obtain access to an interface method parameter, use the following construct:
The parameter name is specified in accordance with the IDL description.
To obtain access to structural elements of parameters, use the constructs corresponding to expressions of the Struct security model.
To use expressions of the Struct security model, the security event description must be sufficiently precise so that it corresponds to IPC messages of the same type (for more details, see "Binding methods of security models to security events"). IPC messages of this type must contain the defined parameters of the interface method, and the interface method parameters must contain the defined structural elements.
Base security model
The Base security model implements basic logic.
A PSL file containing a description of the Base security model is located in the KasperskyOS SDK at the following path:
toolchain/include/nk/base.psl
Base security model object
The base.psl
file contains a declaration that creates a Base security model object named base
. Consequently, inclusion of the base.psl
file into the solution security policy description will create a Base security model object by default. Methods of this object can be called without indicating the object name.
A Base security model object does not have any parameters.
A Base security model object can be covered by a security audit. There are no audit conditions specific to the Base security model.
It is necessary to create additional objects of the Base security model in the following cases:
- You need to configure a security audit differently for different objects of the Base security model (for example, you can apply different audit profiles or different audit configurations of the same profile for different objects).
- You need to distinguish between calls of methods provided by different objects of the Base security model (audit data includes the name of the security model method and the name of the object that provides this method, so you can verify that the method of a specific object was called).
Base security model methods
The Base security model contains the following rules:
grant ()
It has a parameter of the
()
type. It returns the "granted" result.Example:
/* A client of the foo class is allowed * to query a server of the bar class. */ request src=foo dst=bar { grant () }assert (
<Boolean
>)
It returns the "granted" result if the
true
value is passed via the parameter. Otherwise it returns the "denied" result.Example:
/* Any client in the solution will be allowed to query a server of the foo class * by calling the Send method of the net.Net endpoint if the port parameter * of the Send method will be used to pass a value greater than 80. Otherwise any * client in the solution will be prohibited from querying a server of the * foo class by calling the Send method of the net.Net endpoint. */ request dst=foo endpoint=net.Net method=Send { assert (message.port > 80) }deny (
<Boolean
>) | ()
It returns the "denied" result if the
true
or()
value is passed via the parameter. Otherwise it returns the "granted" result.Example:
/* A server of the foo class is not allowed to * respond to a client of the bar class. */ response src=foo dst=bar { deny () }set_level (
<UInt8
>)
It sets the security audit runtime-level equal to the value passed via this parameter. It returns the "granted" result. (For more details about the security audit runtime-level, see "Describing security audit profiles".)
Example:
/* A process of the foo class will receive the "allowed" decision from the * Kaspersky Security Module if it calls the * SetAuditLevel security interface method to change the security audit runtime-level. */ security src=foo method=SetAuditLevel { set_level (message.audit_level) }
Regex security model
The Regex security model implements text data validation based on statically defined regular expressions.
A PSL file containing a description of the Regex security model is located in the KasperskyOS SDK at the following path:
toolchain/include/nk/regex.psl
Regex security model object
The regex.psl
file contains a declaration that creates a Regex security model object named re
. Consequently, inclusion of the regex.psl
file into the solution security policy description will create a Regex security model object by default.
A Regex security model object does not have any parameters.
A Regex security model object can be covered by a security audit. In this case, you also need to define the audit conditions specific to the Regex security model. To do so, use the following constructs in the audit configuration description:
emit : ["match"]
– the audit is performed if thematch
method is called.emit : ["select"]
– the audit is performed if theselect
method is called.emit : ["match", "select"]
– the audit is performed if thematch
orselect
method is called.emit : []
– the audit is not performed.
It is necessary to create additional objects of the Regex security model in the following cases:
- You need to configure a security audit differently for different objects of the Regex security model (for example, you can apply different audit profiles or different audit configurations of the same profile for different objects).
- You need to distinguish between calls of methods provided by different objects of the Regex security model (audit data includes the name of the security model method and the name of the object that provides this method, so you can verify that the method of a specific object was called).
Regex security model methods
The Regex
security model contains the following expressions:
match {text :
<Text
>, pattern :
<Text
>}
Returns a value of the
Boolean
type. If the specifiedtext
matches thepattern
regular expression, it returnstrue
. Otherwise it returnsfalse
.Example:
assert (re.match {text : message.text, pattern : "[0-9]*"})select {text :
<Text
>}
It is intended to be used as an expression that verifies fulfillment of the conditions in the
choice
construct (for details on thechoice
construct, see "Binding methods of security models to security events"). It checks whether the specifiedtext
matches regular expressions. Depending on the results of this check, various options for security event handling can be performed.Example:
choice (re.select {text : "hello world"}) { "hello\ .*": grant () ".*world" : grant () _ : deny () }
Syntax of regular expressions of the Regex security model
A regular expression for the match
method of the Regex security model can be written in two ways: within the multi-line regex
block or as a text literal.
When writing a regular expression as a text literal, all backslash instances must be doubled.
For example, the following two regular expressions are identical:
Regular expressions for the select
method of the Regex security model are written as text literals with a double backslash.
A regular expression is defined as a template string and may contain the following:
- Literals (ordinary characters)
- Metacharacters (characters with special meanings)
- White-space characters
- Character sets
- Character groups
- Operators for working with characters
Regular expressions are case sensitive.
Literals and metacharacters in regular expressions
- A literal can be any ASCII character except the metacharacters
.()*&|!?+[]\
and a white-space character. (Unicode characters are not supported.)For example, the regular expression
KasperskyOS
corresponds to the textKasperskyOS
. - Metacharacters have special meanings that are presented in the table below.
Special meanings of metacharacters
Metacharacter
Special meaning
[]
Square brackets (braces) denote the beginning and end of a set of characters.
()
Round brackets (parentheses) denote the beginning and end of a group of characters.
*
An asterisk denotes an operator indicating that the character preceding it can repeat zero or more times.
+
A plus sign denotes an operator indicating that the character preceding it can repeat one or more times.
?
A question mark denotes an operator indicating that the character preceding it can repeat zero or one time.
!
An exclamation mark denotes an operator excluding the subsequent character from the list of valid characters.
|
A vertical line denotes an operator for selection between characters (logically close to the "OR" conjunction).
&
An ampersand denotes an operator for overlapping of multiple conditions (logically close to the "AND" conjunction).
.
A dot denotes any character.
For example, the regular expression
K.S
corresponds to the sequences of charactersKOS
,KoS
,KES
and a multitude of other sequences consisting of three characters that begin withK
and end withS
, and in which the second character can be any character: literal, metacharacter, or dot.\
\
<metaSymbol
>A backslash indicates that the metacharacter that follows it will lose its special meaning and instead be interpreted as a literal. A backslash placed before a metacharacter is known as an escape character.
For example, a regular expression that consists of a dot metacharacter (
.
) corresponds to any character. However, a regular expression that consists of a backslash with a dot (\.
) corresponds to only a dot character.Accordingly, a backslash also escapes another subsequent backslash. For example, the regular expression
C:\\Users
corresponds to the sequence of charactersC:\Users
. - The
^
and$
characters are not used to designate the start and end of a line.
White-space characters in regular expressions
- A space character has an ASCII code of
20
in a hexadecimal number system and has an ASCII code of40
in an octal number system. Although a space character does not infer any special meaning, it must be escaped to avoid any ambiguous interpretation by the regular expression interpreter.For example, the regular expression
Hello\ world
corresponds to the sequence of charactersHello world
. \r
Carriage return character.
\n
Line break character.
\t
Horizontal tab character.
Definition of a character based on its octal or hexadecimal code in regular expressions
\x{
<hex
>}
Definition of a character using its
hex
code from the ASCII character table. The character code must be less than0x100
.For example, the regular expression
Hello\x{20}world
corresponds to the sequence of charactersHello world
.\o{
<octal
>}
Definition of a character using its
octal
code from the ASCII character table. The character code must be less than0o400
.For example, the regular expression
\o{75}
corresponds to the=
character.
Sets of characters in regular expressions
A character set is defined within square brackets []
as a list or range of characters. A character set tells the regular expression interpreter that only one of the characters listed in the set or range of characters can be at this specific location in a sequence of characters. A character set cannot be left blank.
[
<BracketSpec
>]
– character set.One character corresponds to any character from the
BracketSpec
character set.For example, the regular expression
K[OE]S
corresponds to the sequences of charactersKOS
andKES
.[^
<BracketSpec
>]
– inverted character set.One character corresponds to any character that is not in the
BracketSpec
character set.For example, the regular expression
K[^OE]S
corresponds to the sequences of charactersKAS
,K8S
and any other sequences consisting of three characters that begin withK
and end withS
, excludingKOS
andKES
.
The BracketSpec
character set can be listed explicitly or can be defined as a range of characters. When defining a range of characters, the first and last character in the set must be separated with a hyphen.
[
<Digit1
>-
<DigitN
>]
Any number from the range
Digit1
,Digit2
, ... ,DigitN
.For example, the regular expression
[0-9]
corresponds to any numerical digit. The regular expressions[0-9]
and[0123456789]
are identical.Please note that a range is defined by one character before a hyphen and one character after the hyphen. The regular expression
[1-35]
corresponds only to the characters1
,2
,3
and5
, and does not represent the range of numbers from1
to35
.[
<Letter1
>-
<LetterN
>]
Any English letter from the range
Letter1
,Letter2
, ... ,LetterN
(these letters must be in the same case).For example, the regular expression
[a-zA-Z]
corresponds to all letters in uppercase and lowercase from the ASCII character table.
The ASCII code for the upper boundary character of a range must be higher than the ASCII code for the lower boundary character of the range.
For example, the regular expressions [5-2]
or [z-a]
are invalid.
The hyphen (minus) -
character is interpreted as a special character only within a set of characters. Outside of a character set, a hyphen is a literal. For this reason, the \
metacharacter does not have to precede a hyphen. To use a hyphen as a literal within a character set, it must be indicated first or last in the set.
Examples:
The regular expressions [-az]
and [az-]
correspond to the characters a
, z
and -
.
The regular expression [a-z]
corresponds to any of the 26 English letters from a
to z
in lowercase.
The regular expression [-a-z]
corresponds to any of the 26 English letters from a
to z
in lowercase and -
.
The circumflex (caret character) ^
is interpreted as a special character only within a character set when it is located directly after an opening square bracket. Outside of a character set, a circumflex is a literal. For this reason, the \
metacharacter does not have to precede a circumflex. To use a circumflex as a literal within a character set, it must be indicated in a location other than first in the set.
Examples:
The regular expression [0^9]
correspond to the characters 0
, 9
and ^
.
The regular expression [^09]
corresponds to any character except 0
and 9
.
Within a character set, the metacharacters *.&|!?+
lose their special meaning and are instead interpreted as literals. Therefore, they do not have to be preceded by the \
metacharacter. The backslash \
retains its special meaning within a character set.
For example, the regular expressions [a.]
and [a\.]
are identical and correspond to the character a
and a dot interpreted as a literal.
Groups of characters and operators in regular expressions
A character group uses parentheses ()
to distinguish its portion (subexpression) within a regular expression. Groups are normally used to allocate subexpressions as operands. Groups can be embedded into each other.
Operators are applied to more than one character in a regular expression only if they are immediately before or after the definition of a set or group of characters. If this is the case, the operator is applied to the entire group or set of characters.
The syntax contains definitions of the following operators (listed in descending order of their priority):
!
<Expression
>, whereExpression
can be a character, set or group of characters.This operator excludes the
Expression
from the list of valid expressions.Examples:
The regular expression
K!OS
corresponds to the sequences of charactersKoS
,KES
, and a multitude of other sequences that consist of three characters and begin withK
and end withS
, excludingKOS
.The regular expression
K!(OS)
corresponds to the sequences of charactersKos
,KES
,KOT
, and a multitude of other sequences that consist of three characters and begin withK
, excludingKOS
.The regular expression
K![OE]S
corresponds to the sequences of charactersKoS
,KeS
,K;S
, and a multitude of other sequences that consist of three characters and begin withK
and end withS
, excludingKOS
andKES
.- <
Expression
>*
, whereExpression
can be a character, set or group of characters.This operator means that the
Expression
may occur in the specific position zero or more times.Examples:
The regular expression
0-9*
corresponds to the sequences of characters0-
,0-9
,0-99
, ... .The regular expression
(0-9)*
corresponds to the empty sequence""
and the sequences of characters0-9
,0-90-9
, ... .The regular expression
[0-9]*
corresponds to the empty sequence""
and any non-empty sequence of numbers. - <
Expression
>+
, whereExpression
can be a character, set or group of characters.This operator means that the
Expression
may occur in the specific position one or more times.Examples:
The regular expression
0-9+
corresponds to the sequences of characters0-9
,0-99
,0-999
, ... .The regular expression
(0-9)+
corresponds to the sequences of characters0-9
,0-90-9
, ... .The regular expression
[0-9]+
corresponds to any non-empty sequence of numbers. - <
Expression
>?
, whereExpression
can be a character, set or group of characters.This operator means that the
Expression
may occur in the specific position zero or one time.Examples:
The regular expression
https?://
corresponds to the sequences of charactershttp://
andhttps://
.The regular expression
K(aspersky)?OS
corresponds to the sequences of charactersKOS
andKasperskyOS
. - <
Expression1
><Expression2
> – concatenation.Expression1
andExpression2
can be characters, sets or groups of characters.This operator does not have a specific designation. In the resulting expression,
Expression2
followsExpression1
.For example, concatenation of the sequences of characters
micro
andkernel
will result in the sequence of charactersmicrokernel
. - <
Expression1
>|
<Expression2
> – disjunction.Expression1
andExpression2
can be characters, sets or groups of characters.This operator selects either
Expression1
orExpression2
.Examples:
The regular expression
KO|ES
corresponds to the sequences of charactersKO
andES
, but notKOS
orKES
because the concatenation operator has a higher priority than the disjunction operator.The regular expression
Press (OK|Cancel)
corresponds to the sequences of charactersPress OK
orPress Cancel
.The regular expression
[0-9]|()
corresponds to numbers from0
to9
or an empty string. - <
Expression1
>&
<Expression2
> – conjunction.Expression1
andExpression2
can be characters, sets or groups of characters.This operator intersects the result of
Expression1
with the result ofExpression2
.Examples:
The regular expression
[0-9]&[^3]
corresponds to numbers from0
to9
, excluding3
.The regular expression
[a-zA-Z]&()
corresponds to all English letters and an empty string.
HashSet security model
The HashSet security model associates resources with one-dimensional tables of unique values of the same type, adds or deletes these values, and checks whether a defined value is in the table. For example, a process of the network server can be associated with the set of ports that this server is allowed to open. This association can be used to check whether the server is allowed to initiate the opening of a port.
A PSL file containing a description of the HashSet security model is located in the KasperskyOS SDK at the following path:
toolchain/include/nk/hashmap.psl
HashSet security model object
To use the HashSet security model, you need to create an object or objects of this model.
A HashSet security model object contains a pool of one-dimensional tables of the same size intended for storing the values of one type. A resource can be associated with only one table from the tables pool of each HashSet security model object.
A HashSet security model object has the following parameters:
type Entry
– type of values in tables (these can be integer types,Boolean
type, and dictionaries and tuples based on integer types and theBoolean
type).config
– configuration of the pool of tables:set_size
– size of the table.pool_size
– number of tables in the pool.
All parameters of a HashSet security model object are required.
Example:
A HashSet security model object can be covered by a security audit. There are no audit conditions specific to the HashSet security model.
It is necessary to create multiple objects of the HashSet security model in the following cases:
- You need to configure a security audit differently for different objects of the HashSet security model (for example, you can apply different audit profiles or different audit configurations of the same profile for different objects).
- You need to distinguish between calls of methods provided by different objects of the HashSet security model (audit data includes the name of the security model method and the name of the object that provides this method, so you can verify that the method of a specific object was called).
- You need to use tables of different sizes and/or with different types of values.
HashSet security model init rule
It associates a free table from the tables pool with the sid
resource. If the free table contains values after its previous use, these values are deleted.
It returns the "allowed" result if an association was created between the table and the sid
resource.
It returns the "denied" result in the following cases:
- There are no free tables in the pool.
- The
sid
resource is already associated with a table from the tables pool of the HashSet security model object being used. - The
sid
value is outside of the permissible range.
Example:
HashSet security model fini rule
It deletes the association between the table and the sid
resource (the table becomes free).
It returns the "allowed" result if the association between the table and the sid
resource was deleted.
It returns the "denied" result in the following cases:
- The
sid
resource is not associated with a table from the tables pool of the HashSet security model object being used. - The
sid
value is outside of the permissible range.
HashSet security model add rule
It adds the entry
value to the table associated with the sid
resource.
It returns the "allowed" result in the following cases:
- The rule added the
entry
value to the table associated with thesid
resource. - The table associated with the
sid
resource already contains theentry
value.
It returns the "denied" result in the following cases:
- The table associated with the
sid
resource is completely full. - The
sid
resource is not associated with a table from the tables pool of the HashSet security model object being used. - The
sid
value is outside of the permissible range.
Example:
HashSet security model remove rule
It deletes the entry
value from the table associated with the sid
resource.
It returns the "allowed" result in the following cases:
- The rule deleted the
entry
value from the table associated with thesid
resource. - The table associated with the
sid
resource does not contain theentry
value.
It returns the "denied" result in the following cases:
- The
sid
resource is not associated with a table from the tables pool of the HashSet security model object being used. - The
sid
value is outside of the permissible range.
HashSet security model contains expression
It checks whether the entry
value is in the table associated with the sid
resource.
It returns a value of the Boolean
type. If the entry
value is in the table associated with the sid
resource, it returns true
. Otherwise it returns false
.
It runs incorrectly in the following cases:
- The
sid
resource is not associated with a table from the tables pool of the HashSet security model object being used. - The
sid
value is outside of the permissible range.
When the expression runs incorrectly, the Kaspersky Security Module returns the "denied" decision.
Example:
StaticMap security model
The StaticMap security model associates resources with two-dimensional "key–value" tables, reads and modifies the values of keys. For example, a process of the driver can be associated with the MMIO memory region that this driver is allowed to use. This will require two keys whose values define the base address and the size of the MMIO memory region. This association can be used to check whether the driver can query the MMIO memory region that it is attempting to access.
Keys in the table have the same type but are unique and immutable. The values of keys in the table have the same type.
There are two simultaneous instances of the table: base instance and working instance. Both instances are initialized by the same data. Changes are made first to the working instance and then can be added to the base instance, or vice versa: the working instance can be changed by using previous values from the base instance. The values of keys can be read from the base instance or working instance of the table.
A PSL file containing a description of the StaticMap security model is located in the KasperskyOS SDK at the following path:
toolchain/include/nk/staticmap.psl
StaticMap security model object
To use the StaticMap security model, you need to create an object or objects of this model.
A StaticMap security model object contains a pool of two-dimensional "key–value" tables that have the same size. A resource can be associated with only one table from the tables pool of each StaticMap security model object.
A StaticMap security model object has the following parameters:
type Value
– type of values of keys in tables (integer types are supported).config
– configuration of the pool of tables:keys
– table containing keys and their default values (keys have theKey = Text | List<UInt8>
type).pool_size
– number of tables in the pool.
All parameters of a StaticMap security model object are required.
Example:
A StaticMap security model object can be covered by a security audit. There are no audit conditions specific to the StaticMap security model.
It is necessary to create multiple objects of the StaticMap security model in the following cases:
- You need to configure a security audit differently for different objects of the StaticMap security model (for example, you can apply different audit profiles or different audit configurations of the same profile for different objects).
- You need to distinguish between calls of methods provided by different objects of the StaticMap security model (audit data includes the name of the security model method and the name of the object that provides this method, so you can verify that the method of a specific object was called).
- You need to use tables with different sets of keys and/or different types of key values.
StaticMap security model init rule
It associates a free table from the tables pool with the sid
resource. Keys are initialized by the default values.
It returns the "allowed" result if an association was created between the table and the sid
resource.
It returns the "denied" result in the following cases:
- There are no free tables in the pool.
- The
sid
resource is already associated with a table from the tables pool of the StaticMap security model object being used. - The
sid
value is outside of the permissible range.
Example:
StaticMap security model fini rule
It deletes the association between the table and the sid
resource (the table becomes free).
It returns the "allowed" result if the association between the table and the sid
resource was deleted.
It returns the "denied" result in the following cases:
- The
sid
resource is not associated with a table from the tables pool of the StaticMap security model object being used. - The
sid
value is outside of the permissible range.
StaticMap security model set rule
It assigns the specified value
to the specified key
in the working instance of the table associated with the sid
resource.
It returns the "allowed" result if the specified value
was assigned to the specified key
in the working instance of the table associated with the sid
resource. (The current value of the key will be overwritten even if it is equal to the new value.)
It returns the "denied" result in the following cases:
- The specified
key
is not in the table associated with thesid
resource. - The
sid
resource is not associated with a table from the tables pool of the StaticMap security model object being used. - The
sid
value is outside of the permissible range.
Example:
StaticMap security model commit rule
It copies the values of keys from the working instance to the base instance of the table associated with the sid
resource.
It returns the "allowed" result if the values of keys were copied from the working instance to the base instance of the table associated with the sid
resource.
It returns the "denied" result in the following cases:
- The
sid
resource is not associated with a table from the tables pool of the StaticMap security model object being used. - The
sid
value is outside of the permissible range.
StaticMap security model rollback rule
It copies the values of keys from the base instance to the working instance of the table associated with the sid
resource.
It returns the "allowed" result if the values of keys were copied from the base instance to the working instance of the table associated with the sid
resource.
It returns the "denied" result in the following cases:
- The
sid
resource is not associated with a table from the tables pool of the StaticMap security model object being used. - The
sid
value is outside of the permissible range.
StaticMap security model get expression
It returns the value of the specified key
from the base instance of the table associated with the sid
resource.
It returns a value of the Value
type.
It runs incorrectly in the following cases:
- The specified
key
is not in the table associated with thesid
resource. - The
sid
resource is not associated with a table from the tables pool of the StaticMap security model object being used. - The
sid
value is outside of the permissible range.
When the expression runs incorrectly, the Kaspersky Security Module returns the "denied" decision.
Example:
StaticMap security model get_uncommitted expression
It returns the value of the specified key
from the working instance of the table associated with the sid
resource.
It returns a value of the Value
type.
It runs incorrectly in the following cases:
- The specified
key
is not in the table associated with thesid
resource. - The
sid
resource is not associated with a table from the tables pool of the StaticMap security model object being used. - The
sid
value is outside of the permissible range.
When the expression runs incorrectly, the Kaspersky Security Module returns the "denied" decision.
Page top
Flow security model
The Flow security model associates resources with finite-state machines, receives and modifies the states of finite-state machines, and checks whether the state of the finite-state machine is within the defined set of states. For example, a process can be associated with a finite-state machine to allow or prohibit this process from using storage and/or the network depending on the state of the finite-state machine.
A PSL file containing a description of the Flow security model is located in the KasperskyOS SDK at the following path:
toolchain/include/nk/flow.psl
Flow security model object
To use the Flow security model, you need to create an object or objects of this model.
One Flow security model object associates a set of resources with a set of finite-state machines that have the same configuration. A resource can be associated with only one finite-state machine of each Flow security model object.
A Flow security model object has the following parameters:
type State
– type that determines the set of states of the finite-state machine (variant type that combines text literals).config
– configuration of the finite-state machine:states
– set of states of the finite-state machine (must match the set of states defined by theState
type).initial
– initial state of the finite-state machine.transitions
– description of the permissible transitions between states of the finite-state machine.
All parameters of a Flow security model object are required.
Example:
Diagram of finite-state machine states in the example
A Flow security model object can be covered by a security audit. You can also define the audit conditions specific to the Flow security model. To do so, use the following construct in the audit configuration description:
omit : [
<"state 1"
>[, ...
]]
– the audit is not performed if the finite-state machine is in one of the listed states.
It is necessary to create multiple objects of the Flow security model in the following cases:
- You need to configure a security audit differently for different objects of the Flow security model (for example, you can apply different audit profiles or different audit configurations of the same profile for different objects).
- You need to distinguish between calls of methods provided by different objects of the Flow security model (audit data includes the name of the security model method and the name of the object that provides this method, so you can verify that the method of a specific object was called).
- You need to use finite-state machines with different configurations.
Flow security model init rule
It creates a finite-state machine and associates it with the sid
resource. The created finite-state machine has the configuration defined in the settings of the Flow security model object being used.
It returns the "granted" result if an association was created between the finite-state machine and the sid
resource.
It returns the "denied" result in the following cases:
- The
sid
resource is already associated with a finite-state machine of the Flow security model object being used. - The
sid
value is outside of the permissible range.
Example:
Flow security model fini rule
It deletes the association between the finite-state machine and the sid
resource. The finite-state machine that is no longer associated with the resource is destroyed.
It returns the "granted" result if the association between the finite-state machine and the sid
resource was deleted.
It returns the "denied" result in the following cases:
- The
sid
resource is not associated with a finite-state machine of the Flow security model object being used. - The
sid
value is outside of the permissible range.
Flow security model enter rule
It switches the finite-state machine associated with the sid
resource to the specified state
.
It returns the "granted" result if the finite-state machine associated with the sid
resource was switched to the specified state
.
It returns the "denied" result in the following cases:
- The transition to the specified
state
from the current state is not permitted by the configuration of the finite-state machine associated with thesid
resource. - The
sid
resource is not associated with a finite-state machine of the Flow security model object being used. - The
sid
value is outside of the permissible range.
Example:
Flow security model allow rule
It verifies that the state of the finite-state machine associated with the sid
is in the set of defined states
.
It returns the "granted" result if the state of the finite-state machine associated with the sid
resource is in the set of defined states
.
It returns the "denied" result in the following cases:
- The state of the finite-state machine associated with the
sid
resource is not in the set of definedstates
. - The
sid
resource is not associated with a finite-state machine of the Flow security model object being used. - The
sid
value is outside of the permissible range.
Example:
Flow security model query expression
It is intended to be used as an expression that verifies fulfillment of the conditions in the choice
construct (for details on the choice
construct, see "Binding methods of security models to security events"). It checks the state of the finite-state machine associated with the sid
resource. Depending on the results of this check, various options for security event handling can be performed.
It runs incorrectly in the following cases:
- The
sid
resource is not associated with a finite-state machine of the Flow security model object being used. - The
sid
value is outside of the permissible range.
When the expression runs incorrectly, the Kaspersky Security Module returns the "denied" decision.
Example:
Mic security model
The Mic security model implements mandatory integrity control. In other words, this security model provides the capability to manage data streams between different processes and between processes and the KasperskyOS kernel by controlling the integrity levels of processes, the kernel, and resources that are used via IPC.
In Mic security model terminology, processes and the kernel are called subjects while resources are called objects. However, the information provided in this section slightly deviates from the terminology of the Mic security model. In this section, the term "object" is not used to refer to a "resource".
Data streams are generated between subjects when the subjects interact via IPC.
The integrity level of a subject/resource is the level of trust afforded to the subject/resource. The degree of trust in a subject depends on whether the subject interacts with untrusted external software/hardware systems or whether the subject has a proven quality level, for example. (The kernel has a high level of integrity.) The degree of trust in a resource depends on whether this resource was created by a trusted subject within a software/hardware system running KasperskyOS or if it was received from an untrusted external software/hardware system, for example.
The Mic security model is characterized by the following provisions:
- By default, data streams from subjects with less integrity to subjects with higher integrity are prohibited. You have the option of permitting such data streams if you can guarantee that the subjects with higher integrity will not be compromised.
- A resource consumer is prohibited from writing data to a resource if the integrity level of the resource is higher than the integrity level of the resource consumer.
- By default, a resource consumer is prohibited from reading data from a resource if the integrity level of the resource is lower than the integrity level of the resource consumer. You have the option to allow the resource consumer to perform such an operation if you can guarantee that the resource consumer will not be compromised.
Methods of a Mic security model let you perform the following operations:
- Assign integrity levels to subjects and resources.
- Unassign the integrity level from resources.
- Verify the permissibility of data streams based on a comparison of integrity levels.
- Increase the integrity levels of resources.
A PSL file containing a description of the Mic security model is located in the KasperskyOS SDK at the following path:
toolchain/include/nk/mic.psl
For an example of using the Mic security model, we can examine a secure software update for a software/hardware system running KasperskyOS. Four processes are involved in the update:
Downloader
is a low-integrity process that downloads a low-integrity update image from a remote server on the Internet.Verifier
is a high-integrity process that verifies the digital signature of the low-integrity update image (high-integrity process that can read data from a low-integrity resource).FileSystem
is a high-integrity process that manages the file system.Updater
is a high-integrity process that applies an update.
A software update is performed according to the following scenario:
- The
Downloader
downloads an update image and saves it to a file by transferring the contents of the image to theFileSystem
. A low integrity level is assigned to this file. - The
Verifier
receives the update image from theFileSystem
by reading the high-integrity file, and verifies its digital signature. If the signature is correct, theVerifier
queries theFileSystem
so that theFileSystem
creates a copy of the file containing the update image. A high integrity level is assigned to the new file. - The
Updater
receives the update image from theFileSystem
by reading the high-integrity file, and applies the update.
In this example, the Mic security model ensures that the high-integrity Updater
process can read data only from a high-integrity update image. As a result, the update can be applied only after the digital signature of the update image is verified.
Mic security model object
To use the Mic security model, you need to create an object or objects of this model. You also need to assign a set of integrity levels for subjects and resources.
A Mic security model object has the following parameters:
config
– set of integrity levels or configuration of a set of integrity levels:degrees
– set of gradations for generating a set of integrity levels.categories
– set of categories for generating a set of integrity levels.
Examples:
A set of integrity levels is a partially ordered set that is linearly ordered or contains incomparable elements. The set {LOW, MEDIUM, HIGH} is linearly ordered because all of its elements are comparable to each other. Incomparable elements arise when a set of integrity levels is defined through a set of gradations and a set of categories. In this case, the set of integrity levels L is a Cartesian product of the Boolean set of categories C multiplied by the set of gradations D:
The degrees
and categories
parameters in this example define the following set:
{
{}/low, {}/high,
{net}/low, {net}/high,
{log}/low, {log}/high,
{net,log}/low, {net,log}/high
}
In this set, {} means an empty set.
The order relation between elements of the set of integrity levels L is defined as follows:
According to this order relation, the jth element exceeds the ith element if the subset of categories E includes the subset of categories A, and gradation F is greater than or equal to gradation A. Examples of comparing elements of the set of integrity levels L:
- The {net,log}/high element exceeds the {log}/low element because the "high" gradation is greater than the "low" gradation, and the subset of categories {net,log} includes the subset of categories {log}.
- The {net,log}/low element exceeds the {log}/low element because the levels of gradations for these elements are equal, and the subset of categories {net,log} includes the subset of categories {log}.
- The {net,log}/high element is the highest because it exceeds all other elements.
- The {}/low element is the lowest because all other elements exceed this element.
- The {net}/low and {log}/high elements are incomparable because the "high" gradation is greater than the "low" gradation but the subset of categories {log} does not include the subset of categories {net}.
- The {net,log}/low and {log}/high elements are incomparable because the "high" gradation is greater than the "low" gradation but the subset of categories {log} does not include the subset of categories {net,log}.
For subjects and resources that have incomparable integrity levels, the Mic security model provides conditions that are analogous to the conditions that the security model provides for subjects and resources that have comparable integrity levels.
By default, data streams between subjects that have incomparable integrity levels are prohibited. However, you have the option to allow such data streams if you can guarantee that the subjects receiving data will not be compromised. A resource consumer is prohibited from writing data to a resource and read data from a resource if the integrity level of the resource is incomparable to the integrity level of the resource consumer. You have the option to allow the resource consumer to read data from a resource if you can guarantee that the resource consumer will not be compromised.
A Mic security model object can be covered by a security audit. There are no audit conditions specific to the Mic security model.
It is necessary to create multiple objects of the Mic security model in the following cases:
- You need to configure a security audit differently for different objects of the Mic security model (for example, you can apply different audit profiles or different audit configurations of the same profile for different objects).
- You need to distinguish between calls of methods provided by different objects of the Mic security model (audit data includes the name of the security model method and the name of the object that provides this method, so you can verify that the method of a specific object was called).
- You need to use multiple variants of mandatory integrity control that may have different sets of integrity levels for subjects and resources, for example.
Mic security model create rule
Assign the specified integrity level
to the target
resource in the following situation:
- The
source
process initiates creation of thetarget
resource. - The
target
resource is managed by thedriver
subject, which is the resource provider or the KasperskyOS kernel. - The
container
resource is a container for thetarget
resource (for example, a directory is a container for files and/or other directories).
If the container
field has the value ()
, the target
resource is considered to be the root resource, which means that it has no container.
To define the integrity level
, values of the Level
type are used:
The rule returns the "granted" result if a specific integrity level
was assigned to the target
resource.
The rule returns the "denied" result in the following cases:
- The
level
value exceeds the integrity level of thesource
process,driver
subject orcontainer
resource. - The
level
value is incomparable to the integrity level of thesource
process,driver
subject orcontainer
resource. - An integrity level was not assigned to the
source
process,driver
subject, orcontainer
resource. - The value of
source
,target
,container
ordriver
is outside of the permissible range.
Example:
Mic security model delete rule
Unassigns the integrity level from the target
resource in the following situation:
- The
source
process initiates deletion of thetarget
resource. - The
target
resource is managed by thedriver
subject, which is the resource provider or the KasperskyOS kernel. - The
container
resource is a container for thetarget
resource (for example, a directory is a container for files and/or other directories).
If the container
field has the value ()
, the target
resource is considered to be the root resource, which means that it has no container.
The rule returns the "granted" result if it unassigned the integrity level from the target
resource.
The rule returns the "denied" result in the following cases:
- The integrity level of the
target
resource exceeds the integrity level of thesource
process ordriver
subject. - The integrity level of the
target
resource is incomparable to the integrity level of thesource
process ordriver
subject. - An integrity level was not assigned to the
source
process,driver
subject,target
resource orcontainer
resource. - The value of
source
,target
,container
ordriver
is outside of the permissible range.
Example:
Mic security model execute rule
This assigns the specified integrity level
to the target
subject and defines the minimum integrity level of subjects and resources from which this subject can receive data (levelR
). The code of the target
subject is in the image
executable file.
If the level
field has the value ()
, the integrity level of the image
executable file is assigned to the target
subject. If the image
field has the value ()
, the level
field must have a value other than ()
.
If the levelR
field has the value ()
, the levelR
integrity level is assumed to be equal to the integrity level of the target
subject.
To define the integrity level
and levelR
, values of the Level
type are used. For the definition of the Level
type, see "Mic security model create rule".
The rule returns the "granted" result if it assigned the specified integrity level
to the target
subject and defined the minimum integrity level of subjects and resources from which this subject can receive data (levelR
).
The rule returns the "denied" result in the following cases:
- The
level
value exceeds the integrity level of theimage
executable file. - The
level
value is incomparable to the integrity level of theimage
executable file. - The value of
levelR
exceeds the value oflevel
. - The
level
andlevelR
values are incomparable. - An integrity level was not assigned to the
image
executable file. - The
image
ortarget
value is outside of the permissible range.
Example:
Mic security model upgrade rule
This elevates the previously assigned integrity level of the target
resource to the specified level
in the following situation:
- The
source
process initiates elevation of the integrity level of thetarget
resource. - The
target
resource is managed by thedriver
subject, which is the resource provider or the KasperskyOS kernel. - The
container
resource is a container for thetarget
resource (for example, a directory is a container for files and/or other directories).
If the container
field has the value ()
, the target
resource is considered to be the root resource, which means that it has no container.
To define the integrity level
, values of the Level
type are used. For the definition of the Level
type, see "Mic security model create rule".
The rule returns the "granted" result if it elevated the previously assigned integrity level of the target
resource to the level
value.
The rule returns the "denied" result in the following cases:
- The
level
value does not exceed the integrity level of thetarget
resource. - The
level
value exceeds the integrity level of thesource
process,driver
subject orcontainer
resource. - The integrity level of the
target
resource exceeds the integrity level of thesource
process. - An integrity level was not assigned to the
source
process,driver
subject, orcontainer
resource. - The value of
source
,target
,container
ordriver
is outside of the permissible range.
Mic security model call rule
This verifies the permissibility of data streams from the target
subject to the source
subject.
It returns the "allowed" result in the following cases:
- The integrity level of the
source
subject does not exceed the integrity level of thetarget
subject. - The integrity level of the
source
subject exceeds the integrity level of thetarget
subject, but the minimum integrity level of subjects and resources from which thesource
subject can receive data does not exceed the integrity level of thetarget
subject. - The integrity level of the
source
subject is incomparable to the integrity level of thetarget
subject, but the minimum integrity level of subjects and resources from which thesource
subject can receive data does not exceed the integrity level of thetarget
subject.
It returns the "denied" result in the following cases:
- The integrity level of the
source
subject exceeds the integrity level of thetarget
subject, and the minimum integrity level of subjects and resources from which thesource
subject can receive data exceeds the integrity level of thetarget
subject. - The integrity level of the
source
subject exceeds the integrity level of thetarget
subject, and the minimum integrity level of subjects and resources from which thesource
subject can read data is incomparable to the integrity level of thetarget
subject. - The integrity level of the
source
subject is incomparable to the integrity level of thetarget
subject, and the minimum integrity level of subjects and resources from which thesource
subject can receive data exceeds the integrity level of thetarget
subject. - The integrity level of the
source
subject is incomparable to the integrity level of thetarget
subject, and the minimum integrity level of subjects and resources from which thesource
subject can receive data is incomparable to the integrity level of thetarget
subject. - An integrity level was not assigned to the
source
subject or to thetarget
subject. - The
source
ortarget
value is outside of the permissible range.
Example:
Mic security model invoke rule
This verifies the permissibility of data streams from the source
subject to the target
subject.
It returns the "granted" result if the integrity level of the target
subject does not exceed the integrity level of the source
subject.
It returns the "denied" result in the following cases:
- The integrity level of the
target
subject exceeds the integrity level of thesource
subject. - The integrity level of the
target
subject is incomparable to the integrity level of thesource
subject. - An integrity level was not assigned to the
source
subject or to thetarget
subject. - The
source
ortarget
value is outside of the permissible range.
Mic security model read rule
This verifies that the source
resource consumer is allowed to read data from the target
resource.
It returns the "allowed" result in the following cases:
- The integrity level of the
source
resource consumer does not exceed the integrity level of thetarget
resource. - The integrity level of the
source
resource consumer exceeds the integrity level of thetarget
resource, but the minimum integrity level of subjects and resources from which thesource
resource consumer can receive data does not exceed the integrity level of thetarget
resource. - The integrity level of the
source
resource consumer is incomparable to the integrity level of thetarget
resource, but the minimum integrity level of subjects and resources from which thesource
resource consumer can receive data does not exceed the integrity level of thetarget
resource.
It returns the "denied" result in the following cases:
- The integrity level of the
source
resource consumer exceeds the integrity level of thetarget
resource, and the minimum integrity level of subjects and resources from which thesource
resource consumer can receive data exceeds the integrity level of thetarget
resource. - The integrity level of the
source
resource consumer exceeds the integrity level of thetarget
resource, and the minimum integrity level of subjects and resources from which thesource
resource consumer can receive data is incomparable to the integrity level of thetarget
resource. - The integrity level of the
source
resource consumer is incomparable to the integrity level of thetarget
resource, and the minimum integrity level of subjects and resources from which thesource
resource consumer can receive data exceeds the integrity level of thetarget
resource. - The integrity level of the
source
resource consumer is incomparable to the integrity level of thetarget
resource, and the minimum integrity level of subjects and resources from which thesource
resource consumer can receive data is incomparable to the integrity level of thetarget
resource. - An integrity level was not assigned to the
source
resource consumer or to thetarget
resource. - The
source
ortarget
value is outside of the permissible range.
Example:
Mic security model write rule
This verifies that the source
resource consumer is allowed to write data to the target
resource.
It returns the "granted" result if the integrity level of the target
resource does not exceed the integrity level of the source
resource consumer.
It returns the "denied" result in the following cases:
- The integrity level of the
target
resource exceeds the integrity level of thesource
resource consumer. - The integrity level of the
target
resource is incomparable to the integrity level of thesource
resource consumer. - An integrity level was not assigned to the
source
resource consumer or to thetarget
resource. - The
source
ortarget
value is outside of the permissible range.
Mic security model query_level expression
It is intended to be used as an expression that verifies fulfillment of the conditions in the choice
construct (for details on the choice
construct, see "Binding methods of security models to security events"). It checks the integrity level of the source
resource or subject. Depending on the results of this check, various options for security event handling can be performed.
It runs incorrectly in the following cases:
- An integrity level was not assigned to the subject or
source
resource. - The
source
value is outside of the permissible range.
When the expression runs incorrectly, the Kaspersky Security Module returns the "denied" decision.
Page top
Methods of KasperskyOS core endpoints
From the perspective of the Kaspersky Security Module, the KasperskyOS kernel is a container of components that provide endpoints. The list of kernel components is provided in the Core.edl
file located in the sysroot-*-kos/include/kl/core
directory of the KasperskyOS SDK. This directory also contains the CDL and IDL files for the formal specification of the kernel.
Methods of core endpoints can be divided into secure methods and potentially dangerous methods. Potentially dangerous methods could be used by a cybercriminal in a compromised solution component to cause a denial of service, set up covert data transfer, or hijack an I/O device. Secure methods cannot be used for these purposes.
Access to methods of core endpoints must be restricted as much as possible by the solution security policy (according to the Least Privilege principle). For that, the following requirements must be fulfilled:
- Access to a secure method must be granted only to the solution components that require this method.
- Access to a potentially dangerous method must be granted only to the trusted solution components that require this method.
- Access to a potentially dangerous method must be granted to untrusted solution components that require this method only if the verifiable access conditions limit the possibilities of malicious use of this method, or if the impact from malicious use of this method is acceptable from a security perspective.
For example, an untrusted component may be allowed to use a limited set of I/O ports that do not allow this component to take control of I/O devices. In another example, covert data transfer between untrusted components may be acceptable from a security perspective.
Virtual memory endpoint
This endpoint is intended for managing virtual memory.
Information about methods of the endpoint is provided in the table below.
Methods of the vmm.VMM endpoint (kl.core.VMM interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Allocates (reserves and optionally commits) a virtual memory region. Parameters
|
Allows the following:
|
|
Purpose Commits a virtual memory region that was reserved by the Parameters
|
Exhausts RAM. |
|
Purpose Decommits a virtual memory region. Parameters
|
N/A |
|
Purpose Modifies the access rights to the virtual memory region. Parameters
|
N/A |
|
Purpose Frees up the virtual memory region. Parameters
|
N/A |
|
Purpose Gets information about a virtual memory page. Parameters
|
N/A |
|
Purpose Creates an MDL buffer. Parameters
|
Allows the following:
|
|
Purpose Creates an MDL buffer from physical memory that is mapped to the defined virtual memory region and maps the created MDL buffer to this region. Parameters
|
Allows the following:
|
|
Purpose Gets the size of the MDL buffer. Parameters
|
N/A |
|
Purpose Reserves a virtual memory region and maps the MDL buffer to it. Parameters
|
Allows the following:
|
|
Purpose Creates an MDL buffer based on an existing one. The MDL buffer is created from the same regions of physical memory as the original buffer. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
I/O endpoint
This endpoint is intended for working with I/O ports, MMIO, DMA, and interrupts.
Information about methods of the endpoint is provided in the table below.
Methods of the io.IO endpoint (kl.core.IO interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Registers a sequence of I/O ports. Parameters
|
Allows the following:
|
|
Purpose Registers an MMIO memory region. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
|
Purpose Creates a DMA buffer. Parameters
|
Allows the following:
|
|
Purpose Registers an interrupt. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
|
Purpose Reserves the virtual memory region and maps the MMIO memory region to it. Parameters
|
Allows the following:
|
|
Purpose Opens access to I/O ports. Parameters
|
Allows the following:
|
|
Purpose Attaches the calling thread to an interrupt. Parameters
|
Allows the following:
|
|
Purpose Sends a request to a thread. When this request is fulfilled, the thread must detach from the interrupt. Parameters
|
Stops interrupt handling in another process. |
|
Purpose Allows (unmasks) an interrupt. Parameters
|
Allows an interrupt at the system level. |
|
Purpose Denies (masks) an interrupt. Parameters
|
Denies an interrupt at the system level. |
|
Purpose Modifies the DMA buffer cache settings. Parameters
|
N/A |
|
Purpose Reserves a virtual memory region and maps the DMA buffer to it. Parameters
|
Allows the following:
|
|
Purpose Gets information about a DMA buffer. Parameters
|
N/A |
|
Purpose Gets information about the physical memory that was used to create a DMA buffer. Parameters
|
N/A |
|
Purpose Opens access to a DMA buffer for a device. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
Threads endpoint
This endpoint is intended for managing threads.
Information about methods of the endpoint is provided in the table below.
Methods of the thread.Thread endpoint (kl.core.Thread interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Creates a thread. Parameters
|
Allows the following:
|
|
Purpose Creates the handle of the calling thread. Parameters
|
N/A |
|
Purpose Locks the calling thread. Parameters
|
Locks a thread that has captured a synchronization object that was created in shared memory and is anticipated by a thread of another process. As a result, the thread of the other process may be locked indefinitely. |
|
Purpose Resumes execution of a locked thread. Parameters
|
N/A |
|
Purpose Terminates a thread. Parameters
|
N/A |
|
Purpose Terminates the calling thread. Parameters
|
N/A |
|
Purpose Locks the calling thread until the defined thread is terminated. Parameters
|
N/A |
|
Purpose Defines the priority of a thread. Parameters
|
Allows the priority of a thread to be elevated to reduce the CPU time available to all other threads, including from other processes. It is recommended to monitor thread priority. |
|
Purpose Defines the base address of the Thread Local Storage (TLS) for the calling thread. Parameters
|
N/A |
|
Purpose Locks the calling thread for the specified duration. Parameters
|
N/A |
|
Purpose Gets information about a thread. Parameters
|
N/A |
|
Purpose Detaches the calling thread from the interrupt handled in its context. Parameters
|
N/A |
|
Purpose Gets a thread affinity mask. Parameters
|
N/A |
|
Purpose Defines a thread affinity mask. Parameters
|
N/A |
|
Purpose Defines the scheduler class and priority of the thread. Parameters
|
Allows the following:
|
|
Purpose Gets information about the scheduler class and priority of a thread. Parameters
|
N/A |
Handles endpoint
This endpoint is intended for performing operations with handles.
Information about methods of the endpoint is provided in the table below.
Methods of the handle.Handle endpoint (kl.core.Handle interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Duplicates a handle. As a result of duplication, the calling process receives the handle descendant. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
|
Purpose Creates a handle. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
|
Purpose Closes a handle. Parameters
|
N/A |
|
Purpose Creates and connects the client, server, and listener IPC handles. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
|
Purpose Creates a client IPC handle for querying the Kaspersky Security Module through the security interface. Parameters
|
Allows a multitude of possible kernel process handle values to be used up. |
|
Purpose Receives a security ID (SID) based on a handle. Parameters
|
N/A |
|
Purpose Closes a handle and revokes its descendants. Parameters
|
N/A |
|
Purpose Revokes the handles that make up the inheritance subtree of the specified handle. Parameters
|
N/A |
|
Purpose Creates a resource transfer context object and configures a notification mechanism for monitoring the life cycle of this object. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
Processes endpoint
This endpoint is intended for managing processes.
Information about methods of the endpoint is provided in the table below.
Methods of the task.Task endpoint (kl.core.Task interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Creates a process. Parameters
|
Allows the following:
|
|
Purpose Loads an ELF image segment into process memory from the MDL buffer. Parameters
|
Allows code to be loaded into process memory for subsequent execution of that code. |
|
Purpose Reserves the virtual memory region in a process that was created as an empty process. Parameters
|
Allows the following:
|
|
Purpose Frees the virtual memory region that was reserved by calling the Parameters
|
Frees virtual memory regions in another process that was created as an empty process and has not yet been started (if its handle is available). (The handle permissions mask must allow freeing of virtual memory.) |
|
Purpose Defines the program entry point and the ELF image load offset. Parameters
|
Creates conditions for executing code loaded into process memory. |
|
Purpose Loads the symbol table Parameters
|
N/A |
|
Purpose Writes the ELF image header to the PCB of a process that was created as an empty process. Parameters
|
N/A |
|
Purpose Writes data to the SCP of a child process. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
|
Purpose Deletes the SCP of the calling process. Parameters
|
N/A |
|
Purpose Starts a process. Parameters
|
Allows the following:
|
|
Purpose Terminates the calling process. Parameters
|
N/A |
|
Purpose Terminates a process. Parameters
|
Allows another process to be terminated if its handle is available. (The handle permissions mask must allow termination of the process.) |
|
Purpose Gets information about a terminated process. Parameters
|
N/A |
|
Purpose Gets the context of a thread that is part of a frozen process. Parameters
|
Enables disrupted isolation of a process that is in a frozen state. For example, the thread context may contain the values of variables. |
|
Purpose Gets information about the virtual memory region that belongs to a frozen process. Parameters
|
Enables disrupted isolation of a process that is in a frozen state. Process isolation is disrupted due to the opened access to the process memory region. |
|
Purpose Terminates a frozen process. Parameters
|
Enables termination of a frozen process. This does not allow collection of data about this process for diagnostic purposes. |
|
Purpose Gets the name of a calling process. Parameters
|
N/A |
|
Purpose Gets the name of the executable file (in ROMFS) that was used to create the calling process. Parameters
|
N/A |
|
Purpose Gets the priority of the initial thread of a process. Parameters
|
N/A |
|
Purpose Defines the priority of the initial thread of a process. Parameters
|
Allows the priority of the initial thread of a process to be elevated to reduce the CPU time available to all other threads, including from other processes. It is recommended to monitor the priority of an initial thread. |
|
Purpose Gets information about existing processes. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
|
Purpose Defines the scheduler class and priority of the initial thread of a process. Parameters
|
Allows the following:
|
|
Purpose Defines the seed value for ASLR support. Parameters
|
N/A |
|
Purpose Gets the address and size of the symbol table Parameters
|
N/A |
|
Purpose Transfers a handle to a process that is not yet running. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
|
Purpose Gets the process ID (PID). Parameters
|
N/A |
Synchronization endpoint
This endpoint is intended for working with futexes.
Information about methods of the endpoint is provided in the table below.
Methods of the sync.Sync endpoint (kl.core.Sync interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Locks execution of the calling thread if the futex value is equal to the expected value. Parameters
|
N/A |
|
Purpose Resumes execution of threads that were blocked by a Parameters
|
N/A |
File system endpoints
These endpoints are intended for working with the ROMFS file system used by the KasperskyOS kernel.
Information about methods of endpoints is provided in the tables below.
Methods of the fs.FS endpoint (kl.core.FS interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Opens a file. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
|
Purpose Closes a file. Parameters
|
N/A |
|
Purpose Reads data from a file. Parameters
|
N/A |
|
Purpose Gets the size of a file. Parameters
|
N/A |
|
Purpose Gets the unique ID of a file. Parameters
|
N/A |
|
Purpose Gets the number of files in the file system. Parameters
|
N/A |
|
Purpose Gets the name and unique ID of a file based on the file index. Parameters
|
N/A |
|
Purpose Gets the size of the file system. Parameters
|
N/A |
Methods of the fs.FSUnsafe endpoint (kl.core.FSUnsafe interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Changes the file system image. A different ROMFS image loaded into process memory will be used instead of the ROMFS image that was created during the solution build. Parameters
|
Allows the following:
|
Time endpoint
This endpoint is intended for setting the system time.
Information about methods of the endpoint is provided in the table below.
Methods of the time.Time endpoint (kl.core.Time interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Sets the system time. Parameters
|
Allows the system time to be set. |
|
Purpose Starts gradual adjustment of the system time. Parameters
|
Allows the system time to be changed. |
|
Purpose Gets the correction time value remaining for system time adjustment so that gradual adjustment can be fully completed. Parameters
|
N/A |
Hardware abstraction layer endpoint
This endpoint is intended for receiving the values of HAL parameters, working with privileged registers, clearing the processor cache, providing diagnostic output, and receiving hardware-generated random numbers.
Information about methods of the endpoint is provided in the table below.
Methods of the hal.HAL endpoint (kl.core.HAL interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Gets the value of a HAL parameter. Parameters
|
Gets values of HAL parameters that could contain critical system information. |
|
Purpose Gets the value of a privileged register. Parameters
|
Sets up a data transfer channel with a process that has access to the It is recommended to monitor the name of a register. |
|
Purpose Sets the value of a privileged register. Parameters
|
Allows the following:
It is recommended to monitor the name of a register. |
|
Purpose Gets the value of a privileged register. Parameters
|
Sets up a data transfer channel with a process that has access to the It is recommended to monitor the name of the registers range and the register offset in this range. |
|
Purpose Sets the value of a privileged register. Parameters
|
Allows the following:
It is recommended to monitor the name of the registers range and the register offset in this range. |
|
Purpose Clears the processor cache. Parameters
|
Allows the processor cache to be cleared. |
|
Purpose Puts data into the diagnostic output that is written, for example, to a COM port or USB port (version 3.0 or later, with DbC support). Parameters
|
Populates diagnostic output with fictitious (uninformative) data. |
|
Purpose Gets hardware-generated random numbers. Parameters
|
Creates a load on the hardware-based random number generator with frequent method calls so that other processes are unable to receive random numbers using this generator. |
XHCI controller management endpoint
This endpoint is intended for disabling and re-enabling debug mode for the XHCI controller (with DbC support) when it is restarted.
Information about methods of the endpoint is provided in the table below.
Methods of the xhcidbg.XHCIDBG endpoint (kl.core.XHCIDBG interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Enables debug mode of the XHCI controller. Parameters
|
Configures the XHCI controller to sends diagnostic output through a USB port (version 3.0 or later). |
|
Purpose Disables debug mode of the XHCI controller. Parameters
|
Configures the XHCI controller to not send diagnostic output through a USB port (version 3.0 or later). |
Audit endpoint
This endpoint is intended for reading messages from KasperskyOS kernel logs. There are two kernel logs: kss
and core
. The kss
log contains security audit data. The core
log contains diagnostic output. (Diagnostic output includes kernel output and the output of programs.)
Information about methods of the endpoint is provided in the table below.
Methods of the audit.Audit endpoint (kl.core.Audit interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Opens the kernel log to read data from it. Parameters
|
N/A |
|
Purpose Closes the kernel log. Parameters
|
N/A |
|
Purpose Receives a message from a kernel log. Parameters
|
Extracts messages from the kernel log so that these messages are not received by another process. |
Profiling endpoint
This endpoint is intended for profiling and collecting code coverage, and for receiving the values of performance counters.
Information about methods of the endpoint is provided in the table below.
Methods of the profiler.Profiler endpoint (kl.core.Profiler interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Gets information about code coverage. Parameters
|
N/A |
|
Purpose Output of data on code coverage in Parameters
|
N/A |
|
Purpose Output of data on code coverage in Parameters
|
N/A |
|
Purpose Gets the values of performance counters. Parameters
|
N/A |
|
Purpose Gets the values of performance counters for a system resource (process or thread). Parameters
|
N/A |
|
Purpose Starts sample code profiling. Sample profiling results in code execution statistics that reflect the duration of code section execution. Parameters
|
N/A |
|
Purpose Stops sample code profiling. Parameters
|
N/A |
|
Purpose Gets data containing the code execution statistics received from sample profiling. Parameters
|
Gets the addresses and names of functions of other processes. |
|
Purpose Adds a process to the list of profiled processes. Parameters
|
N/A |
|
Purpose Clears the list of profiled processes. Parameters
|
N/A |
|
Purpose Saves information about the loaded ELF image segment in the kernel. (This is necessary so that the code execution statistics received from sample profiling can contain additional information that lets you associate these statistics with the source code.) Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
|
Purpose Deletes information about the loaded ELF image segment that was saved in the kernel using the Parameters
|
N/A |
|
Purpose Allocates the resources required for collecting kernel code coverage data when handling system calls executed by the calling process. Parameters
|
Exhausts RAM. |
|
Purpose Frees the resources required for collecting kernel code coverage data when handling system calls executed by the calling process. Parameters
|
N/A |
|
Purpose Starts the collection of kernel code coverage data when handling system calls executed by the calling thread. Parameters
|
N/A |
|
Purpose Stops the collection of kernel code coverage data when handling system calls executed by the calling thread. Also gets information about kernel code coverage. Parameters
|
N/A |
I/O memory isolation management endpoint
This endpoint is intended for managing the isolation of physical memory regions used by devices on a PCIe bus for DMA. (Isolation is provided by the IOMMU.)
Information about methods of the endpoint is provided in the table below.
Methods of the iommu.IOMMU endpoint (kl.core.IOMMU interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Attaches a device on a PCIe bus to the IOMMU domain associated with the calling process. Parameters
|
Attaches a device on a PCIe bus managed by another process to an IOMMU domain associated with the calling process, which leads to failure of the device. It is recommended to monitor the address of a device on a PCIe bus. |
|
Purpose Detaches a device on a PCIe bus from the IOMMU domain associated with the calling process. Parameters
|
N/A |
Connections endpoint
This endpoint is intended for dynamic creation of IPC channels.
Information about methods of the endpoint is provided in the table below.
Methods of the cm.CM endpoint (kl.core.CM interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Requests to create an IPC channel with a server for use of the defined endpoint. Parameters
|
Creates a load on a server by sending a large number of requests to create an IPC channel. |
|
Purpose Receives a client request to create an IPC channel for use of an endpoint. Parameters
|
N/A |
|
Purpose Rejects a client request to create an IPC channel for use of the defined endpoint. Parameters
|
N/A |
|
Purpose Accepts a client request to create an IPC channel for use of the defined endpoint. Parameters
|
N/A |
Power management endpoint
This endpoint is intended for changing the power management mode of a computer (for example, shutting down or restarting the computer), and for enabling and disabling processors (processor cores).
Information about methods of the endpoint is provided in the table below.
Methods of the pm.PM endpoint (kl.core.PM interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Requests to change the power mode of a computer. Parameters
|
Allows the computer power mode to be changed. |
|
Purpose Requests to enable and/or disable processors. Parameters
|
Disables and enables processors. |
|
Purpose Gets information regarding which processors are in the active state. Parameters
|
N/A |
Notifications endpoint
This endpoint is intended for working with notifications about events that occur with resources.
Information about methods of the endpoint is provided in the table below.
Methods of the notice.Notice endpoint (kl.core.Notice interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Creates a notification receiver. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
|
Purpose Adds a "resource–event mask" entry to the notification receiver so that it can receive notifications about events that occur with the defined resource and match the defined event mask. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
|
Purpose Removes from the notification receiver "resource—event mask" entries with the specified identifier to prevent the receiver from getting notifications about events that match these entries. Parameters
|
N/A |
|
Purpose Removes from the notification receiver "resource—event mask" entries that match the specified resource to prevent the receiver from getting notifications about events that match these entries. Parameters
|
N/A |
|
Purpose Extracts notifications from the receiver. Parameters
|
N/A |
|
Purpose Removes from the specified notification receiver all "resource—event mask" entries, resumes all threads waiting for notifications to appear in the specified receiver; optionally prohibits adding of "resource—event mask" entries to the specified notification receiver. Parameters
|
N/A |
|
Purpose Signals that events from the defined event mask occurred with the defined user resource. Parameters
|
N/A |
Hypervisor endpoint
This endpoint is intended for working with a hypervisor.
Methods of the hypervisor.Hypervisor
endpoint (kl.core.Hypervisor
interface) are potentially dangerous. Access to these methods can be granted only to the specialized vmapp
program.
Trusted Execution Environment endpoints
These endpoints are intended for transferring data between a Trusted Execution Environment (TEE) and a Rich Execution Environment (REE), and for obtaining access to the physical memory of the REE from the TEE.
Information about methods of endpoints is provided in the tables below.
Methods of the tee.TEE endpoint (kl.core.TEE interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Sends and receives messages transferred between a TEE and a REE. This method is used in the TEE and in the REE. Parameters
|
Allows a process in a REE to receive a response from a TEE regarding a request from another process in the REE. |
|
Purpose Frees the values of unique IDs of messages transferred between a TEE and a REE. (These values must be freed so that they can become available for re-use.) This method is used in REE. Parameters
|
Frees the values used by other processes in a REE as unique IDs of messages transferred between a TEE and a REE. |
Methods of the tee.TEEVMM endpoint (kl.core.TEEVMM interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Creates a blank MDL buffer so that physical memory from an REE can be subsequently added to it. This method is used in TEE. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
|
Purpose Adds a REE physical memory region to the blank MDL buffer created by the This method is used in TEE. Parameters
|
Allows access to an arbitrary region of the physical memory of a REE from a TEE. |
IPC interrupt endpoint
This endpoint is intended for interrupting the Call()
and Recv()
locking system calls. (For example, this may be required to correctly terminate a process.)
Information about methods of the endpoint is provided in the table below.
Methods of the ipc.IPC endpoint (kl.core.IPC interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Creates an IPC synchronization object. An IPC synchronization object is used to interrupt The handle of an IPC synchronization object cannot be transferred to another process because the necessary flag for this operation is not set in the permissions mask of this handle. Parameters
|
Allows the kernel memory to be used up by creating a multitude of objects within it. |
|
Purpose Switches the defined IPC synchronization object to a state in which the Parameters
|
N/A |
|
Purpose Switches the defined IPC synchronization object to a state in which the Parameters
|
N/A |
CPU frequency management endpoint
This endpoint is intended for changing the frequency of processors (processor cores).
Information about methods of the endpoint is provided in the table below.
Methods of the cpufreq.CpuFreq endpoint (kl.core.CpuFreq interface)
Method |
Method purpose and parameters |
Potential danger of the method |
---|---|---|
|
Purpose Allows you to receive information about processor groups. Processor group information lists the existing processor groups while indicating the possible values of the performance parameter for each of them. This parameter is a combination of the matching frequency and voltage (Operating Performance Point, or OPP). The frequency is indicated in kiloherz (kHz) and the voltage is indicated in microvolts (µV). Parameters
|
N/A |
|
Purpose Gets the index of the current OPP for the defined processor group. Parameters
|
N/A |
|
Purpose Sets the defined OPP for the defined processor group. Parameters
|
Changes the frequency of a processor group. |
Using the system programs Klog and KlogStorage to perform a security audit
To perform a security audit, the system program Klog
receives audit data from the KasperskyOS kernel by using the libkos
library, decodes this data and forwards it via IPC to the system program KlogStorage
, which acts as the server in this IPC interaction. The KlogStorage
program sends audit data to standard output (or standard error) or saves it to a file by using VFS. The KlogStorage
program can also forward file-written audit data to other programs via IPC.
The executable files of the Klog
and KlogStorage
programs are not provided in the KasperskyOS SDK. You will need to create them based on the provided static libraries.
Example of adding the system program Klog to a solution
Source code of the program
einit/src/klog_entity.c
Building a program
einit/CMakeLists.txt
Program process dictionary in the init description template
einit/src/init.yaml.in
Policy description for the program
einit/src/security.psl.in
einit/src/core.psl
Example of adding the system program KlogStorage to a solution to forward audit data to standard error
Source code of the program
klog_storage/src/klog_storage_entity.c
Building a program
klog_storage/CMakeLists.txt
Program process dictionary in the init description template
einit/src/init.yaml.in
Policy description for the program
einit/src/security.psl.in
einit/src/core.psl
Example of adding the system program KlogStorage to a solution to write audit data to a file
Source code of the program
klog_storage/src/klog_storage_entity.c
Building a program
The difference between the CMake
commands for building the KlogStorage
program that writes audit data to a file and the CMake
commands for building the version of this program that sends audit data to standard error comprises the following modification:
klog_storage/CMakeLists.txt
Program process dictionary in the init description template
einit/src/init.yaml.in
Security policy description for the program
The difference between a policy description for a KlogStorage
program that writes audit data to a file and a policy description for a version of this program that sends audit data to standard error comprises the following addition:
einit/src/security.psl.in
einit/src/vfs.psl
Forwarding audit data to other programs
To forward file-written audit data via IPC, the KlogStorage
program provides the read
and readRange
interface methods defined in the file sysroot-*-kos/include/kl/KlogStorage.idl
from the KasperskyOS SDK.
The executable file of the program that needs to receive the audit data must be linked to the client library of the KlogStorage
program:
klog_reader/CMakeLists.txt
Source code for receiving audit data from the KlogStorage
program:
klog_reader/src/klog_reader.c
Security patterns for development under KasperskyOS
Each KasperskyOS-based solution has specific usage scenarios and is designed to counteract specific security threats. Nonetheless, there are some typical scenarios and threats encountered in many different solutions. This section describes the typical risks and threats, and contains a description of architectural patterns that can be employed to increase the security of a solution.
A security pattern (or template) describes a specific recurring security issue that arises in certain known contexts, and provides a well-proven, general scheme for resolving this kind of security issue. A pattern is not a finished project that can be converted directly into code. Instead, it is a solution to a general problem encountered in various projects.
A security pattern system is a set of security patterns together with instructions on their implementation, combination, and practical use when designing secure software systems.
Security patterns resolve security issues at different levels, beginning with patterns at the architectural level, including high-level design of the system, and ending with implementation-level patterns that contain recommendations on how to implement functions or methods.
This section describes the set of security patterns whose implementation examples are provided in KasperskyOS Community Edition.
Security patterns are described in a multitude of information security resources. Each pattern is accompanied by a list of the resources that were used to prepare its description.
Distrustful Decomposition pattern
Description
When using a monolithic application, a single process must be granted all the privileges necessary for the application to operate. This issue is resolved by the Distrustful Decomposition
pattern.
The purpose of the Distrustful Decomposition
pattern is to divide application functionality among individual processes that require different levels of privileges, and to control the interaction between these processes instead of creating a monolithic application.
Using the Distrustful Decomposition
pattern reduces the following:
- Attack surface for each process.
- Functionality and data that a hacker will be able to access if one of the processes is compromised.
Alternate names
Privilege Reduction
.
Context
Different functions of an application require different levels of privileges.
Problem
An unsophisticated implementation of an application combines many functions requiring different privileges into one component. This component would need to be run with the maximum level of privileges required for any one of these many functions.
Solution
The Distrustful Decomposition
pattern divides functionality among individual processes and isolates potential vulnerabilities within a small subset of the system. A cybercriminal who conducts a successful attack will be able to use only the functionality and data of a single compromised component instead of the entire application.
Structure
This pattern divides one monolithic application into multiple applications that are run as individual processes that could potentially have different privileges. Each process implements a small, clearly defined set of functions of the application. Processes use interprocess communication mechanism to exchange data.
Operation
- In KasperskyOS, an application is divided into processes.
- Processes can exchange messages via IPC.
- A user or remote system connects to the process that provides the necessary functionality with the level of privileges sufficient to perform the requested functions.
Implementation recommendations
Interaction between processes can be unidirectional or bidirectional. It is recommended to always use unidirectional interaction whenever possible. Otherwise, the potential attack surface of individual components increases, which reduces the overall security of the entire system. If bidirectional IPC is used, processes should not trust bidirectional data exchange. For example, if a file system is used for IPC, file contents cannot be trusted.
Specialized implementation in KasperskyOS
In universal operating systems such as Linux or Windows, this pattern does not use anything except the standard process/privileges model that already exists in these operating systems. Each program is run in its own process space with potentially different privileges of the specific user in each process. However, an attack on the OS kernel would reduce the effectiveness of this pattern.
Use of this pattern when developing for KasperskyOS means that control over processes and IPC is entrusted to the microkernel, which is difficult to successfully attack. The Kaspersky Security Module is used for IPC control.
Use of KasperskyOS mechanisms ensures a high level of reliability of the software system with the same or less effort required from the developer when compared to the use of this pattern in programs running under universal operating systems.
In addition, KasperskyOS provides the capability for flexible configuration of security policies. Moreover, the process of defining and editing security policies is potentially independent of the process of developing the applications.
Linked patterns
Use of the Distrustful Decomposition
pattern involves use of the Defer to Kernel and Policy Decision Point patterns.
Implementation examples
Examples of an implementation of the Distrustful Decomposition
pattern:
Sources of information
The Distrustful Decomposition
pattern is described in detail in the following resources:
- Chad Dougherty, Kirk Sayre, Robert C. Seacord, David Svoboda, Kazuya Togashi (JPCERT/CC), "Secure Design Patterns" (March-October 2009). Software Engineering Institute. https://resources.sei.cmu.edu/asset_files/TechnicalReport/2009_005_001_15110.pdf
- Dangler, Jeremiah Y., "Categorization of Security Design Patterns" (2013). Electronic Theses and Dissertations. Paper 1119. https://dc.etsu.edu/etd/1119
Secure Logger example
The Secure Logger
example demonstrates use of the Distrustful Decomposition pattern for separating event log read/write functionality.
Example architecture
The security goal of the Secure Logger
example is to prevent any possibility of distortion or deletion of information from the event log. This example utilizes the capabilities provided by KasperskyOS to achieve this security goal.
A logging system can be examined by distinguishing the following functional steps:
- Generate information to be written to the log.
- Save information to the log.
- Read entries from the log.
- Provide entries in a convenient format for the consumer.
Accordingly, the logging subsystem can be divided into four processes depending on the required functional capabilities of each process.
For this purpose, the Secure Logger
example contains the following four programs: Application
, Logger
, Reader
and LogViewer
.
- The
Application
program initiates the creation of entries in the event log maintained by theLogger
program. - The
Logger
program creates entries in the log and writes them to the disk. - The
Reader
program reads entries from the disk to send them to theLogViewer
program. - The
LogViewer
program sends entries to the user.
The IPC interface provided by the Logger
program is intended only for writing to storage. The IPC interface of the Reader
program is intended only for reading from storage. The example architecture looks as follows:
- The
Application
program uses the interface of theLogger
program to save log entries. - The
LogViewer
program uses the interface of theReader
program to read the log entries and present them to a user.
The LogViewer
program normally has external channels for interacting with a user (for example, to receive data write commands and to provide data to a user). Naturally, this program is an untrusted component of the system, and therefore could potentially be used to conduct an attack. However, even if a successful attack results in the infiltration of unauthorized executable code into the LogViewer
program, information in the log cannot be distorted through this program. This is because the program can only utilize the data read interface, which cannot actually be used to distort or delete data. Moreover, the LogViewer
program does not have the capability to gain access to other interfaces because this access is controlled by the security module.
A security policy in the Secure Logger
example has the following characteristics:
- The
Application
program has the capability to query theLogger
program to create a new entry in the event log. - The
LogViewer
program has the capability to query theReader
program to read entries from the event log. - The
Application
program does not have the capability to query theReader
program to read entries from the event log. - The
LogViewer
program does not have the capability to query theLogger
program to create a new entry in the event log.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
Separate Storage example
The Separate Storage
example demonstrates use of the Distrustful Decomposition pattern to separate data storage for trusted and untrusted applications.
Example architecture
The Separate Storage
example contains two user programs: UserManager
and CertificateManager
.
These programs work with data located in the corresponding files:
- The
UserManager
program works with data from theuserlist.txt
file. - The
CertificateManager
program works with data from thecertificate.cer
file.
Each of these programs uses its own instance of the VFS program to access a separate file system. Each VFS program includes a block device driver linked to an individual logical drive partition. The UserManager
program does not have access to the file system of the CertificateManager
program, and vice versa.
This architecture guarantees that if there is an attack or error in any of the UserManager
or CertificateManager
programs, this program will not be able to access any file that was not intended for the specific program's operations.
A security policy in the Separate Storage
example has the following characteristics:
- The
UserManager
program has access to the file system only through theVfsUser
program. - The
CertificateManager
program has access to the file system only through theVfsCertificate
program.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
To run an example on QEMU, go to the directory containing the example, build the example and run the following commands:
See also Building and running examples section.
Preparing an SD card to run on Raspberry Pi 4 B
To run the Separate Storage
example on Raspberry Pi 4 B, the following additional actions are necessary:
- Create a
/lib
directory in the SD card boot sector unless one already exists. - Copy the contents of the
build/hdd/part1/lib
directory that was generated while building the example to the/lib
directory in the SD card boot sector. - The SD card must contain both a bootable partition with the solution image as well as 2 additional partitions with the
ext2
orext3
file systems. - The first additional partition must contain the
userlist.txt
file from the./resources/files/
directory. - The second additional partition must contain the
certificate.cer
file from the./resources/files/
directory.
To run the Separate Storage
example on Raspberry Pi 4 B, you can use an SD card prepared for running the vfs_extfs
example on Raspberry Pi 4 B after copying the userlist.txt
and certificate.cer
files to the appropriate partitions.
Defer to Kernel pattern
Description
The Defer to Kernel
pattern takes advantage of permission control at the OS kernel level.
The purpose of this pattern is to utilize mechanisms available at the OS kernel level to clearly separate the functionality requiring elevated privileges from the functionality that does not require elevated privileges. By using kernel mechanisms, we do not have to implement new tools for arbitrating security decisions at the user level.
Alternate names
Policy Enforcement Point (PEP)
, Protected System
, Enclave
.
Context
The Defer to Kernel
pattern is applicable if the system has the following characteristics:
- The system has processes that run without elevated privileges, including user processes.
- Some system functions require elevated privileges that must be verified before processes are granted access to data.
- You need to verify not only the privileges of the requesting process, but also the overall permissibility of the requested operation within the operational context of the entire system and its overall security.
Problem
When functionality is divided among various processes with different levels of privileges, these privileges must be verified when a request is made from one process to another. These verifications must be carried out and their resulting permissions must be granted by trusted code that has a minimal risk of being compromised. The trustworthiness of application code is almost always questionable due to its sheer volume and due to its primary orientation toward implementation of functional requirements.
Solution
Clearly separate privileged functionality and data from non-privileged functionality and data at the process level, and give the OS kernel control of interprocess communication (IPC), including verification of access rights when there is a request for functionality or data requiring elevated privileges, and verification of the overall state of the system and the states of individual processes at the time of the request.
Structure
Operation
- Functionality and management of data with various privileges are compartmentalized among processes.
- The OS kernel ensures isolation of processes.
Process-1
wants to request privileged functionality or data fromProcess-2
using IPC.- The kernel controls IPC and allows or denies communication based on security policies and based on the available information regarding the operational context and state of
Process-1
.
Implementation recommendations
To ensure that a specific implementation of a pattern operates securely and reliably, the following is required:
- Isolation
Complete and guaranteed isolation of processes must be ensured.
- Inability to bypass the kernel
Absolutely all IPC interactions must be controlled by the kernel.
- Kernel self-defense
The trustworthiness of the kernel must be ensured through its own means of protection against compromise.
- Provability
The kernel requires a certain level of guaranteed security and reliability.
- Capability for external computation of access permissions
Access permissions must be computed at the OS level, and must not be implemented in application code.
For this purpose, tools must be provided for describing access policies so that security policies are detached from the business logic.
Specialized implementation in KasperskyOS
The KasperskyOS kernel guarantees isolation of processes and serves as a Policy Enforcement Point (PEP).
Linked patterns
The Defer to Kernel
pattern is a special case of the Distrustful Decomposition and Policy Decision Point patterns. The Policy Decision Point
pattern defines the abstraction process that intercepts all requests to resources and verifies that they comply with the defined security policy. The distinctive feature of the Defer to Kernel
pattern is that the verification process is performed by the OS kernel, which is a more reliable and portable solution that reduces the time spent on development and testing.
Impacts
By making the OS kernel responsible for applying the access policy, you separate the security policy from the business logic (which may be very complicated) and thereby simplify development and improve portability through the use of OS kernel functions.
This also makes it possible to prove the overall security of a solution by simply demonstrating that the kernel is operating correctly. The difficulty in proving correct execution of code grows nonlinearly as the size of the code increases. The Defer to Kernel
pattern minimizes the amount of trusted code, provided that the OS kernel itself is not too large.
Implementation examples
Example of a Defer to Kernel
pattern implementation: Defer to Kernel example.
Sources of information
The Defer to Kernel
pattern is described in detail in the following resources:
- Chad Dougherty, Kirk Sayre, Robert C. Seacord, David Svoboda, Kazuya Togashi (JPCERT/CC), "Secure Design Patterns" (March-October 2009). Software Engineering Institute. https://resources.sei.cmu.edu/asset_files/TechnicalReport/2009_005_001_15110.pdf
- Dangler, Jeremiah Y., "Categorization of Security Design Patterns" (2013). Electronic Theses and Dissertations. Paper 1119. https://dc.etsu.edu/etd/1119
- Schumacher, Markus, Fernandez-Buglioni, Eduardo, Hybertson, Duane, Buschmann, Frank, and Sommerlad, Peter. "Security Patterns: Integrating Security and Systems Engineering" (2006)
Defer to Kernel example
The Defer to Kernel
example demonstrates the use of Defer to Kernel and Policy Decision Point patterns.
The Defer to Kernel
example contains three user programs: PictureManager
, ValidPictureClient
and NonValidPictureClient
.
In this example, the ValidPictureClient
and NonValidPictureClient
programs query the PictureManager
program to receive information.
Only the ValidPictureClient
program is allowed to interact with the PictureManager
program.
The KasperskyOS kernel guarantees isolation of running programs (processes).
Control of interaction between programs in KasperskyOS is delegated to the Kaspersky Security Module. The subsystem analyzes each sent request and response and decides whether to allow or deny delivery based on the defined security policy.
A security policy in the Defer to Kernel
example has the following characteristics:
- The
ValidPictureClient
program is explicitly allowed to interact with thePictureManager
program. - The
NonValidPictureClient
program is explicitly not allowed to interact with thePictureManager
program. This means that this interaction is denied (based on the Default Deny principle).
Dynamically created IPC channels
The example also demonstrates the capability to dynamically create IPC channels between processes. IPC channels are dynamically created by using a name server, which is a special kernel service provided by the NameServer
program. The capability to dynamically create IPC channels allows you to change the topology of interaction between programs on the fly.
Any program that is allowed to interact with NameServer
via IPC can register its own interfaces in the name server. Another program can request the registered interfaces from the name server, and then connect to the relevant interface.
The security module is used to control interactions via IPC (even those that were created dynamically).
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
Policy Decision Point pattern
Description
The Policy Decision Point
pattern encapsulates the computation of decisions based on security model methods into a separate system component that ensures that these security methods are performed in their full scope and correct sequence.
Alternate names
Check Point
, Access Decision Function
.
Context
The system has functions with different levels of privileges, and the security policy is complex (contains many security model methods bound to security events).
Problem
If security policy checks are divided among different system components, the following issues arise:
- You have to carefully make sure that all necessary checks are performed in all required cases.
- It is difficult to ensure that all checks are performed in the correct order.
- It is difficult to prove that the verification system is operating correctly, has no conflicts, and its integrity has not been compromised.
- The security policy is linked to the business logic. This means that any modification of the security policy requires changes to the business logic, which complicates support and increases the likelihood of errors.
Solution
All verifications of security policy compliance are conducted in a separate component called a Policy Decision Point (PDP). This component is responsible for ensuring that verifications are conducted in their correct sequence and scope. Policy checks are separated from the code that implements the business logic.
Structure
Operation
- A Policy Enforcement Point (PEP) receives a request to access functionality or data.
For example, the PEP may be the OS kernel. For more details, refer to Defer to Kernel pattern.
- The PEP gathers the request attributes required for making decisions on access control.
- The PEP requests an access control decision from the Policy Decision Point (PDP).
- The PDP computes a decision on whether to grant access based on the security policy and based on the information received in the request from the PEP.
- The PEP denies or allows interaction based on the decision of the PDP.
Implementation recommendations
Implementations must take into account the problem of "Verification time vs. Usage time". For example, if a security policy depends on the quickly changing status of a specific system object, a computed decision loses its relevance as quickly as the status changes. In a system that utilizes the Policy Decision Point pattern, you must take care to minimize the time interval between the access decision and the time when the request based on this decision is fulfilled.
Specialized implementation in KasperskyOS
The KasperskyOS kernel guarantees isolation of processes and serves as a Policy Enforcement Point (PEP).
Control of interaction between processes in KasperskyOS is delegated to the Kaspersky Security Module. This module analyzes each sent request and response and decides whether to allow or deny delivery based on the defined security policy. Therefore, the Kaspersky Security Module performs the role of the Policy Decision Point (PDP).
Impacts
This pattern configures a security policy without making any modifications to the code that implements the business logic, and delegates system support involving information security.
Linked patterns
Use of the Policy Decision Point
pattern involves use of the Distrustful Decomposition and Defer to Kernel patterns.
Implementation examples
Example of a Policy Decision Point
pattern implementation: Defer to Kernel example.
Sources of information
The Policy Decision Point
pattern is described in detail in the following resources:
- Chad Dougherty, Kirk Sayre, Robert C. Seacord, David Svoboda, Kazuya Togashi (JPCERT/CC), "Secure Design Patterns" (March-October 2009). Software Engineering Institute. https://resources.sei.cmu.edu/asset_files/TechnicalReport/2009_005_001_15110.pdf
- Dangler, Jeremiah Y., "Categorization of Security Design Patterns" (2013). Electronic Theses and Dissertations. Paper 1119. https://dc.etsu.edu/etd/1119
- Schumacher, Markus, Fernandez-Buglioni, Eduardo, Hybertson, Duane, Buschmann, Frank, and Sommerlad, Peter. "Security Patterns: Integrating Security and Systems Engineering" (2006)
- Bob Blakley, Craig Heath, and members of The Open Group Security Forum. "Security Design Patterns" (April 2004). The Open Group. https://pubs.opengroup.org/onlinepubs/9299969899/toc.pdf
Privilege Separation pattern
Description
The Privilege Separation
pattern involves the use of non-privileged isolated system modules for interaction with clients (other modules or users) that do not have any privileges. The purpose of the Privilege Separation
pattern is to reduce the amount of code that is executed with special privileges without impacting or restricting application functionality.
The Privilege Separation
pattern is a special case of the Distrustful Decomposition pattern.
Example
An unauthenticated user connects to a system that has functions requiring elevated privileges.
Context
The system has components with a large attack surface due to their high number of connections with unsafe sources and/or a complicated, potentially error-prone implementation.
Problem
When a client with unknown privileges interacts with a privileged component of the system, there are risks that the data and functionality accessible to that component could be compromised.
Solution
Interactions with unsafe clients must be conducted only through specially allocated components that have no privileges. The Privilege Separation
pattern does not modify system functionality. Instead, it merely separates functionality into components with different privileges.
Operation
Pattern operations can be divided into two phases:
- Pre-Authentication. The client is not yet authenticated. It sends a request to a privileged master process. The master process creates a child process with no privileges (and no access to the file system). This child process performs client authentication.
- Post-Authentication. The client is authenticated and authorized. The privileged master process creates a new child process that has privileges corresponding to the permissions of the client. This process is responsible for all subsequent interaction with the client.
Recommendations on implementation in KasperskyOS
At the Pre-Authentication phase, the master process can save the state of each non-privileged process in the form of a finite-state machine and change the state of the finite-state machine during authentication.
Requests from child processes to the master process are performed using standard IPC mechanisms. However, interaction control is conducted using the Kaspersky Security Module.
Impacts
If attackers gain control of a non-privileged process, they will not gain access to any privileged functions or data. If attackers gain control of an authorized process, they will obtain only the privileges of this process.
In addition, code that is organized in this manner is easier to check and test. You just have to pay special attention to the functionality that operates with elevated privileges.
Implementation examples
Example of a Privilege Separation
pattern implementation: Device Access example.
Sources of information
The Privilege Separation
pattern is described in detail in the following resources:
- Chad Dougherty, Kirk Sayre, Robert C. Seacord, David Svoboda, Kazuya Togashi (JPCERT/CC), "Secure Design Patterns" (March-October 2009). Software Engineering Institute. https://resources.sei.cmu.edu/asset_files/TechnicalReport/2009_005_001_15110.pdf
- Dangler, Jeremiah Y., "Categorization of Security Design Patterns" (2013). Electronic Theses and Dissertations. Paper 1119. https://dc.etsu.edu/etd/1119
Device Access example
The Device Access
example demonstrates use of the Privilege Separation pattern.
Example architecture
The example contains the following three programs: Device
, LoginManager
and Storage
.
In this example, the Device
program queries the Storage
program to receive information and queries the LoginManager
program for authorization.
The Device
program obtains access to the Storage
program after successful authorization.
This example demonstrates the capability to separate the authorization logic and the data access logic into independent components. This separation guarantees that data access can be opened only after successful authorization. The security module monitors whether authorization was successfully completed. This architecture also enables independent development and testing of the authorization logic and the data access provision logic.
A security policy in the Device Access
example has the following characteristics:
- The
Device
program has the capability to query theLoginManager
program for authorization. - Calls of the
GetInfo()
method of theStorage
program are managed by methods of the Flow security model:- The finite-state machine described in the
session
object configuration has two states:unauthenticated
andauthenticated
. - The initial state is
unauthenticated
. - Only transitions from
unauthenticated
toauthenticated
and vice versa are allowed. - The
session
object is created when theDevice
program is started. - When the
Device
program successfully calls theLogin()
method of theLoginManager
program, the state of thesession
object changes toauthenticated
. - When the
Device
program successfully calls theLogout()
method of theLoginManager
program, the state of thesession
object changes tounauthenticated
. - When the
Device
program calls theGetInfo()
method of theStorage
program, the current state of thesession
object is verified. The call is allowed only if the current state of the object isauthenticated
.
- The finite-state machine described in the
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
Information Obscurity pattern
Description
The purpose of the Information Obscurity
pattern is to encrypt confidential data in otherwise unsafe environments and thereby protect against data theft.
Context
This pattern should be used when data is frequently transferred between parts of a system and/or between the system and other (external) systems.
Problem
Confidential data may be transmitted through an untrusted environment within one system (through untrusted components) or between different systems (through untrusted networks). If this environment is compromised, confidential data could be intercepted by a cybercriminal.
Solution
Data must be separated based on its specific level of confidentiality so that you can determine which data should be encrypted and which encryption algorithms should be used. Encryption and decryption may take a lot of time, therefore their use should be limited whenever possible. The Information Obscurity
pattern resolves this issue by utilizing a specific confidentiality level to determine what exactly must be concealed with encryption.
Implementation examples
Example of an Information Obscurity
pattern implementation: Secure Login example.
Sources of information
The Information Obscurity
pattern is described in detail in the following resources:
- Dangler, Jeremiah Y., "Categorization of Security Design Patterns" (2013). Electronic Theses and Dissertations. Paper 1119. https://dc.etsu.edu/etd/1119
- Schumacher, Markus, Fernandez-Buglioni, Eduardo, Hybertson, Duane, Buschmann, Frank, and Sommerlad, Peter. "Security Patterns: Integrating Security and Systems Engineering" (2006)
Secure Login (Civetweb, TLS-terminator) example
The Secure Login
example demonstrates use of the Information Obscurity pattern. This example demonstrates the capability to transmit critical system information through an untrusted environment.
Example architecture
This example simulates the acquisition of remote access to an IoT device by sending user account credentials (user name and password) to this device. The untrusted environment within the IoT device is the web server that responds to requests from users. Practical experience has shown that this kind of web server is easy to detect and frequently attacked successfully because IoT devices do not have built-in tools for protection against intrusion and other attacks. Users also gain access to the IoT device through an untrusted network. Obviously, encryption algorithms must be used in these types of conditions to protect user account credentials from being compromised.
In terms of the architecture in these systems, the following objects can be distinguished:
- Data source: user's browser.
- Point of communication with the device: web server.
- Subsystem for processing information from the user: authentication subsystem.
To employ cryptographic protection, the following steps must be completed:
- Configure interaction between the data source and the device over the HTTPS protocol. This helps prevent unauthorized surveillance of HTTP traffic and MITM (man-in-the-middle) attacks.
- Generate a shared secret between the data source and the information processing subsystem.
- Use this secret to encrypt information on the data source side and to decrypt the information on the information processing subsystem side. This helps prevent data within the device from being compromised (at the point of communication).
The Secure Login
example includes the following components:
Civetweb
web server (untrusted component,WebServer
program).- User authentication subsystem (trusted component,
AuthService
program). - TLS terminator (trusted component,
TlsEntity
program). This component supports the TLS (transport layer security) mechanism. Together with the web server, the TLS terminator supports the HTTPS protocol on the device side (the web server interacts with the browser through the TLS terminator).
The user authentication process occurs as follows:
- Using their browser, the user opens the page at
https://localhost:1106
(when running the example on QEMU) or athttps://<Raspberry Pi IP address>:1106
(when running the example on Raspberry Pi 4 B). HTTP traffic between the browser and TLS terminator will be transmitted in encrypted form, but the web server will work only with unencrypted HTTP traffic.This example uses a self-signed certificate, so most up-to-date browsers will warn you that the connection is not secure. You need to agree to use this "insecure" connection, which will actually be encrypted despite the warning. In some browsers, you may encounter the message
"TLS: Error performing handshake: -30592: errno = Success"
. - The
Civetweb
web server running in theWebServer
program displays theindex.html
page containing an authentication prompt. - The user clicks the
Log in
button. - The
WebServer
program queries theAuthService
program via IPC to get the page containing the user name and password input form. - The
AuthService
program performs the following actions:- Generates a private key and public settings, and calculates the public key based on the Diffie-Hellman algorithm.
- Creates the
auth.html
page containing the user name and password input form (the page code contains the public settings and the public key). - Transfers the received page to the
WebServer
program via IPC.
- The
Civetweb
web server running in theWebServer
program displays theauth.html
page containing the user name and password input form. - The user completes the form and clicks the
Submit
button (correct data for authentication is contained in the filesecure_login/auth_service/src/authservice.cpp
). - The
auth.html
page code executed by the browser performs the following actions:- Generates a private key and calculates the public key and shared secret key based on the Diffie-Hellman algorithm.
- Encrypts the password by using the
XOR
operation with the shared secret key. - Transmits the user name, encrypted password and public key to the web server.
- The
WebServer
program queries theAuthService
program via IPC to get the page containing the authentication result by transmitting the user name, encrypted password and public key. - The
AuthService
program performs the following actions:- Calculates the shared secret key based on the Diffie-Hellman algorithm.
- Decrypts the password by using the shared secret key.
- Returns the
result_err.html
page orresult_ok.html
page depending on the authentication result.
- The
Civetweb
web server running in theWebServer
program displays theresult_err.html
page or theresult_ok.html
page.
This way, confidential data is transmitted only in encrypted form through the network and web server. In addition, all HTTP traffic is transmitted through the network in encrypted form. Data is transferred between components via IPC interactions controlled by the Kaspersky Security Module.
Unit testing using the GoogleTest framework
In addition to the Information Obscurity pattern, the Secure Login
example demonstrates use of the GoogleTest framework to conduct unit testing of applications developed for KasperskyOS (this framework is provided in KasperskyOS Community Edition).
The source code of the tests is located at the following path:
/opt/KasperskyOS-Community-Edition-<version>/examples/secure_login/tests
These unit tests are designed for verification of certain CPP modules of the authentication subsystem and web server.
To start testing:
- Go to the directory with the
Secure Login
example. - Delete the
build
directory containing the results of the previous build by running the following command:sudo rm -rf build/ - Run the command to start testing:$ RUN_TESTS=YES ./cross-build.sh
Tests are conducted in the TestEntity
program. The AuthService
and WebServer
programs are not started in this case. Therefore, the example cannot be used to demonstrate the Information Obscurity pattern when testing is being conducted.
After testing is finished, the results of the tests are displayed.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
To run an example on QEMU, go to the directory containing the example, build the example and run the following commands:
See also Building and running examples section.
To ensure that the secure_login
example will correctly run in Raspberry Pi, you must do the following after building the example and preparing your bootable SD card:
- Copy the
certs
andwww
directories located at the path/opt/KasperskyOS-Community-Edition-<version>/examples/secure_login/resources/hdd
to the root directory of the bootable SD card. - Create the
/lib
directory on the bootable SD card if this directory doesn't already exist. - Open the
build/hdd/lib
directory that was generated when building the example and copy the directory contents to the/lib
directory on the bootable SD card.
Appendices
This section provides additional information to supplement the primary text of the document.
Additional examples
This section provides descriptions of additional examples that are included in KasperskyOS Community Edition.
See also the descriptions of security pattern implementation examples:
- Secure Logger example
- Separate Storage example
- Defer to Kernel example
- Device Access example
- Secure Login (Civetweb, TLS-terminator) example
hello example
The hello.c
code looks familiar and simple to a developer that uses C, and is fully compatible with POSIX:
hello.c
Compile this code using aarch64-kos-gcc
, which is included in the development tools of KasperskyOS Community Edition:
The program name (and, consequently, the name of the executable file) must begin with an uppercase letter.
EDL description of the Hello process class
A static description of the Hello
program consists of a single file named Hello.edl
that must indicate the name of the process class:
Hello.edl
The process class name must begin with an uppercase letter. The name of an EDL file must match the name of the class that it describes.
Creating the Einit initializing program
When KasperskyOS is loaded, the kernel starts a program named Einit
. The Einit
program starts all other programs included in the solution, which means that it serves as the initializing program.
The KasperskyOS Community Edition toolkit includes the einit tool, which generates the code of the initializing program (einit.c
) based on the init description. In the example provided below, the file containing the init description is named init.yaml
, but it can have any name.
For more details, refer to "Starting processes".
If you want the Hello
program to start after the operating system is loaded, all you need to do is specify its name in the init.yaml
file and build an Einit
program based on it.
init.yaml
Building the security module
The hello example contains a basic solution security policy (security.psl
) that allows all interactions.
The security module (ksm.module
) is built based on security.psl
.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
The general build scheme for the hello example looks as follows:
echo example
The echo example demonstrates the use of IPC transport.
It shows how to use the main tools that let you implement interaction between programs.
The echo example describes a basic case of interaction between two programs:
- The
Client
program sends a number (value
) to theServer
program. - The
Server
program modifies this number and sends the new number (result
) to theClient
program. - The
Client
program prints theresult
number to the screen.
To set up this interaction between programs:
- Connect the
Client
andServer
programs by using the init description. - On the server, implement an interface with a single
Ping
method that has one input argument (the original number (value
)) and one output argument (the modified number (result
)).Description of the
Ping
method in the IDL language:Ping(in UInt32 value, out UInt32 result); - Create static description files in the EDL, CDL and IDL languages. Use the NK compiler to generate files containing transport methods and types (proxy object, dispatchers, etc.).
- In the code of the
Client
program, initialize all required objects (transport, proxy object, request structure, etc.) and call the interface method. - In the code of the
Server
program, prepare all the required objects (transport, component dispatcher and program dispatcher, etc.), accept the request from the client, process it and send a response.
Example files
The code of the example and build scripts are available at the following path:
The echo example consists of the following source files:
client/src/client.c
contains implementation of theClient
program.server/src/server.c
contains implementation of theServer
program.resources/Server.edl
,resources/Client.edl
,resources/Responder.cdl
,resources/Pingable.idl
are static descriptions.init.yaml
contains the init description.
Building and running example
See Building and running examples section.
The build scheme for the echo example looks as follows:
ping example
The ping example demonstrates the use of a solution security policy to control interactions between programs.
The ping example includes four programs: Client
, Server
, KlogEntity
and KlogStorageEntity
.
The Server
program provides two identical Ping
and Pong
methods that receive a number and return a modified number:
The Client
program calls both of these methods in a different sequence. If the method call is denied by the solution security policy, a message regarding the failed call attempt is displayed.
The system programs KlogEntity
and KlogStorageEntity
perform a security audit.
The transport part of the ping example is virtually identical to its counterpart in the echo example. The only difference is that the ping example uses two methods (Ping
and Pong
) instead of just one.
Solution security policy in the ping example
The solution security policy in this example allows startup of the KasperskyOS kernel and the Einit program, which is allowed to start all programs in the solution. Queries to the Server
program are managed by methods of the Flow security model.
The finite-state machine described in the configuration of the request_state
Flow security model object has two states: not_sent
and sent
. The initial state is not_sent
. Only transitions from not_sent
to sent
and vice versa are allowed.
When the Ping
and Pong
methods are called, the current state of the request_state
object is checked. In the not_sent
state, only a Ping
call is allowed, in which case the state changes to sent
. Likewise, in the sent
state, only a Pong
call is allowed, in which case the state changes to not_sent
.
Therefore, the Ping
and Pong
methods can be called only in succession.
Fragment of the security.psl file
The security policy description in the ping example also contains a section for solution security policy tests.
For an example of such a policy, see the "Example 2" section in "Examples of tests for KasperskyOS-based solution security policies".
The full security policy description for the ping example is located in the security.psl.in
and core.psl
files at the following path: /opt/KasperskyOS-Community-Edition-<version>/examples/ping/einit/src
.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
net_with_separate_vfs example
This example presents a basic case of network interaction using Berkeley sockets.
The example consists of Client
and Server
programs linked by a TCP socket using a loopback interface. Standard POSIX functions are used in the code of the programs.
To connect programs using a socket through a loopback, they must use the same network stack instance. This means that they must interact with a "shared" VFS program (in this example, this program is called NetVfs
).
The CMake system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
net2_with_separate_vfs example
This example demonstrates the special features of a solution in which a program uses standard POSIX functions to interact with an external server.
The net2_with_separate_vfs
example is a modified net_with_separate_vfs
example. In contrast to the net_with_separate_vfs
example, in this example a program interacts over the network with an external server rather than another program running in KasperskyOS.
This example consists of the Client
program running in KasperskyOS on QEMU or Raspberry Pi and the Server
program running in a Linux host operating system. The Client
program and Server
program are bound by a TCP socket. Standard POSIX functions are used in the code of the Client
program.
To connect the Client
program and the Server
program using a socket, the Client
program must interact with the NetVfs
program. During the build, the NetVfs
program is linked to a network driver that supports interaction with the Server
program running in Linux.
The CMake system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
To ensure that an example runs correctly, you must run the Server
program in a Linux host operating system or on a computer connected to Raspberry Pi.
After performing the build, the server
executable file of the Server
program is located in the following directory:
To independently build the executable file of the Server
program, you need to run the following commands:
embedded_vfs example
This example demonstrates how to embed the virtual file system (VFS) provided in KasperskyOS Community Edition into a program being developed.
In this example, the Client
program fully encapsulates the VFS implementation from KasperskyOS Community Edition. This lets you eliminate the use of IPC for all the standard I/O functions (stdio.h
, socket.h
, etc.) for debugging or performance improvement purposes, for example.
The Client
program tests the following operations:
- Create a folder.
- Create and delete a file.
- Read from a file and write to a file.
Supplied resources
The example includes the hdd.img
image of a hard drive with the FAT32 file system.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
vfs_extfs example
This example shows how to embed a new file system into the virtual file system (VFS) that is provided in KasperskyOS Community Edition.
In this example, the Client
program tests the operation of file systems (ext2
, ext3
, ext4
) on block devices. To do so, the Client
queries the virtual file system (the FileVfs
program) via IPC, and FileVfs
in turn queries the block device via IPC.
The ext2
and ext3
file systems work with the default settings. The ext4
file system works if you disable extent
(mkfs.ext4 -O ^64bit,^extent /dev/foo
).
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Preparing an SD card to run on Raspberry Pi 4 B
To run the vfs_extfs
example on Raspberry Pi 4 B, the SD card must have a bootable partition with the solution image as well as 3 additional partitions with the ext2
, ext3
and ext4
file systems, respectively.
multi_vfs_ntpd example
This example shows how to use an external NTP server in KasperskyOS. The Ntpd
program is included in KasperskyOS Community Edition and is an implementation of an NTP client, which gets time parameters from external NTP servers in the background and passes them to the KasperskyOS kernel.
The example also demonstrates the use of various virtual file systems (VFS) in a single solution:
- The
VfsNet
program is used for working with the network. - The
VfsSdCardFs
program is used to work with the file system.
The Client
program uses standard libc
library functions for getting time data. These functions are converted into queries to the VFS program via IPC.
The CMake system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Supplied resources
- The directory
./resources/edl
contains theClient.edl
file, which contains a static description of theClient
program. - The directory
./resources/hdd/etc
contains the configuration files for theVfsNet
,Dhcpcd
andNtpd
programs:hosts
,dhcpcd.conf
andntp.conf
, respectively.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
multi_vfs_dns_client example
This example shows how to use an external DNS server in KasperskyOS.
The example also demonstrates the use of various virtual file systems (VFS) in a single solution:
- The
VfsNet
program is used for working with the network. - The
VfsSdCardFs
program is used to work with the file system.
The Client
program uses standard libc
library functions for contacting an external DNS service. These functions are converted into queries to the VfsNet
program via IPC.
The CMake system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Supplied resources
- The directory
./resources/edl
contains theClient.edl
file, which contains a static description of theClient
program. - The directory
./resources/hdd/etc
contains the configuration files for theVfsNet
andDhcpcd
programs:hosts
anddhcpcd.conf
, respectively.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
multi_vfs_dhcpcd example
Example use of the kl.rump.Dhcpcd
program.
The Dhcpcd
program is an implementation of a DHCP client, which gets network interface parameters from an external DHCP server in the background and passes them to a virtual file system (hereinafter referred to as a VFS).
The example also demonstrates the use of different VFSes in a single solution. The example uses different VFS to access the functions for working with the file system and functions for working with the network:
- The
VfsNet
program is used for working with the network. - The
VfsSdCardFs
program is used to work with the file system.
The Client
program uses standard libc
library functions for getting information on network interfaces (ioctl
). These functions are converted into queries to the VFS program via IPC.
The CMake system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Supplied resources
The ./resources/hdd/etc
directory contains configuration files for the VFS and Dhcpcd
programs. The standard syntax of dhcpcd.conf
is used for the Dhcpcd
program configuration.
The CMakeLists.txt
root file defines the values of variables that determine the selected configuration file:
DHCPCD_FALLBACK
Dynamically receive the parameters of network interfaces from an external DHCP server but statically define the parameters if the DHCP server is not available. This value is used by default.
DHCPCD_DYNAMIC
Dynamically receive the parameters of network interfaces from an external DHCP server.
DHCPCD_STATIC
Statically define the parameters of network interfaces.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
mqtt_publisher (Mosquitto) example
Example use of the MQTT protocol in KasperskyOS.
In this example, an MQTT subscriber must be started on the host operating system, and an MQTT publisher must be started on KasperskyOS. The Publisher
program is an implementation of an MQTT publisher that publishes the current time with a 5-second interval.
When the example starts and runs successfully, an MQTT subscriber started on the host operating system prints a "received PUBLISH"
message with a "datetime"
topic.
The example also demonstrates the use of various virtual file systems (VFS) in a single solution:
- The
VfsNet
program is used for working with the network. - The
VfsSdCardFs
program is used to work with the file system.
The CMake system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Starting Mosquitto
To run this example, a Mosquitto MQTT broker must be installed and started on the host system. To install and start Mosquitto, run the following commands:
To start an MQTT subscriber on the host system, run the following command:
Supplied resources
- The directory
./resources/edl
contains thePublisher.edl
file, which contains a static description of thePublisher
program. - The directory
./resources/hdd/etc
contains the configuration files for theVfsNet
,Dhcpcd
andNtpd
programs:hosts
,dhcpcd.conf
andntp.conf
, respectively.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
mqtt_subscriber (Mosquitto) example
Example use of the MQTT protocol in KasperskyOS.
In this example, an MQTT publisher must be started on the host operating system, and an MQTT subscriber must be started on KasperskyOS. The Subscriber
program is an implementation of an MQTT subscriber.
When the example starts and runs successfully, an MQTT subscriber started on KasperskyOS prints a "Got message with topic: my/awesome/topic, payload: hello"
message.
The example also demonstrates the use of various virtual file systems (VFS) in a single solution:
- The
VfsNet
program is used for working with the network. - The
VfsSdCardFs
program is used to work with the file system.
The CMake system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Starting Mosquitto
To run this example, a Mosquitto MQTT broker must be installed and started on the host system. To install and start Mosquitto, run the following commands:
To start an MQTT publisher on the host system, run the following command:
Supplied resources
- The directory
./resources/edl
contains theSubscriber.edl
file, which contains a static description of theSubscriber
program. - The directory
./resources/hdd/etc
contains the configuration files for theVfsNet
,Dhcpcd
andNtpd
programs:hosts
,dhcpcd.conf
andntp.conf
, respectively.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
gpio_input example
Example use of the GPIO driver.
This example lets you verify the functionality of GPIO input pins. The "gpio0" port is used. All pins except those indicated in exceptionPinArr
array are set for input by default. The voltage on the pins corresponds to the state of the registers of the pull-up resistors. The state of all pins, starting from GPIO0 (accounting for the pins indicated in the exceptionPinArr
array), will be read in succession. Messages about the state of the pins will be displayed on the console. The delay between the readings of adjacent pins is determined by the DELAY_S
macro (the time is indicated in seconds).
exceptionPinArr
is an array of GPIO pin numbers that need to be excluded from the example. This may be necessary if some pins are already being used for other functions, e.g. if pins are being used for a UART connection during debugging.
If you build and run this example on QEMU, an error will occur. This is the expected behavior, because there is no GPIO driver for QEMU.
If you build and run this example on Raspberry Pi 4 B, an error will occur.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
gpio_output example
Example use of the GPIO driver.
This example lets you verify the functionality of GPIO output pins. The "gpio0" port is used. The initial state of all GPIO pins should correspond to a logical zero (no voltage on the pin). All pins other than those indicated in the exceptionPinArr
array are configured for output. Each pin, starting with GPIO0 (accounting for those indicated in the exceptionPinArr
array), will be sequentially changed to a logical one (voltage on the pin) and then to a logical zero. The delay between the changes of pin state is determined by the DELAY_S
macro (the time is indicated in seconds). The pins are turned on/off from GPIO0
to GPIO27
and then back against to GPIO0
.
exceptionPinArr
is an array of GPIO pin numbers that need to be excluded from the example. This may be necessary if some pins are already being used for other functions, e.g. if pins are being used for a UART connection during debugging.
If you build and run this example on QEMU, an error will occur. This is the expected behavior, because there is no GPIO driver for QEMU.
If you build and run this example on Raspberry Pi 4 B, an error will occur.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
gpio_interrupt example
Example use of the GPIO driver.
This example lets you verify the functionality of GPIO pin interrupts. The "gpio0" port is used. In the pinsBitmap
bitmask of the CallBackContext
interrupt context, the pins from exceptionPinArr
array are marked as handled so that the example can properly terminate later. All pins other than those indicated in the exceptionPinArr
array are switched to the PINS_MODE
state. An interrupt handler will be registered for all pins other than those indicated in the exceptionPinArr
array.
In an endless loop, the example checks whether the pinsBitmap
bitmask from the CallBackContext
interrupt context is equal to the DONE_BITMASK
bitmask (which corresponds to the condition when an interrupt has occurred on each GPIO pin). Additionally, the handler function for the latest interrupted pin is removed in the loop. When a pin is interrupted for the first time, the handler function is called, which marks the corresponding pin in the pinsBitmap
bitmask in the CallBackContext
interrupt context. The handler function for this pin is removed later.
Keep in mind how the example may be affected by the initial state of the registers of pull-up resistors for each pin.
Interrupts for the GPIO_EVENT_LOW_LEVEL
and GPIO_EVENT_HIGH_LEVEL
events are not supported.
exceptionPinArr
is an array of GPIO pin numbers that need to be excluded from the example. This may be necessary if some pins are already being used for other functions, e.g. if pins are being used for a UART connection during debugging.
If you build and run this example on QEMU, an error will occur. This is the expected behavior, because there is no GPIO driver for QEMU.
If you build and run this example on Raspberry Pi 4 B, an error will occur.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
gpio_echo example
Example use of the GPIO driver.
This example makes it possible to verify the functionality of GPIO pins as well as the operation of GPIO interrupts. The "gpio0" port is used. The output pin (GPIO_PIN_OUT
) should be connected to the input pin (GPIO_PIN_IN
). The output pin (the pin number is defined in the GPIO_PIN_OUT
macro) as well as the input pin (GPIO_PIN_IN
) are configured. Use of the input pin is configured in the IN_MODE
macro. The interrupt handler for the input pin is registered. The state of the output pin changes several times. If the example works correctly, then when the state of the output pin changes the interrupt handler will be called and will display the state of the input pin. What's more, the state of the output pin and the input pin must match.
If you build and run this example on QEMU, an error will occur. This is the expected behavior, because there is no GPIO driver for QEMU.
If you build and run this example on Raspberry Pi 4 B, an error will occur.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
koslogger example
This example demonstrates use of the spdlog
library in KasperskyOS using the KOSLogger
wrapper library.
In this example, the Client
program creates log entries that are saved on an SD card (when running the example on Raspberry Pi) or in the image file named build/einit/sdcard0.img
(when running the example in QEMU).
The example also demonstrates the use of various virtual file systems (VFS) in a single solution. The example uses different VFS to access the functions for working with the file system and functions for working with the network:
- The
VfsNet
program is used for working with the network. - The
VfsSdCardFs
program is used to work with the file system.
The kl.Ntpd
program is included in KasperskyOS Community Edition and is an implementation of an NTP client, which gets time parameters from external NTP servers in the background and passes them to the KasperskyOS kernel.
The kl.rump.Dhcpcd
program is included in KasperskyOS Community Edition and is an implementation of a DHCP client, which gets the parameters of network interfaces from an external DHCP server in the background and passes them to the virtual file system.
The CMake
system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
To ensure that the koslogger
example will correctly run in Raspberry Pi, you must do the following after building the example and preparing your bootable SD card:
- Create the
/lib
directory on the bootable SD card if this directory doesn't already exist. - Open the
build/hdd/lib
directory that was generated when building the example and copy the directory contents to the/lib
directory on the bootable SD card.
pcre example
This example demonstrates use of the pcre
library in KasperskyOS.
In this example, the Client
program uses the pcre
library and prints the results to the console.
The CMake
system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
To ensure that the pcre
example will correctly run in Raspberry Pi, you must do the following after building the example and preparing your bootable SD card:
- Create the
/lib
directory on the bootable SD card if this directory doesn't already exist. - Open the
build/hdd/lib
directory that was generated when building the example and copy the directory contents to the/lib
directory on the bootable SD card.
messagebus example
This example demonstrates use of the MessageBus
component in KasperskyOS.
In this example, the Publisher
, SubscriberA
and SubscriberB
programs use the MessageBus component to exchange messages.
The MessageBus
component implements the message bus. The Publisher
program is the publisher that transfers messages to the bus. The SubscriberA
and SubscriberB
programs are the subscribers that receive messages from the bus.
The example also demonstrates the use of various virtual file systems (VFS) in a single solution. The example uses different VFS to access the functions for working with the file system and functions for working with the network:
- The
VfsNet
program is used for working with the network. - The
VfsSdCardFs
program is used to work with the file system.
The kl.Ntpd
program is included in KasperskyOS Community Edition and is an implementation of an NTP client, which gets time parameters from external NTP servers in the background and passes them to the KasperskyOS kernel.
The kl.rump.Dhcpcd
program is included in KasperskyOS Community Edition and is an implementation of a DHCP client, which gets the parameters of network interfaces from an external DHCP server in the background and passes them to the virtual file system.
The CMake
system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
To ensure that the messagebus
example will correctly run in Raspberry Pi, you must do the following after building the example and preparing your bootable SD card:
- Create the
/lib
directory on the bootable SD card if this directory doesn't already exist. - Open the
build/hdd/lib
directory that was generated when building the example and copy the directory contents to the/lib
directory on the bootable SD card.
I2c_ds1307_rtc example
This example demonstrates use of the i2c
driver (Inter-Integrated Circuit) in KasperskyOS.
In this example, the I2cClient
program uses the i2c
driver interface.
The client library of the i2c
driver is statically linked to the I2cClient
program. The i2c
driver implementation uses a BSP (Board Support Platform) subsystem for configuring clock frequencies (Clocks) and pins multiplexing (PinMux). Therefore, to ensure correct operation of the driver, you need to do the following:
- Link the
I2cClient
program to thei2c_CLIENT_LIB
client library. - Link the
I2cClient
program to thebsp_CLIENT_LIB
client library. - Create an IPC channel between the
I2cClient
program and thekl.drivers.I2C
driver. - Create an IPC channel between the
I2cClient
program and thekl.drivers.BSP
driver.
The CMake
system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
This example is intended to run only on Raspberry Pi. For the example to work correctly, you must connect a DS1307Z real-time clock module to the i2c port.
See Building and running examples section.
Page top
iperf_separate_vfs example
This example demonstrates use of the iperf
library in KasperskyOS.
In this example, the Server
program uses the iperf
library.
By default, the example uses network software emulation (SLIRP) in QEMU. If you configured TAP interfaces for QEMU, you need to change the network settings for starting QEMU (QEMU_FLAGS
variable) in the einit/CMakeLists.txt
file to make sure that the example works correctly (for more details, see the comments in the file).
The example does not use DHCP, therefore the IP address of the network interface must be manually indicated in the code of the Server
program (server/src/main.cpp
). SLIRP uses the default values.
The iperf
library in the example is used in server mode. To connect to this server, install the iperf3
program on the host machine and run it by using the iperf3 -c localhost
command. If you configured TAP interfaces, indicate the current IP address instead of localhost
.
The first startup of the example may take a long time because the iperf
client uses /dev/urandom
to fill packets with random data. To avoid this, run the iperf
client with the --repeating-payload
parameter.
The CMake
system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
Uart example
Example use of the UART driver.
This example shows how to print "Hello World!" to the appropriate port using the UART driver.
When running the example simulation in QEMU, -serial stdio
is indicated in the QEMU flags. This means that the first UART port will be printed only to the standard stream of the host machine.
A full description of the UART driver interface is provided in the file /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/uart/uart.h
.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
spi_check_regs example
This example demonstrates use of the SPI
(Serial Peripheral Interface) driver in KasperskyOS.
The example shows how to work with the SPI interface on the Sense HAT add-on board for Raspberry Pi. In this example, the Client
program uses the SPI
driver interface. The program opens an SPI channel, displays its parameters and sets the necessary operating mode. Then the program sends a data sequence over this channel and waits to receive the ID of the ATTiny controller installed on the Sense HAT board.
The client library of the SPI
driver is statically linked to the Client
program. The Client
program also uses the gpio
driver to set the controller operating mode and the BSP (Board Support Platform) subsystem for configuring clock frequencies (Clocks) and pins multiplexing (PinMux).
The CMake
system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
This example is intended to run only on Raspberry Pi. For the example to work correctly, you must connect the Sense HAT module to the SPI port.
See Building and running examples section.
Page top
barcode_scanner example
This example demonstrates use of a USB
(Universal Serial Bus) driver in KasperskyOS using the libevdev
library.
In this example, the BarcodeScanner
program uses the libevdev
library for interaction with a barcode scanner connected to the USB port of Raspberry Pi.
The program waits for signals from the barcode scanner and prints the obtained data to stderr
.
The CMake
system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
This example is intended to run only on Raspberry Pi. For the example to work correctly, you must connect a barcode scanner running in keyboard emulation mode (such as Zebra Symbol LS2208) to the USB port.
See Building and running examples section.
Page top
perfcnt example
This example demonstrates use of the performance counters in KasperskyOS.
The example includes two programs: Worker
and Monitor
.
The Worker
program performs computations in a loop by periodically loading the processor and utilizing memory.
The Monitor
program uses the KnProfilerGetCounter()
function of the libkos
library to get the values of performance counters for the Worker
program and prints them to the console.
The CMake
system, which is included with KasperskyOS Community Edition, is used to build and run the example.
If you build and run this example on QEMU, some performance counters may not function correctly.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
watchdog_system_reset example
This example demonstrates use of the Watchdog
driver in KasperskyOS.
In this example, the Client
program uses the Watchdog
driver interface to interact with the Watchdog timer as follows:
- Receives the current parameters of the
Watchdog
driver and prints them tostderr
. - Changes the default value of the timer to a new value and starts the timer.
- Resets the timer several times.
- Waits for the system to restart when the timer is triggered.
The client library of the Watchdog
driver is statically linked to the Client
program.
The CMake
system, which is included with KasperskyOS Community Edition, is used to build and run the example.
Example files
The code of the example and build scripts are available at the following path:
Building and running example
See Building and running examples section.
Page top
Information about certain limits set in the system
Header files and IDL files from the KasperskyOS SDK contain constants that set limits in the system (see table below).
Constants that set limits in the system
Subsystem |
Constants |
---|---|
|
The constants in the |
|
The constants in the
|
|
The constants in the
|
|
The constants in the
The constants in the
|
|
The constants in the
|
|
The constants in the
|
|
The constants in the
|
|
The constants in the
|
|
The constants in the
|
|
The constants in the
|
|
The constants in the
|
Licensing
The End User License Agreement is a legally binding agreement between you and AO Kaspersky that stipulates the terms on which you may use KasperskyOS Community Edition.
Please carefully read the terms of the End User License Agreement before you begin using KasperskyOS Community Edition.
You can view the terms of the End User License Agreement (EULA) in the following ways:
- Read the text of the End User License Agreement before downloading the KasperskyOS Community Edition distribution package.
- Read the document named EULA.<language code>.txt located in the directory
/opt/KasperskyOS-Community-Edition-<version>
after installing KasperskyOS Community Edition.
You accept the terms of the End User License Agreement by selecting the I agree check box under the text of the End User License Agreement before downloading the KasperskyOS Community Edition distribution package.
If you do not accept the terms of the End User License Agreement, you must cancel the download of the distribution package and must not use KasperskyOS Community Edition.
Page top
Data provision
KasperskyOS Community Edition versions
KasperskyOS Community Edition is distributed in two different versions:
- Version that can be downloaded from the Russian-language website https://os.kaspersky.ru/development.
- Version that can be downloaded from the English-language website https://os.kaspersky.com/development.
Versions differ in the contents of their End User License Agreement, file and differ in the specific information that they automatically transmit to Kaspersky servers when a solution is built using CMake libraries from the SDK.
Data provision in KasperskyOS Community Edition
The version of KasperskyOS Community Edition downloaded from the Russian-language website automatically transmits the following information to Kaspersky servers when a solution build is started:
- Number of the installed version of KasperskyOS Community Edition.
- Unique hardware ID consisting of the checksum of the creation date of the directory /opt/KasperskyOS-Community-Edition-<version>.
The version of KasperskyOS Community Edition downloaded from the English-language website automatically transmits the following information to Kaspersky servers when a solution build is started:
- Number of the installed version of KasperskyOS Community Edition.
In addition to data transmission during the solution build process, both versions also check for the availability of a newer version of KasperskyOS Community Edition.
If there is no active Internet connection, the solution build occurs without transmitting data or checking for updates.
You can disable the check for SDK updates and transmission of SDK version information to the Kaspersky server by using the NO_NEW_VERSION_CHECK
parameter of the CMake command initialize_platform()
during the solution build.
Data is transmitted to account for the number of users of KasperskyOS Community Edition and to obtain information about the distribution and use of KasperskyOS Community Edition.
Any received information is protected by Kaspersky in accordance with the requirements established by law and in accordance with current Kaspersky regulations. Data is transmitted over encrypted communication channels.
Page topGlossary
Application
Program that is designed for interaction with a solution user and for performing user tasks.
Related sections:
Building a KasperskyOS-based solution
Arena chunk descriptor
Structure containing the offset of the arena chunk in bytes (relative to the start of the arena) and the size of the arena chunk in bytes.
Related sections:
Working with an IPC message arena
Arena descriptor
Structure containing three pointers: one pointer to the start of the arena, one pointer to the start of the unused part of the arena, and one pointer to the end of the arena.
Related sections:
Working with an IPC message arena
Callable handle
A callable handle is a client IPC handle that simultaneously identifies an IPC channel to a server and an endpoint of this server.
Related sections:
Creating handles
Capability
Each handle is associated with access rights to the resource identified by this handle, which means it is a capability in terms of the capability-based security mechanism known as Object Capability (OCap). By receiving a handle, a process obtains the access rights to the resource that is identified by this handle. For example, these access rights may consist of read permissions, write permissions, and/or permissions to allow another process to perform operations on the resource (handle transfer permission).
Related sections:
Managing access to resources
Managing handles (handle_api.h)
CDL
Component Definition Language is a declarative language used to create a formal specification of a solution component.
Related sections:
Formal specifications of KasperskyOS-based solution components
CDL description
Client
In the context of IPC, client refers to the client process.
Client library of the solution component
Transport library that converts local calls into IPC requests.
Related sections:
Transport code for IPC
Client Process
Process that uses the endpoint of another process via the IPC mechanism. One process can use multiple IPC channels at the same time. A process may act as a client for some IPC channels while acting as a server for other IPC channels.
Related sections:
IPC mechanism
Conditional variable
Synchronization primitive that is used to notify one or more threads about the fulfillment of a condition required by these threads. It is used together with a mutex.
Related sections:
Using synchronization primitives (event.h, mutex.h, rwlock.h, semaphore.h, condvar.h)
Constant part of an IPC message
The part of an IPC message that contains the RIID, MID and (optionally) fixed-size parameters of interface methods.
Related sections:
Overview: IPC message structure
Working with an IPC message arena
IDL data types
Critical section
Section of code in which the resources shared by threads are accessed.
Related sections:
Using synchronization primitives (event.h, mutex.h, rwlock.h, semaphore.h, condvar.h)
Description of a security policy for a KasperskyOS-based solution
A set of interrelated text files with the psl
extension that contain declarations in the PSL language.
Related sections:
Describing a security policy for a KasperskyOS-based solution
General information about a KasperskyOS-based solution security policy description
Security.psl.in template
Example descriptions of basic security policies for KasperskyOS-based solutions
Methods of KasperskyOS core endpoints
Direct memory access
Direct memory access (DMA) is a feature that allows data exchange between devices and the main system memory independently of the processor.
Related sections:
Using DMA (dma.h)
Managing I/O memory isolation (iommu_api.h)
DMA
DMA buffer
Buffer that consists of one or more physical memory regions (blocks) that are used for direct memory access.
Related sections:
Using DMA (dma.h)
Managing I/O memory isolation (iommu_api.h)
EDL
Entity Definition Language is a declarative language used to create a formal specification of a solution component.
Related sections:
Formal specifications of KasperskyOS-based solution components
EDL description
Endpoint
Set of logically related methods available via the IPC mechanism (for example, an endpoint for receiving and transmitting data over the network, or an endpoint for handling interrupts).
Related sections:
Overview
IPC mechanism
Methods of KasperskyOS core endpoints
Endpoint ID
A Runtime Implementation Identifier (RIID) is the sequence number of an endpoint within the set of endpoints of a server (starting at zero).
Related sections:
IPC mechanism
Overview: IPC message structure
Endpoint Interface
Set of signatures for endpoint methods. The endpoint interface is defined in the IDL description.
Related sections:
Formal specifications of KasperskyOS-based solution components
IDL description
Endpoint method
Endpoint Method ID
An Endpoint Method ID (MID) is the sequence number of the endpoint method within the set of methods of this endpoint (starting at zero).
Related sections:
IPC mechanism
Overview: IPC message structure
Event
Synchronization primitive that is used to notify one or more threads about the fulfillment of a condition required by these threads.
Related sections:
Using synchronization primitives (event.h, mutex.h, rwlock.h, semaphore.h, condvar.h)
Event mask
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 common part describes events that are not specific to any resources. The specialized part describes events that are specific to certain resources.
Related sections:
Using notifications (notice_api.h)
Transferring handles
Execute interface
Interface used by the KasperskyOS kernel when querying the Kaspersky Security Module to notify it about kernel startup or about initiating the startup of a process by the kernel or by other processes.
Related sections:
Setting the global parameters of a KasperskyOS-based solution security policy
Binding methods of security models to security events
Formal specification of the KasperskyOS-based solution component
A system of IDL, CDL and EDL descriptions of a solution component (IDL and CDL descriptions are optional).
Related sections:
Formal specifications of KasperskyOS-based solution components
Handle
A handle is an identifier of a resource (for example, a memory area, port, network interface, or IPC channel). The handle of an IPC channel is called an IPC handle.
Related sections:
Managing access to resources
Managing handles (handle_api.h)
KasperskyOS also uses the following descriptors:
Handle dereferencing
Operation in which the client sends a 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 client that called methods for working with a resource (such as read/write or access closure) sends the server the handle that was received from this server when access to the resource was opened.
Related sections:
Managing handles (handle_api.h)
Dereferencing handles
Handle inheritance tree
Hierarchy of generated resource handles stored in the KasperskyOS kernel.
Related sections:
Managing handles (handle_api.h)
Handle permissions mask
Value whose bits are interpreted as access rights to the resource that is identified by the specific handle.
Related sections:
Managing access to resources
Handle permissions mask
Managing handles (handle_api.h)
Handle transport container
Structure consisting of the following three fields: handle field, handle permissions mask field, and the resource transfer context field. It is used to transfer handles via IPC.
Related sections:
Transferring handles
OCap usage example
Hardware interrupt
Signal sent from a device to direct the processor to immediately pause execution of the current program and instead handle an event related to this device. For example, pressing a key on the keyboard invokes a hardware interrupt that ensures the required response to this pressed key (for example, input of a character).
Related sections:
Managing interrupt processing (irq.h)
IDL
Interface Definition Language is a declarative language used to create a formal specification of a solution component.
Related sections:
Formal specifications of KasperskyOS-based solution components
IDL description
Init description
An init description consists of a text file containing data in YAML format that identifies the processes and IPC channels that are created when the solution starts. The init description file is normally named init.yaml
.
Related sections:
Overview: Einit and init.yaml
Example init descriptions
Init.yaml.in template
Initializing program
The Einit
program, which is started by the KasperskyOS kernel, starts other programs according to the init description and creates IPC channels.
Related sections:
Overview: Einit and init.yaml
CMakeLists.txt file for building the Einit program
Structure and startup of a KasperskyOS-based solution
einit
Interface Method
Subprogram that is called via IPC.
Related sections:
IPC mechanism
IDL description
Methods of KasperskyOS core endpoints
Interprocess communication
Interprocess communication (IPC) is a mechanism for interaction between different processes and between a process and the KasperskyOS kernel.
Related sections:
IPC
Initializing IPC transport for interprocess communication and managing IPC request processing (transport-kos.h, transport-kos-dispatch.h)
POSIX support limitations
IPC
IPC channel
KasperskyOS kernel object that allows processes to interact with each other by transmitting IPC messages. An IPC channel has a client side and a server side, which are identified by a client and server IPC handle, respectively.
Related sections:
IPC mechanism
Creating IPC channels
IPC handle
An IPC handle is a handle that identifies an IPC channel. 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. The callable handle and listener handle are IPC handles.
Related sections:
IPC mechanism
Creating handles
Creating IPC channels
IPC message
Data packet that is transmitted between different processes and between processes and the KasperskyOS kernel for IPC. An IPC message contains a constant part and an (optional) arena.
Related sections:
Overview: IPC message structure
IPC message arena
Optional part of an IPC message that contains variable-size parameters of interface methods (and/or elements of these parameters).
Related sections:
Overview: IPC message structure
Working with an IPC message arena
IPC request
IPC message sent to a server from a client.
Related sections:
IPC mechanism
IPC response
IPC message sent to a client from a server.
Related sections:
IPC mechanism
IPC transport
Add-on that works on top of system calls for sending and receiving IPC messages and works separately with the constant part and arena of IPC messages. Transport code works on top of this add-on.
Related sections:
Initializing IPC transport for interprocess communication and managing IPC request processing (transport-kos.h, transport-kos-dispatch.h)
Initializing IPC transport for querying the security module (transport-kos-security.h)
KasperskyOS
A specialized operating system based on a separation microkernel and security monitor.
Related sections:
Overview
KasperskyOS Security Model
Framework for implementing security policies for solutions.
Related sections:
Describing a security policy for a KasperskyOS-based solution
KasperskyOS security models
KasperskyOS-based solution
System software (including the KasperskyOS kernel and Kaspersky Security Module) and applications integrated to work as part of a software/hardware system.
Related sections:
Overview
Structure and startup of a KasperskyOS-based solution
Building a KasperskyOS-based solution
KasperskyOS-based solution component
Program included in a solution.
Related sections:
Overview
Formal specifications of KasperskyOS-based solution components
KSM
The Kaspersky Security Module is the KasperskyOS kernel module that allows or denies interaction between different processes and between processes and the kernel, and handles queries of processes via the security interface.
Related sections:
IPC control
Managing access to resources
KSS
Kaspersky Security System technology lets you implement solution security policies. This technology prescribes the creation of formal specifications of solution components and descriptions of solution security policies using security models.
Related sections:
Overview
Developing security policies
Listener handle
A listener handle is a server IPC handle with extended rights that allow it to add IPC channels to the set of IPC channels identified by this handle.
Related sections:
Creating IPC channels
Creating handles
Initializing IPC transport for interprocess communication and managing IPC request processing (transport-kos.h, transport-kos-dispatch.h)
Dynamically creating IPC channels (cm_api.h, ns_api.h)
Memory barrier
A memory barrier is an instruction for a compiler or processor that guarantees that memory access operations specified in source code before setting a barrier will be executed before the memory access operations specified in source code after setting a barrier.
Related sections:
Using memory barriers (barriers.h)
Message signaled interrupt (MSI)
Message signaled interrupt (MSI) is a hardware interrupt that occurs when the device accesses the interrupt controller via MMIO memory.
Related sections:
Managing interrupt processing (irq.h)
MID
Mutex
A synchronization primitive that provides for mutually exclusive execution of critical sections.
Related sections:
Using synchronization primitives (event.h, mutex.h, rwlock.h, semaphore.h, condvar.h)
POSIX support limitations
Notification receiver
KasperskyOS kernel object that collects notifications about events that occur with resources.
Related sections:
Using notifications (notice_api.h)
Managing handles (handle_api.h)
OCap
Object Capability is a security mechanism that is based on capabilities.
Related sections:
Managing access to resources
Managing handles (handle_api.h)
Operating Performance Point
Operating Performance Point (OPP) is a combination of the matching frequency and voltage for a processor group.
Related sections:
CPU frequency management endpoint
OPP
PAL
Policy Assertion Language is a declarative language used to create solution security policy tests.
Related sections:
Creating and performing tests for a KasperskyOS-based solution security policy
Examples of tests for KasperskyOS-based solution security policies
Process
A running program that has the following distinguishing characteristics:
- It can provide endpoints to other processes and/or use the endpoints of other processes via the IPC mechanism.
- It uses KasperskyOS core endpoints via the IPC mechanism.
- It is associated with a solution security policy that regulates the interactions of the process with other processes and with the KasperskyOS kernel.
Related sections:
Overview
Starting processes
init.yaml.in template
Program
Code that is executed within the context of an individual process.
Related sections:
Building a KasperskyOS-based solution
PSL
Policy Specification Language is a declarative language used to create a solution security policy description.
Related sections:
Describing a security policy for a KasperskyOS-based solution
PSL language syntax
Read-write lock
Synchronization primitive that is used to allow access to resources shared between threads for either write access for one thread or read access for multiple threads at the same time.
Related sections:
Using synchronization primitives (event.h, mutex.h, rwlock.h, semaphore.h, condvar.h)
Recursive mutex
Mutex that can be acquired by a single thread multiple times.
Related sections:
Using synchronization primitives (event.h, mutex.h, rwlock.h, semaphore.h, condvar.h)
Resource
KasperskyOS-based software/hardware system object that can be accessed by processes. Resources can be system resources or user resources.
Related sections:
Managing access to resources
Resource consumer
Process that uses the resources provided by the KasperskyOS kernel or other processes.
Related sections:
Managing access to resources
Managing handles (handle_api.h)
Mic security model
Resource integrity level
Level of trust afforded to a resource. The level of trust in a resource depends on whether this resource was created by a trusted subject within a software/hardware system running KasperskyOS or if it was received from an untrusted external software/hardware system, for example.
Related sections:
Mic security model
Resource provider
Process that manages user resources and manages access to those resources for other processes. For example, drivers are resource providers.
Related sections:
Managing access to resources
Managing handles (handle_api.h)
Resource transfer context
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.
Related sections:
Managing handles (handle_api.h)
Resource transfer context object
KasperskyOS kernel object that stores the pointer to the resource transfer context.
Related sections:
Managing handles (handle_api.h)
RIID
Security audit
A security audit consists of the following sequence of actions. The Kaspersky Security Module provides the KasperskyOS kernel with information about decisions made by this module. Then the kernel forwards this data to the system program Klog
, which decodes this information and forwards it to the system program KlogStorage
(data is transmitted via IPC). The latter sends the received audit data to standard output (or standard error) or writes it to a file.
Related sections:
Creating security audit profiles
Examples of security audit profiles
Using the system programs Klog and KlogStorage to perform a security audit
Security audit configuration
Element of a security audit profile that defines the security model objects covered by the security audit and the conditions for performing the security audit.
Related sections:
Creating security audit profiles
Examples of security audit profiles
Security audit data
Information about decisions made by the Kaspersky Security Module, including the actual decisions ("granted" or "denied"), descriptions of security events, results from calling methods of security models, and data on incorrect IPC messages.
Related sections:
Creating security audit profiles
Security audit profile
Set of security audit configurations, each of which defines the security model objects covered by the security audit and the conditions for performing the security audit.
Related sections:
Creating security audit profiles
Binding methods of security models to security events
Examples of security audit profiles
Setting the global parameters of a KasperskyOS-based solution security policy
Security audit runtime-level
The security audit runtime-level is a global parameter of a solution security policy and consists of an unsigned integer that defines the active security audit configuration. (The word "level" here refers to the configuration variant and does not necessarily involve a hierarchy.)
Related sections:
Creating security audit profiles
Setting the global parameters of a KasperskyOS-based solution security policy
Security context
Data that is associated with a security ID and is used by the Kaspersky Security Module to make decisions.
Related sections:
Managing access to resources
Security event
A signal indicating the initiation of communication between a process and another process or between a process and the KasperskyOS kernel.
Related sections:
General information about a KasperskyOS-based solution security policy description
Binding methods of security models to security events
Examples of binding security model methods to security events
Security ID
A Security Identifier (SID) is a globally unique identifier of a resource. The Kaspersky Security Module identifies resources based on their security IDs.
Related sections:
Managing access to resources
Getting a security ID (SID)
Security interface
Interface that is used for interaction between a process and the Kaspersky Security Module. The security interface is defined in the IDL description.
Related sections:
Formal specifications of KasperskyOS-based solution components
EDL description
CDL description
IDL description
Binding methods of security models to security events
Initializing IPC transport for querying the security module (transport-kos-security.h)
Security model expression
Security model method that returns values that can be used as input data for other methods of security models.
Related sections:
General information about a KasperskyOS-based solution security policy description
Binding methods of security models to security events
KasperskyOS security models
Security model method
Element of a security model that determines the permissibility of interactions between various processes and between processes and the KasperskyOS kernel.
Related sections:
General information about a KasperskyOS-based solution security policy description
Binding methods of security models to security events
Examples of binding security model methods to security events
KasperskyOS security models
Security model object
Instance of a class whose definition is a formal description of a security model (in a PSL file).
Related sections:
General information about a KasperskyOS-based solution security policy description
Creating security model objects
KasperskyOS security models
Security model rule
Security model method that returns a "granted" or "denied" decision.
Related sections:
General information about a KasperskyOS-based solution security policy description
Binding methods of security models to security events
Examples of binding security model methods to security events
KasperskyOS security models
Security module decision
A decision on whether to allow or deny a specific interaction between different processes or between a process and the KasperskyOS kernel.
Related sections:
Overview
IPC control
General information about a KasperskyOS-based solution security policy description
Security pattern
A security pattern (or security template) describes a specific recurring security issue that arises in certain known contexts, and provides a well-proven, general scheme for resolving this kind of security issue. A pattern is not a finished project that can be converted directly into code. Instead, it is a solution to a general problem encountered in various projects.
Related sections:
Security patterns for development under KasperskyOS
Security pattern system
Set of security patterns together with instructions on their implementation, combination, and practical use when designing secure software systems.
Related sections:
Security patterns for development under KasperskyOS
Security policy for a KasperskyOS-based solution
Logic for processing security events in the solution. This logic is implemented by the Kaspersky Security Module. The source code of the Kaspersky Security Module is generated from the solution security policy description and formal specifications of solution components.
Related sections:
Overview
Security template
A security template (or security pattern) describes a specific recurring security issue that arises in certain known contexts, and provides a well-proven, general scheme for resolving this kind of security issue. A template is not a finished project that can be converted directly into code. Instead, it is a solution to a general problem encountered in various projects.
Related sections:
Security patterns for development under KasperskyOS
Seed
Starting number of the random number generator (seed) , which determines the sequence of the generated random numbers. In other words, if the same seed value is set, the generator creates identical sequences of random numbers. (The entropy of these numbers is fully determined by the entropy of the seed value, which means that these numbers are not entirely random, but pseudorandom.)
Related sections:
Generating random numbers (random_api.h)
Semaphore
Synchronization primitive based on a counter whose value can be atomically changed.
Related sections:
Using synchronization primitives (event.h, mutex.h, rwlock.h, semaphore.h, condvar.h)
POSIX support limitations
Server
In the context of IPC, server refers to the server process.
Server library of the solution component
Transport library that converts IPC requests into local calls.
Related sections:
Transport code for IPC
Server process
Process that provides endpoints to other processes via the IPC mechanism. One process can use multiple IPC channels at the same time. A process may act as a server for some IPC channels while acting as a client for other IPC channels.
Related sections:
IPC mechanism
SID
Subject integrity level
Level of trust afforded to a subject. The trust level of a subject depends on whether the subject interacts with untrusted external software/hardware systems or whether it has a proven standard of quality, for example.
Related sections:
Mic security model
System program
A program that creates the infrastructure for application software (for example, it facilitates hardware operations, supports the IPC mechanism, and implements file systems and network protocols).
Related sections:
Building a KasperskyOS-based solution
System resource
Resource that is managed by the KasperskyOS kernel. Some examples of system resources include processes, memory regions, and interrupts.
Related sections:
Managing access to resources
Managing handles (handle_api.h)
Thread
A thread is an abstraction used to manage the execution of program code. One process can include one or more threads. CPU time is allocated separately for each thread. Each thread may execute the entire code of the program or just a part of the code. The same program code may be executed in multiple threads.
Related sections:
POSIX support limitations
Transport code
C-language methods and types for IPC.
Related sections:
Transport code for IPC
Example generation of transport methods and types
Transport library
To use a supplied solution component via IPC, the KasperskyOS SDK provides the following transport libraries:
- Solution component's client library, which converts local calls into IPC requests.
- Solution component's server library, which converts IPC requests into local calls.
Related sections:
Transport code for IPC
User resource
Resource that is managed by a process. Examples of user resources: files, input-output devices, data storage.
Related sections:
Managing access to resources
Managing handles (handle_api.h)
User resource context
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.
Related sections:
Managing handles (handle_api.h)
Information about third-party code
Information about third-party code is contained in the file named legal_notices.txt in the application installation folder.
Page top
Trademark notices
Registered trademarks and endpoint marks are the property of their respective owners.
Arm and Mbed are registered trademarks or trademarks of Arm Limited (or its subsidiaries) in the US and/or elsewhere.
Docker and the Docker logo are trademarks or registered trademarks of Docker, Inc. in the United States and/or other countries. Docker, Inc. and other parties may also have trademark rights in other terms used herein.
Eclipse Mosquitto is a trademark of Eclipse Foundation, Inc.
GoogleTest is a trademark of Google LLC.
Linux is the registered trademark of Linus Torvalds in the U.S. and other countries.
OpenSSL is a trademark owned by the OpenSSL Software Foundation.
Python is a trademark or registered trademark of the Python Software Foundation.
Raspberry Pi is a trademark of the Raspberry Pi Foundation.
QT is a trademark or registered trademark of The Qt Company Ltd.
Ubuntu is a registered trademark of Canonical Ltd.
UNIX is a registered trademark in the United States and other countries, licensed exclusively through X/Open Company Limited.
Visual Studio and Windows are trademarks of the Microsoft group of companies.
Page top