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

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.