/*
 * spy: suid wrapper for python
 *
 * Usage:
 *  spy script [arg1 arg2 ...] (root privileges)
 *  spy -username script [arg1 arg2 ...] (given user privileges)
 *  #!/path/to/spy [-username] (shebang usage)
 *
 * - this binary and py locations are hard-coded.
 * - only allows execution to hard-coded users.
 * - checks self and script for uid=root, -w and +s bits.
 *
 * Mike Kazantsev <mk.fraggod@gmail.com>
 * 05.03.2009
*/

#include <pwd.h>
#include <grp.h>
#include <sys/stat.h>

int main(int argc, char **argv) {
	if ( argc < 2 ) return(1);

	// Check if user is allowed to SUID
	char *name = getpwuid(getuid())->pw_name; // Caller name
	char *pass[] = {"root", "www"};
	int passc = sizeof(pass) / sizeof(name); // Hacky array size as ( size / pointer size )
	while (passc-- > 0) if ( strcmp(pass[passc], name) == 0 ) break;
	if ( passc < 0 ) return(2);

	// Parse target uid / gid
	int uid, gid;
	if ((int) argv[1][0] == (int) '-' && argc > 2) {
		uid = getpwnam(argv[1]+1)->pw_uid;
		gid = getgrnam(argv[1]+1)->gr_gid;
	} else uid = gid = 0;

	// Relevant paths
	char *target = uid ? argv[2] : argv[1]; // script to be executed
	char *self = "/usr/local/bin/spy"; // this binary, hard-coded on purpose
	char *py = "/usr/bin/python"; // interpreter path, might be hard-coded as well ;P

	// Permissions checker
	int chk_stat(char *file, int chk_suid) {
		struct stat fstat;
		if ( stat(file, &fstat) ) return(1); // path exists
		if ( fstat.st_uid ) return(2); // file is owned by root
		if ( fstat.st_mode & (S_IWGRP|S_IWOTH) ) return(3); // file is not world-writable
		if ( chk_suid && ( fstat.st_mode & S_ISUID ) != S_ISUID ) return(4); // file has suid bit set
		return(0);
	}

	// Security checks
	int chk;
	if ( !strcmp(self, target) ) return(10);
	if ( chk = chk_stat(self, 1) ) return(10 + chk); // wrapper check
	if ( chk = chk_stat(target, 1) ) return(20 + chk); // script check
	if ( chk = chk_stat(py, 0) ) return(30 + chk); // py check

	// Switch uid/gid
	setuid(uid); seteuid(uid);
	setgid(gid); setegid(gid);

	// Execution
	argv = uid ? argv+1 : argv;
	execv(py, argv);
}
