mrrl-logincaps

MRRL version of logincaps
git clone https://ccx.te2000.cz/git/mrrl-logincaps
Log | Files | Refs

commit db4884b0d814de659a75e87cfa9dc33d472a1139
parent ac19c0ce064461b808ffa9a9a6276ca0ae050591
Author: Jan Pobrislo <ccx@webprojekty.cz>
Date:   Sat, 25 May 2019 02:00:58 +0200

Add X11 spawinig capability
Diffstat:
Alogin.c | 224+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asbin/login-keepenv | 2++
Msbin/logincaps | 34++++++++++++++++++++++++++++++++++
3 files changed, 260 insertions(+), 0 deletions(-)

diff --git a/login.c b/login.c @@ -0,0 +1,224 @@ +/* login.c - Start a session on the system. + * + * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com> + * + * No support for PAM/securetty/selinux/login script/issue/utmp + * Relies on libcrypt for hash calculation. + * + * TODO: this command predates "pending" but needs cleanup. It #defines + * random stuff, calls exit() form a signal handler... yeah. + +USE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT)) + +config LOGIN + bool "login" + default y + depends on TOYBOX_SHADOW + help + usage: login [-p] [-h host] [-f USERNAME] [USERNAME] + + Log in as a user, prompting for username and password if necessary. + + -p Preserve environment + -h The name of the remote host for this login + -f login as USERNAME without authentication +*/ + +#define FOR_login +#include "toys.h" + +GLOBALS( + char *hostname; + char *username; + + int login_timeout, login_fail_timeout; +) + +static void login_timeout_handler(int sig __attribute__((unused))) +{ + printf("\nLogin timed out after %d seconds.\n", TT.login_timeout); + exit(0); +} + +void login_main(void) +{ + char *forbid[] = { + "BASH_ENV", "ENV", "HOME", "IFS", "LD_LIBRARY_PATH", "LD_PRELOAD", + "LD_TRACE_LOADED_OBJECTS", "LD_BIND_NOW", "LD_AOUT_LIBRARY_PATH", + "LD_AOUT_PRELOAD", "LD_NOWARN", "LD_KEEPDIR", "SHELL" + }; + int hh = toys.optflags&FLAG_h, count, tty; + int pipefd[2] = {-1, -1}; + int child_pid; + char uu[33], *username, *pass = 0, *ss; + struct passwd *pwd = 0; + char *loginexec = 0; + unsigned char use_loginexec = 0; + + for (tty=0; tty<3; tty++) if (isatty(tty)) break; + if (tty == 3) error_exit("no tty"); + + for (count = 0; count < ARRAY_LEN(forbid); count++) unsetenv(forbid[count]); + + openlog("login", LOG_PID | LOG_CONS, LOG_AUTH); + xsignal(SIGALRM, login_timeout_handler); + + if (TT.username) username = TT.username; + else username = *toys.optargs; + for (count = 0; count < 3; count++) { + alarm(TT.login_timeout = 60); + tcflush(0, TCIFLUSH); + + if (!username) { + int i; + + memset(username = uu, 0, sizeof(uu)); + gethostname(uu, sizeof(uu)-1); + printf("%s%slogin: ", *uu ? uu : "", *uu ? " " : ""); + fflush(stdout); + + if(!fgets(uu, sizeof(uu)-1, stdin)) _exit(1); + + // Remove trailing \n and so on + for (i = 0; i<sizeof(uu); i++) if (uu[i]<=' ' || uu[i]==':') uu[i]=0; + if (!*uu) { + username = 0; + continue; + } + } + + // If user exists and isn't locked + pwd = getpwnam(username); + if (pwd && *pwd->pw_passwd != '!' && *pwd->pw_passwd != '*') { + + // Pre-authenticated or passwordless + if (TT.username || !*pwd->pw_passwd) break; + + // fetch shadow password if necessary + if (*(pass = pwd->pw_passwd) == 'x') { + struct spwd *spwd = getspnam (username); + + if (spwd) pass = spwd->sp_pwdp; + } + } else if (TT.username) error_exit("bad -f '%s'", TT.username); + + loginexec = xmprintf("%s/loginexec", pwd->pw_dir); + use_loginexec = !access(loginexec, X_OK); + + // Verify password. (Prompt for password _before_ checking disable state.) + if (!read_password(toybuf, sizeof(toybuf), "Password: ")) { + int x = pass && (ss = crypt(toybuf, pass)) && !strcmp(pass, ss); + + if (x && use_loginexec) { + if(!pipe(pipefd)) { + if((child_pid = fork())) { + if(child_pid == -1) { + /* error */ + perror("fork"); + close(pipefd[0]); + close(pipefd[1]); + pipefd[0] = pipefd[1] = -1; + } else { + /* parent */ + close(pipefd[1]); + } + } else { + /* child */ + dup2(pipefd[1], 1); + xprintf("%s\n", toybuf); + exit(0); + } + } else { + /* creating pipe failed */ + perror("pipe"); + pipefd[0] = pipefd[1] = -1; + } + if (!access("/etc/loginexec", X_OK)) { + if (!(toys.optflags&FLAG_p)) { + char *term = getenv("TERM"); + + clearenv(); + if (term) setenv("TERM", term, 1); + } + + setenv("USER", pwd->pw_name, 1); + setenv("LOGNAME", pwd->pw_name, 1); + setenv("HOME", pwd->pw_dir, 1); + setenv("SHELL", pwd->pw_shell, 1); + if(pipefd[0] >= 0) { + setenv("LOGINPASS_FD", xmprintf("%d", pipefd[0]), 1); + } + /* TODO */ + /* set UID, GID and GIDLIST env variables */ + + execl("/etc/loginexec", "/etc/loginexec", loginexec, (char *)0); + } + } + // password go bye-bye now. + memset(toybuf, 0, sizeof(toybuf)); + if (x) break; + } + + syslog(LOG_WARNING, "invalid password for '%s' on %s %s%s", pwd->pw_name, + ttyname(tty), hh ? "from " : "", hh ? TT.hostname : ""); + + sleep(3); + puts("Login incorrect"); + + username = 0; + pwd = 0; + } + + alarm(0); + // This had password data in it, and we reuse for motd below + memset(toybuf, 0, sizeof(toybuf)); + + if (!pwd) error_exit("max retries (3)"); + + // Check twice because "this file exists" is a security test, and in + // theory filehandle exhaustion or other error could make open/read fail. + if (pwd->pw_uid && !access("/etc/nologin", R_OK)) { + ss = readfile("/etc/nologin", toybuf, sizeof(toybuf)); + puts ((ss && *ss) ? ss : "nologin"); + free(ss); + toys.exitval = 1; + + return; + } + + xsetuser(pwd); + + if (chdir(pwd->pw_dir)) printf("bad $HOME: %s\n", pwd->pw_dir); + + if (!(toys.optflags&FLAG_p)) { + char *term = getenv("TERM"); + + clearenv(); + if (term) setenv("TERM", term, 1); + } + + setenv("USER", pwd->pw_name, 1); + setenv("LOGNAME", pwd->pw_name, 1); + setenv("HOME", pwd->pw_dir, 1); + setenv("SHELL", pwd->pw_shell, 1); + if(pipefd[0] >= 0) { + setenv("LOGINPASS_FD", xmprintf("%d", pipefd[0]), 1); + } + + // Message of the day + if ((ss = readfile("/etc/motd", 0, 0))) { + puts(ss); + free(ss); + } + + syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name, + ttyname(tty), hh ? "from" : "", hh ? TT.hostname : ""); + + if(use_loginexec) { + execl(loginexec, loginexec, (char *)0); + } else { + // not using xexec(), login calls absolute path from filesystem so must exec() + execl(pwd->pw_shell, xmprintf("-%s", pwd->pw_shell), (char *)0); + perror_exit("exec shell '%s'", pwd->pw_shell); + } +} diff --git a/sbin/login-keepenv b/sbin/login-keepenv @@ -0,0 +1,2 @@ +#!/command/execlineb -s0 +/usr/local/bin/toybox login -p $@ diff --git a/sbin/logincaps b/sbin/logincaps @@ -7,6 +7,31 @@ trap 'printf "\nlogincaps: ALRM!\n"' ALRM pretendrun() { : "$@" } typeset -f -t pretendrun +initialize() { + if (($+LOGIN_TTY)) && [[ $LOGIN_TTY = /dev/tty[1-4] ]]; then + typeset -g X + X=$[ ${LOGIN_TTY#/dev/tty} + 4 ] + fi +} +typeset -f -t initialize + +cleanup() { + if (($+X)); then + s6-svc -d /run/service/X$X + fi +} + +run_x() { + (($+X)) || return 1 + [[ $X == $1 ]] || return 2 + shift + pretendrun s6-svc -wU -o /run/service/X$X + pretendrun truncate -s 0 /run/Xauthority.$X + pretendrun chmod 600 /run/Xauthority.$X + pretendrun chown $USER /run/Xauthority.$X + pretendrun cat /run/service/X$X/data/Xauthority \> /run/Xauthority.$X +} + main() { local line term_cmd REPLY while read line; do @@ -28,9 +53,18 @@ main() { printf 'OK\n' ;; + (X [5678]*) + if run_x "${(Q@)${(z)${line#X }}}"; then + printf 'OK\n' + else + printf 'EX%d\n' $? + fi + ;; (*) printf 'ECMD\n';; esac done } +initialize main +cleanup