Example 5-1 shows a sample setuid Apache startup script.

Note the line marked WORKAROUND, which fixes an obscure error when starting a mod_perl-enabled Apache, by setting the real UID to the effective UID. Without this workaround, a mismatch between the real and the effective UIDs causes Perl to croak on the -e switch.

This script depends on using a version of Perl that recognizes and emulates the setuid bits. This script will do different things depending on whether it is named start_httpd, stop_httpd, or restart_httpd; use symbolic links to create the names in the filesystem.

Example 5-1. suid_apache_ctl

#!/usr/bin/perl -T
use strict;

# These constants will need to be adjusted.
my $PID_FILE = '/home/httpd/httpd_perl/logs/httpd.pid';
my $HTTPD = '/home/httpd/httpd_perl/bin/httpd_perl ';
$HTTPD   .= '-d /home/httpd/httpd_perl';

# These prevent taint checking failures
$ENV{PATH} = '/bin:/usr/bin';
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};

# This sets the real to the effective ID, and prevents
# an obscure error when starting apache/mod_perl
$< = $>; # WORKAROUND
$( = $) = 0; # set the group to root too

# Do different things depending on our name
my $name = $0;
$name =~ m|([^/]+)$|;

if ($name eq 'start_httpd') {
    system $HTTPD and die "Unable to start HTTPD";
    print "HTTP started.\n";
    exit 0;
}

# extract the process id and confirm that it is numeric
my $pid = `cat $PID_FILE`;
$pid =~ /^(\d+)$/ or die "PID $pid not numeric or not found";
$pid = $1;

if ($name eq 'stop_httpd') {
    kill 'TERM', $pid or die "Unable to signal HTTPD";
    print "HTTP stopped.\n";
    exit 0;
}

if ($name eq 'restart_httpd') {
    kill 'HUP', $pid or die "Unable to signal HTTPD";
    print "HTTP restarted.\n";
    exit 0;
}

# script is named differently
die "Script must be named start_httpd, stop_httpd, or restart_httpd.\n";