Hi, folks! Just one little question. Is it possible to have some sort of program intended to use in initscripts that have functionality similar to su one, but with small difference. It should not be setuid. It should allow to su to user without valid shell. It should not log auth actions to syslog (maybe?). The purpose of this is so that one can setup services started from initscripts that does not require root access. One example here is oracle startup/shutdown -- I need just to set uid to oracle (and gid also) and start database(s) and listener(s). Plain su is not well suited for this since it have different command line requirements (not very restrective), it doesn't allow su to user without valid shell (shell is not needed here at all). Also I want to start some daemon with permissions of some account that uses restricted shell and so that this daemon can't be started by this user when logged on in normal way. Here is small example program that I use for this purpose (attached suexec.c file) and example of usage (complicated shell script oracle.init to startup/shutdown Oracle databases). Maybe I'm missing something and, e.g. su can be used for this, but I don't know. And maybe I can compromise security here -- not logging su actions maybe sometimes dangerous... Comments?
#include <unistd.h> #include <pwd.h> #include <grp.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> int main(int argc, char **argv) { struct passwd *pw; uid_t uid; if (argc < 3) { fprintf(stderr, "%s: usage is: \"%s [#]uid program [arguments]\"\n", argv[0], argv[0]); return 1; } if (argv[1][0] == '#') { /* numeric id */ char *p; uid = strtol(++(argv[1]), &p, 0); if (*p != '\0') { fprintf(stderr, "%s: invalid numeric uid \"%s\"\n", argv[0], argv[1]); return 1; } pw = getpwuid(uid); } else pw = getpwnam(argv[1]); if (!pw) { fprintf(stderr, "%s: unknown uid \"%s\"\n", argv[0], argv[1]); return 2; } uid = pw->pw_uid; if (initgroups(pw->pw_name, pw->pw_gid) != 0) { perror("initgroups"); return 3; } if (setgid(pw->pw_gid) != 0) { perror("setgid"); return 3; } endpwent(); if (setuid(uid) != 0) { perror(argv[1]); return 3; } execvp(argv[2], argv + 2); perror(argv[2]); return -1; }
#! /bin/sh # # chkconfig: 2345 89 10 # description: Starts/Stops the Oracle Database(s) # # action is here to force RedHat's rc script to execute this directly. # Oracle Software Owner # All oracle homes SHOULD be uwned by this user! ORACLE_OWNER=oracle # ORATAB file. Each line have format: # sid:home:start # where sid is ORACLE_SID of instance, # home is ORACLE_HOME (default is home directory of $ORACLE_OWNER) # start can be Y to start instance on system startup or other if no. # There is should be $home/init$sid.ora file. ORATAB=/etc/oratab # Shmmax parameter -- max shared memory segment size (100Mb here) SHMMAX=$[100*1024*1024] # where is kernel parameter for shmmax SHMMAXF=/proc/sys/kernel/shmmax # Connect string for database control CONNECT_STRING="internal as sysdba" # STARTUP/SHUTDOWN strings SHUTDOWN_STRING='shutdown immediate' STARTUP_STRING='startup' # Home directory of Oracle Owner eval ORACLE_OWNER_HOME=~$ORACLE_OWNER [ -d $ORACLE_OWNER_HOME ] || exit 0 # Oracle Owner Home not mounted [ -f $ORATAB ] || exit 0 # No ORATAB file lk=/var/lock/subsys/oracle shmmax() { if [ `cat "$SHMMAXF"` -lt $SHMMAX ] ; then echo -n "Setting SHMMAX to $SHMMAX" echo $SHMMAX > $SHMMAXF && echo_success || echo_failure echo fi } # is_running sid isrunning() { ps -u $ORACLE_OWNER -o cmd | fgrep -q ora_pmon_$1 } oratab() { sed -n \ '-es|#.*||' \ '-es|^\([^:]*:\)[ \t]*:|\1'$ORACLE_OWNER_HOME':|' \ '-es|^[ \t]*\([a-zA-Z0-9_]\{1,8\}\)[ \t]*:[ \t]*\([^: ]*\)[ \t]*:[ \t]*|\1 \2 |p' \ $ORATAB } # do_oracle ORACLE_HOME ORACLE_SID COMMAND do_oracle() { errs=` echo "$3" | \ ORACLE_HOME="$1" \ NLS_LANG=american_america.us7ascii \ ORACLE_SID=$2 \ LD_LIBRARY_PATH=$1/lib \ /sbin/suexec $ORACLE_OWNER $1/bin/sqlplus -s "$CONNECT_STRING" \ 2>&1` } # startup ORACLE_HOME ORACLE_SID startup() { echo -n " ...startup instance $2:" do_oracle $1 $2 "$STARTUP_STRING" if isrunning $2 ; then echo_success else initlog $INITLOG_ARGS -n "$ORACLE_OWNER[startup:$2]" -e2 -s "$errs" echo_failure fi echo } # shutdown ORACLE_HOME ORACLE_SID shutdown() { echo -n " ...shutdown instance $2:" do_oracle $1 $2 "$SHUTDOWN_STRING" if isrunning $2 ; then initlog $INITLOG_ARGS -n "$ORACLE_OWNER[shutdown:$2]" -e2 -s "$errs" echo_failure else echo_success fi echo } notify() { if [ -z "$noted" ] ; then echo "$notify Oracle Database(s):" noted=y fi } noted= . /etc/rc.d/init.d/functions case "$1" in startup|start) shmmax notify="Starting Up" oratab | \ while read sid home start ; do [ "$start" = Y -o "$start" = y ] || continue if isrunning $sid ; then : ; else notify startup $home $sid fi done touch $lk ;; restart|reload) shmmax notify=Restarting oratab | \ while read sid home start ; do if isrunning $sid ; then notify shutdown "$home" $sid elif [ "$start" != Y -a "$start" != y ] ; then continue else notify fi startup $home $sid done touch $lk ;; stop|shutdown|shut) running=`ps -u "$ORACLE_OWNER" -o cmd | sed -n 's|^ora_pmon_||p'` notify="Shutting Down" for sid in $running ; do home=`awk -F: '$1 == "'"$sid"'" { print $2; exit; }' $ORATAB` [ -n "$home" ] || home=$ORACLE_OWNER_HOME notify shutdown $home $sid done rm -f $lk ;; *) echo "Usage: $0 {start|stop|reload|restart}" >&2 exit 1 ;; esac