/*
//	@(#) chrootuid -- Jail with specs
//
//	Original code by Wietse Venema
//	ftp://ftp.porcupine.org/pub/security/index.html
//
//	Modified to specify resource limits 
//	Dropped support for syslog as wasn't required
//
//	License: Creative Commons CC0 
//		http://creativecommons.org/publicdomain/zero/1.0/legalcode
//	Author:	James Sainsbury
//		toves@sdf.lonestar.org
//
*/

#include <unistd.h>
#include <stdlib.h>
#include <pwd.h>
#include <grp.h>
#include <sys/resource.h>

void	Usage() {
	static	char	msg[]	= "chrootuid -u user -t /jail [-n nfiles] [-P prio ] -- command\n";
	write (2, msg, sizeof(msg)-1);
	exit (EXIT_FAILURE);
}
int     main (int argc, char* argv[]) {
	int	result	= -1;
	int	tflag	= 0;
	int	uflag	= 0;
	int	nflag	= 0;
	int	Pflag	= 0;
	int	prio	= 0;	/* Normal proc prio */
	char*	user	= 0;
	char*	chroot_dir	= 0;
	rlim_t	nofile	= 0;
	int	opt	= 0;
	struct	passwd*	pwd	= 0;

  	while ((opt = getopt (argc, argv, "P:n:u:t:")) != -1) {
		switch (opt) {
		case	't':
			if (tflag++)
				Usage();
			chroot_dir	= optarg;
		break;
		case	'u':
			if (uflag++)
				Usage();
			user	= optarg;
		break;
		case	'n':
			if (nflag++)
				Usage();
			nofile	= atoi (optarg);
		break;
		case	'P':
			if (Pflag++)
				Usage();
			prio	= atoi (optarg);
		break;
		case	'?':
		default:
			Usage();
		}
	}
	if (!tflag || !uflag)
		Usage();
	result	= chdir (chroot_dir);
	if (result != 0) {
		exit (EXIT_FAILURE);
	}

	if ((pwd = getpwnam(user)) == 0) {
		exit (EXIT_FAILURE);
	}
	result	= initgroups(pwd->pw_name, pwd->pw_gid);
	if (result != 0) {
		exit (EXIT_FAILURE);
	}
    	endgrent();

	result	= chroot (chroot_dir);
	if (result != 0) {
		exit (EXIT_FAILURE);
	}
	if (nflag) {
		struct	rlimit	rl;
		rl.rlim_max	= nofile;
		getrlimit (RLIMIT_NOFILE, &rl);
		if (rl.rlim_cur > nofile) {
			rl.rlim_cur	= nofile;
		}
		rl.rlim_max	= nofile;
		setrlimit (RLIMIT_NOFILE, &rl);
	}
	if (Pflag) {
		int	which	= PRIO_PROCESS;
		pid_t	pid	= getpid ();
		setpriority (which, pid, prio); /* if it fails no matter */
	}
    	result	= setgid(pwd->pw_gid);
	if (result != 0) {
		exit (EXIT_FAILURE);
	}
	result	= setuid(pwd->pw_uid);
	if (result != 0) {
		exit (EXIT_FAILURE);
	}
	endpwent();

    /* Run the command and hope for the best. */

	execv(argv[optind], &argv[optind]);
    	_exit (EXIT_FAILURE);
}
