Forum OpenACS Q&A: restart-aolserver not working for group member restarts

restart-aolserver is a perl script which finds the pid for an AOLserver process and kills the process. Initially I had the permissions set incorrectly on this file, like so:

-rwxr-sr-x  1 root     users  378 Dec 13 17:21 restart-aol

With these settings, the script works if run as root, or the real user of the AOLserver process, but not for any other members of the group the process is running as.

This confused me, and then I realized that the file permissions needed an 's' on the user. Once I did this I discovered that the script was failing with the following output:

Can't do setuid

I wrote a simple test script which seems to indicate that the setuid operation is failing. The script works on RH 7.3, where restart-aolserver also works as expected. Here is the script:

#!/usr/bin/perl

use strict;
undef %ENV;
$ENV{'PATH'} = '/sbin:/bin';

print "real UID: $< \n";
print "effective UID: $> \n";

$< = $>; # set realuid to effective uid (root)

print "Set real to effective uid\n";
print "real UID: $< \n";
print "effective UID: $> \n";

When run as a member of the 'users' group, the program will produce output similar to the following:

real UID: 515
effective UID: 0
Set real to effective uid
real UID: 0
effective UID: 0

When the script fails it produces the following output instead:

Can't do setuid

I think the problem is on RedHat the rpm perl-suidperl is now not installed by default.

One link to the issue is http://www.sympa.org/fom-serve/cache/207.html

I have installed this package, but don't yet know what to include so it works.

However, assuming I get this to work, I think the permissions on the restart-aolserver file can be downgraded so that the program doesn't run as root, but just the owner of the AOLserver process. All this script does is to remove the pid file and kill the server. It doesn't have to be root to do this. In fact this was working because of the incorrect pemissions I was using on the file. Anyone have ideas on how to get the suidperl package to work?

I seem to be running into advice that sudo should be used instead of suidperl. Btw, even if perl is what is listed as the shell, as in !#/usr/bin/perl, when the suid bit is set, perl starts suidperl, at least that is what is claimed in the documents I am finding on the subject.

I created a script file for killing aolserver containing the following:

#!/bin/bash

SERVER="server1"
PIDFILE="/tmp/aolserver/log/nspid.$SERVER";
PID=`cat $PIDFILE`;

rm -f $PIDFILE
/usr/bin/kill -9 $PID

Permissions were set to 750 and ownership cams.web:

-rwxr-x---   1 cams   web 138 Dec 14 00:32 kill-aolserver

I added the following line to /etc/sudoers:

# anyone in the web group:
%web    localhost = (cams) /tmp/aolserver/bin/kill-aolserver
# or just tom:
tom     localhost = (cams) /tmp/aolserver/bin/kill-aolserver

Now users in the 'web' group can restart this instance of AOLserver with the command:

$sudo -u cams /tmp/aolserver/bin/kill-aolserver

Is it more secure to run sudo and have it run the kill script as the AOLserver process owner? Note that I have changed the file contents, command line arguments are not used, so if several servers were running, separate files would be needed, probably something like 'restart-server1', etc. In addition, the cams user does not have a login shell.

Would it add anything to put each server in its own group and place the pid file in a different directory?

Is this an alternative to using daemontools or an alternative to svc -t /service/service0?  (Or something else?)

Joel, it is just a different way to execute the shutdown of AOLserver so it can be restarted, by either daemontools or inittab. However, I just realized that the original restart-aolserver script could still be used with sudo, avoiding potential problems with sh/bash shell execution.

Summarizing the issues I came across while looking into this:

  • Is perl's setuid feature still available on Linux? I haven't been able to get it to work. The simple test is to create an empty perl script file, and try to execute when the setuid bit is set:
    # echo "#!/usr/bin/perl" > test-script
    # chmod 4750 test-script
    # ./test-script
    Can't do setuid.
    #
    
  • Was setuid removed from the standard perl installation because of security concerns?
  • Sudo offers fine grained control. You can choose the user to run a script, and either groups or users which can run the script. suid alternatives are more limited.
  • Is sudo a safe alternative to using suidperl? The script would be run as the process owner instead of root. However, the sudo command is setuid.
  • Should parameters be passed in on the command line? Why not hard code the server into each file. In perl any passed in variable is considered tainted. Any information created based on that variable is also tainted. Untainting requires running the information through a regular expression, giving you a series of $1, $2 ... untainted variables.

I left out a few other issues:

  • Should each AOLserver be given its own process group? As it stands, all are in the web group.
  • Could a tcl script do the same thing as the perl script? It seems relatively safe. You would need to use sudo I think.

I wrote a tcl script to replace the perl script. When used with sudo, it has the following features:

  • Permission can be as restrictive as 0500 when owned by the user which owns the AOLserver process.
  • Any combination of users and groups can be allowed access to the restart/kill script.
  • The script is run as the user which owns the AOLserver process
  • Tcl doesn't support setuid, so just running the script without sudo, and with the setuid bit set, doesn't work.
  • root cannot execute the script except through sudo.
  • The script doesn't take command line parameters, each server requires a separate script file, but command line parsing is eliminated.
  • The pid file has to be a regular file.
  • Pid file format is limited. It must start with integer characters and ends with \n or "". The integer value must be less than 32768.
  • The pid file is read one char at a time to avoid running out of memory, or any other type of buffer overflow condition, it also might avoid any kind of subst type problem as each char must be an integer.
  • The pidfile is removed using tcl's file delete command, an uses 'exec /usr/bin/kill -9 $pid' to kill the process.

Seems like a waste of time to investigate a script I never use, but I ran into an installation that needed it. I noticed the documentation on that part of the install isn't really maintained, and has fallen behind. I'm also working on a semi-auto installation script for OpenACS related software, and putting nonworking scripts in it wasn't an option.

Here is the script:

#!/usr/local/aolserver/bin/tclsh8.4

# to allow a user other than the file owner to execute this file,
# edit the /etc/sudoers file (using visudo), adding a line similar to:
# otheruser localhost = (scriptowner) /path/to/this/file/scriptfile
# for an entire group:
# %groupname localhost = (scriptowner) /path/to/this/file/scriptfile

# Avoid command line arguments.
set SERVER "server1"
set PIDFILE "/tmp/aolserver/log/nspid.$SERVER"

########## Nothing to edit below this line ################

#Tests:
# 1: Pidfile exists, and path to file is readable
if {![file exists $PIDFILE]} {
    puts stdout "Pidfile does not exist, or path not readable by this user."
    exit
}
# 2. Pidfile is regular file, not directory, symlink, or other
# type of file.
if {![file isfile $PIDFILE]} {
    puts stdout "Pidfile isn't regular file"
    exit
}
# 3. Pidfile is owned by user running this script:
if {![file owned $PIDFILE]} {
    puts stdout "Pidfile isn't owned by current user"
    exit
}
# Open Pidfile and read it.
set FD [open $PIDFILE r]

# Make sure we catch any expected or unexpected errors
# while the pidfile is open.
# The Pidfile must start with numbers and end with EOF
# or end with \nEOF. AOLserver writes the latter format.
# Reading one char at a time to avoid memory exhaustion,
# and any unexpected variable substitutions.

if {[catch {
    set PIDVALUE 0
    while {![eof $FD] && ($PIDVALUE < 32768)} {
        set CHAR [read $FD 1]
        if {[string is integer -strict "${CHAR}"]} {
            set PIDVALUE [expr (10 * ${PIDVALUE} ) + "${CHAR}"]
        } elseif {[string eq "\n" "${CHAR}"] || [string eq "" "${CHAR}"] } {
            break
        } else {
            # pid format is 123\n or just 123.
            error "Unexpected format in Pid file"
        }
    }
} err ]} {
    close $FD
    puts stdout $err
    exit
} else {
    close $FD
}

# Last Test: Pidvalue is in range:
if {!($PIDVALUE < 32768) || ($PIDVALUE < 2) } {
    puts stdout "Pidvalue out of range."
}

# a race condition exists where two users could be running this script,
# or otherwise deleting the pidfile and restarting the server. In this
# case, the new pidfile, containing the pid of the running server may
# be deleted. The following kill command will not have any effect, but
# will return the error to the terminal. The result will be a running
# server without the pidfile. This condition is will likely never
# occur.

file delete "$PIDFILE"
exec /usr/bin/kill -9 "$PIDVALUE"

Any suggestions or comments are welcome.