diff options
Diffstat (limited to '')
| -rw-r--r-- | asroot.c | 358 | ||||
| -rw-r--r-- | config.mk | 4 | 
2 files changed, 288 insertions, 74 deletions
| @@ -1,13 +1,33 @@  #include <errno.h> +#include <fcntl.h>  #include <pwd.h> +#include <shadow.h>  #include <stdio.h>  #include <stdlib.h>  #include <string.h> +#include <termios.h>  #include <unistd.h> +#ifdef WITH_LIBPASSPHRASE +# include <passphrase.h> +#endif +  #include "arg.h" +#define EXIT_ERROR  125 +#define EXIT_EXEC   126 +#define EXIT_NOENT  127 + +#ifndef ERROR_SLEEP +# define ERROR_SLEEP 1 +#endif + +#ifndef PROMPT +# define PROMPT "%s (%s@%s) password: ", argv0, pwd->pw_name, hostname +#endif + +  char *argv0;  extern char **environ; @@ -74,7 +94,264 @@ static void  usage(void)  {  	fprintf(stderr, "usage: %s [-e] command [argument] ...\n", argv0); -	exit(125); +	exit(EXIT_ERROR); +} + + +static char ** +set_environ(void) +{ +	char **new_environ; +	size_t i, j, n, len; +	struct passwd *pw; + +	new_environ = calloc(sizeof(env_whitelist) / sizeof(*env_whitelist) + 5, sizeof(*env_whitelist)); +	if (!new_environ) { +		fprintf(stderr, "%s: calloc %zu %zu: %s\n", +			argv0, sizeof(env_whitelist) / sizeof(*env_whitelist) + 5, sizeof(*env_whitelist), strerror(errno)); +		exit(errno == ENOENT ? 127 : 126); +	} +	for (i = 0, n = 0; env_whitelist[i]; i++) { +		len = strlen(env_whitelist[i]); +		for (j = 0; environ[j]; j++) { +			if (!strncmp(environ[j], env_whitelist[i], len)) { +				new_environ[n++] = environ[j]; +				break; +			} +		} +	} + +	errno = 0; +	pw = getpwuid(0); +	if (!pw) { +		if (errno) +			fprintf(stderr, "%s: getpwuid 0: %s\n", argv0, strerror(errno)); +		else +			fprintf(stderr, "%s: cannot find root user\n", argv0); +		exit(EXIT_ERROR); +	} + +	if (pw->pw_dir && *pw->pw_dir) { +		len = strlen(pw->pw_dir); +		len += sizeof("HOME="); +		new_environ[n] = malloc(len); +		if (!new_environ[n]) +			fprintf(stderr, "%s: malloc %zu: %s\n", argv0, len, strerror(errno)); +		stpcpy(stpcpy(new_environ[n++], "HOME="), pw->pw_dir); +	} +	if (pw->pw_name && *pw->pw_name) { +		len = strlen(pw->pw_name); +		len += sizeof("LOGNAME="); +		new_environ[n] = malloc(len); +		if (!new_environ[n]) +			fprintf(stderr, "%s: malloc %zu: %s\n", argv0, len, strerror(errno)); +		stpcpy(stpcpy(new_environ[n++], "LOGNAME="), pw->pw_name); + +		len -= sizeof("LOGNAME="); +		len += sizeof("USER="); +		new_environ[n] = malloc(len); +		if (!new_environ[n]) +			fprintf(stderr, "%s: malloc %zu: %s\n", argv0, len, strerror(errno)); +		stpcpy(stpcpy(new_environ[n++], "USER="), pw->pw_name); + +		len -= sizeof("USER="); +		len += sizeof("MAIL=/var/spool/mail/"); +		new_environ[n] = malloc(len); +		if (!new_environ[n]) +			fprintf(stderr, "%s: malloc %zu: %s\n", argv0, len, strerror(errno)); +		stpcpy(stpcpy(new_environ[n++], "MAIL=/var/spool/mail/"), pw->pw_name); +	} +	if (pw->pw_shell && *pw->pw_shell) { +		len = strlen(pw->pw_dir); +		len += sizeof("SHELL="); +		new_environ[n] = malloc(len); +		if (!new_environ[n]) +			fprintf(stderr, "%s: malloc %zu: %s\n", argv0, len, strerror(errno)); +		stpcpy(stpcpy(new_environ[n++], "SHELL="), pw->pw_dir); +	} +	new_environ[n] = NULL; + +	return new_environ; +} + + +#ifndef WITH_LIBPASSPHRASE +static char * +read_passphrase(int fd) +{ +	char *passphrase = NULL, *new; +	size_t size = 0, len = 0; +	ssize_t r; + +	for (;;) { +		if (len == size) { +			new = realloc(passphrase, size += 32); +			if (!new) { +				fprintf(stderr, "\n%s: realloc %zu: %s\n", argv0, size, strerror(errno)); +				if (passphrase) { +					memset(passphrase, 0, len); +					free(passphrase); +				} +				return NULL; +			} +			passphrase = new; +		} +		r = read(fd, &passphrase[len], 1); +		if (r < 0) { +			fprintf(stderr, "\n%s: read /dev/tty 1: %s\n", argv0, strerror(errno)); +			memset(passphrase, 0, len); +			free(passphrase); +			return NULL; +		} else if (!r || passphrase[len] == '\n') { +			break; +		} else { +			len += 1; +		} +	} + +	fprintf(stderr, "\n"); +	passphrase[len] = 0; +	return passphrase; +} +#endif + + +static void +check_password(void) +{ +	struct passwd *pwd; +	struct spwd *shdw; +	const char *expected, *got; +	char *hostname = NULL; +	char *passphrase; +	size_t size = 8; +#ifndef WITH_LIBPASSPHRASE +	struct termios stty_original; +#endif +	struct termios stty_enter, stty_sleep; +	int fd; + +	errno = 0; +	for (;;) { +		hostname = realloc(hostname, size *= 2); +		if (!hostname) { +			fprintf(stderr, "%s: realloc %zu: %s\n", argv0, size, strerror(errno)); +		} +		*hostname = 0; +		if (!gethostname(hostname, size)) { +			break; +		} else if (errno != ENAMETOOLONG) { +			fprintf(stderr, "%s: gethostname %zu: %s\n", argv0, size, strerror(errno)); +			exit(EXIT_ERROR); +		} +	} + +	errno = 0; +	pwd = getpwuid(getuid()); +	if (!pwd || !pwd->pw_name || !*pwd->pw_name) { +		if (errno) +			fprintf(stderr, "%s: getpwuid: %s\n", argv0, strerror(errno)); +		else +			fprintf(stderr, "%s: your user does not exist\n", argv0); +		exit(EXIT_ERROR); +	} + +	shdw = getspnam(pwd->pw_name); +	if (!shdw || !shdw->sp_pwdp) { +		if (errno) +			fprintf(stderr, "%s: getspnam: %s\n", argv0, strerror(errno)); +		else +			fprintf(stderr, "%s: your user does not have a shadow entry\n", argv0); +		exit(EXIT_ERROR); +	} + +	expected = shdw->sp_pwdp; + +	if (!*expected) { +		fprintf(stderr, "%s: your user does not have a password\n", argv0); +		exit(EXIT_ERROR); +	} else if (expected[expected[0] == '!'] == '*' || expected[expected[0] == '!'] == 'x') { +		fprintf(stderr, "%s: login to your account is disabled\n", argv0); +		exit(EXIT_ERROR); +	} else if (expected[0] == '!') { +		fprintf(stderr, "%s: your account is currently locked\n", argv0); +		exit(EXIT_ERROR); +	} + +	fd = open("/dev/tty", O_RDWR); +	if (fd < 0) { +		fprintf(stderr, "%s: open /dev/tty O_RDWR: %s\n", argv0, strerror(errno)); +		exit(EXIT_ERROR); +	} + +#ifdef WITH_LIBPASSPHRASE +	passphrase_disable_echo1(fd); + +	memset(&stty_enter, 0, sizeof(stty_enter)); +	if (tcgetattr(fd, &stty_enter)) { +		fprintf(stderr, "%s: tcgetattr /dev/tty: %s\n", argv0, strerror(errno)); +		passphrase_reenable_echo1(fd); +		close(fd); +		exit(EXIT_ERROR); +	} +#else +	memset(&stty_original, 0, sizeof(stty_original)); +	if (tcgetattr(fd, &stty_original)) { +		fprintf(stderr, "%s: tcgetattr /dev/tty: %s\n", argv0, strerror(errno)); +		close(fd); +		exit(EXIT_ERROR); +	} +	memcpy(&stty_enter, &stty_original, sizeof(stty_original)); +	stty_enter.c_lflag &= (tcflag_t)~ECHO; +	tcsetattr(fd, TCSAFLUSH, &stty_enter); +#endif + +	memcpy(&stty_sleep, &stty_enter, sizeof(stty_enter)); +	stty_sleep.c_lflag = 0; + +again: +	fprintf(stderr, PROMPT); +	fflush(stderr); + +#ifdef WITH_LIBPASSPHRASE +	passphrase = passphrase_read2(fd, PASSPHRASE_READ_EXISTING); +	if (!passphrase) { +		fprintf(stderr, "%s: passphrase_read2 /dev/tty PASSPHRASE_READ_EXISTING: %s\n", argv0, strerror(errno)); +		passphrase_reenable_echo1(fd); +		close(fd); +		exit(EXIT_ERROR); +	} +#else +	passphrase = read_passphrase(fd); +	if (!passphrase) { +		tcsetattr(fd, TCSAFLUSH, &stty_original); +		close(fd); +		exit(EXIT_ERROR); +	} +#endif + +	got = crypt(passphrase, expected); +#ifdef WITH_LIBPASSPHRASE +	passphrase_wipe1(passphrase); +#else +	memset(passphrase, 0, strlen(passphrase)); +#endif +	free(passphrase); + +	if (strcmp(got, expected)) { +		fprintf(stderr, "%s: incorrect password, please try again\n", argv0); +		tcsetattr(fd, TCSAFLUSH, &stty_sleep); +		sleep(ERROR_SLEEP); +		tcsetattr(fd, TCSAFLUSH, &stty_enter); +		goto again; +	} + +#ifdef WITH_LIBPASSPHRASE +	passphrase_reenable_echo1(fd); +#else +	tcsetattr(fd, TCSAFLUSH, &stty_original); +#endif +	close(fd);  } @@ -83,8 +360,6 @@ main(int argc, char *argv[])  {  	int keep_env = 0;  	char **new_environ = NULL; -	size_t i, j, n, len; -	struct passwd *pw;  	ARGBEGIN {  	case 'e': @@ -97,84 +372,21 @@ main(int argc, char *argv[])  	if (!argc)  		usage(); -	/* TODO check password */ +	check_password(); + +	if (!keep_env) +		new_environ = set_environ();  	if (setgid(0)) {  		fprintf(stderr, "%s: setgid 0: %s\n", argv0, strerror(errno)); -		return 125; +		exit(EXIT_ERROR);  	}  	if (setuid(0)) {  		fprintf(stderr, "%s: setuid 0: %s\n", argv0, strerror(errno)); -		return 125; -	} - -	if (!keep_env) { -		new_environ = calloc(sizeof(env_whitelist) / sizeof(*env_whitelist) + 5, sizeof(*env_whitelist)); -		if (!new_environ) { -			fprintf(stderr, "%s: calloc %zu %zu: %s\n", -			        argv0, sizeof(env_whitelist) / sizeof(*env_whitelist) + 5, sizeof(*env_whitelist), strerror(errno)); -			return errno == ENOENT ? 127 : 126; -		} -		for (i = 0, n = 0; env_whitelist[i]; i++) { -			len = strlen(env_whitelist[i]); -			for (j = 0; environ[j]; j++) { -				if (!strncmp(environ[j], env_whitelist[i], len)) { -					new_environ[n++] = environ[j]; -					break; -				} -			} -		} -		errno = 0; -		pw = getpwuid(0); -		if (!pw) { -			if (errno) -				fprintf(stderr, "%s: getpwuid 0: %s\n", argv0, strerror(errno)); -			else -				fprintf(stderr, "%s: cannot find root user\n", argv0); -			exit(125); -		} -		if (pw->pw_dir && *pw->pw_dir) { -			len = strlen(pw->pw_dir); -			len += sizeof("HOME="); -			new_environ[n] = malloc(len); -			if (!new_environ[n]) -				fprintf(stderr, "%s: malloc %zu: %s\n", argv0, len, strerror(errno)); -			stpcpy(stpcpy(new_environ[n++], "HOME="), pw->pw_dir); -		} -		if (pw->pw_name && *pw->pw_name) { -			len = strlen(pw->pw_name); -			len += sizeof("LOGNAME="); -			new_environ[n] = malloc(len); -			if (!new_environ[n]) -				fprintf(stderr, "%s: malloc %zu: %s\n", argv0, len, strerror(errno)); -			stpcpy(stpcpy(new_environ[n++], "LOGNAME="), pw->pw_name); - -			len -= sizeof("LOGNAME="); -			len += sizeof("USER="); -			new_environ[n] = malloc(len); -			if (!new_environ[n]) -				fprintf(stderr, "%s: malloc %zu: %s\n", argv0, len, strerror(errno)); -			stpcpy(stpcpy(new_environ[n++], "USER="), pw->pw_name); - -			len -= sizeof("USER="); -			len += sizeof("MAIL=/var/spool/mail/"); -			new_environ[n] = malloc(len); -			if (!new_environ[n]) -				fprintf(stderr, "%s: malloc %zu: %s\n", argv0, len, strerror(errno)); -			stpcpy(stpcpy(new_environ[n++], "MAIL=/var/spool/mail/"), pw->pw_name); -		} -		if (pw->pw_shell && *pw->pw_shell) { -			len = strlen(pw->pw_dir); -			len += sizeof("SHELL="); -			new_environ[n] = malloc(len); -			if (!new_environ[n]) -				fprintf(stderr, "%s: malloc %zu: %s\n", argv0, len, strerror(errno)); -			stpcpy(stpcpy(new_environ[n++], "SHELL="), pw->pw_dir); -		} -		new_environ[n] = NULL; +		exit(EXIT_ERROR);  	}  	execvpe(argv[0], argv, new_environ ? new_environ : environ);  	fprintf(stderr, "%s: execvpe %s: %s\n", argv0, argv[0], strerror(errno)); -	return errno == ENOENT ? 127 : 126; +	return errno == ENOENT ? EXIT_NOENT : EXIT_EXEC;  } @@ -3,4 +3,6 @@ MANPREFIX = $(PREFIX)/share/man  CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE  CFLAGS   = -std=c99 -Wall -O2 $(CPPFLAGS) -LDFLAGS  = -s +LDFLAGS  = -s -lcrypt + +# To use libpassphrase, add -DWITH_LIBPASSPHRASE to CPPFLAGS and -lpassphrase to LDFLAGS | 
