661 lines
16 KiB
C
661 lines
16 KiB
C
/*
|
|
* hlogin(1)
|
|
*
|
|
* Copyleft (C) 2022 ~keith <keith@keithhacks.cyou>
|
|
*
|
|
* This file is part of hlogin.
|
|
*
|
|
* hlogin is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation, version 3.
|
|
*
|
|
* hlogin is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*
|
|
* 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>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
|
|
#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>
|
|
#include <security/pam_appl.h>
|
|
#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"
|
|
|
|
int pam_conv_handler(int msgc, const struct pam_message *msgv[], struct pam_response **resp, void *appdata_ptr)
|
|
{
|
|
if (msgc <= 0)
|
|
return PAM_CONV_ERR;
|
|
|
|
struct pam_response *result = calloc(msgc, sizeof(struct pam_response));
|
|
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();
|
|
if (r != 0) {
|
|
_pam_overwrite(text);
|
|
free(text);
|
|
text = NULL;
|
|
}
|
|
result[i].resp_retcode = 0;
|
|
result[i].resp = text;
|
|
break;
|
|
case PAM_ERROR_MSG:
|
|
case PAM_TEXT_INFO:
|
|
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;
|
|
|
|
FAILURE:
|
|
if (result) {
|
|
for (int i = 0; i < msgc; i++) {
|
|
if (result[i].resp) {
|
|
_pam_overwrite(result[i].resp);
|
|
free(result[i].resp);
|
|
}
|
|
}
|
|
free(result);
|
|
result = NULL;
|
|
}
|
|
return PAM_CONV_ERR;
|
|
}
|
|
|
|
/** 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[])
|
|
{
|
|
int result;
|
|
|
|
setlocale(LC_ALL, "");
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
textdomain(PACKAGE);
|
|
|
|
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(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));
|
|
sleep(fail_delay);
|
|
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();
|
|
|
|
char *login_name;
|
|
for (unsigned int try = 1; try <= max_tries; try++) {
|
|
login_name = try_login(pam, try);
|
|
if (login_name)
|
|
break;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// 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) {
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|