Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Clues on writing a secure daemon

by n3dst4 (Scribe)
on Oct 16, 2004 at 11:58 UTC ( [id://399753]=perlquestion: print w/replies, xml ) Need Help??

n3dst4 has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to write a daemon that will fulfill the same role as a *nix FTP daemon, but using the DAV protocol. CPAN gives me the complicated bits in the form of HTTP::Daemon and Net::DAV::Server. The daemon should accept incoming DAV requests, fork, authenticate the user against the system password database, setuid to the user if they pass, and serve up DAV responses from their home directory. Then exit. My searching has not helped, so I seek help here:
  • What is the best way to validate a system user? Using getpwnam() and crypt() seems so open to exploitation. Ideally, I'd like to be able to leave as much as possible of the authentication to the OS.
  • It seems that in order for children to setuid to an arbitrary user, the master process needs to run as root. Is there a good way for a non-root process to "su", given that it has the username and password of the user?
  • Is getting involved in PAM going to help me in any way? Could I pretend to be an FTP service given that my requirements are identical?
I throw myself to the mercy of the Monks.

Replies are listed 'Best First'.
Re: Clues on writing a secure daemon
by Roger (Parson) on Oct 16, 2004 at 12:23 UTC
    I suggest you to have a look at perlsec. It gives quite a few good suggestions, especially the -T (taint) flag for server programs.
      I've been reading that. And the example of dropping setuid permissions has utterly baffled me (my comments added):
      my @temp = ($EUID, $EGID); # Store EUID and EGID my $orig_uid = $UID; # Store UID and GID my $orig_gid = $GID; $EUID = $UID; # Set EUID and EGID to + UID and GID $EGID = $GID; # Drop privileges $UID = $orig_uid; # Set UID and GID to.. +. themselves? $GID = $orig_gid; # Make sure privs are really gone ($EUID, $EGID) = @temp; # Set effective UID an +d GID what # they were before? He +lp! die "Can't drop privileges" unless $UID == $EUID && $GID eq $EGID; # Test UID +and GID
      I don't get that.
Re: Clues on writing a secure daemon
by cbraga (Pilgrim) on Oct 16, 2004 at 14:17 UTC
    Two bits of advice:
    • Never use the system's users to authenticate web apps. That's a high security risk and even if everything is encrypted via SSL you're still open to careless users writing their passwords in insecure places. And a persistent hacker could use the web interface to guess user passwords (90%+ of user-picked passwords are trivial) and then attack your system.
    • Any good reason not to use apache? Apache will do all the serving for you and, through mod_perl, you can still program everything you need in Perl. I never tried mod_perl and DAV integration but Apache::DAV seems to be worth trying at least.

    ESC[78;89;13p ESC[110;121;13p

      Yes, privileges: I want the file serving to be performed as the user who has logged in, by fork()ing and setresuid()ing. Unless I run apache as root and handle requests in CGI, it won't be able to do that.

      My intention is to replace ftpd with a DAV equivalent. As far as I can see (and I've been researching this all day) I'm going to have to take the hit of having my master parent process run as root, but at least my children can be setuid'ed when they get round to actually doing anything.

      This is not just another webapp. I must authenticate system users, because my objective is to give them access to their home directories. This is identical to the requirements of an FTP server. In fact, the only difference is the protocol used.

        You don't need to run the listener as root.

        On startup, you open a pipe. You then fork. Process A drops privs to a junk user like nobody. Process B stays as root and blocks reading the pipe. Process A then listens on the network and performs any work, and then just passes a simple message onto B. B double-checks its input, and if it's good, forks a process as the requested user.

        The openssh have a good writeup, they call it Privilege Separation.

Re: Clues on writing a secure daemon
by jaldhar (Vicar) on Oct 16, 2004 at 20:18 UTC

    If you would "like to be able to leave as much as possible of the authentication to the OS" and your OS supports it, I would suggest using Authen::PAM. PAM provides the most flexibility for the local adminstrator ranging from just crypt, to Kerberos or something.

    Also read Lincoln Steins' book for more information on setuid in network servers.

    --
    જલધર

Re: Clues on writing a secure daemon
by DrHyde (Prior) on Oct 18, 2004 at 09:04 UTC
    Tackling your three suggestions in order ...
    • Using crypt() assumes that you have crypt()ed passwords. While this may be the case on some systems, it isn't on all of 'em. For instance, if you enable "shadow" passwords and md5 passwords on Linux (both of which are good ideas) this won't work. It's certainly not portable.
    • A non-root process can't switch to another UID. You could spawn su, and then use su to run another copy of your perl program with the new priveleges to do the real work. But I'd be very very careful about this. As an aside, this is just the situation where suidperl might be helpful.
    • Yes, use PAM for authentication. Don't pretend to be something you're not though, as you want PAM to log accurate information.
Re: Clues on writing a secure daemon
by n3dst4 (Scribe) on Oct 19, 2004 at 08:50 UTC

    Well, thanks to everyone who commented. I thought I'd fill you all in on my progress since I posted the question:

    • I still find it faintly implausible that no-one's made a DAV-based replacement for ftpd, which makes me think that I'm either missing something about DAV which makes it utterly unsuitable for the job, or I've been searching in the wrong places. Oh well, it's a learning project.
    • Right now I have: a root-owned process which accepts connections and spawns a child which looks for basic authentication. The child then bottles out or drops privileges to said user, fires up a Net::DAV::Server and starts serving requests from ~user. It works with cadaver and Windows Web Folders. Security-wise, the root-owned process is still doing far too much work, but at least the file serving aspects are done with the right privileges.
    • Net::DAV::Server is an 80% solution for the protocol aspects. I have made some changes to it which I may send back to the author, although he does mention that he's working on it.
    • Authen::SimplePam provides a great way of checking system users without having to know too much about PAM. Seriously, if I'd had to start understanding PAM as well, I probably wouldn't have got the job done. Thanks to jaldhar and DrHyde for confirming that PAM was the way to go.
    • Thanks to roju, I am now adding privsep, although it does add to the complexity of the code overall. But hey, better a few hours now than a compromise somewhere down the line, eh kids?
    • Permanently dropping privileges in Perl seems to be non-trivial on platforms that have a "saved UID". For this reason I am using the (experimental) Proc::UID. I get a better feeling from that than simply setting $< and $> but it is very much prerelease. Any clarification on this subject would be welcome: I'm still researching.
    • The future will include: proper privsep; more general user authentication; HTTPS (a must).

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://399753]
Approved by Roger
Front-paged by grinder
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (4)
As of 2024-04-24 02:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found