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) 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;

View File

@ -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

View File

@ -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 <sys/wait.h>
#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
@ -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;
}
}

View File

@ -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