/*
 * chrlogin.c  -- chroot login
 * acts as login shell in /etc/passwd for a user who has to completely
 * live in a chroot environment
 *
 * Harald Weidner <hweidner@gmx.net>  1999-6-30
 *
 * Installation:
 * compile:  gcc -Wall -O2 -s chrlogin.c -o chrlogin
 * install:  cp to /usr/local/sbin, chown root, chmod 4755
 * create chroot directory (here: /home/chroot)
 * create base file system under /home/chroot
 * DISABLE all setuid root binaries under /home/chroot !!!
 *
 * Install a user:
 * create a user using 'adduser'; set password with 'passwd'
 * set /usr/local/sbin/chrlogin als login shell for that user in /etc/passwd
 * create a user in the chroot-Environment
 * (e.g. by filling out /home/chroot/etc/passwd and creating
 * /home/chroot/home/<username> by hand; that user should have the same
 * uid and gid as in /etc/passwd; login shell must be /bin/bash)
 *
 */


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>

#define CHROOT_DIR "/home/chroot"
#define SHELL "/bin/bash"
#define MAX_HOME_DIR 1024

int main(int argc, char *argv[], char *envp[])
{
  int real_user = getuid();
  struct passwd *pw_ent = NULL;
  struct stat stat_buf;
  char home_dir[MAX_HOME_DIR];

  /* sanity checks */

  if(geteuid() != 0) {
    fprintf(stderr, "%s: This program needs to be setuid root.\n",
	    argv[0]);
    exit(-1);
  }

  if(real_user == 0) {
    fprintf(stderr, "%s: The target user must not be root.\n",
	    argv[0]);
    exit(-1);
  }

  /* look up user in system's /etc/passwd */

  if((pw_ent = getpwuid(real_user)) == NULL) {
    fprintf(stderr, "%s: User #%d does not exit in /etc/passwd.\n",
	    argv[0], real_user);
    exit(-1);
  }

  /* check home directory */

  if(strncmp(pw_ent->pw_dir, CHROOT_DIR, strlen(CHROOT_DIR))) {
    fprintf(stderr, "%s: Home directory %s does not lie in chroot dir %s.\n",
	    argv[0], pw_ent->pw_dir, CHROOT_DIR);
    exit(-1);
  }

  /* check existance of SHELL */

  if(stat(CHROOT_DIR""SHELL, &stat_buf) != 0) {
    fprintf(stderr, "%s: Could not access login shell %s:\n%s\n",
	    argv[0], CHROOT_DIR""SHELL, strerror(errno));
    exit(-1);
  }
  if(!S_ISREG(stat_buf.st_mode)) {
    fprintf(stderr, "%s: Login shell %s must be a regular file.\n",
	    argv[0], CHROOT_DIR""SHELL);
    exit(-1);
  }

  /* enter chroot environment */

  if(chdir(CHROOT_DIR) != 0) {
    fprintf(stderr,
	    "%s: Could not chdir() to new root directory %s:\n%s\n",
	    argv[0], CHROOT_DIR, strerror(errno));
    exit(-1);
  }
  if(chroot(CHROOT_DIR) != 0) {
    fprintf(stderr,
	    "%s: Could not chroot() to new root directory %s:\n%s\n",
	    argv[0], CHROOT_DIR, strerror(errno));
    exit(-1);
  }
  setuid(real_user);

  /* look up user in chroot's /etc/passwd */

  if((pw_ent = getpwuid(real_user)) == NULL) {
    fprintf(stderr, "%s: Could not find user #%d in chroot's /etc/passwd.\n",
	    argv[0], real_user);
    exit(-1);
  }

  /* change to users home directory */

  if(chdir(pw_ent->pw_dir) != 0) {
    fprintf(stderr,
	    "%s: Could not chdir to new home directory %s for user #%d:\n%s\n",
	    argv[0], pw_ent->pw_dir, real_user, strerror(errno));
    exit(-1);
  }

  /* adapt command name */
  argv[0] = pw_ent->pw_shell;

  /* adapt HOME environment variable */
  strcpy(home_dir, "HOME=");
  strncat(home_dir, pw_ent->pw_dir, MAX_HOME_DIR - strlen(home_dir));
  putenv(home_dir);

  /* execute shell */
  execve(SHELL, argv, envp);

  return 0;
}

