Question

I'm working on a simple command-line utility that encrypt/decrypts a small file based on a user-provided passphrase. In order to prevent the user from having to retype his/her passphrase over and over again in short periods of time, I need to come up with a solution that temporarily caches this passphrase (or derived symmetric key). This is similar to functionality provided by programs like sudo and ssh-agent.

The solution I've come up with so far, is a separate daemon-like cache program that keeps track of the encrypted files (though usually there will only be one file) and corresponding keys. The files are identified by a checksum (SHA-256), and the communication with the encryption client is done with Unix domain sockets.

Here's a simplified excerpt of the RPC service that I've created with the help of the net/rpc package in Go:

type Checksum [ChecksumSize]byte

type SumKeyPair struct {
    Sum Checksum
    Key []byte
}

type KeyReply struct {
    Key       []byte
    Available bool
}

type Cache map[Checksum][]byte

// Fetches key that corresponds with file checksum
func (c Cache) RequestKey(sum Checksum, reply *KeyReply) error {
    reply.Key, reply.Available = c[sum]
    return nil
}

// Adds or updates key that corresponds with file checksum
func (c Cache) SetKey(pair SumKeyPair, reply *bool) error {
    _, *reply = c[pair.Sum]
    c[pair.Sum] = pair.Key
    return nil
}

RequestKey will be called every time the user wants to decrypt the file. SetKey will be called every time a correct passphrase is provided (with the updated checksum).

Is it safe to transfer sensitive data between processes like this? In the case of Unix domain sockets, is the protection similar to the permissions on the socket file? If there are better ways, please let me know.

Two quasi-related side questions:

  • What is a good cross-platform IPC/cache mechanism? I'm focusing on Linux, but Windows support would be a plus.
  • I know keeping passwords/keys in memory isn't 100% secure and is kind of a chicken-egg problem. But would encrypting the cached keys increase security?
Était-ce utile?

La solution

If you are aiming for a daemon that acts like ssh-agent or gpg-agent, then you probably don't want a RequestKey operation.

Those other agents are designed such that the private key is never sent to the client process. So rather than ssh retrieving the private key from the agent so it can perform challenge based authentication, it sends the challenge to the agent which then returns the signed version of the challenge ready to be sent to the server.

To put it simply, if the agent never sends the private key over the IPC mechanism, then the IPC mechanism can't be used to snoop on the private key.

If you want to further improve the security of your agent, if you are using UNIX domain sockets on Linux you can make use of the SO_PEERCRED socket option to query the identity of who you are talking to:

func getPeerCred(conn net.Conn) (*syscall.Ucred, error) {
    file, err := conn.(*net.UnixConn).File()
    if err != nil {
        return nil, err
    }
    defer file.Close()
    return syscall.GetsockoptUcred(int(file.Fd()), syscall.SOL_SOCKET, syscall.SO_PEERCRED)
}

The returned Ucred structure tells you the process, user, and group IDs of the party at the other end of the socket. You can use this information to decide whether or not you want to communicate with them. The information comes from the kernel, so can not be forged by the client.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top