Signing programs for Linux
At his 2017 Open Source Summit North America talk, Matthew Garrett looked at the state of cryptographic signing and verification of programs for Linux. Allowing policies that would restrict Linux from executing programs that are not signed would provide a measure of security for those systems, but there is work to be done to get there. Garrett started by talking about "binaries", but programs come in other forms (e.g. scripts) so any solution must look beyond simply binary executables.
There are a few different reasons to sign programs. The first is to provide an indication of the provenance of a program; whoever controls the key actually did sign it at some point. So if something is signed by a Debian or Red Hat key, it is strong evidence that it came from those organizations (assuming the keys have been securely handled). A signed program might be given different privileges based on the trust you place in a particular organization, as well.
Signing also provides a form of tamper resistance. It is not possible to modify a program without invalidating the signature. Package signing does not provide this assurance, however. It shows that the package was not tampered with up until it was installed on the system, after that, there is no guarantee that the contents have not changed.
There is another benefit to signing that is related to the ability to know the provenance of the code. If it is determined that a certain key has either been compromised or is signing untrustworthy programs, all trust in that key can be removed from the system. This provides a way to blacklist programs emanating from a malicious (or insecure) organization, he said.
There have been various efforts to add signatures to programs along the way, including a way to integrate signatures in ELF binaries. That particular solution does not handle all of the use cases, though. For one thing, not all programs are ELF binaries; scripts for Python and other languages are semantically equivalent to ELF binaries but are just text files on disk. Scripting languages give access to system calls and other security-sensitive facilities. There are also binaries run on Linux that are not ELF, including Windows binaries that are run under Wine and binaries for other Unix platforms. So ELF signatures are not an approach that solves the problem, Garrett said.
IMA and friends
The kernel's Integrity Measurement Architecture (IMA) is another approach. The initial implementation was "fairly straightforward", he said. It will calculate hash values for files and log them based on a configurable policy. It is not restricted to binaries and simply provides an audit trail of the hashes of files accessed.
The policies are fairly fine-grained, so IMA could hash all files executed by anyone, for example, or all files opened by the root user. If there is a malware outbreak detected on one machine, others can be checked to see if they executed something with the same hash. IMA hooks into filesystem access, so it only does the hashing the first time the file is accessed; if the file is changed, it gets rehashed when it is next accessed. But IMA itself is not signing and there is no enforcement mechanism, he said.
Signing is the process of hashing a file then encrypting the hash using some kind of key (typically the private key of a public/private key pair). In order to verify the signature, the file is hashed and the encrypted hash is decrypted using the public key. If the two hashes match, the signature is verified. IMA is a great starting point for signing programs, he said, but more is needed.
The IMA appraisal feature adds the ability to store the raw hash or a signature in the security.ima extended attribute (xattr) of a file. Most filesystems have support for xattrs and the security xattr namespace is managed by the kernel, which protects those attributes from unprivileged updates. IMA appraisal allows the creation of policies controlling what happens when hashes or signatures do not match. For example, if there is no signature or the hash in the signature does not match the hash of the file, execution can be blocked.
All of that is only useful if you have signatures associated with the files that get installed on your system. Right now, those are not really available as distributions are not shipping signatures for the files in their packages. He has been working with the Debian dpkg maintainer to add metadata to .deb files; that metadata could be used to store signatures.
Build time is an obvious place to add signatures but it doesn't have to be done then; the .deb file could be pulled apart and signed later. There are some distinct advantages to doing things that way; since build systems are pretty much required to run arbitrary code, it is best if they do not have access to the signing keys. Moving the signing to the mirroring system would remove that danger. IBM has some patches to add signature information to RPMs, but support for signing in the Debian or Fedora mirroring infrastructure has not yet been added.
That all means that we can "potentially have a future" where the files in the packages on your mirrors have signatures that could be written to the xattrs of the files as they are installed. The policy can be set so that lack of a signature means that the file cannot be executed, so there is no window for a race between installing the file and setting the xattr. Policy could be set locally (or by a distribution) to require all binaries to have signatures, or just those run by root.
In addition, IMA appraisal allows tying its policies to security labels as maintained by SELinux, AppArmor, or other Linux security modules (LSMs). So the system could be set up to require signature verification for files that have certain (high-security) labels. The appraisal can be ignored for most cases and only applied for security-critical programs and files.
It is important to note that Linux systems are rarely static; they are updated regularly (or should be), but there is more than just that. Many Linux systems, especially development machines, have locally built programs. Signing software that is locally built brings with it the danger of placing keys in harm's way.
A multi-level security scheme with multiple different security contexts might be one way to provide a useful development machine that still protects the security-critical parts of the system. Anything that wants access to the higher security contexts would require signature verification. Those in lower contexts would be blocked from accessing things like networking, D-Bus, and sensitive parts of the filesystem. Seccomp restrictions could be added as well. This would provide the best of both worlds by giving developers a functional environment while still protecting the most important and dangerous features.
The extended verification module (EVM) is meant to protect the IMA xattrs and other file attributes from offline tampering. A hash or signature of the xattr values, LSM labels, owner, group, and other metadata is stored for each file. EVM is not suitable for use by distributions, though, because one of the pieces of metadata it hashes is the inode number of the file, which is not something that is known by the distribution. EVM is geared toward systems that have temporary access to the key material; they can calculate and sign the EVM value, then lose access to the key.
Shortcomings
IMA appraisal has some shortcomings, however, Garrett said. There is no way to tie policy decisions to which key was used, so you can't set it up to do less appraisal for more-trusted keys and stronger appraisal for less-trusted keys. In addition, the action taken for appraisal failure is set at boot time, so it cannot be changed at runtime.
Beyond that, there are still problems for interpreted languages. The Python binary would likely be signed, for example, thus might be eligible to run in a higher security context. But code can just be piped to Python. That is why other operating systems are moving toward adding some awareness of security contexts into the language interpreter itself.
It turns out that Linux systems typically have many language interpreters installed, including some that may not be obvious at first glance. For example, Emacs and various media file interpreters can also execute code. Garrett has been thinking about ways to not have to modify all of these language interpreters.
One way might be to change how LSM security transitions happen. Currently, they happen when something is executed, but adding a way to taint a process based a file-open event could prevent piping code to the interpreters. The Linux pipefs could be changed to taint the processes using the pipe. For example, a command like the following:
$ curl ... | bash
The pipe would taint the bash process so that it would not have
access to higher security contexts. That still will not protect against
interpreters that take command-line arguments
(e.g. python -c), though.
In summary, Garrett said, IMA is an incredibly powerful tool, IMA appraisal goes even further, and IMA appraisal coupled with LSMs goes further still. It is not quite at the point of being deployable, but is getting close. It may even make sense for general-purpose distributions soon.
In the Q&A, James Morris pointed out that there are some complex issues regarding revoking access to resources that have already been opened, which would make Garrett's tainting idea difficult. Garrett acknowledged that, but said that after 15 years of discussing it, perhaps the kernel community should find a way to implement revoke(). Given that the process had the access from a higher security context prior to the taint, though, there may be situations where the lack of a way to revoke that access is still workable.
He was asked about a way to generalize handling language interpreters that can take code from the command line. Garrett said that each interpreter probably needs to be modified to provide a view into what code it is running. For example, the Python community is looking into that; he referred attendees to a recent LWN article about those efforts. Over time, all the different interpreters (Ruby, Lua, Perl, ...) will need to be modified to help support these features; it will take a lot of work over a long period of time, he said. There was some discussion of restricting command-line arguments to the interpreters for certain security contexts but, even if it is workable, it will take some more thinking to determine that.
Another audience member asked about how these mechanisms would work with multi-threaded programs. With a grin, Garrett responded: "wonderful question ... next question". He noted that there were some difficult problems to solve here and that the community should "not get too wrapped up in a perfect solution"; there may be good solutions that will suffice for some use cases. It is those solutions that should be pursued.
[I would like to thank the Linux Foundation for travel assistance to attend
OSS in Los Angeles.]
| Index entries for this article | |
|---|---|
| Security | Signing code |
| Conference | Open Source Summit North America/2017 |
