views:

315

answers:

5

I have a daemon process which spawns subprocesses. Sometimes these subprocesses need to communicate back to the daemon. I want to ensure that only these subprocesses are authorized to communicate with the daemon.

I want to implement this as follows:

  • During startup, the daemon generates a random 128-byte secret token by reading /dev/urandom. /dev/random is no good because it may block the reader for an arbitrary amount of time.
  • The daemon listens on a Unix domain socket.
  • The daemon puts the secret token and the filename of the socket in environment variables. Every subprocess that it spawns can connect to the daemon using the filename and the secret token.
  • The daemon rejects the connection unless the secret token is correct.

Questions:

  • I know that /dev/random has higher entropy than /dev/urandom. But is /dev/urandom good enough? If not, what should I use?
  • Is the size of the token large enough?
  • Should I lock the memory in which the token is stored? I don't think it's necessary because the daemon generates a different token every time it's started, so by the time an attacker manages to steal the hard drive and extract the token from the swap file, it should already be useless.
  • Should I nullify the memory in which the token is stored during shutdown?
  • Anything else I should do?

And because of various requirements, I cannot use anonymous pipes to allow communication between the daemon and subprocesses.

+2  A: 

Well, if you're going to put the token into an environment variable then anyone with the same or greater privileges (i.e. UID) as those processes will be able to read then use the token! That kinda makes the rest of the question a moot point!? If you are worried about security between processes on the same box (you spoke of local IPC) then don't use an environment variable to store a token - it is easy to inspect these (EVs).

You mean that external processes can inspect any process's environment variables as long as they have the same UID? Okay, in that case I can pass the token through a temporary pipe and have it stored in the subprocess's memory.
Hongli
And of course if you have sufficient rights, you can inspect the memory of other processes as well. I'm afraid that you're going to realize that security through obscurity is not a roadblock, it's a speedbump. There is no way you can protect this 100%, but you can make it harder.
Lasse V. Karlsen
If the attacker has sufficient rights to inspect environment variables or memory, you've already lost.
Lasse V. Karlsen
Suppose that the attacker has local non-root access, but no access to the account under which the daemon and the subproceses are running. Would my scheme be secure enough?
Hongli
+1  A: 

If you're forking (but not exec()ing), just keeping them in local memory should be enough. If you're also exec()ing, you'll probably (as you stated in your comment to Jim) have to pass the token (and domain socket path) over a pipe.

If you're running this on head-less servers, /dev/random MAY be a bit starved, so using /dev/urandom will (probably) be a better option, unless you have a suitable source of noise to feed /dev/random with.

Vatine
A: 

I'll admit up front that I don't know anything about its randomness but wouldn't something like /proc/sys/kernel/random/uuid be sufficient and simpler? How much randomness is required? It seems your daemon should be able to recognize a couple of million bad attempts coming from an internal source.

Offhand it seems like you are focusing too much on the randomness and not enough (yet, anyway) on how to keep the key secure.

Duck
Perhaps; I don't know whether my current method is at all secure. So keeping the token in memory is not secure if the attacker has access to the same account under which the process is running. But what if he only has access to another (non-root) account? Will my method be secure enough?As for /proc/.../uuid, is that better than 128 bytes from /dev/urandom?
Hongli
+2  A: 

The simplest approach would be to simply create a pipe/socketpair in the server for each subprocess. Give one end to the subprocess and keep the other end. Anything that comes in on that pipe/socket must be from that subprocess.

Another approach would be to ask the OS for the credentials (pid, uid, gid) from the Unix socket. On Linux you would use getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) (man 7 socket). Solaris has getpeerucred. Unfortunately this is not portable but many systems have a similar capability for Unix sockets. Although it is complex, D-Bus contains code that does this on a number of different systems.

mark4o
He said he couldn't use pipes; don't know if that extends to socketpair. +1 on credentials. They haven't entered my mind in a dog's age.
Duck
+1  A: 

Yes, the security provided by /dev/urandom is plenty good enough. Lots of software uses it for randomness (for SSL, authentication, etc). Pretty much the only time /dev/random is a good idea is when generating some kind of a token that needs to be secure for years, such as a private key for a certificate.

Someone mentioned ability to look at process's memory if you have the same UID. You can avoid that by making kernel think it's a setuid-process, i.e. if the master process runs as root you can fork, exec and setuid() to unprivileged user. Other processes with same UID won't be able to look at that process's memory then.

The credentials lookup approach also works with named UNIX sockets, not just socketpairs.