Blame login.c

Ørjan Malde 24d238
#define _GNU_SOURCE
Ørjan Malde 24d238
#include <sys/types.h>
Ørjan Malde 24d238
#include <unistd.h>
Ørjan Malde 24d238
#include <stdlib.h>
Ørjan Malde 24d238
#include <stdio.h>
Ørjan Malde 24d238
#include <stdbool.h>
Ørjan Malde 24d238
#include <string.h>
Ørjan Malde 24d238
#include <grp.h>
Ørjan Malde 24d238
#include <pwd.h>
Ørjan Malde 24d238
#include <errno.h>
Ørjan Malde 24d238
#include <signal.h>
Ørjan Malde 24d238
#ifdef __midipix__
Ørjan Malde 24d238
#include <windows.h>
Ørjan Malde 24d238
Ørjan Malde 24d238
#define IFLAG 1
Ørjan Malde 24d238
#define WFLAG 1
Ørjan Malde 24d238
#else
Ørjan Malde 24d238
#define IFLAG 0
Ørjan Malde 24d238
#define WFLAG 0
Ørjan Malde 24d238
#endif
Ørjan Malde 24d238
Ørjan Malde 24d238
#include "login.h"
Ørjan Malde 24d238
Ørjan Malde 24d238
Ørjan Malde 24d238
static char* get_win32_username(void)
Ørjan Malde 24d238
{
Ørjan Malde 24d238
#ifdef __midipix__
Ørjan Malde 24d238
	size_t i;
Ørjan Malde 24d238
	char usernam[257];
Ørjan Malde 24d238
	unsigned int usernam_siz = sizeof(usernam);
Ørjan Malde 24d238
	if(GetUserNameA(usernam, &usernam_siz) == 0)
Ørjan Malde 24d238
		return "";
Ørjan Malde 24d238
	for(i = 0; i < 257; i++)
Ørjan Malde 24d238
		usernam[i] = tolower(usernam[i]);
Ørjan Malde 24d238
	return strdup(usernam);
Ørjan Malde 24d238
#else
Ørjan Malde 24d238
	return "";
Ørjan Malde 24d238
#endif
Ørjan Malde 24d238
}
Ørjan Malde 24d238
Ørjan Malde 24d238
/* force memset */
Ørjan Malde 24d238
static void __attribute__((optimize("O0"))) memset_noopt(void* mem, int c, size_t memsiz)
Ørjan Malde 24d238
{
Ørjan Malde 24d238
	memset(mem, c, memsiz);
Ørjan Malde 24d238
}
Ørjan Malde 24d238
Ørjan Malde 24d238
static bool switch_user_context(struct passwd* pw, const char* username)
Ørjan Malde 24d238
{
Ørjan Malde 24d238
	/* temporary */
Ørjan Malde 24d238
	#ifdef INSECURE
Ørjan Malde 24d238
	if(initgroups(username, pw->pw_gid) == -1) {
Ørjan Malde 24d238
		printf("initgroups failed: %s", strerror(errno));
Ørjan Malde 24d238
		exit(1);
Ørjan Malde 24d238
	}
Ørjan Malde 24d238
	#endif
Ørjan Malde 24d238
	
Ørjan Malde 24d238
	#ifdef INSECURE
Ørjan Malde 24d238
		if((setuid(pw->pw_uid) != -1) && (setgid(pw->pw_gid) != -1))
Ørjan Malde 24d238
			return true;
Ørjan Malde 24d238
		return false;
Ørjan Malde 24d238
	#else
Ørjan Malde 24d238
	gid_t oldgid = getegid();
Ørjan Malde 24d238
	uid_t olduid = geteuid();
Ørjan Malde 24d238
	
Ørjan Malde 24d238
	if((setegid(pw->pw_gid) != -1) && (setgid(pw->pw_gid) != -1) && (seteuid(pw->pw_uid) != -1) && (setuid(pw->pw_uid) != -1)) {
Ørjan Malde 24d238
		gid_t newgid = getgid();
Ørjan Malde 24d238
		uid_t newuid = getuid();
Ørjan Malde 24d238
		#ifdef debug
Ørjan Malde 24d238
		printf("old gid %d\n", oldgid);
Ørjan Malde 24d238
		printf("old uid %d\n", olduid);
Ørjan Malde 24d238
		printf("new gid %d\n", newgid);
Ørjan Malde 24d238
		printf("new uid %d\n", newuid);
Ørjan Malde 24d238
		#endif
Ørjan Malde 24d238
		if((newgid == oldgid) && (newuid == olduid))
Ørjan Malde 24d238
			return true;
Ørjan Malde 24d238
	}
Ørjan Malde 24d238
	return false;
Ørjan Malde 24d238
	#endif
Ørjan Malde 24d238
}
Ørjan Malde 24d238
Ørjan Malde 24d238
int main(int argc, char **argv)
Ørjan Malde 24d238
{
Ørjan Malde 24d238
	/* we don't want ^D etc to mess up the logic */
Ørjan Malde 24d238
	(void)signal(SIGTSTP, SIG_IGN);
Ørjan Malde 24d238
	(void)signal(SIGQUIT, SIG_IGN);
Ørjan Malde 24d238
	(void)signal(SIGINT, SIG_IGN);
Ørjan Malde 24d238
Ørjan Malde 24d238
	struct passwd *pwd;
Ørjan Malde 24d238
	char* username = NULL;
Ørjan Malde 24d238
	int c;
Ørjan Malde 24d238
	bool authenticated = false;
Ørjan Malde 24d238
	int pflag, fflag, iflag, wflag = 0;
Ørjan Malde 24d238
Ørjan Malde 24d238
	while((c = getopt(argc, argv, "pfh:iw")) != -1)
Ørjan Malde 24d238
		switch(c) {
Ørjan Malde 24d238
			case 'p':
Ørjan Malde 24d238
				pflag = 1; break;
Ørjan Malde 24d238
			case 'f':
Ørjan Malde 24d238
				fflag = 1; break; /* unused, pointless currently */
Ørjan Malde 24d238
			case 'h':
Ørjan Malde 24d238
				if(getuid()) {
Ørjan Malde 24d238
					exit(1);
Ørjan Malde 24d238
				}
Ørjan Malde 24d238
				break;
Ørjan Malde 24d238
			case 'i':
Ørjan Malde 24d238
				iflag = IFLAG; break;
Ørjan Malde 24d238
			case 'w':
Ørjan Malde 24d238
				wflag = WFLAG; break;
Ørjan Malde 24d238
			default:
Ørjan Malde 24d238
			case '?':
Ørjan Malde 24d238
				usage();
Ørjan Malde 24d238
				break;
Ørjan Malde 24d238
		}
Ørjan Malde 24d238
		argv += optind;
Ørjan Malde 24d238
Ørjan Malde 24d238
	if(wflag)
Ørjan Malde 24d238
		username = get_win32_username();
Ørjan Malde 24d238
	else if(*argv)
Ørjan Malde 24d238
		username = *argv;
Ørjan Malde 24d238
	else {
Ørjan Malde 24d238
		/* TODO: hostname, emulate shadow's login(1) prompt */
Ørjan Malde 24d238
		printf("login: ");
Ørjan Malde 24d238
		fflush(0);
Ørjan Malde 24d238
		(void)scanf("%ms", &username);
Ørjan Malde 24d238
	}
Ørjan Malde 24d238
	pwd = getpwnam(username);
Ørjan Malde 24d238
	
Ørjan Malde 24d238
	if(!iflag)
Ørjan Malde 24d238
	{
Ørjan Malde 24d238
		char* pw = getpass("Password: ");
Ørjan Malde 24d238
		if(pwd) {
Ørjan Malde 24d238
			/* if hash == 0 then authenticated = true */
Ørjan Malde 24d238
			if(*pwd->pw_passwd == '\0') {
Ørjan Malde 24d238
				authenticated = true;
Ørjan Malde 24d238
			}
Ørjan Malde 24d238
			else if(pw) {
Ørjan Malde 24d238
				char* pw_encrypted = crypt(pw, pwd->pw_passwd);
Ørjan Malde 24d238
				if(!timingsafe_memcmp(pw_encrypted, pwd->pw_passwd, strlen(pw_encrypted)))
Ørjan Malde 24d238
					authenticated = true;
Ørjan Malde 24d238
			}
Ørjan Malde 24d238
			else {
Ørjan Malde 24d238
				puts("not authenticated");
Ørjan Malde 24d238
				authenticated = false;
Ørjan Malde 24d238
			}
Ørjan Malde 24d238
		} else {
Ørjan Malde 24d238
			authenticated = false;
Ørjan Malde 24d238
		}
Ørjan Malde 24d238
		memset_noopt(pw, 0, strlen(pw));
Ørjan Malde 24d238
	} else
Ørjan Malde 24d238
		authenticated = true; /* iflag */
Ørjan Malde 24d238
	
Ørjan Malde 24d238
	endpwent();
Ørjan Malde 24d238
Ørjan Malde 24d238
	if(!authenticated) {
Ørjan Malde 24d238
		puts("Login incorrect.");
Ørjan Malde 24d238
		exit(1);
Ørjan Malde 24d238
	}
Ørjan Malde 24d238
Ørjan Malde 24d238
	/* authenticated, attempt to set user context and spawn user shell */
Ørjan Malde 24d238
	if(switch_user_context(pwd, username)) {
Ørjan Malde 24d238
		if(*pwd->pw_shell == '\0')
Ørjan Malde 24d238
			pwd->pw_shell = "/bin/sh"; /* if /bin/sh doesn't exist, I do not care. blow up. */
Ørjan Malde 24d238
Ørjan Malde 24d238
		if(chdir(pwd->pw_dir) < 0) {
Ørjan Malde 24d238
			printf("no home directory %s!\n", pwd->pw_dir); // handle -EPERM, -ENOMEM, -ESYMLNK
Ørjan Malde 24d238
				if(chdir("/") == -1) {
Ørjan Malde 24d238
					printf("chdir failed with %s", strerror(errno));
Ørjan Malde 24d238
					exit(1); /* no-one can save you now */
Ørjan Malde 24d238
				}
Ørjan Malde 24d238
			pwd->pw_dir = "/";
Ørjan Malde 24d238
		}
Ørjan Malde 24d238
Ørjan Malde 24d238
		if(!pflag)
Ørjan Malde 24d238
			(void)clearenv();
Ørjan Malde 24d238
Ørjan Malde 24d238
		(void)setenv("HOME", pwd->pw_dir, 1);
Ørjan Malde 24d238
		(void)setenv("SHELL", pwd->pw_shell, 1);
Ørjan Malde 24d238
		(void)setenv("TERM", "xterm", 0); /* rrrr. needs researching */
Ørjan Malde 24d238
		(void)setenv("LOGNAME", pwd->pw_name, 1);
Ørjan Malde 24d238
		(void)setenv("USER", pwd->pw_name, 1);
Ørjan Malde 24d238
#if 0
Ørjan Malde 24d238
		(void)setenv("PS1", "$ ", 0);
Ørjan Malde 24d238
#endif
Ørjan Malde 24d238
		(void)setenv("PATH", "/local/sbin:/local/bin:/sbin:/bin", 0);
Ørjan Malde 24d238
Ørjan Malde 24d238
		execlp(pwd->pw_shell, pwd->pw_shell, (const char*)NULL);
Ørjan Malde 24d238
		printf("login failed with error: %s", strerror(errno));
Ørjan Malde 24d238
		exit(1);
Ørjan Malde 24d238
	}
Ørjan Malde 24d238
	puts("could not switch to specified user.");
Ørjan Malde 24d238
Ørjan Malde 24d238
Ørjan Malde 24d238
}
Ørjan Malde 24d238
Ørjan Malde 24d238
void usage(void) {
Ørjan Malde 24d238
	puts("login -i (skip authentication, only works on midipix, temporary)");
Ørjan Malde 24d238
	puts("login -w (acquire username through Win32)");
Ørjan Malde 24d238
	puts("login -p (preserve environment)");
Ørjan Malde 24d238
	puts("login -f (no secondary authentication, unused)");
Ørjan Malde 24d238
	puts("login -h (pass remote server name to login, unused)");
Ørjan Malde 24d238
	exit(0);
Ørjan Malde 24d238
}