Have the OS give user permissions on "privileged" IP ports.

Topic: 

Very technical post here. Among the children of Unix (Linux/BSDs/MacOS) there is a convention that for a program to open a TCP or UDP port from 0 to 1023, it must have superuser permission. The idea is that these ports are privileged, and you don't want just any random program taking control of such a port and pretending to be (or blocking out) a system service like Email or DNS or the web.

This makes sense, but the result is that all programs that provide such services have to start their lives as the all-powerful superuser, which is a security threat of its own. Many programs get superuser powers just so they can open their network port and, and then discard the powers. This is not good security design.

While capability-based-security (where the dispatcher that runs programs gives them capability handles for all the activities they need to do) would be much better, that's not an option here yet.

I propose a simple ability to "chown" ports (ie. give ownership and control like a file) to specific Unix users or groups. For example, if there is a "named" user that manages the DNS name daemon, give ownership of the DNS port (53) to that user. Then a program running as that user could open that port, and nobody else except root (superuser) could do so. You could also open some ports to any user, if you wanted.

This could be a file read by the kernel, or an actual filesystem with special nodes could be created (/dev/port/tcp/nnn) so programs could actually use the official chown call on it. (Indeed, these files could actually work as the sockets, but most programs would continue to use the standard TCP socket call, which would have to check the permissions.) Alas, a /proc fake filesystem might work as an interface, but would not have the permanence required.

Many programs could run in this environment without modification, in that they would run as an ordinary user, open the socket (thinking they are root) and then setuid to their real self (which would do nothing.) However, you would no longer be trusting them with root. Of course some programs still need root (mail agents need to write mailboxes owned by anybody in most mail systems, some web browsers like to run CGIs as the user, etc.) though many of them can be altered to not need it, or just make occasional use of an isolated suid program (as qmail does for mail.) And of course, until a kernel with this feature is widely distributed, servers would have to support both methods.

Note there is no reason you couldn't apply this system to the entire port space. In particular, while the default of allowing all higher ports to be used by any program needs to be preserved, you could grant ownership of specific high ports to specific users so that they can be sure no other program will grab their expected port to try to interfere with them. However, a wise program would be able to deal with not getting its favourite port, since we don't want to build a general port reservation system for any program that arises.

Comments

Take a look at AppArmor . It has a finer grained access control mechanism to accomplish what you are describing. The FOSDEM lecture (200+ MB avi file) describes restricting the NTP daemon as an example. The necessary unix kernel mods are available in linux. I don't know whether they've been ported to other Unix variants but the concepts do carry across. The rest of the administrative structure needed to implement and maintain these controls is also available as open source.

It looks much more tractable than the SELinux approach, with only a modest loss of capability. It deals with much more than just port restrictions efficiently.

In fact, as I noted, I think the long term is to move towards things like capability based security, though that typically involves rewriting programs, and languages and libraries being adapted to capability systems. In capaiblity systems, such as E a program can never do more that you give it capabilities to do, so you can be a lot more comfortable about it.

The key to this idea is to find some baby-steps which are easy to implement and easy to maintain.

1. Start program as root
2. Bind to a low port
3. setuid
4. Do everything else

What's the problem? Can't you at least call bind() without screwing up?

Keep in mind that *simplicity* promotes security, while fancy complicated schemes hinder it.

Give the program all powers so it can perform a minor operation and trust it to give them back? That's hardly a solution. In a truly secure system, you should be able to run programs without having to trust them at all. Especially in this world of software that is updated regularly and without much attention by 99% of the sysadmins -- or even updated automatically, as is now common on Windows.

In a "truly secure system" as you describe, you need something like Proof-Carrying Code, so that software can be proved safe to run (if not correct).

As for solving the problem using the unix-like systems of today, this is easy. Start the "lowportbind" program as root. It binds to a low port, setuid()s to a less privileged user, dup2()s the connection onto stdin, then execv()s the daemon. This would be similar to what inetd does.

All you do is carefully audit lowportbind's code and make sure you trust it. And, of course, make sure the other services you run are also pretty secure, since a flaw can still compromise parts of the system no matter what; the breach just won't be as drastic.

On that note, when you mentioned "this world of software that is updated regularly and without much attention by 99% of the sysadmins", you pointed out a much bigger problem. Even if one day all code is proved safe to run, if you don't know what you're doing, your system will still be insecure, perhaps because you won't understand the assumptions made in the proof. That's why simplicity is of paramount importance. Your proposal, while well-intentioned, is unnecessary complexity and a recipe for confusion. Imagine if every time there was a security problem we fixed it by making the system we use more complicated. What do you think got us confused enough to cause the problem in the first place?

That is fine, but means the program has to change. If you are willing to rewrite the daemons, then you should go all the way to a capability design. In such a design a program can't do anything you didn't grant it the capability to do, no matter how you update it.

But for now I was proposing something to let the programs be the same but not run as root.

The program would have to change, sure, but not be rewritten. It could just have a command line option to say "don't bind, and listen to the socket on stdin." Should be a pretty small change.

How would I do this securely for port 443 on a UNIX server running in a QZ between firewalls?

This is a proposal for internal security on a system itself, nothing to do with firewalls.

Remember, firewalls are a hoax. They are an insecure way to protect systems which we use and sell because our systems themselves are unacceptably insecure, and since they often run windows, unfixable.

The name firewall should remind you of the strategy of making all your buildings out of straw, and then putting a brick wall around them. One flaming arrow over the top of the wall and you lose everything. Oh yeah, and the straw cars (laptops) are constantly roaming back and forth in and out of the firewall, bringing back incindiaries to launch when they get inside.

Add new comment