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

Reply via email to