/*
 * Copyright (C) Tildeslash Ltd. All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations
 * including the two.
 *
 * You must obey the GNU Affero General Public License in all respects
 * for all of the code used other than OpenSSL.  
 */

#include "config.h"

#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif

#ifdef HAVE_PWD_H
#include <pwd.h>
#endif

#ifdef HAVE_STRING_H
#include <string.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif

#ifdef HAVE_CRT_EXTERNS_H
#include <crt_externs.h>
#endif

#include "monit.h"

#ifndef MAXPATHLEN
#define MAXPATHLEN STRLEN
#endif

#ifdef DARWIN
#define environ (*_NSGetEnviron())
#endif

/* Private prototypes */
static void set_sandbox(void);
static void set_environment(void);

/**
 *  Setup this program for safer exec, and set required runtime
 *  "environment" variables.
 *
 *  @file
 */


/* ------------------------------------------------------------------ Public */


/**
 * Initialize the program environment
 */
void init_env() {

  /* Setup for safe(r) exec */
  set_sandbox();

  /* Setup program environment */
  set_environment();

}


/* ----------------------------------------------------------------- Private */


/**
 *  DESCRIPTION
 *    This code was originally posted by Wietse Venema, years ago, in
 *    a discussion on news on how to create safe suid wrappers. For
 *    those interested in NNTP archeology, here's the post:
 *    
 *  Article 5648 of comp.security.unix:
 *  From: wietse@wzv.win.tue.nl (Wietse Venema)
 *  Newsgroups: comp.security.unix
 *  Subject: Re: [8lgm]-Advisory-7.UNIX.passwd.11-May-1994
 *  Date: 18 May 1994 07:52:05 +0200
 *  Organization: Eindhoven University of Technology, The Netherlands
 *  Lines: 68
 *  
 *  milton@picard.med.miami.edu (H. Milton Johnson) writes:
 *  >OK, I admit it, I'm a totally incompetent sysadmin because I am not
 *  >sure I could write a bullet-proof setuid wrapper.  However, if one of
 *  >the competent sysadmins subscribing to this group could post or point
 *  >the way to an example of a bullet- proof setuid wrapper, I'm sure that
 *  >I could use it as a template to address this/future/other problems.
 *  
 *  Ok, here is a first stab. Perhaps we can make this into a combined
 *  effort and get rid of the problem once and for all.
 *  
 *           Wietse
 *
 *  [code - see the function below, only marginally changed to suit monit]    
 *
 *
 */
static void set_sandbox(void) {

  int    i = 0;
  struct stat st;
  extern char **environ;
  char   *path = "PATH=/bin:/usr/bin:/sbin:/usr/sbin";
  char   *tz;

  /*
   * Purge the environment, but keep the TZ variable as the time.h family depends on it at least on AIX
   */
  for (tz = environ[0]; tz; tz = environ[++i]) {
    if (! strncasecmp(tz, "TZ=", 3)) {
      environ[0] = tz;
      environ[1] = 0;
      break;
    }
  }
  if (! tz)
    environ[0] = 0;

  if (putenv(path)) {
    LogError("%s: cannot set the PATH variable -- %s\n", prog, STRERROR);
    exit(1);
  }

  /*
   * Require that file descriptors 0,1,2 are open. Mysterious things
   * can happen if that is not the case.
   */
  for(i= 0; i < 3; i++) {

    if(fstat(i, &st) == -1 && open("/dev/null", O_RDWR) != i) {

      LogError("Cannot open /dev/null -- %s\n", STRERROR);
      exit(1);

    }

  }

  Util_closeFds();

}


/**
 * Get and set required runtime "environment" variables.
 */
static void set_environment(void) {

  struct passwd *pw;

  /* Get password struct */
  if ( ! (pw= getpwuid(geteuid())) ) {
    LogError("%s: You don't exist. Go away.\n", prog);
    exit(1);
  }
  Run.Env.home= Str_dup(pw->pw_dir);
  Run.Env.user= Str_dup(pw->pw_name);

  /* Get CWD */
  Run.Env.cwd= CALLOC(sizeof(char), MAXPATHLEN+1);
  if ( ! (getcwd(Run.Env.cwd, MAXPATHLEN)) ) {
    LogError("%s: Cannot read current directory -- %s\n", prog, STRERROR);
    exit(1);
  }

  /*
   * Save and clear the file creation mask
   */
  Run.umask= umask(0);

}

