📜 ⬆️ ⬇️

Introduction to security based on mandatory references (Capability-based security)

In most of today's operating systems, the security model is rooted in Unix, inheriting the assumption that the user can trust the programs that he runs. However, as practice shows, this statement is fundamentally wrong.

Marc Stiegler compares this to the situation when you, hiring a cleaner, give him the Main And Single Key, which not only opens the room in which you need to do the cleaning, but opens all the doors, including and a safe door with gold. Those. you have only two alternatives - either not to give this key to the cleaner - and then he will not be able to do his job, or to give and hope for his decency.

The model of security based on mandatory references (capability-based security) offers a solution to this problem.

The model is guided by the principle of minimum privileges . For example, by opening a file in a text editor, you give the editor only access to one specific file. You still can not be sure that the program will edit the file as you expect it, but will not encrypt and require money for decryption - but the rates are now significantly lower.
')
By default, any program (and ideally any piece of code) runs in the ideal sandbox — it does not have access to the file system, the network, or other programs. In some implementations, there is not even the right to allocate memory and consume processor resources!

The program can go beyond the boundaries of the sandbox using a mandatory link (English capability, not to be confused with POSIX Capabilities) to external resources.

To date, there is simply no Russian-language literature on this topic - so much so that in order to write this article, I had to invent a translation for the term “capability” myself. So, get acquainted:

A mandatory reference is a special form of reference that, firstly, identifies an object (and therefore a “link”), and secondly, defines operations that are valid on it (and therefore a “mandatory” operation). Mandatory references that refer to the same object, but specify different sets of valid operations, can coexist.

The one who owns the mandatory reference can always perform any operations on the object that are allowed by this reference — no additional checks are performed during the execution of operations. All checks are done before the subject receives a mandatory reference - if he received it, this serves as proof that all checks by the subject have been passed.

For comparison, the path to the file as a string identifies the object, but does not determine the operations allowed on it - therefore, every time an operation is performed on a file in the specified way, the OS must perform an access right check.

On the other hand, an open file descriptor can be considered a mandatory reference. The permission check is done before the descriptor is created, and the mode in which the file was opened is stored in the data structures associated with the descriptor.

The terms “resource” and “subject” can be found in the security literature based on mandatory references ( PDF ). "Resource" is the role played by the object to which the mandatory reference points. A “subject” is one who has a mandatory reference and can perform operations on a resource. There is a many-to-many relationship between a subject and a resource: each subject can have an arbitrary number of mandatory references to different resources, an arbitrary number of subjects can refer to one resource.

In some implementations, “subject” and “resource” are roles that the same objects can play depending on the context. In others , these are heterogeneous objects, but a kind of resource is a channel of communication with another subject. Anyway, objects can communicate with each other with the help of mandatory references - the object owning the link can send a message to the referenced object, as in the OOP. In the future, for convenience of presentation, we will assume that objects are homogeneous — “Subject” and “resource” are nothing more than situational roles.

The model assumes that the internal state of objects is encapsulated and cannot be changed except by sending a message.

Mandatory links movement


An object can receive a mandatory link in a limited number of ways:
  1. As part of the initial conditions — during system initialization (for example, OS installation), some initial object graph is created, which is further developed according to the rules described here, but the initial graph can be created using cheat methods.
  2. When creating a new object , the operation of creating a new object returns a mandatory reference with the maximum amount of available permissions.
  3. In a dowry - by creating a child object, the parent can transfer to him a part of the powers available to him (pass in the arguments of the constructor in OOP terminology)
  4. Through mutual friends - if Alice does not have a link to Bob, then Carol can (if she wants!) Give her that link. To do this, in OOP terminology, Carol sends a message to Alice, passing a reference to Bob as a parameter.
    This can only happen if Kerol has mandatory references to both Alice and Bob. The link to Alice should allow the transfer of this message. Carol cannot transfer authority to access Bob to a greater extent than she has (maybe to a lesser extent, but more on that later).
These rules allow us to represent the system under consideration as a graph of objects and formally analyze existing communication channels, as well as the places and conditions for the emergence of new connections.

From the point of view of the model, a set of global variables (in any form, including the traditional file system) is a single object that is accessible to everyone. The existence of such an object does not contradict the model, but is a hole in the security of the system. The load of singletons and global access to the file system in the standard Java library has become a serious problem before the creators of the J-Kernel system . The same problems are present in the .NET platform.

As you can see, the above rules describe how access rights can be distributed throughout the system, but do not describe how you can reduce previously granted rights. Indeed, the security model based on mandatory references is based on the assumption that once granted rights cannot be selected. This property is the reason for criticizing the model, but in fact is not a problem ( PDF ).

Instead of giving the right to “read the file,” the program is given the right to “read the file until access has been withdrawn.” This is implemented using proxy objects. The following code illustrates this mechanism in a very simplified form:

using System; using System.Collections.Generic; namespace CapRevokeDemo { //  ,      interface IFile { char read(int index); int length { get; } } //  ,    interface IApplication { void openFile(IFile reader); } class FileAlreadyExistsException : Exception { } class FileNotFoundException : Exception { } class AccessRevokedException : Exception { } // ,       class FileManager { public FileManager() { files_ = new Dictionary<string, IFile>(); openedFiles_ = new Dictionary<string,List<FileProxy>>(); } public void addFile(string fileName, IFile reader) { if (files_.ContainsKey(fileName)) { throw new FileAlreadyExistsException(); } files_.Add(fileName, reader); } public void openFileInApplication(string fileName, IApplication app) { IFile reader; if(!files_.TryGetValue(fileName, out reader)) { throw new FileNotFoundException(); } FileProxy proxy = new FileProxy(reader); List<FileProxy> proxies; if(!openedFiles_.TryGetValue(fileName, out proxies)) { proxies = new List<FileProxy>(); openedFiles_.Add(fileName, proxies); } proxies.Add(proxy); app.openFile(proxy); } public void revokeAccessToFile(string fileName) { List<FileProxy> proxies; if(openedFiles_.TryGetValue(fileName, out proxies)) { foreach(FileProxy proxy in proxies) { proxy.revokeAccess(); } openedFiles_.Remove(fileName); } } // -,      . private class FileProxy : IFile { public FileProxy(IFile file) { file_ = file; } public char read(int index) { if(file_ == null) { throw new AccessRevokedException(); } return file_.read(index); } public int length { get { if(file_ == null) { throw new AccessRevokedException(); } return file_.length; } } public void revokeAccess() { file_ = null; } private IFile file_; } private Dictionary<string, IFile> files_; private Dictionary<string, List<FileProxy> > openedFiles_; } //   class Program { class DemoFile : IFile { public DemoFile(string data) { data_ = data; } public char read(int index) { return data_[index]; } public int length { get { return data_.Length; } } private string data_; } class DemoApp : IApplication { public void openFile(IFile file) { file_ = file; index_ = 0; } public void closeFile() { file_ = null; index_ = 0; } public void read() { char c = file_.read(index_++); System.Console.WriteLine("c = {0}", c); } private IFile file_ = null; private int index_ = 0; } public static void Main(string[] args) { try { FileManager manager = new FileManager(); manager.addFile("file", new DemoFile("SECRET")); DemoApp app = new DemoApp(); manager.openFileInApplication("file", app); app.read(); app.read(); app.read(); manager.revokeAccessToFile("file"); app.read(); } catch(Exception e) { System.Console.WriteLine("Exception: {0}", e.Message); } } } } 


I want to note that from this approach it follows that the possibility of withdrawing access must be taken care of in advance . If no measures were taken for this, then it is impossible to do this after the fact!

Proxy objects can be used not only to limit access by time, but also by other parameters - you can provide access only to a piece of the file or, having read-write access, create a proxy object that allows read-only access. Similarly, you can configure permissions that are specific to the subject area — you can allow to leave comments in the document, but prohibit changing the contents of the document itself. In traditional systems, these operations cannot be divided - at the OS level, they both look like a modification of a binary file.

Proxy objects can be used recursively: one object delegates part of its rights to another, the second to the third, and so on. At each step, only part of the rights is transferred, and the rest are cut off using proxy objects. With each link, the amount of available privileges becomes less and less. This is called attenuation of the access rights.

Counterfeit Security


Mandate reference must have an important property - security against forgery. It should be impossible to “guess” a mandatory reference that was not provided in accordance with the above rules.

For descriptors, this property is incorrect within the framework of a single process: knowing the principle of generating descriptor codes, it is possible to generate the correct descriptor in reasonable time. Those. A plugin loaded into the host process can access data that could not be accessed via the host interface with the plugin.

For file paths, this property is not satisfied in principle - it is enough to write in the code “C: \ Windows \ system32 \ drivers \ etc \ hosts” - and now, we already have a link to an important system file.

How to protect mandatory references from fakes? To ensure this , I know four basic approaches:
  1. Mandatory references as descriptors - Subjects are processes. All work with mandatory links is performed through the OS functions. For each process, the OS keeps a list of mandatory references that are currently available to it. A process can perform an operation on a capability link only if that link is in the corresponding list.
  2. Cryptographic tools - this approach does not give an absolute guarantee that it will be impossible to forge a link, but instead provides the necessary minimum complexity of a fake. The program provides an encrypted credential reference to the OS functions, which verify its correctness and decrypt before use. Due to the high overhead of encryption / decryption of mandatory references, this method is not used to implement security within the same machine, but is used in network systems .
  3. Mandate links with passwords (English password-capabilities) - a hybrid of the previous two methods (more here and here ). The credential reference consists of authentication information and a password. The password is a random sequence of bits generated by the system. The probability of guessing a password is 2 -k , where k is the password length. The system stores a dictionary associated password with identification information. The mandatory reference is considered correct if the password in the mandatory reference matches the password in the dictionary.
  4. Protection at the command / language level — the target machine's command system does not contain any commands at all that allow to interpret an arbitrary sequence of bits as a mandatory reference, or such commands are privileged and are available only to the kernel. At the same time, the target machine can be either a real device ( Plessey System 250 , IBM System / 38 , RSRE FLEX Computer System) or a virtual machine (the J-Kernel project is implemented on the basis of JVM)

Serialization


For practical applicability, a system that implements security based on mandatory references must support serialization of the latter, and this is a very nontrivial task. The system must ensure that the serialized image of the mandatory reference has not been tampered with, that the resource referred to by the link still exists, that the process performing deserialization is indeed a revival of the process that performed the serialization.

The mechanism of transparent or orthogonal persistence proved to be an excellent solution to all these problems: the operating system takes all the care of preserving and restoring state to itself, and applications work with an abstract persistent single-level memory. From time to time, the operating system saves a complete snapshot of the state of all processes to disk, and when loading it loads the last successfully saved version into memory. Programs do not have direct disk access and cannot programmatically forge a serialized image on a disk.

Imprisonment


Suppose Alice wants to use a program created by Bob to process her data. Bob wants no one else to have access to the insides of his program. In addition, Bob may want to parameterize his program data. This is a reason to provide Alice with a program not as an array of bytes with executable code, but as an object. But Alice wants to be sure that this program will not transfer her confidential data to outsiders. And objects can store links to other objects or have a changeable state that other objects can read, i.e. the use of objects in this case is a danger to Alice’s safety.

Imposing restrictions on an object passed to Alice that will make this object safe for Alice to use in this scenario is called object confinement.

There is a patented solution that allows to achieve imprisonment in the framework of the model described above. However, as the authors of this solution themselves point out, a more elegant and simple solution will be the introduction of an attribute of transitive constancy, the correctness of which should be provided by the system. This ensures that the program will simply have nowhere to write Alice's confidential data. In this case, if Alice needs an object with a changeable state, then the object passed to it should be designed as a factory creating new objects from the data contained in the factory itself and the data provided by Alice.

Conclusion


The security model based on mandatory references allows you to securely interact with mutually suspicious components. The model allows you to safely run a random code from an unknown source on your machine without fear of consequences - this is unthinkable for traditional systems. The model can use any component of the system, with any privileges, while in traditional systems, to create new users and groups, administrative privileges are required.

In my opinion, security based on mandatory references is most conveniently and elegantly implemented along with OOP, language-based security and orthogonal persistence. I believe that the platform implementing all these concepts will write the next chapter in the OS evolution. But so far, security based on mandatory references remains the lot of experimental operating systems. I hope I managed to interest the reader in the idea of ​​mandatory references and thus bring the day when a similar project becomes a mass OS a bit closer.

References:


  1. Marc Stiegler et al. Introduction To Capability Based Security
  2. J-Kernel
  3. Mark S. Miller et al. Capability Myths Demolished [PDF]
  4. N. A. Olifer, W. G. Olifer. Network operating systems. Chapter 6. The Microkernel Mach
  5. Tahoe-lafs wiki
  6. Cunningham & Cunningham, Inc. wiki Password Capability Model
  7. Ronald pose. Password-capabilities:
  8. Norm Hardy. The Confused Deputy (or why you might have been invented)
  9. Mark S. Miller. The KeyKOS Factory

Source: https://habr.com/ru/post/148743/


All Articles