Allow overriding the login session, refactor special users.

This commit is contained in:
dzwdz 2023-04-02 20:20:09 +00:00 committed by Juhani Krekelä
parent 2799f04cad
commit d3aee98c1a
4 changed files with 121 additions and 29 deletions

View File

@ -1,6 +1,7 @@
/* /*
* Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen. * Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2021 Juhani 'nortti' Krekelä. * Copyright (c) 2021 Juhani 'nortti' Krekelä.
* Copyright (c) 2023 dzwdz.
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * 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_username;
static struct textbox textbox_password; static struct textbox textbox_password;
static char* username = NULL;
static char* session = NULL;
struct glogin struct glogin
{ {
@ -605,7 +608,7 @@ static void think(struct glogin* state)
} }
if ( result ) if ( result )
{ {
if ( !login(textbox_username.text) ) if ( !login(username, session) )
state->warning = strerror(errno); state->warning = strerror(errno);
state->stage = STAGE_USERNAME; state->stage = STAGE_USERNAME;
textbox_reset(&textbox_username); textbox_reset(&textbox_username);
@ -626,24 +629,28 @@ static void keyboard_event(struct glogin* state, uint32_t codepoint)
{ {
if ( codepoint == '\n' ) if ( codepoint == '\n' )
{ {
enum special_action action;
state->warning = NULL; state->warning = NULL;
switch ( state->stage ) switch ( state->stage )
{ {
case STAGE_USERNAME: case STAGE_USERNAME:
if ( !strcmp(textbox_username.text, "exit") ) free(username);
exit(0); free(session);
if ( !strcmp(textbox_username.text, "poweroff") ) username = NULL;
exit(0); session = NULL;
if ( !strcmp(textbox_username.text, "reboot") ) if ( !parse_username(textbox_username.text, &username,
exit(1); &session, &action) )
if ( !strcmp(textbox_username.text, "halt") ) {
exit(2); state->warning = "Invalid username";
break;
}
handle_special(action);
state->stage = STAGE_PASSWORD; state->stage = STAGE_PASSWORD;
textbox_reset(&textbox_password); textbox_reset(&textbox_password);
break; break;
case STAGE_PASSWORD: case STAGE_PASSWORD:
state->stage = STAGE_CHECKING; state->stage = STAGE_CHECKING;
if ( !check_begin(&state->chk, textbox_username.text, if ( !check_begin(&state->chk, username,
textbox_password.text, true) ) textbox_password.text, true) )
{ {
state->stage = STAGE_USERNAME; state->stage = STAGE_USERNAME;

View File

@ -34,6 +34,9 @@ script if it exists and is executable, otherwise attempting
.Pa /etc/session , .Pa /etc/session ,
and ultimately falling back on the user's shell from and ultimately falling back on the user's shell from
.Xr passwd 5 . .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 .Pp
Type a special username to perform special options: Type a special username to perform special options:
.Pp .Pp

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2014, 2015, 2018, 2022 Jonas 'Sortie' Termansen. * Copyright (c) 2014, 2015, 2018, 2022 Jonas 'Sortie' Termansen.
* Copyright (c) 2023 dzwdz.
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -22,6 +23,7 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <assert.h> #include <assert.h>
#include <ctype.h>
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
@ -201,7 +203,7 @@ static int setcloexecfrom(int from)
return 0; return 0;
} }
bool login(const char* username) bool login(const char* username, const char* session)
{ {
char login_pid[sizeof(pid_t) * 3]; char login_pid[sizeof(pid_t) * 3];
snprintf(login_pid, sizeof(login_pid), "%" PRIiPID, getpid()); snprintf(login_pid, sizeof(login_pid), "%" PRIiPID, getpid());
@ -217,6 +219,8 @@ bool login(const char* username)
char* login_shell; char* login_shell;
if ( asprintf(&login_shell, "-%s", pwd->pw_shell) < 0 ) if ( asprintf(&login_shell, "-%s", pwd->pw_shell) < 0 )
return close(pipe_fds[0]), close(pipe_fds[1]), false; return close(pipe_fds[0]), close(pipe_fds[1]), false;
if ( session && *session == '\0' )
session = login_shell;
sigset_t oldset, sigttou; sigset_t oldset, sigttou;
sigemptyset(&sigttou); sigemptyset(&sigttou);
sigaddset(&sigttou, SIGTTOU); sigaddset(&sigttou, SIGTTOU);
@ -252,11 +256,14 @@ bool login(const char* username)
tcsetpgrp(0, getpgid(0)) || tcsetpgrp(0, getpgid(0)) ||
sigprocmask(SIG_SETMASK, &oldset, NULL) < 0 || sigprocmask(SIG_SETMASK, &oldset, NULL) < 0 ||
settermmode(0, TERMMODE_NORMAL) < 0 || settermmode(0, TERMMODE_NORMAL) < 0 ||
(execlp("./.session", "./.session", (const char*) NULL) < 0 && (session ?
errno != ENOENT && errno != EACCES) || execlp(session + (session[0] == '-'), session, (const char*) NULL) < 0
(execlp("/etc/session", "/etc/session", (const char*) NULL) < 0 && :
errno != ENOENT && errno != EACCES) || (execlp("./.session", "./.session", (const char*) NULL) < 0 &&
execlp(pwd->pw_shell, login_shell, (const char*) NULL)); 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)); write(pipe_fds[1], &errno, sizeof(errno));
_exit(127); _exit(127);
} }
@ -318,6 +325,61 @@ static bool read_terminal_line(char* buffer, size_t size)
return true; 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) int textual(void)
{ {
unsigned int termmode = TERMMODE_UNICODE | TERMMODE_SIGNAL | TERMMODE_UTF8 | unsigned int termmode = TERMMODE_UNICODE | TERMMODE_SIGNAL | TERMMODE_UTF8 |
@ -326,6 +388,9 @@ int textual(void)
err(2, "settermmode"); err(2, "settermmode");
unsigned int pw_termmode = termmode & ~(TERMMODE_ECHO); unsigned int pw_termmode = termmode & ~(TERMMODE_ECHO);
char* username = NULL;
char* session = NULL;
while ( true ) while ( true )
{ {
char hostname[HOST_NAME_MAX + 1]; char hostname[HOST_NAME_MAX + 1];
@ -333,9 +398,9 @@ int textual(void)
gethostname(hostname, sizeof(hostname)); gethostname(hostname, sizeof(hostname));
printf("%s login: ", hostname); printf("%s login: ", hostname);
fflush(stdout); fflush(stdout);
char username[256]; char input[256];
errno = 0; errno = 0;
if ( !read_terminal_line(username, sizeof(username)) ) if ( !read_terminal_line(input, sizeof(input)) )
{ {
printf("\n"); printf("\n");
if ( errno && errno != EINTR ) if ( errno && errno != EINTR )
@ -347,14 +412,17 @@ int textual(void)
continue; continue;
} }
if ( !strcmp(username, "exit") ) enum special_action action;
exit(0); free(username);
if ( !strcmp(username, "poweroff") ) free(session);
exit(0); username = NULL;
if ( !strcmp(username, "reboot") ) session = NULL;
exit(1); if ( !parse_username(input, &username, &session, &action) )
if ( !strcmp(username, "halt") ) {
exit(2); printf("Invalid username\n\n");
continue;
}
handle_special(action);
if ( settermmode(0, pw_termmode) < 0 ) if ( settermmode(0, pw_termmode) < 0 )
err(2, "settermmode"); err(2, "settermmode");
@ -389,11 +457,10 @@ int textual(void)
continue; continue;
} }
if ( !login(username) ) if ( !login(username, session) )
{ {
warn("logging in as %s", username); warn("logging in as %s", username);
printf("\n"); printf("\n");
continue;
} }
} }

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2014, 2015 Jonas 'Sortie' Termansen. * Copyright (c) 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2023 dzwdz.
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -35,7 +36,15 @@ struct check
bool pipe_nonblock; 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_real(const char* username, const char* password);
bool check_begin(struct check* chk, bool check_begin(struct check* chk,
const char* username, const char* username,
@ -46,4 +55,10 @@ bool check(const char* username, const char* password);
int graphical(void); int graphical(void);
int textual(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 #endif