#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

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

#define CHROOT_BASE "/home/chroots/"

struct chroot_map {
	const char *name;
	const char *subdir;
	const char *users; // This should be comma delimited, but...
};

struct chroot_map chroots[] = {
	{ "unstable", "unstable", NULL             },
	{ "testing", "testing", NULL             },
	{ "stable",   "stable",   NULL             },
	{ "oldstable",   "oldstable",   NULL             },
	{ "sid", "sid", NULL             },
	{ "sarge", "sarge", NULL             },
	{ "woody",   "woody",   NULL             },
	{ "potato",   "potato",   NULL             },
	{ 0, 0, 0 }
};

extern char *__progname;

void list_chroots(const char *name) {
	int i;
	fputs("Available chroots:", stderr);
	for (i = 0; chroots[i].name; i++)
		if (!chroots[i].users || !strcmp(chroots[i].users, name))
			fprintf(stderr, " %s", chroots[i].name);
	fputs("\n", stderr);
}

int main (int argc, char *argv[]) {
	char chroot_to[PATH_MAX];
#ifndef USE_LOGIN
	char shell[PATH_MAX];
#endif
	int exist = 0;
	struct passwd *pwd = getpwuid(getuid());
	struct stat buf;
	int i;
	int ret;

	strcpy(chroot_to, CHROOT_BASE);

	if (pwd == NULL) {
		fprintf(stderr, "FATAL: could not get passwd entry for current user!\n");
		exit(10);
	}

	if (argc != 2 || !strcmp(argv[1], "--help") ||
	    !strcmp(argv[1], "-h")) {
		fprintf(stderr, "Usage: %s <chroot>\n", __progname);
		list_chroots(pwd->pw_name);
		exit(1);
	}

	for (i = 0; chroots[i].name; i++)
		if (!strcmp(chroots[i].name, argv[1])) {
			strncat(chroot_to, chroots[i].subdir,
				sizeof(chroot_to) - strlen(CHROOT_BASE) - 1);
			exist = 1;
			break;
		}

	if (!exist) {
		list_chroots(pwd->pw_name);
		exit(1);
	}

	if (getuid() && chroots[i].users && strcmp(chroots[i].users, pwd->pw_name)) {
		fprintf(stderr, "You are not authorized to access the `%s' chroot!\n",
			argv[1]);
		exit(1);
	}

	/* First we chdir to the chroot, so our '.' is the proper inode */
printf("chroot_to: %s\n", chroot_to); fflush(stdout);
	if (chdir(chroot_to)) {
		perror("chdir");
		exit(1);
	}

	/* All is well, so far, chroot */
	if (chroot(".") == -1) {
		perror("chroot");
		exit(1);
	}

printf("chroot_to: %s\n", "/"); fflush(stdout);
	if (chdir("/")) {
		perror("chdir");
		exit(1);
	}

#ifndef USE_LOGIN
        /* Revert to /bin/sh if the login shell is not found */
        if (stat(pwd->pw_shell, &buf) && (errno == ENOENT)) {
                pwd->pw_shell = "/bin/sh";
                printf("Your default shell is not available. "
			"Reverting to /bin/sh.\n");
        }
        else if (errno) {
                perror("login shell");
                exit(1);
        }

	/* Now switch to the home directory */
printf("chroot_to: %s\n", pwd->pw_dir); fflush(stdout);
	if (chdir(pwd->pw_dir)) {
		perror("chdir");
		exit(1);
	}

	ret = snprintf(shell, sizeof(shell), "-%s", basename(pwd->pw_shell));
	if (ret == -1 || ret > sizeof(shell) - 1) {
		perror("snprintf");
		exit(1);
	}

	if (initgroups(pwd->pw_name, pwd->pw_gid)) {
		perror("initgroups");
		exit(1);
	}
	if (setuid(pwd->pw_uid)) {
		perror("setuid");
		exit(1);
	}

	/* Sanity check */
	if (geteuid() != pwd->pw_uid) {
		fprintf(stderr, "WHOA, we aren't running as your uid...something gone astray!\n");
		exit(10);
	}

	fprintf(stderr, "Executing shell in chroot: %s\n", chroot_to);
	execle(pwd->pw_shell, shell, NULL, environ);
#else
	fprintf(stderr, "Logging in to chroot: %s\n", chroot_to);
	/* We need full root privs for this to work */
	setuid(0);
	execle("/bin/login", "login", "-f", pwd->pw_name, NULL, environ);
#endif

	perror("execle");
	exit(1);
}
