// for crypt()
#define _XOPEN_SOURCE
extern char *crypt(const char *key, const char *salt);
#include <strings.h>
#include <sys/ioctl.h>
#include <termio.h>
#include <shadow.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <stdlib.h>

int tty_echo(int fd, int echo) {
	struct termio term_mode;
	
	if ( ! isatty(fd) ) { 
		return 0; 
	}
	if ( ioctl( fd, TCGETA, &term_mode ) < 0 ) {
		return -1;
	}
	if ( echo ) {
		term_mode.c_lflag|=ECHO;
	}else{
		term_mode.c_lflag&=(~ECHO);
	}
	if ( ioctl( fd, TCSETA, &term_mode ) ) {
		return -1;
	}
	return 0;
}

struct passwd * get_pw_entry(char *username ){
	char pwnam[128];

	if (strcmp(username, "") == 0) {
		strcpy(pwnam, "root");
	}else{
		strcpy(pwnam, username);
	}
	return getpwnam( pwnam );
}

void become_uid( uid_t uid ) {
	setuid( uid );
	execl("/bin/sh", "sh", (char *)NULL);
	exit(0);
}

void authenticate( struct passwd * pw ) {
	struct spwd  *spw;
	char pass[1024];
	//char salt[12];
	char *encp;

	bzero(pass, sizeof(pass));

	// ask passwd from user
	printf("Password: ");
	fflush(stdout);
	tty_echo( fileno(stdin), 0 );
	read(0, pass, sizeof(pass));
	pass[strlen(pass)-1] = '\0';

	tty_echo( fileno(stdin), 1 );
	printf("\n"); 

	// lookup in /etc/shadow
	spw = getspnam( pw->pw_name ); 
	if ( spw == (struct spwd *)NULL ) {
		printf("can't get a shadow passwd entry, exiting....\n");
		exit(1);
	}
	//memcpy(salt, spw->sp_pwdp, sizeof(salt) );
	encp = crypt( pass, spw->sp_pwdp );
	//printf("got %s == %s\n", spw->sp_pwdp, encp);
	if (strcmp( spw->sp_pwdp, encp ) == 0 ) {
		become_uid( pw->pw_uid );
	}
}


int main(int argc, char **argv) {
	char user[1024];
	struct passwd *pw;

	bzero(user, sizeof(user));
	
	if ( argc == 2) {
		strcpy(user, argv[1]);
	}

	pw = get_pw_entry( user );
	if ( pw == (struct passwd *) NULL ) {
		printf("No such user: %s\n", user);
		exit(1);
	}

	if (getuid() == 0) {
		become_uid(pw->pw_uid);
	}

	// else try to authenticate them
	authenticate( pw );

	printf("Sorry.\n");
	return 0;
}

