[bind10-dev] PrivilegedSocketCreator thoughts

Shane Kerr shane at isc.org
Tue Jun 8 12:31:22 UTC 2010


I was listing the tests to do for the PrivilegedSocketCreator, and I had
a few thoughts.

The design now only talks about an IP+port combination. However, we need
to be able to bind both UDP and TCP ports. Should we always bind() both
UDP and TCP, or is there a case where we would want to bind() one and
not the other?

If we may want UDP but not TCP, then we need to support that. I am
inclined to always provide both UDP and TCP for now, until we discover
that we need one but not the other.

There is a possible question about robustness in the protocol to
communicate to/from the process.

Originally I was thinking that the best thing to do would be to use a
fixed-sized message to the process. I was considering the case where the
Boss process is compromised in some way, and an attacker tries to get
the PrivilegedSocketCreator to do something incorrect. My idea was that
with a fixed-size packet this would be more difficult.

However, in the case where there is an error for some reason, then it
would be a bit difficult to get to a known-good state. Take for example:

        foo.write(socket.inet_pton(socket.AF_INET6, addr))

If there is a programming error (say a bogus port value), then a partial
command may be written down the pipe. This means that if the boss
recovers and tries to get a socket, that it will read the first few
bytes of the address as the last couple of bytes of the port.

Basically in this case the boss and the PrivilegedSocketCreator will be
out of sync and never recover.

A few possible solutions:

     1. Be careful. Always do write() operations with a well-formed
     2. Use a protocol that can recover.

I *think* the first solution is "good enough", only failing in the case
of a compromise of the boss process (see below for more thoughts about

With the second solution, you could use a simple text-based protocol
with commands like this:

        create_socket 53\n

This is simple to parse, and while an error may cause problems, the
PrivilegedSocketCreator can recover by skipping to the next '\n'. But it
is not *as* simple as a fixed-size request format, and may be solving a
non-existent problem in exchange for more complexity.

Another issue is that there are still a few attack vectors with this

One is that someone who compromises the boss process can still try to
bind() a lot of ports, possibly causing resource exhaustion on the
system. Also someone can bind() an important port (say port 22, the SSH
port), capturing data on that.

My answer to this is that the approach is not designed to prevent all
possible problems, but rather to limit the possible problems to bind()
of ports, rather than a generic system compromise. So, we can document
the problem and move on.

The final thing is that it we actually need to have a sort of
mini-database to store bound ports.

The issue here is that if one auth-srv is already listening on port 53,
then bind() will fail if attempted again. So we need to keep track of
which ports we have, and re-use them.

The approach I am thinking of here is to make a Python library which
handles this:

	class SocketAllocator:
		def __init__(self):
			self.bound_sockets = {}
		def get_bound_socket(self, address):
			if address in self.bound_sockets:
				return self.bound_sockets[address]
			# get socket from PrivilegedSocketCreator here
		def release_bound_socket(self, address):

The bound ports would be stored in the boss process. I think this is a
better approach than moving this into the PriviligedSocketCreator,
because it minimizes complexity in that program.


More information about the bind10-dev mailing list