KasperskyOS: project structure

What are the layers in application projects for KasperskyOS?
Alexander Lifanov
Product Manager, KasperskyOS Community Edition

In this article, I’ll be talking about the project structure for applications in KasperskyOS based on the project for a training course on Stepik.

To start…

… let’s cover the main features of KasperskyOS to better understand the reasons behind certain architectural features.

KasperskyOS Community Edition in its current version (Version 1.1.1) is intended for use in various devices.

Firstly, this means that a project assembly isn’t an executable file, but rather a downloadable image for a specific device. In this respect, KasperskyOS resembles Yocto, OpenWRT and similar projects. The image contains a bootloader, OS kernel, security system, as well as application executable files.

Secondly, in the solution that’s assembled by compiling this image, there’s no out-of-the-box console, graphical interface, command interpreter, or shell. To control what’s happening inside the program, the developer needs to deal with this separately.

Thirdly, the development platform and execution platform don’t match. At all. (Development is usually carried out on x86, and KasperskyOS CE is now designed to run on ARM processors.) So the toolkit includes a cross-compiler.

KasperskyOS is intended for use in places where the solution has cybersecurity requirements, but there isn’t an option to install other protection solutions.

Security becomes part of the operating system and solution as a whole. To implement such protection, the old “divide and conquer” pinciple is used =) The entire application code is divided into parts so the interactions between them can be strictly controlled.

About the project

Application solution projects for KasperskyOS have three layers:

  1. Application code
  2. Component declarations and security policies
  3. CMake build system

Now let’s take a look at each individual layer.

Application code

Not really that interesting taken by itself. The application code is written in C languages (in this example, for the gcc compiler) or C++ (the g++ compiler). It must consist of one or more files that are referenced to each other, one of which contains the main() function. These files are typically located in folders with process class names.

But there is one relatively complex subtlety. The architecture of an application that’s more complex than “hello world” will most likely contain more than one process for security purposes. Each process in KasperskyOS is packaged in a separate executable file with its own main(). The Kaspersky Security Module only controls the interaction of individual executable files with each other, as well as with the kernel.

Executables in hello_vfs

For the hello_vfs example, there are four executables:

  • hello
  • precompiled_vfs, with its main() function contained inside the library from the SDK
  • ksm.module, which is built from the security policies described in the next section
  • einit, which is functionally equivalent to the autoexec.bat everyone knows

Component declarations and security policies

These two parts are required for the automatic compilation of the transport code and Kaspersky Security Module.

The generated transport code is responsible for packing, addressing, and unpacking IPC messages between processes. It forms the “holes” in the right places in the logical walls that separate processes from each other. The transport code is formed on the basis of the descriptions of entities (EDL), components (CDL) and their interfaces (IDL).

If there’s a static description of an IPC channel, a description of the message structure is taken from the corresponding files. The sender and recipient are specified in the init.yaml.in file. If the channel is formed dynamically, the API functions of the KasperskyOS kernel are used to perform these actions.

The init.yaml.in file with the definition of user processes and the IPC channel between them

The Kaspersky Security Module is responsible for the timely opening and closing of these “holes.” The KSM code is also generated from a set of PSL files usually found in the einit folder as a separate executable file. Every IPC request that goes between security domains (in reality, between executable files) is redirected by the kernel to ksm.module to receive a verdict about permission to execute the request. The returned response will also be checked by the security module.

CMake build system

By this stage, the individual compiled components and libraries are ready to be assembled into a single whole (bootable image for the target platform or emulator). To carry out this process, the KasperskyOS SDK uses the CMake system. It is controlled by a set of CMakeLists.txt files that refer to each other using add_subdirectory() statements.

The main CMakeLists with links to the precompiled VFS (as a separate ready-made executable file) and two source folders (hello and einit)

The main CMakeLists defines links to folders with external (from the SDK) and internal (from the project) source and precompiled files. In the control files of the underlying folders, a correspondence is assigned between the class name and source file, and dependencies are established from the libraries included inside the corresponding executable files.

The CMakeLists is called last. It determines the assembly of the einit file because it checks the readiness of all other components, including KSM, and performs the final assembly of images to run.

In this article, I’ll be talking about the project structure for applications in KasperskyOS based on the project for a training course on Stepik.

To start…

… let’s cover the main features of KasperskyOS to better understand the reasons behind certain architectural features.

KasperskyOS Community Edition in its current version (Version 1.1.1) is intended for use in various devices.

Firstly, this means that a project assembly isn’t an executable file, but rather a downloadable image for a specific device. In this respect, KasperskyOS resembles Yocto, OpenWRT and similar projects. The image contains a bootloader, OS kernel, security system, as well as application executable files.

Secondly, in the solution that’s assembled by compiling this image, there’s no out-of-the-box console, graphical interface, command interpreter, or shell. To control what’s happening inside the program, the developer needs to deal with this separately.

Thirdly, the development platform and execution platform don’t match. At all. (Development is usually carried out on x86, and KasperskyOS CE is now designed to run on ARM processors.) So the toolkit includes a cross-compiler.

KasperskyOS is intended for use in places where the solution has cybersecurity requirements, but there isn’t an option to install other protection solutions.

Security becomes part of the operating system and solution as a whole. To implement such protection, the old “divide and conquer” pinciple is used =) The entire application code is divided into parts so the interactions between them can be strictly controlled.

About the project

Application solution projects for KasperskyOS have three layers:

  1. Application code
  2. Component declarations and security policies
  3. CMake build system

Now let’s take a look at each individual layer.

Application code

Not really that interesting taken by itself. The application code is written in C languages (in this example, for the gcc compiler) or C++ (the g++ compiler). It must consist of one or more files that are referenced to each other, one of which contains the main() function. These files are typically located in folders with process class names.

But there is one relatively complex subtlety. The architecture of an application that’s more complex than “hello world” will most likely contain more than one process for security purposes. Each process in KasperskyOS is packaged in a separate executable file with its own main(). The Kaspersky Security Module only controls the interaction of individual executable files with each other, as well as with the kernel.

Executables in hello_vfs

For the hello_vfs example, there are four executables:

  • hello
  • precompiled_vfs, with its main() function contained inside the library from the SDK
  • ksm.module, which is built from the security policies described in the next section
  • einit, which is functionally equivalent to the autoexec.bat everyone knows

Component declarations and security policies

These two parts are required for the automatic compilation of the transport code and Kaspersky Security Module.

The generated transport code is responsible for packing, addressing, and unpacking IPC messages between processes. It forms the “holes” in the right places in the logical walls that separate processes from each other. The transport code is formed on the basis of the descriptions of entities (EDL), components (CDL) and their interfaces (IDL).

If there’s a static description of an IPC channel, a description of the message structure is taken from the corresponding files. The sender and recipient are specified in the init.yaml.in file. If the channel is formed dynamically, the API functions of the KasperskyOS kernel are used to perform these actions.

The init.yaml.in file with the definition of user processes and the IPC channel between them

The Kaspersky Security Module is responsible for the timely opening and closing of these “holes.” The KSM code is also generated from a set of PSL files usually found in the einit folder as a separate executable file. Every IPC request that goes between security domains (in reality, between executable files) is redirected by the kernel to ksm.module to receive a verdict about permission to execute the request. The returned response will also be checked by the security module.

CMake build system

By this stage, the individual compiled components and libraries are ready to be assembled into a single whole (bootable image for the target platform or emulator). To carry out this process, the KasperskyOS SDK uses the CMake system. It is controlled by a set of CMakeLists.txt files that refer to each other using add_subdirectory() statements.

The main CMakeLists with links to the precompiled VFS (as a separate ready-made executable file) and two source folders (hello and einit)

The main CMakeLists defines links to folders with external (from the SDK) and internal (from the project) source and precompiled files. In the control files of the underlying folders, a correspondence is assigned between the class name and source file, and dependencies are established from the libraries included inside the corresponding executable files.

The CMakeLists is called last. It determines the assembly of the einit file because it checks the readiness of all other components, including KSM, and performs the final assembly of images to run.