it works!
This commit is contained in:
parent
ac49127e85
commit
ad66313883
3 changed files with 751 additions and 74 deletions
560
hlogin.c
560
hlogin.c
|
@ -17,6 +17,8 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with hlogin. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#define __USE_GNU
|
||||
#define _GNU_SOURCE
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
@ -26,6 +28,10 @@
|
|||
#include <locale.h>
|
||||
#include "gettext.h"
|
||||
|
||||
#define VAR_(x) #x
|
||||
#define VAR(x) VAR_(x)
|
||||
#pragma message "LOCALEDIR=" VAR(LOCALEDIR)
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <security/_pam_types.h>
|
||||
|
@ -33,13 +39,16 @@
|
|||
#include <security/pam_misc.h>
|
||||
#include <syslog.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <utmpx.h>
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "ui.h"
|
||||
|
||||
#define VAR_(x) #x
|
||||
#define VAR(x) VAR_(x)
|
||||
#pragma message "LOCALEDIR=" VAR(LOCALEDIR)
|
||||
|
||||
int pam_conv_handler(int msgc, const struct pam_message *msgv[], struct pam_response **resp, void *appdata_ptr)
|
||||
{
|
||||
if (msgc <= 0)
|
||||
|
@ -49,12 +58,21 @@ int pam_conv_handler(int msgc, const struct pam_message *msgv[], struct pam_resp
|
|||
if (!result)
|
||||
return PAM_CONV_ERR;
|
||||
|
||||
char *info_concat = NULL;
|
||||
size_t info_len;
|
||||
|
||||
for (int i = 0; i < msgc; i++) {
|
||||
int r;
|
||||
char *text;
|
||||
switch (msgv[i]->msg_style) {
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
if (info_concat) {
|
||||
ui_setup_message(info_concat);
|
||||
ui_message_run();
|
||||
free(info_concat);
|
||||
info_concat = NULL;
|
||||
}
|
||||
ui_setup_dialog((char *) msgv[i]->msg, msgv[i]->msg_style == PAM_PROMPT_ECHO_OFF);
|
||||
r = ui_run();
|
||||
text = ui_get_text();
|
||||
|
@ -68,12 +86,40 @@ int pam_conv_handler(int msgc, const struct pam_message *msgv[], struct pam_resp
|
|||
break;
|
||||
case PAM_ERROR_MSG:
|
||||
case PAM_TEXT_INFO:
|
||||
goto FAILURE; // TODO implement info/error dialog
|
||||
if (!info_concat) {
|
||||
info_len = strlen((char *) msgv[i]->msg);
|
||||
info_concat = realloc(info_concat, sizeof(char) * (info_len + 1));
|
||||
strcpy(info_concat, (char *) msgv[i]->msg);
|
||||
info_concat[info_len] = '\0';
|
||||
} else {
|
||||
info_concat[info_len] = '\n';
|
||||
size_t pos = info_len + 1;
|
||||
info_len = pos + strlen((char *) msgv[i]->msg);
|
||||
info_concat = realloc(info_concat, sizeof(char) * (info_len + 1));
|
||||
strcpy(info_concat + pos, (char *) msgv[i]->msg);
|
||||
info_concat[info_len] = '\0';
|
||||
}
|
||||
result[i].resp_retcode = 0;
|
||||
result[i].resp = NULL;
|
||||
break;
|
||||
default:
|
||||
if (info_concat) {
|
||||
ui_setup_message(info_concat);
|
||||
ui_message_run();
|
||||
free(info_concat);
|
||||
info_concat = NULL;
|
||||
}
|
||||
goto FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (info_concat) {
|
||||
ui_setup_message(info_concat);
|
||||
ui_message_run();
|
||||
free(info_concat);
|
||||
info_concat = NULL;
|
||||
}
|
||||
|
||||
*resp = result;
|
||||
return PAM_SUCCESS;
|
||||
|
||||
|
@ -91,8 +137,167 @@ FAILURE:
|
|||
return PAM_CONV_ERR;
|
||||
}
|
||||
|
||||
/** number of seconds to hang after a login failure or error **/
|
||||
/** number of seconds to hang after a login failure or error */
|
||||
unsigned int fail_delay = 3;
|
||||
/** maximum number of login attempts before a failure */
|
||||
unsigned int max_tries = 5;
|
||||
|
||||
char *origin = "test_origin";
|
||||
char *tty_path = NULL;
|
||||
char *tty_name = NULL;
|
||||
char *tty_id = NULL;
|
||||
char *remote_host = NULL;
|
||||
|
||||
pid_t login_pid;
|
||||
|
||||
void log_utmp(char *login_name)
|
||||
{
|
||||
struct utmpx ut;
|
||||
memset(&ut, 0, sizeof(ut));
|
||||
struct utmpx *ent;
|
||||
|
||||
setutxent();
|
||||
while ((ent = getutxent()))
|
||||
if (ent->ut_pid == login_pid && ent->ut_type >= INIT_PROCESS && ent->ut_type <= DEAD_PROCESS)
|
||||
break;
|
||||
|
||||
// try by tty_name
|
||||
if (ent == NULL && tty_name) {
|
||||
setutxent();
|
||||
ut.ut_type = LOGIN_PROCESS;
|
||||
strncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
|
||||
ent = getutxline(&ut);
|
||||
}
|
||||
|
||||
// try by tty_id
|
||||
if (ent == NULL && tty_id) {
|
||||
setutxent();
|
||||
ut.ut_type = DEAD_PROCESS;
|
||||
strncpy(ut.ut_id, tty_id, sizeof(ut.ut_id));
|
||||
ent = getutxid(&ut);
|
||||
}
|
||||
|
||||
// copy or create
|
||||
if (ent)
|
||||
memcpy(&ut, ent, sizeof(ut));
|
||||
else
|
||||
memset(&ut, 0, sizeof(ut));
|
||||
|
||||
// update utmp entry
|
||||
strncpy(ut.ut_user, login_name, sizeof(ut.ut_user));
|
||||
ut.ut_type = USER_PROCESS;
|
||||
ut.ut_pid = login_pid;
|
||||
if (tty_id && ut.ut_id[0] == 0)
|
||||
strncpy(ut.ut_id, tty_id, sizeof(ut.ut_id));
|
||||
if (tty_name)
|
||||
strncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
|
||||
if (remote_host)
|
||||
strncpy(ut.ut_host, remote_host, sizeof(ut.ut_host));
|
||||
// TODO copy remote_addr
|
||||
|
||||
// set time
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
ut.ut_tv.tv_sec = tv.tv_sec;
|
||||
ut.ut_tv.tv_usec = tv.tv_usec;
|
||||
|
||||
pututxline(&ut);
|
||||
endutxent();
|
||||
updwtmpx(_PATH_WTMP, &ut);
|
||||
}
|
||||
|
||||
const char *CANCEL_SENTINEL = "\0CANCEL\0";
|
||||
|
||||
/** try_login - prompt for login name & password and attempt to log in
|
||||
* returns login_name on success or NULL on failure
|
||||
*/
|
||||
char *try_login(pam_handle_t *pam, unsigned int try_count)
|
||||
{
|
||||
int result;
|
||||
|
||||
// prompt for username
|
||||
ui_setup_dialog(_("Login name:"), false);
|
||||
result = ui_run();
|
||||
char *login_name = ui_get_text();
|
||||
if (result != 0)
|
||||
return CANCEL_SENTINEL;
|
||||
|
||||
result = pam_set_item(pam, PAM_USER, login_name);
|
||||
if (result != PAM_SUCCESS) {
|
||||
syslog(LOG_NOTICE, _("Couldn't set PAM_USER: %s"), pam_strerror(pam, result));
|
||||
ui_setup_message(_("Failed to begin authentication."));
|
||||
ui_message_run();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = pam_authenticate(pam, 0);
|
||||
if (result != PAM_SUCCESS) {
|
||||
// log to syslog
|
||||
if (result == PAM_MAXTRIES || try_count >= max_tries)
|
||||
syslog(LOG_NOTICE, _("TOO MANY LOGIN TRIES (%u) FROM '%s' FOR '%s', %s"),
|
||||
try_count, origin, login_name, pam_strerror(pam, result));
|
||||
else
|
||||
syslog(LOG_NOTICE, _("FAILED LOGIN (%u) FROM '%s' FOR '%s', %s"),
|
||||
try_count, origin, login_name, pam_strerror(pam, result));
|
||||
|
||||
// display error message
|
||||
ui_setup_message(_("Authentication failed."));
|
||||
ui_message_run();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return login_name;
|
||||
}
|
||||
|
||||
int get_pam_login_name(pam_handle_t *pam, char **login_name)
|
||||
{
|
||||
const void *item = (const void *) *login_name;
|
||||
int result = pam_get_item(pam, PAM_USER, &item);
|
||||
*login_name = (char *) item;
|
||||
return result;
|
||||
}
|
||||
|
||||
void init_env(bool no_destroy, pam_handle_t *pam, struct passwd *pwd)
|
||||
{
|
||||
char *buf = getenv("TERM");
|
||||
if (buf)
|
||||
buf = strdup(buf);
|
||||
|
||||
if (!no_destroy)
|
||||
environ = calloc(1, sizeof(char *));
|
||||
|
||||
setenv("HOME", pwd->pw_dir, 0);
|
||||
setenv("USER", pwd->pw_name, 1);
|
||||
setenv("LOGNAME", pwd->pw_name, 1);
|
||||
setenv("SHELL", pwd->pw_shell, 1);
|
||||
setenv("TERM", buf ? buf : "dumb", 1);
|
||||
free(buf);
|
||||
|
||||
// TODO use ENV_PATH and ENV_SUPATH from logindefs
|
||||
setenv("PATH", _PATH_DEFPATH, 0);
|
||||
|
||||
// apparently mailx is a bitch without this
|
||||
asprintf(&buf, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
|
||||
setenv("MAIL", buf, 0);
|
||||
free(buf);
|
||||
|
||||
char **pam_env = pam_getenvlist(pam);
|
||||
for (int i = 0; pam_env && pam_env[i]; i++)
|
||||
putenv(pam_env[i]);
|
||||
}
|
||||
|
||||
static pid_t child_pid;
|
||||
static volatile sig_atomic_t got_sig = 0;
|
||||
static void handle_sig(int signal)
|
||||
{
|
||||
if (child_pid)
|
||||
kill(-child_pid, signal);
|
||||
else
|
||||
got_sig = 1;
|
||||
if (signal == SIGTERM)
|
||||
kill(-child_pid, SIGHUP);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
@ -104,10 +309,40 @@ int main(int argc, char* argv[])
|
|||
|
||||
openlog("hlogin", LOG_ODELAY, LOG_AUTHPRIV);
|
||||
|
||||
login_pid = getpid();
|
||||
|
||||
if (isatty(STDIN_FILENO))
|
||||
tty_path = ttyname(STDIN_FILENO);
|
||||
else if (isatty(STDOUT_FILENO))
|
||||
tty_path = ttyname(STDOUT_FILENO);
|
||||
|
||||
if (tty_path) {
|
||||
if (strncmp(tty_path, "/dev/", 5) == 0)
|
||||
tty_name = tty_path + 5;
|
||||
else
|
||||
tty_name = tty_path;
|
||||
|
||||
for (char *x = tty_name; x && *x; x++) {
|
||||
if ((*x) >= '0' && (*x) <= '9') {
|
||||
tty_id = x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO remote login stuff
|
||||
|
||||
if (remote_host)
|
||||
origin = remote_host;
|
||||
else if (tty_name)
|
||||
origin = tty_name;
|
||||
else
|
||||
syslog(LOG_NOTICE, _("Couldn't determine origin: neither remote host nor TTY"));
|
||||
|
||||
pam_handle_t *pam = NULL;
|
||||
struct pam_conv conv = { .conv = pam_conv_handler, .appdata_ptr = NULL };
|
||||
|
||||
result = pam_start("login", NULL, &conv, &pam);
|
||||
result = pam_start(remote_host ? "remote" : "login", NULL, &conv, &pam);
|
||||
if (result != PAM_SUCCESS) {
|
||||
syslog(LOG_ERR, _("Couldn't initialize PAM: %s"), pam_strerror(pam, result));
|
||||
fprintf(stderr, _("pam_start error: %s. Abort.\n"), pam_strerror(pam, result));
|
||||
|
@ -115,30 +350,311 @@ int main(int argc, char* argv[])
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
result = pam_set_item(pam, PAM_RHOST, remote_host);
|
||||
if (result != PAM_SUCCESS) {
|
||||
syslog(LOG_ERR, _("Couldn't set PAM_RHOST: %s"), pam_strerror(pam, result));
|
||||
fprintf(stderr, _("pam_set_item error: %s. Abort.\n"), pam_strerror(pam, result));
|
||||
sleep(fail_delay);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (tty_path) {
|
||||
result = pam_set_item(pam, PAM_TTY, tty_path);
|
||||
if (result != PAM_SUCCESS) {
|
||||
syslog(LOG_ERR, _("Couldn't set PAM_TTY: %s"), pam_strerror(pam, result));
|
||||
fprintf(stderr, _("pam_set_item error: %s. Abort.\n"), pam_strerror(pam, result));
|
||||
sleep(fail_delay);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
ui_init();
|
||||
|
||||
// prompt for username
|
||||
ui_setup_dialog(_("Login name:"), false);
|
||||
result = ui_run();
|
||||
char *login_name = ui_get_text();
|
||||
if (result != 0)
|
||||
goto END;
|
||||
char *login_name;
|
||||
for (unsigned int try = 1; try <= max_tries; try++) {
|
||||
login_name = try_login(pam, try);
|
||||
if (login_name)
|
||||
break;
|
||||
}
|
||||
|
||||
result = pam_set_item(pam, PAM_USER, login_name);
|
||||
if (result != PAM_SUCCESS) {
|
||||
endwin();
|
||||
printf("pam_set_item error: %s\n", pam_strerror(pam, result));
|
||||
if (login_name == CANCEL_SENTINEL) {
|
||||
syslog(LOG_NOTICE, _("Login from '%s' aborted"), origin);
|
||||
ui_end();
|
||||
sleep(fail_delay);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (!login_name) {
|
||||
char *msg;
|
||||
// TRANSLATORS: default value is 5
|
||||
asprintf(&msg, _("Exceeded maximum login attempts (%u).\n"), max_tries);
|
||||
ui_setup_message(msg);
|
||||
ui_message_run();
|
||||
|
||||
sleep(fail_delay);
|
||||
ui_end();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
result = pam_authenticate(pam, 0);
|
||||
// Is user account valid?
|
||||
result = pam_acct_mgmt(pam, 0);
|
||||
if (result == PAM_NEW_AUTHTOK_REQD)
|
||||
result = pam_chauthtok(pam, PAM_CHANGE_EXPIRED_AUTHTOK);
|
||||
if (result != PAM_SUCCESS) {
|
||||
endwin();
|
||||
printf("pam_authenticate error: %s\n", pam_strerror(pam, result));
|
||||
syslog(LOG_ERR, _("User validation failed for '%s': %s"), login_name, pam_strerror(pam, result));
|
||||
|
||||
switch (result) {
|
||||
/*
|
||||
case PAM_ACCT_EXPIRED:
|
||||
asprintf(&msg, _("The user account '%s' has expired and cannot log in."), login_name);
|
||||
break;
|
||||
*/
|
||||
case PAM_AUTHTOK_ERR:
|
||||
case PAM_AUTHTOK_RECOVERY_ERR:
|
||||
case PAM_AUTHTOK_LOCK_BUSY:
|
||||
case PAM_AUTHTOK_DISABLE_AGING:
|
||||
// asprintf(&msg, _("Failed to update login credentials for '%s'."), login_name);
|
||||
ui_setup_message(_("Failed to update login credentials."));
|
||||
break;
|
||||
default:
|
||||
// asprintf(&msg, _("Cannot log in as '%s'. Is the user account valid?"), login_name);
|
||||
ui_setup_message(_("Cannot log in."));
|
||||
}
|
||||
ui_message_run();
|
||||
|
||||
sleep(fail_delay);
|
||||
ui_end();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
// ensure we have the correct login_name
|
||||
result = get_pam_login_name(pam, &login_name);
|
||||
if (result != PAM_SUCCESS) {
|
||||
syslog(LOG_ERR, _("Couldn't get PAM_USER: %s"), pam_strerror(pam, result));
|
||||
ui_setup_message(_("Cannot log in. The user account data is missing or invalid."));
|
||||
ui_message_run();
|
||||
|
||||
sleep(fail_delay);
|
||||
ui_end();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if ((!login_name) || login_name[0] == '\0') {
|
||||
syslog(LOG_ERR, _("NULL user name. Abort."));
|
||||
ui_setup_message(_("Cannot log in. The user account data is missing or invalid."));
|
||||
ui_message_run();
|
||||
|
||||
sleep(fail_delay);
|
||||
ui_end();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
END:
|
||||
endwin();
|
||||
struct passwd *pwd = getpwnam(login_name);
|
||||
if (!pwd) {
|
||||
syslog(LOG_ERR, _("No passwd entry for '%s'. Abort."), login_name);
|
||||
ui_setup_message(_("Cannot log in. The user account data is missing or invalid."));
|
||||
ui_message_run();
|
||||
|
||||
sleep(fail_delay);
|
||||
ui_end();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
result = (pwd->pw_uid != 0)
|
||||
? initgroups(login_name, pwd->pw_gid)
|
||||
: setgroups(0, NULL);
|
||||
if (result < 0) {
|
||||
syslog(LOG_ERR, _("groups initialization failed: %m"));
|
||||
ui_setup_message(_("Cannot log in. Unable to establish a session."));
|
||||
ui_message_run();
|
||||
|
||||
sleep(fail_delay);
|
||||
ui_end();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// begin PAM session
|
||||
result = pam_setcred(pam, PAM_ESTABLISH_CRED);
|
||||
if (result != PAM_SUCCESS) {
|
||||
syslog(LOG_ERR, _("Couldn't init PAM session: %s"), pam_strerror(pam, result));
|
||||
ui_setup_message(_("Cannot log in. Unable to establish a session."));
|
||||
ui_message_run();
|
||||
|
||||
sleep(fail_delay);
|
||||
ui_end();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
result = pam_open_session(pam, 0);
|
||||
if (result != PAM_SUCCESS) {
|
||||
pam_setcred(pam, PAM_DELETE_CRED);
|
||||
syslog(LOG_ERR, _("Couldn't init PAM session: %s"), pam_strerror(pam, result));
|
||||
ui_setup_message(_("Cannot log in. Unable to establish a session."));
|
||||
ui_message_run();
|
||||
|
||||
sleep(fail_delay);
|
||||
ui_end();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
result = pam_setcred(pam, PAM_REINITIALIZE_CRED);
|
||||
if (result != PAM_SUCCESS) {
|
||||
pam_close_session(pam, 0);
|
||||
syslog(LOG_ERR, _("Couldn't init PAM session: %s"), pam_strerror(pam, result));
|
||||
ui_setup_message(_("Cannot log in. Unable to establish a session."));
|
||||
ui_message_run();
|
||||
|
||||
sleep(fail_delay);
|
||||
ui_end();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
endpwent();
|
||||
ui_end();
|
||||
|
||||
log_utmp(login_name);
|
||||
// TODO log audit and lastlog
|
||||
syslog(LOG_INFO, _("LOGIN FROM '%s' BY '%s'"), origin, login_name);
|
||||
|
||||
// chown TTY
|
||||
if (tty_path) {
|
||||
// TODO set group as well
|
||||
if (chown(tty_path, pwd->pw_uid, -1) != 0)
|
||||
syslog(LOG_ERR, _("failed to chown TTY '%s': %m"), tty_path);
|
||||
}
|
||||
|
||||
if (setgid(pwd->pw_gid) < 0 && pwd->pw_gid) {
|
||||
syslog(LOG_ALERT, _("setgid() failed: %m"));
|
||||
fprintf(stderr, _("error applying user GID: %m. Abort.\n"));
|
||||
sleep(fail_delay);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if ((!pwd->pw_shell) || pwd->pw_shell[0] == '\0')
|
||||
pwd->pw_shell = _PATH_BSHELL;
|
||||
|
||||
init_env(false, pam, pwd);
|
||||
|
||||
// Get ready to fork!
|
||||
|
||||
// signal handlers - i think this is in case something comes in while we're forking?
|
||||
struct sigaction sa, prev_hup, prev_term;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
|
||||
signal(SIGALRM, SIG_DFL);
|
||||
signal(SIGQUIT, SIG_DFL);
|
||||
signal(SIGTSTP, SIG_IGN);
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
sigaction(SIGHUP, &sa, &prev_hup);
|
||||
|
||||
if (tty_path)
|
||||
ioctl(0, TIOCNOTTY, NULL);
|
||||
|
||||
sa.sa_handler = handle_sig;
|
||||
sigaction(SIGHUP, &sa, NULL);
|
||||
sigaction(SIGTERM, &sa, &prev_term);
|
||||
|
||||
closelog();
|
||||
|
||||
child_pid = fork();
|
||||
if (child_pid < 0) {
|
||||
fprintf(stderr, _("error forking: %m. Abort.\n"));
|
||||
openlog("hlogin", LOG_ODELAY, LOG_AUTHPRIV);
|
||||
syslog(LOG_ALERT, _("fork() failed: %m"));
|
||||
|
||||
pam_setcred(pam, PAM_DELETE_CRED);
|
||||
pam_end(pam, pam_close_session(pam, 0));
|
||||
|
||||
sleep(fail_delay);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (child_pid) { // we're in the parent
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigaction(SIGQUIT, &sa, NULL);
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
|
||||
while (wait(NULL) == -1 && errno == EINTR);
|
||||
openlog("hlogin", LOG_ODELAY, LOG_AUTHPRIV);
|
||||
|
||||
pam_setcred(pam, PAM_DELETE_CRED);
|
||||
pam_end(pam, pam_close_session(pam, 0));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// child
|
||||
|
||||
// reset signal handlers
|
||||
sigaction(SIGHUP, &prev_hup, NULL);
|
||||
sigaction(SIGTERM, &prev_term, NULL);
|
||||
if (got_sig)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
setsid();
|
||||
|
||||
// reopen tty
|
||||
if (tty_path) {
|
||||
int fd = open(tty_path, O_RDWR | O_NONBLOCK);
|
||||
if (fd == -1) {
|
||||
syslog(LOG_ERR, _("can't reopen tty: %m"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!isatty(fd)) {
|
||||
close(fd);
|
||||
syslog(LOG_ERR, _("%s is not a tty"), tty_path);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
|
||||
|
||||
// set tty as stdin, stdout, and stderr
|
||||
for (int i = 0; i < fd; i++)
|
||||
close(i);
|
||||
for (int i = 0; i < 3; i++)
|
||||
if (fd != i)
|
||||
dup2(fd, i);
|
||||
if (fd >= 3)
|
||||
close(fd);
|
||||
}
|
||||
openlog("hlogin", LOG_ODELAY, LOG_AUTHPRIV);
|
||||
|
||||
if (ioctl(0, TIOCSCTTY, 1) != 0)
|
||||
syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));
|
||||
|
||||
signal(SIGINT, SIG_DFL);
|
||||
|
||||
// continue becoming the user
|
||||
if (setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
|
||||
syslog(LOG_ALERT, _("setuid() failed: %m"));
|
||||
fprintf(stderr, _("error applying user UID: %m. Abort.\n"));
|
||||
sleep(fail_delay);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (chdir(pwd->pw_dir) < 0) {
|
||||
syslog(LOG_ALERT, _("failed to chdir to home (%s): %m"), pwd->pw_dir);
|
||||
fprintf(stderr, _("error changing to home directory: %m. Abort.\n"));
|
||||
sleep(fail_delay);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pam_end(pam, PAM_SUCCESS | PAM_DATA_SILENT);
|
||||
|
||||
// execute login shell
|
||||
char **shell_argv = calloc(2, sizeof(char *));
|
||||
char *shell_base = strrchr(pwd->pw_shell, '/');
|
||||
if (shell_base)
|
||||
shell_base++;
|
||||
else
|
||||
shell_base = pwd->pw_shell;
|
||||
|
||||
shell_argv[0] = malloc(sizeof(char) * (strlen(shell_base) + 2));
|
||||
shell_argv[0][0] = '-';
|
||||
strcpy(shell_argv[0] + 1, shell_base);
|
||||
|
||||
execvp(pwd->pw_shell, shell_argv);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
258
ui.c
258
ui.c
|
@ -55,6 +55,9 @@ PANEL *error_shadow_p;
|
|||
const int login_rows = 8;
|
||||
const int login_cols = 48;
|
||||
|
||||
int error_rows, error_cols;
|
||||
int error_curx;
|
||||
|
||||
char *input_buf;
|
||||
size_t input_buflen;
|
||||
int input_pos;
|
||||
|
@ -120,6 +123,22 @@ FAILURE:
|
|||
return unknown_str;
|
||||
}
|
||||
|
||||
void safe_free(void *ptr, size_t size)
|
||||
{
|
||||
memset(ptr, (int)'\0', size);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void *safe_realloc(void *ptr, size_t size, size_t new_size)
|
||||
{
|
||||
void *new_ptr = malloc(new_size);
|
||||
if (new_ptr == NULL)
|
||||
return NULL;
|
||||
memcpy(new_ptr, ptr, size);
|
||||
safe_free(ptr, size);
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
char *xgetnisdomainname()
|
||||
{
|
||||
|
@ -148,14 +167,20 @@ char erase_ch;
|
|||
|
||||
void ui_setup_dialog(char *prompt, bool password)
|
||||
{
|
||||
input_buf[0] = '\0';
|
||||
memset(input_buf, (int)'\0', sizeof(char) * input_buflen);
|
||||
input_pos = 0;
|
||||
input_scroll = 0;
|
||||
btn_focus = 0;
|
||||
btn_count = 2;
|
||||
|
||||
msg = prompt;
|
||||
hide_input = password;
|
||||
|
||||
hide_panel(error_p);
|
||||
hide_panel(error_shadow_p);
|
||||
show_panel(login_shadow_p);
|
||||
top_panel(login_p);
|
||||
|
||||
wclear(login_w);
|
||||
draw_outline(login_w, login_rows, login_cols, 0, 0, false);
|
||||
paint_login();
|
||||
|
@ -163,12 +188,56 @@ void ui_setup_dialog(char *prompt, bool password)
|
|||
|
||||
void ui_setup_message(char *text)
|
||||
{
|
||||
msg = text;
|
||||
// TODO insert word wrap logic here
|
||||
int msg_len = strlen(text);
|
||||
btn_focus = 0;
|
||||
btn_count = 1;
|
||||
|
||||
int tcols = login_cols - 4;
|
||||
int trows = 1;
|
||||
char **msg_lines = word_wrap(text, tcols, tcols / 2, &trows);
|
||||
|
||||
if (trows == 1)
|
||||
tcols = strlen(msg_lines[0]);
|
||||
if (tcols < 16)
|
||||
tcols = 16;
|
||||
|
||||
error_rows = trows + 4;
|
||||
error_cols = tcols + 4;
|
||||
|
||||
hide_panel(login_shadow_p);
|
||||
|
||||
wresize(error_w, error_rows, error_cols);
|
||||
wresize(error_shadow_w, error_rows, error_cols);
|
||||
|
||||
replace_panel(error_shadow_p, error_shadow_w);
|
||||
show_panel(error_shadow_p);
|
||||
move_panel(error_shadow_p, ((term_rows - error_rows) / 2) + 1, ((term_cols - error_cols) / 2) + 1);
|
||||
|
||||
replace_panel(error_p, error_w);
|
||||
show_panel(error_p);
|
||||
move_panel(error_p, (term_rows - error_rows) / 2, (term_cols - error_cols) / 2);
|
||||
|
||||
wbkgd(error_shadow_w, ' ' | COLOR_PAIR(4));
|
||||
wclear(error_shadow_w);
|
||||
touchwin(error_shadow_w);
|
||||
|
||||
wclear(error_w);
|
||||
draw_outline(error_w, error_rows, error_cols, 0, 0, false);
|
||||
|
||||
// draw text
|
||||
wattron(error_w, COLOR_PAIR(2));
|
||||
for (int i = 0; i < trows; i++) {
|
||||
wmove(error_w, i + 1, 2);
|
||||
waddstr(error_w, msg_lines[i]);
|
||||
free(msg_lines[i]);
|
||||
}
|
||||
free(msg_lines);
|
||||
|
||||
// draw button
|
||||
wmove(error_w, error_rows - 2, 2);
|
||||
wattron(error_w, A_REVERSE);
|
||||
wprintw(error_w, "< %s >", _("OK"));
|
||||
error_curx = error_w->_curx - 2;
|
||||
wattroff(error_w, A_REVERSE);
|
||||
}
|
||||
|
||||
/** ui_init - initialize the UI subsystem
|
||||
|
@ -201,7 +270,7 @@ void ui_init()
|
|||
init_pair(1, COLOR_WHITE, COLOR_BLUE);
|
||||
init_pair(2, COLOR_BLACK, COLOR_WHITE);
|
||||
init_pair(3, COLOR_WHITE, COLOR_WHITE);
|
||||
init_pair(4, COLOR_RED, COLOR_WHITE);
|
||||
init_pair(4, COLOR_BLACK, COLOR_BLACK);
|
||||
|
||||
// set up background window
|
||||
back_w = newwin(term_rows, term_cols, 0, 0);
|
||||
|
@ -214,10 +283,10 @@ void ui_init()
|
|||
// set up login shadow window
|
||||
login_shadow_w = newwin(login_rows, login_cols, ((term_rows - login_rows) / 2) + 1, ((term_cols - login_cols) / 2) + 1);
|
||||
login_shadow_p = new_panel(login_shadow_w);
|
||||
wbkgd(login_shadow_w, ' ' | COLOR_PAIR(0));
|
||||
wbkgd(login_shadow_w, ' ' | COLOR_PAIR(4));
|
||||
wclear(login_shadow_w);
|
||||
|
||||
// set up dialog window
|
||||
// set up login window
|
||||
login_w = newwin(login_rows, login_cols, (term_rows - login_rows) / 2, (term_cols - login_cols) / 2);
|
||||
login_p = new_panel(login_w);
|
||||
wattron(login_w, COLOR_PAIR(2));
|
||||
|
@ -226,8 +295,42 @@ void ui_init()
|
|||
draw_outline(login_w, login_rows, login_cols, 0, 0, false);
|
||||
paint_login();
|
||||
|
||||
// set up error shadow window
|
||||
error_shadow_w = newwin(2, 2, 1, 1);
|
||||
error_shadow_p = new_panel(error_shadow_w);
|
||||
wbkgd(error_shadow_w, ' ' | COLOR_PAIR(4));
|
||||
wclear(error_shadow_w);
|
||||
hide_panel(error_shadow_p);
|
||||
|
||||
// set up error window
|
||||
error_w = newwin(1, 1, 0, 0);
|
||||
error_p = new_panel(error_w);
|
||||
wbkgd(error_w, ' ' | COLOR_PAIR(2));
|
||||
wclear(error_w);
|
||||
hide_panel(error_p);
|
||||
|
||||
// enable keypad mode
|
||||
keypad(login_w, true);
|
||||
keypad(error_w, true);
|
||||
}
|
||||
|
||||
void ui_end()
|
||||
{
|
||||
safe_free(input_buf, input_buflen);
|
||||
|
||||
del_panel(back_p);
|
||||
del_panel(login_p);
|
||||
del_panel(login_shadow_p);
|
||||
del_panel(error_p);
|
||||
del_panel(error_shadow_p);
|
||||
|
||||
delwin(back_w);
|
||||
delwin(login_w);
|
||||
delwin(login_shadow_w);
|
||||
delwin(error_w);
|
||||
delwin(error_shadow_w);
|
||||
|
||||
endwin();
|
||||
}
|
||||
|
||||
/** paint_back - redraw info displayed on background
|
||||
|
@ -327,10 +430,55 @@ int ui_update()
|
|||
return -1;
|
||||
}
|
||||
|
||||
int ui_error_update()
|
||||
{
|
||||
paint_back();
|
||||
wmove(error_w, error_rows - 2, error_curx);
|
||||
|
||||
update_panels();
|
||||
doupdate();
|
||||
|
||||
int ch = wgetch(error_w);
|
||||
if (ch == ERR)
|
||||
return -1;
|
||||
|
||||
if (ch == 0x1B)
|
||||
return -2;
|
||||
else if (ch == KEY_ENTER || ch == '\r' || ch == '\n')
|
||||
return btn_focus;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ui_run()
|
||||
{
|
||||
int result;
|
||||
while ((result = ui_update()) == -1);
|
||||
btn_focus = -1;
|
||||
paint_back();
|
||||
paint_login();
|
||||
update_panels();
|
||||
doupdate();
|
||||
return result;
|
||||
}
|
||||
|
||||
int ui_message_run()
|
||||
{
|
||||
int result;
|
||||
while ((result = ui_error_update()) == -1);
|
||||
btn_focus = -1;
|
||||
paint_back();
|
||||
wmove(error_w, error_rows - 2, 2);
|
||||
wattron(error_w, COLOR_PAIR(2));
|
||||
wprintw(error_w, "< %s >", _("OK"));
|
||||
wmove(error_w, error_rows - 2, error_curx);
|
||||
update_panels();
|
||||
doupdate();
|
||||
hide_panel(error_p);
|
||||
hide_panel(error_shadow_p);
|
||||
show_panel(login_shadow_p);
|
||||
top_panel(login_p);
|
||||
clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -339,6 +487,7 @@ char *ui_get_text()
|
|||
unsigned int len = strlen(input_buf) + 1;
|
||||
char *buf = malloc(len * sizeof(char));
|
||||
strcpy(buf, input_buf);
|
||||
memset(input_buf, (int)'\0', input_buflen * sizeof(char));
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
@ -346,8 +495,8 @@ void input_add_char(char ch)
|
|||
{
|
||||
// resize if necessary
|
||||
if ((strlen(input_buf) + 2) > input_buflen) {
|
||||
input_buf = safe_realloc(input_buf, input_buflen * sizeof(char), input_buflen * 2 * sizeof(char));
|
||||
input_buflen *= 2;
|
||||
input_buf = realloc(input_buf, input_buflen * sizeof(char));
|
||||
}
|
||||
// insert new char & shift everything over
|
||||
for (int i = (input_pos++); i < input_buflen; i++) {
|
||||
|
@ -412,46 +561,37 @@ void draw_outline(WINDOW *win, int height, int width, int y, int x, bool inset)
|
|||
/** output_word - used in word_wrap
|
||||
*/
|
||||
void output_word(char *word, size_t len,
|
||||
char **out, size_t *out_size, int *out_pos,
|
||||
int *line_pos, int *lines, int width)
|
||||
char ***out, int *line_pos, int *lines, int width)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < len) {
|
||||
int space = width - (*line_pos);
|
||||
int remain = len - i;
|
||||
if (space > remain) { // can fit on line
|
||||
if ((*out_size) < (*out_pos) + remain) {
|
||||
*out_size *= 2;
|
||||
*out = realloc(*out, sizeof(char) * (*out_size));
|
||||
}
|
||||
// copy word
|
||||
memcpy((*out) + (*out_pos), word + i, sizeof(char) * remain);
|
||||
memcpy((*out)[(*lines) - 1] + (*line_pos), word + i, sizeof(char) * remain);
|
||||
i += remain;
|
||||
*out_pos += remain;
|
||||
*line_pos += remain;
|
||||
} else {
|
||||
if ((*out_size) < (*out_pos) + space + 1) {
|
||||
*out_size *= 2;
|
||||
*out = realloc(*out, sizeof(char) * (*out_size));
|
||||
}
|
||||
// copy part of word
|
||||
memcpy((*out) + (*out_pos), word + i, sizeof(char) * space);
|
||||
memcpy((*out)[(*lines) - 1] + (*line_pos), word + i, sizeof(char) * space);
|
||||
i += space;
|
||||
*out_pos += space;
|
||||
*line_pos += space;
|
||||
// add newline
|
||||
(*out)[(*out_pos)++] = '\n';
|
||||
(*out)[(*lines) - 1][(*line_pos)] = '\0';
|
||||
*line_pos = 0;
|
||||
(*lines)++;
|
||||
*out = realloc(*out, sizeof(char *) * (*lines));
|
||||
(*out)[(*lines) - 1] = malloc(sizeof(char) * (width + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *word_wrap(char *text, int width, int max_word, int *lines)
|
||||
char **word_wrap(char *text, int width, int max_word, int *lines)
|
||||
{
|
||||
// output buffer
|
||||
size_t out_size = 256;
|
||||
char *out = malloc(sizeof(char) * out_size);
|
||||
int out_pos = 0;
|
||||
// output lines
|
||||
*lines = 1;
|
||||
char **out = malloc(sizeof(char *) * (*lines));
|
||||
out[0] = malloc(sizeof(char) * (width + 1));
|
||||
// word buffer
|
||||
char *word = malloc(sizeof(char) * max_word);
|
||||
int word_len = 0;
|
||||
|
@ -464,31 +604,28 @@ char *word_wrap(char *text, int width, int max_word, int *lines)
|
|||
case ' ':
|
||||
if (word[0] == '\0')
|
||||
word_len = 0;
|
||||
// ensure buffer is large enough
|
||||
if (out_size < out_pos + word_len + 2) {
|
||||
out_size *= 2;
|
||||
out = realloc(out, sizeof(char) * out_size);
|
||||
}
|
||||
if (word_len > 0) {
|
||||
// do we have to wrap to a new line?
|
||||
if (line_pos + word_len > width) {
|
||||
out[out_pos++] = '\n';
|
||||
out[(*lines) - 1][line_pos] = '\0';
|
||||
line_pos = 0;
|
||||
(*lines)++;
|
||||
out = realloc(out, sizeof(char *) * (*lines));
|
||||
out[(*lines) - 1] = malloc(sizeof(char) * (width + 1));
|
||||
}
|
||||
memcpy(out + out_pos, word, sizeof(char) * word_len);
|
||||
word_len = 0;
|
||||
out_pos += word_len;
|
||||
memcpy(out[(*lines) - 1] + line_pos, word, sizeof(char) * word_len);
|
||||
line_pos += word_len;
|
||||
word_len = 0;
|
||||
}
|
||||
// output \n
|
||||
if (ch == '\n' || line_pos >= width) {
|
||||
out[out_pos++] = '\n';
|
||||
out[(*lines) - 1][line_pos] = '\0';
|
||||
line_pos = 0;
|
||||
(*lines)++;
|
||||
out = realloc(out, sizeof(char *) * (*lines));
|
||||
out[(*lines) - 1] = malloc(sizeof(char) * (width + 1));
|
||||
} else {
|
||||
out[out_pos++] = ' ';
|
||||
line_pos++;
|
||||
out[(*lines) - 1][line_pos++] = ' ';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -497,26 +634,45 @@ char *word_wrap(char *text, int width, int max_word, int *lines)
|
|||
if (word[0] != '\0') {
|
||||
// write what we have
|
||||
output_word(word, word_len,
|
||||
&out, &out_size, &out_pos,
|
||||
&line_pos, lines, width);
|
||||
&out, &line_pos, lines, width);
|
||||
word[0] = '\0';
|
||||
}
|
||||
// ensure buffer is large enough
|
||||
if (out_size < out_pos + 2) {
|
||||
out_size *= 2;
|
||||
out = realloc(out, sizeof(char) * out_size);
|
||||
}
|
||||
// write char and maybe newline
|
||||
out[out_pos++] = ch;
|
||||
if ((++line_pos) >= width) {
|
||||
out[out_pos++] = '\n';
|
||||
out[(*lines) - 1][line_pos++] = ch;
|
||||
if (line_pos >= width) {
|
||||
out[(*lines) - 1][line_pos] = '\0';
|
||||
line_pos = 0;
|
||||
(*lines)++;
|
||||
out = realloc(out, sizeof(char *) * (*lines));
|
||||
out[(*lines) - 1] = malloc(sizeof(char) * (width + 1));
|
||||
}
|
||||
} else
|
||||
word[word_len++] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
if (word_len > 0 && word[0] != '\0') {
|
||||
// do we have to wrap to a new line?
|
||||
if (line_pos + word_len > width) {
|
||||
out[(*lines) - 1][line_pos] = '\0';
|
||||
line_pos = 0;
|
||||
(*lines)++;
|
||||
out = realloc(out, sizeof(char *) * (*lines));
|
||||
out[(*lines) - 1] = malloc(sizeof(char) * (width + 1));
|
||||
}
|
||||
memcpy(out[(*lines) - 1] + line_pos, word, sizeof(char) * word_len);
|
||||
line_pos += word_len;
|
||||
word_len = 0;
|
||||
}
|
||||
|
||||
free(word);
|
||||
|
||||
if (line_pos == 0)
|
||||
// remove last line
|
||||
free(out[--(*lines)]);
|
||||
else
|
||||
// non-empty line, add null terminator
|
||||
out[(*lines) - 1][line_pos] = '\0';
|
||||
|
||||
return out;
|
||||
}
|
||||
|
|
7
ui.h
7
ui.h
|
@ -27,7 +27,11 @@
|
|||
|
||||
extern int term_rows, term_cols;
|
||||
|
||||
void *safe_realloc(void *ptr, size_t size, size_t new_size);
|
||||
void safe_free(void *ptr, size_t size);
|
||||
|
||||
void ui_init();
|
||||
void ui_end();
|
||||
|
||||
void paint_back();
|
||||
void paint_login();
|
||||
|
@ -37,6 +41,7 @@ void ui_setup_message(char *text);
|
|||
|
||||
int ui_update();
|
||||
int ui_run();
|
||||
int ui_message_run();
|
||||
char *ui_get_text();
|
||||
|
||||
void input_add_char(char ch);
|
||||
|
@ -45,6 +50,6 @@ void input_del_char();
|
|||
|
||||
void draw_outline(WINDOW *win, int height, int width, int y, int x, bool inset);
|
||||
|
||||
char *word_wrap(char *text, int width, int max_word, int *lines);
|
||||
char **word_wrap(char *text, int width, int max_word, int *lines);
|
||||
|
||||
#endif // __UI_H
|
||||
|
|
Loading…
Reference in a new issue