// This file was downloaded from http://www.nbtsc.org/chaz/src/

/* Psuedo 1.1
 *
 * A pseudo-sudo. Do not use.
 * 
 * Released into the public domain 1 May 01
 * by Noam Sturmwind <ns@canada.com> (code)
 * and Charlie Loyd <char@nbtsc.org> (idea)
 * 
 * Note for stupid people: psuedo removes 97% of your system's security.
 * Do NOT install it. To install it, do something like this as root:
 *
 * 	# gcc psuedo.c -o psuedo
 * 	# chmod +s psuedo
 * 	# mv psuedo /usr/bin
 * 
 * Please pronounce psuedo "puh-SWAY-doe". It is "psuedo", not "pseudo".
 * 
 */

/* Changes:
 *
 * 20 Nov 01, by Charlie Loyd:
 *	+ Revised & expanded the comments & messages.
 *	+ #definied & referred to DATE string & VERSION int (yeah, int).
 *	
 */

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

#include <time.h>

// Wait .. wait ... wait ... okay, go.

#define DATE "14 Dec 01"
#define RELEASE 3
/* Yeah, an int. Come on -- who loves the a.b.c convention?
 * It's so often abused that it's utterly redundant for small
 * and medium-sized projects. (Where I come from, the proper
 * style is compatibility.update.bugfix, but it gets out
 * of hand one way or another -- either overflow (e.g., 1.1.9
 * has to be followed by 1.2.0 for some reason) or inflation
 * (e.g., Steve Jobs's thing: MacOSes 7.6.1, 8.0, 8.1, 8.5, 8.6,
 * 9.0, 9.1, X, X 10.1, and 9.5, or whatever).) Don't even
 * mention the naming of entire OSes for the year after their
 * scheduled release and then numbering "service packs".
 * Most programs would be conveniently identified by one
 * int -- it's short, clear, and instantly comparable. Dig it.
 */

// Well, that was dumb. Let's get on with the code.

extern int errno;

#define MAX_RESPONSE_SIZE 1025
#define REALLY_PHRASE_COUNT 5
char * really_phrase[] = {
	"Are you sure you want to do that?",
	"I'm not sure you should do that ... do you think it's safe?",
	"I never know when you humans are kidding -- you sure?",
	"Uh, did you mean that?",
	"Hahah, good joke. Oh, uh, did you really mean it?"
}; // Add more. The positive answer has to be "yes" (change that too).

void showusage(void) {
	printf("\nPsuedo %i (%s).\n\n", RELEASE, DATE);
	printf("psuedo [-u <user>] <program> [<args>]\n");
	printf("runs <program> with <args> as <user> or root.\n\n");
	printf("Since this gives everyone setuid, do not install it.\n\n");
} /* Clear usage blurbs are not hard, people. */

int findugid(char * name, uid_t * uid, gid_t * gid) {
	struct passwd * info;

	info = getpwnam(name);
	if (info == NULL) {
		return 1;
	}

	*uid = info->pw_uid;
	*gid = info->pw_gid;

	return 0; // Like an overly-cautious mutual fund.
}

int main(int argc, char * argv[]) {
	int c;
	int i;
	uid_t uid = 0;
	gid_t gid = 0;
	char ** args;

	char response[MAX_RESPONSE_SIZE];
	
	/* Parse them options! Yeah! Parse! Parse! Parse! */
	while ((c = getopt(argc, argv, "+u:")) != -1) {
		switch ((char) c) {
			/* Gone git me a user. */
			case 'u':
				if (findugid(optarg, &uid, &gid)) {
					if (errno == ENOENT) {
						fprintf(stderr, "No such user %s\n", optarg);
					} else {
						perror("Can't look up user. Creepy.");
					}
					exit (1);
				}
				break;
			default:
				exit (1);
		}	
	}
	
	/* How many remaining non-option arguments? */
	/* We need at least the name of the program to run. */
	if ((argc - optind) < 1) {
		showusage();
		return 1;
	}

	if ((args = (char **) malloc((argc-optind+1)*sizeof(char *))) == NULL) {
		perror ("I canna get memory for thees array, sur!");
		exit (1);
	}
	for (i = 0; i < (argc-optind); i++) { // boooooring!
		if ((args[i] = (char *) malloc(strlen(argv[optind+i])*sizeof(char))) == NULL) {
			perror ("I know it's weird, but I couldn't get memory for even a tiny little string. RAM is cheap, you know.");
			exit (1);
		}
		strcpy(args[i], argv[optind+i]);
	}
	args[i] = NULL;

	/* First, get root perms (whee!) */
	if (setuid(0) == -1) {
		perror ("Whoopsie -- can't setuid root. Error: ");
		exit (1);
	}
	
	/* Now change to the requested user. Have to change gid first, since
	 * once we're no longer uid=0 we can't do priviliged stuff ... duh. */
	if (setgid(gid) == -1) {
		perror ("Um, setgid isn't playing nice. Error: ");
		exit (1);
	}
	if (setuid(uid) == -1) {
		perror ("Setuid hates me. Error: ");
		exit (1);
	}
	
	srand(time(NULL));
	i=(int) ((double)REALLY_PHRASE_COUNT*rand()/RAND_MAX);
	printf("%s\n", really_phrase[i]);
loop:
	fgets(response, MAX_RESPONSE_SIZE, stdin);
   	if (strstr(response, "yes")) { // Should prolly take y*.
		i=(int) (100.0*rand()/RAND_MAX);
		if (i < 55) {
			if (execvp(args[0], args) == -1) {
				perror("Unable to run program.");
			}
		} else if (i < 70) {
			printf("Hah! :P\n");
		} else if (i < 85) {
			printf("No way, buddy.\n");
		} else if (i < 95) {
			printf("I can't do that, Dave.\n"); // Obligatory.
		} else {
			printf("Intruder alert! Intruder alert!\n");
		} // Please add calls to $(pom) &c.
	} else if (strstr(response, "no")) {
		printf("Well, then.\n");
	} else {
		printf("I don't understand you! (What's that blue thing doing here?)\n");
		// One They Might Be Giants reference per *.c file is required by POSIX (or was it ANSI?).
		goto loop; // or Dengo.
	}
	
	for (i = 0; i < (argc-optind); i++) {
		free(args[i]);
	}
	free(args);

	return 0;
}

// Share and Enjoy! Bye, Douglas.
