[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

init scripts and su



For Postgresql there is a minor security issue.

The start scripts do "su - postgres" to launch the daemon, this is to run the 
~/.bash_profile file to get settings for the database.

The problem with this is that such scripts are writable by the postgres user 
and thus the postgres user can cause their own program to run which can stuff 
key-presses into the input buffer of the controlling terminal, this 
controlling terminal is in many instances (*) the terminal of an 
administrative shell, and commands such as "chmod 666 /etc/shadow" could be 
executed.

To solve this I have written a program named init_su to provide the necessary 
functionality from su(1) without the terminal issue.

init_su closes all file handles other than 1 and 2 (stdout and stderr).  File 
handles 1 and 2 are fstat()'d, if they are regular files or pipes then they 
are left open (no attack is possible through a file or pipe), otherwise they 
are closed and /dev/null is opened instead.  /dev/null is opened for file 
handle 0 regardless of what it might have pointed to previously.  Then 
setsid() is called to create a new session for the process (make it a group 
leader), this invalidates /dev/tty.  Then the uid is changed and the daemon 
is started.


I have attached the source code to init_su, please check it out and tell me 
what you think.


Also this solves a minor problem with the SE Linux patched su and sudo not 
doing quite what we want for daemon startup.


(*)  On system boot and shutdown there is no problem.  It's when the 
administrator uses /etc/init.d/postgresql to start or stop the database that 
there is potential for attack.

-- 
http://www.coker.com.au/selinux/   My NSA Security Enhanced Linux packages
http://www.coker.com.au/bonnie++/  Bonnie++ hard drive benchmark
http://www.coker.com.au/postal/    Postal SMTP/POP benchmark
http://www.coker.com.au/~russell/  My home page
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pwd.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>

void usage(const char * const msg)
{
  if(msg)
    fprintf(stderr, "Error: %s\n\n", msg);
  fprintf(stderr, "Usage: init_su [-l] user -c command\n");
  exit(1);
}

int main(int argc, char **argv)
{
  int i, fd;
  int login = 0;
  char *command = NULL, *user = NULL, *shell = NULL, *nu_argv[4];
  struct passwd *pw;

  int int_c = 0;
  while(int_c != -1)
  {
    int_c = getopt(argc, argv, "-lc:s:");
    switch(int_c)
    {
      case 1:
        if(!strcmp(optarg, "-"))
        {
          login = 1;
        }
        else
        {
          user = optarg;
        }
      break;
      case 'l':
        login = 1;
      break;
      case 's':
        shell = optarg;
      break;
      case 'c':
        command = optarg;
      break;
    }
  }
  if(!user || !command)
    usage(NULL);
  pw = getpwnam(user);
  if(!pw)
    usage("User unknown.");
  if(setregid(pw->pw_gid, pw->pw_gid))
    usage("Can't setgid(), are you root?");
  if(setreuid(pw->pw_uid, pw->pw_uid))
    usage("Can't setuid(), are you root?");
  if(!shell)
    shell = pw->pw_shell;
  if(login)
  {
    nu_argv[0] = strrchr(shell, '/');
    if(!nu_argv[0])
      usage("Bad shell.");
    nu_argv[0] = strdup(nu_argv[0]);
    nu_argv[0][0] = '-';
  }
  else
    nu_argv[0] = shell;
  nu_argv[1] = "-c";
  nu_argv[2] = command;
  nu_argv[3] = NULL;
  close(0);
  for(i = 3; i < 1024; i++)
    close(i);
  openlog("initrc_su", LOG_CONS | LOG_NOWAIT, LOG_DAEMON);
  fd = open("/dev/null", O_RDWR);
  if(fd == -1)
  {
    syslog(LOG_ERR, "Can't open /dev/null when trying to execute program %s", command);
    return 1;
  }
  for(i = 0; i < 3; i++)
  {
    struct stat sbuf;
    if(i != fd && (fstat(i, &sbuf) == -1 || (!S_ISREG(sbuf.st_mode) && !S_ISFIFO(sbuf.st_mode)) ))
    {
      close(i);
      if(dup2(fd, i) != i)
      {
        syslog(LOG_ERR, "Can't dup2() when trying to execute program %s", command);
        return 1;
      }
    }
  }
  if(fd >= 3)
    close(fd);
  setsid();  /* it's OK if this fails as we get the right result anyway */
  execv(shell, nu_argv);
  syslog(LOG_ERR, "Can't exec program %s", command);
  return 1;
}

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]