From d3aee98c1a3cbd7d21c00529d1e4980fd05a5ee6 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Sun, 2 Apr 2023 20:20:09 +0000 Subject: [PATCH] Allow overriding the login session, refactor special users. --- login/graphical.c | 27 +++++++----- login/login.8 | 3 ++ login/login.c | 103 ++++++++++++++++++++++++++++++++++++++-------- login/login.h | 17 +++++++- 4 files changed, 121 insertions(+), 29 deletions(-) diff --git a/login/graphical.c b/login/graphical.c index ed1e5daf..10e677e5 100644 --- a/login/graphical.c +++ b/login/graphical.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen. * Copyright (c) 2021 Juhani 'nortti' Krekelä. + * Copyright (c) 2023 dzwdz. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -87,6 +88,8 @@ static inline void arrow_initialize() static struct textbox textbox_username; static struct textbox textbox_password; +static char* username = NULL; +static char* session = NULL; struct glogin { @@ -605,7 +608,7 @@ static void think(struct glogin* state) } if ( result ) { - if ( !login(textbox_username.text) ) + if ( !login(username, session) ) state->warning = strerror(errno); state->stage = STAGE_USERNAME; textbox_reset(&textbox_username); @@ -626,24 +629,28 @@ static void keyboard_event(struct glogin* state, uint32_t codepoint) { if ( codepoint == '\n' ) { + enum special_action action; state->warning = NULL; switch ( state->stage ) { case STAGE_USERNAME: - if ( !strcmp(textbox_username.text, "exit") ) - exit(0); - if ( !strcmp(textbox_username.text, "poweroff") ) - exit(0); - if ( !strcmp(textbox_username.text, "reboot") ) - exit(1); - if ( !strcmp(textbox_username.text, "halt") ) - exit(2); + free(username); + free(session); + username = NULL; + session = NULL; + if ( !parse_username(textbox_username.text, &username, + &session, &action) ) + { + state->warning = "Invalid username"; + break; + } + handle_special(action); state->stage = STAGE_PASSWORD; textbox_reset(&textbox_password); break; case STAGE_PASSWORD: state->stage = STAGE_CHECKING; - if ( !check_begin(&state->chk, textbox_username.text, + if ( !check_begin(&state->chk, username, textbox_password.text, true) ) { state->stage = STAGE_USERNAME; diff --git a/login/login.8 b/login/login.8 index aa2a776c..5218b2e6 100644 --- a/login/login.8 +++ b/login/login.8 @@ -34,6 +34,9 @@ script if it exists and is executable, otherwise attempting .Pa /etc/session , and ultimately falling back on the user's shell from .Xr passwd 5 . +It can be overriden by suffixing the username with a colon and the name of the +program to launch. +If the program name is skipped, the login shell is launched. .Pp Type a special username to perform special options: .Pp diff --git a/login/login.c b/login/login.c index 3c7c75de..9eb669c7 100644 --- a/login/login.c +++ b/login/login.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2014, 2015, 2018, 2022 Jonas 'Sortie' Termansen. + * Copyright (c) 2023 dzwdz. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,6 +23,7 @@ #include #include +#include #include #include #include @@ -201,7 +203,7 @@ static int setcloexecfrom(int from) return 0; } -bool login(const char* username) +bool login(const char* username, const char* session) { char login_pid[sizeof(pid_t) * 3]; snprintf(login_pid, sizeof(login_pid), "%" PRIiPID, getpid()); @@ -217,6 +219,8 @@ bool login(const char* username) char* login_shell; if ( asprintf(&login_shell, "-%s", pwd->pw_shell) < 0 ) return close(pipe_fds[0]), close(pipe_fds[1]), false; + if ( session && *session == '\0' ) + session = login_shell; sigset_t oldset, sigttou; sigemptyset(&sigttou); sigaddset(&sigttou, SIGTTOU); @@ -252,11 +256,14 @@ bool login(const char* username) tcsetpgrp(0, getpgid(0)) || sigprocmask(SIG_SETMASK, &oldset, NULL) < 0 || settermmode(0, TERMMODE_NORMAL) < 0 || - (execlp("./.session", "./.session", (const char*) NULL) < 0 && - errno != ENOENT && errno != EACCES) || - (execlp("/etc/session", "/etc/session", (const char*) NULL) < 0 && - errno != ENOENT && errno != EACCES) || - execlp(pwd->pw_shell, login_shell, (const char*) NULL)); + (session ? + execlp(session + (session[0] == '-'), session, (const char*) NULL) < 0 + : + (execlp("./.session", "./.session", (const char*) NULL) < 0 && + errno != ENOENT && errno != EACCES) || + (execlp("/etc/session", "/etc/session", (const char*) NULL) < 0 && + errno != ENOENT && errno != EACCES) || + execlp(pwd->pw_shell, login_shell, (const char*) NULL))); write(pipe_fds[1], &errno, sizeof(errno)); _exit(127); } @@ -318,6 +325,61 @@ static bool read_terminal_line(char* buffer, size_t size) return true; } +bool parse_username(const char* input, + char** username, + char** session, + enum special_action* action) +{ + *username = NULL; + *session = NULL; + *action = SPECIAL_ACTION_NONE; + if ( !strcmp(input, "exit") ) + return *action = SPECIAL_ACTION_POWEROFF, true; + else if ( !strcmp(input, "poweroff") ) + return *action = SPECIAL_ACTION_POWEROFF, true; + else if ( !strcmp(input, "reboot") ) + return *action = SPECIAL_ACTION_REBOOT, true; + else if ( !strcmp(input, "halt") ) + return *action = SPECIAL_ACTION_HALT, true; + + // Skip leading spaces to allow logging in as special accounts. + while ( isspace(*input) ) + input++; + char* colon = strchr(input, ':'); + if ( !colon ) + { + *username = strdup(input); + return *username != NULL; + } + if ( strchr(colon + 1, ':') ) + { + // Input contains more than one colon. + return false; + } + *username = strndup(input, colon - input); + if ( !*username ) + return false; + *session = strdup(colon + 1); + if ( !*session ) + { + free(*username); + *username = NULL; + return false; + } + return true; +} + +void handle_special(enum special_action action) +{ + switch ( action ) + { + case SPECIAL_ACTION_NONE: return; + case SPECIAL_ACTION_POWEROFF: exit(0); + case SPECIAL_ACTION_REBOOT: exit(1); + case SPECIAL_ACTION_HALT: exit(2); + } +} + int textual(void) { unsigned int termmode = TERMMODE_UNICODE | TERMMODE_SIGNAL | TERMMODE_UTF8 | @@ -326,6 +388,9 @@ int textual(void) err(2, "settermmode"); unsigned int pw_termmode = termmode & ~(TERMMODE_ECHO); + char* username = NULL; + char* session = NULL; + while ( true ) { char hostname[HOST_NAME_MAX + 1]; @@ -333,9 +398,9 @@ int textual(void) gethostname(hostname, sizeof(hostname)); printf("%s login: ", hostname); fflush(stdout); - char username[256]; + char input[256]; errno = 0; - if ( !read_terminal_line(username, sizeof(username)) ) + if ( !read_terminal_line(input, sizeof(input)) ) { printf("\n"); if ( errno && errno != EINTR ) @@ -347,14 +412,17 @@ int textual(void) continue; } - if ( !strcmp(username, "exit") ) - exit(0); - if ( !strcmp(username, "poweroff") ) - exit(0); - if ( !strcmp(username, "reboot") ) - exit(1); - if ( !strcmp(username, "halt") ) - exit(2); + enum special_action action; + free(username); + free(session); + username = NULL; + session = NULL; + if ( !parse_username(input, &username, &session, &action) ) + { + printf("Invalid username\n\n"); + continue; + } + handle_special(action); if ( settermmode(0, pw_termmode) < 0 ) err(2, "settermmode"); @@ -389,11 +457,10 @@ int textual(void) continue; } - if ( !login(username) ) + if ( !login(username, session) ) { warn("logging in as %s", username); printf("\n"); - continue; } } diff --git a/login/login.h b/login/login.h index 641096f3..438d574d 100644 --- a/login/login.h +++ b/login/login.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2014, 2015 Jonas 'Sortie' Termansen. + * Copyright (c) 2023 dzwdz. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -35,7 +36,15 @@ struct check bool pipe_nonblock; }; -bool login(const char* username); +enum special_action +{ + SPECIAL_ACTION_NONE, + SPECIAL_ACTION_POWEROFF, + SPECIAL_ACTION_REBOOT, + SPECIAL_ACTION_HALT, +}; + +bool login(const char* username, const char* session); bool check_real(const char* username, const char* password); bool check_begin(struct check* chk, const char* username, @@ -46,4 +55,10 @@ bool check(const char* username, const char* password); int graphical(void); int textual(void); +bool parse_username(const char* input, + char** out_username, + char** out_session, + enum special_action* action); +void handle_special(enum special_action action); + #endif