in reply to Change UID of executing POE TCP server

I would recommend writing a very short application to open port 23, switch UIDs, then exec your real server. That means your only root security risks are in the listener program; once the other program has started, there's no way to get back root status. Plus, it's easy to give a thorough security audit to a 56-line program.

Speaking of 56-line programs,

here's a short little program that takes a port number and a program to exec on the command line, with optional UID and GID, and opens the socket on STDIN, binds it to the requested port, switches UID/GID, then execs the new program.
#!/usr/bin/perl -w use strict; use Socket; sub usage { die "Usage: $0 [-u uid] [-g gid] port prog-to-exec [args ...]\n"; } # Parse Options use vars qw(%opt); while (@ARGV && $ARGV[0] =~ /^-([ugh])$/) { shift; $opt{$1}=shift; } $opt{h} && usage(); $opt{u} = 2 # daemon unless $opt{u}; $opt{g} = 2 # daemon unless $opt{g}; # Get port and verify it my $port = shift; $port && $port =~ /^\d+$/ or usage(); # Make sure there's something to exec @ARGV >= 1 or usage(); # We're going to give the client app the socket on STDIN, so close the # old one... close(STDIN); # Create the socket and bind it to the port socket(STDIN,PF_INET,SOCK_STREAM,getprotobyname('tcp')) or die "Socket error: $!"; setsockopt(STDIN,SOL_SOCKET, SO_REUSEADDR, pack("l",1)) or die "setsockopt error: $!"; bind(STDIN,sockaddr_in($port,INADDR_ANY)) or die "bind error: $!"; # Switch to our new user and group $( = $) = $opt{g}; $< = $> = $opt{u}; if ($( != $opt{g} or $) != $opt{g}) { die "setgid failed\n"}; if ($> != $opt{u} or $< != $opt{u}) { die "setuid failed\n"}; # And get on with the show! exec(@ARGV) or die "exec error: $!\n";
You can use it by writing your client to expect a socket on STDIN, then just start listening on it. Here's a simplified variation of the server in the camel book:
#!/usr/bin/perl -Tw use Socket; use strict; listen(STDIN,SOMAXCONN) or die "listen error: $!\n"; for(;my $paddr=accept(CLIENT,STDIN); close(CLIENT)) { print CLIENT "The time is now ",scalar localtime,"\n"; }
So, for example, to start this up I run, as root:
# ~/tmp/listener -u 500 -g 500 23 ~/tmp/server
The other thing I would strongly recommend is that you write your server with perl's taint-checking (-T) switch. It's no substitute for a secure mindset, but it catches many types of silly errors and many more subtle errors. It makes sure any data coming from the user is sanitized before it's used for anything dangerous.