In the previous posts of this series, I've described the operating environment of a password or private key agent and given a summary of their tasks. This time, we'll see how some real-world agents are implemented.
But before that, a disclaimer: I'm merely an interested observer of all of the tools mentioned below. All my knowledge is from looking at their documentation, source code and from practical experiments. If you plan to use any of them for your private, sensitive data, you should definitely not rely solely on this analysis.
Part 3: Real-world password and private key agents
The first tool we'll be looking at is my personal favorite of the batch:
ssh-agent. Its job is to protect an user's private SSH authentication keys.
Usually, those keys are stored in the user's home directory, encrypted with a symmetric key derived from a passphrase that has to be entered every time the key is used to connect to a remote server using SSH;
ssh-agent was developed to avoid having to type it that often.
When an instance of
ssh-agent is started, it creates a Unix domain socket; the file system path of that socket will usually be stored in an environment variable called
SSH_AUTH_SOCKET. Starting the agent and setting the variable is usually handled by a few lines in the user's desktop and/or shell configuration files. This socket is then used by
ssh-agent's clients to request various operations.
First of all, to be of any use, the private keys have to be actually loaded into the memory of the agent. This is performed by a tool called
ssh-add, which basically asks the user for his passphrase and the location of his private keys, decrypts them in memory, and sends them over the Unix socket to the agent.
The nice thing about
ssh-agent's protocol spoken over the socket is this: There is no command to extract a private key from it. Clients (mostly instances of the SSH client,
ssh, really) can request the agent to sign some data on their behalf, which in turn allows them to authenticate against a remote SSH server. There are some other commands (e.g. to remove some or all private keys, temporarily lock or unlock the agent with a password, or get a list of currently loaded keys), but except for security bugs or other side channels, there is no way to make the agent reveal the private keys.
As I've mentioned, a security tool can only be as secure as the environment it's running in and on whose security measures it is relying on. In the case of
ssh-agent, this is the user's Unix account, and in many cases some graphical desktop environment.
ssh-agent (or at least the version of OpenSSH included in Ubuntu) tries to limit the ways other applications in the same context can interact with its virtual memory by disabling the
ptrace(2) facility of the operating system. I'll write a lot more on that in a future post, but for now, it suffices to say that this (hopefully) makes it impossible for other processes to peek into an agent's memory space (using
gdb or the
The next tool on our list seems to be quite similar to
gpg-agent also protects private keys, uses a Unix socket and an environment variable to answer to requests, and runs with the user's permissions, started in one of the various startup scripts of the desktop environment. It is used to protect a user's private (or secret) GnuPG encryption and signature keys.
Unfortunately, the similarity ends when it comes to how
gpg-agent protects the user's private keys. In fact, I couldn't believe my eyes when I ran
strace on an instance of
gpg while executing a private-key operation:
$ strace gpg --armor --gen-revoke 2F5BBF5C write(8, "GET_PASSPHRASE 1AA19BADB016B8BF3"..., 203) = 203 # [...] read(8, "OK 70617373776F7264", 1002) = 19
gpg-agent is not a private key agent at all! It merely caches the private key passphrase, handing it out to anyone asking niecly over the Unix domain socket (which means every application running with the user's privileges). This negates almost all of the security benefits of using an agent in the first place, and on top of that, it is even less secure than just storing the private key in the agent's memory: If the user's session is compromised, not only the private key, but also the passphrase can be recovered by an attacker.
I can only guess that there are various historical reasons for
gpg-agent's architecture, but
ssh-agent shows that there is a better way to handle private key caching in the userspace.
GNOME is used as the default desktop environment for many Linux distributions; and even more are using only some parts while providing their own user interface (window manager, compositor, default applicatoins etc.) – Ubuntu is a famous example of the latter category.
The GNOME applications include a handy tool called GNOME Keyring, which is primarily a password manager, but can also act as a private-key manager for both SSH and GnuPG. I'm not using its private-key features any more for various reasons, but it is still my password manager of choice for everything else (primarily for web browsers).
The documentation page of the software is very upfront about what the tool can and cannot achieve: The developers openly state that for the current desktop architecture, secure privilege separation between applications is simply not possible.
There used to be some kind of access control system for applications, but as it is now, every application running with the user's privileges can store and request plaintext passwords to and from the agent, which in turn stores them in an encrypted database in the user's home directory.
The encryption key is derived from a passphrase defined by the user; if it is equal to their Unix login password, the keychain is conveniently "unlocked" (i.e., the encryption key is stored in the Keyring daemon's memory) as soon as the user logs in to their desktop.
gnome-keyring doesn't perform any security theater to make it seem as if the passwords of an unlocked keyring were somehow more secure than they are, but like
ssh-agent, its memory is protected from access by other user processes on my system (because its binary has a Linux capability enabled).
OS X Keychain
Like GNOME, OS X provides users with a way to securely store their passwords on the disk while still granting automatic access to other applications as long as the user is logged in: OS X Keychain. However, it aims to go even further than that: By using an ACL based on the code signature or binary hash of requesting programs, it restricts access to the stored password to a subset of all applications running with the user's permissions.
The OS X desktop environment, at least theoretically, also seems to implement more security measures than X11: Applications do not seem to be able to install global keyloggers without first requesting special permissions from the user and the
ptrace system call (or its cousin,
task_for_pid, as it is known on OS X) is only available to privileged users or signed debugging tools (which in turn require user authentication).
Additionally, the Keychain service seems to be running with superuser privileges, so it might theoretically be able to perform some additional verifications of the process requesting a password (maybe the aforementioned checks of the binary hash and/or code signature).
But is that really enough to protect all potentially malicious, non-root accesses to the stored passwords? That will be the topic of the article concluding this little series, but before that, we'll see how process memory isolation of binaries running with the same user permissions could possibly be achieved.