mirror of https://github.com/fairyglade/ly
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
457 lines
8.5 KiB
C
457 lines
8.5 KiB
C
#define _XOPEN_SOURCE 700
|
|
#define _DEFAULT_SOURCE
|
|
|
|
#include "config.h"
|
|
#include "login.h"
|
|
#include "widgets.h"
|
|
#include "desktop.h"
|
|
#include "termbox.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <grp.h>
|
|
#include <pwd.h>
|
|
#include <signal.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <sys/wait.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <security/pam_appl.h>
|
|
#include <security/pam_misc.h>
|
|
|
|
#ifdef LY_SYSTEMD
|
|
#define PAM_SYSTEMD
|
|
#endif
|
|
|
|
#include <security/pam_modules.h>
|
|
#include <security/pam_modutil.h>
|
|
|
|
int login_conv(int num_msg, const struct pam_message** msg,
|
|
struct pam_response** resp, void* appdata_ptr)
|
|
{
|
|
int i;
|
|
int result = PAM_SUCCESS;
|
|
|
|
if(!(*resp = calloc(num_msg, sizeof(struct pam_response))))
|
|
{
|
|
return PAM_BUF_ERR;
|
|
}
|
|
|
|
for(i = 0; i < num_msg; i++)
|
|
{
|
|
char* username, *password;
|
|
|
|
switch(msg[i]->msg_style)
|
|
{
|
|
case PAM_PROMPT_ECHO_ON:
|
|
username = ((char**) appdata_ptr)[0];
|
|
(*resp)[i].resp = strdup(username);
|
|
break;
|
|
|
|
case PAM_PROMPT_ECHO_OFF:
|
|
password = ((char**) appdata_ptr)[1];
|
|
(*resp)[i].resp = strdup(password);
|
|
break;
|
|
|
|
case PAM_ERROR_MSG:
|
|
fprintf(stderr, "%s\n", msg[i]->msg);
|
|
result = PAM_CONV_ERR;
|
|
break;
|
|
|
|
case PAM_TEXT_INFO:
|
|
printf("%s\n", msg[i]->msg);
|
|
break;
|
|
}
|
|
|
|
if(result != PAM_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(result != PAM_SUCCESS)
|
|
{
|
|
free(*resp);
|
|
*resp = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int get_free_display()
|
|
{
|
|
int i;
|
|
char xlock[256];
|
|
|
|
for(i = 0; i < 200; ++i)
|
|
{
|
|
snprintf(xlock, sizeof(xlock), "/tmp/.X%d-lock", i);
|
|
|
|
if(access(xlock, F_OK) == -1)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
enum err init_env(pam_handle_t* handle, struct passwd* pw)
|
|
{
|
|
u16 i;
|
|
u16 len;
|
|
char tmp[256];
|
|
char** env;
|
|
char* termenv;
|
|
|
|
termenv = getenv("TERM");
|
|
setenv("HOME", pw->pw_dir, 0);
|
|
setenv("USER", pw->pw_name, 1);
|
|
setenv("SHELL", pw->pw_shell, 1);
|
|
setenv("LOGNAME", pw->pw_name, 1);
|
|
|
|
|
|
if (termenv)
|
|
{
|
|
setenv("TERM", termenv, 1);
|
|
}
|
|
else
|
|
{
|
|
setenv("TERM", "linux", 1);
|
|
}
|
|
|
|
snprintf(tmp, sizeof(tmp), "%s/%s", pw->pw_dir, config.xauthority);
|
|
setenv("XAUTHORITY", tmp, 0);
|
|
len = snprintf(tmp, sizeof(tmp), "%s/%s", _PATH_MAILDIR, pw->pw_name);
|
|
|
|
if ((len > 0) && ((size_t) len < sizeof(tmp)))
|
|
{
|
|
setenv("MAIL", tmp, 0);
|
|
}
|
|
|
|
if (setenv("PATH", config.path, 1))
|
|
{
|
|
return ERR;
|
|
}
|
|
|
|
env = pam_getenvlist(handle);
|
|
|
|
for (i = 0; env && env[i]; i++)
|
|
{
|
|
putenv(env[i]);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
void init_xdg(const char* tty_id, const char* display_name,
|
|
enum display_server display_server)
|
|
{
|
|
setenv("XDG_SESSION_CLASS", "user", 0);
|
|
setenv("XDG_SEAT", "seat0", 0);
|
|
setenv("XDG_VTNR", tty_id, 0);
|
|
setenv("DISPLAY", display_name, 1);
|
|
|
|
switch(display_server)
|
|
{
|
|
case DS_SHELL:
|
|
setenv("XDG_SESSION_TYPE", "tty", 0);
|
|
break;
|
|
case DS_WAYLAND:
|
|
setenv("XDG_SESSION_TYPE", "wayland", 0);
|
|
break;
|
|
case DS_XINITRC:
|
|
case DS_XORG:
|
|
setenv("XDG_SESSION_TYPE", "x11", 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void reset_terminal(struct passwd* pwd)
|
|
{
|
|
pid_t pid;
|
|
int status;
|
|
char cmd[256];
|
|
|
|
pid = fork();
|
|
strncpy(cmd, "exec tput reset", sizeof(cmd));
|
|
|
|
if (pid == 0)
|
|
{
|
|
execl(pwd->pw_shell, pwd->pw_shell, "-c", cmd, NULL);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
waitpid(pid, &status, 0);
|
|
}
|
|
|
|
void launch_wayland(struct passwd* pwd, pam_handle_t* pam_handle,
|
|
const char* de_command)
|
|
{
|
|
char cmd[32];
|
|
snprintf(cmd, 32, "exec %s", de_command);
|
|
reset_terminal(pwd);
|
|
execl(pwd->pw_shell, pwd->pw_shell, "-l", "-c", cmd, NULL);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
void launch_shell(struct passwd* pwd, pam_handle_t* pam_handle)
|
|
{
|
|
char* pos;
|
|
char args[256 + 2]; // arbitrary
|
|
args[0] = '-';
|
|
strncpy(args + 1, ((pos = strrchr(pwd->pw_shell,
|
|
'/')) ? pos + 1 : pwd->pw_shell), sizeof(args) - 1);
|
|
reset_terminal(pwd);
|
|
execl(pwd->pw_shell, args, NULL);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
void launch_xorg(struct passwd* pwd, pam_handle_t* pam_handle,
|
|
const char* de_command, const char* display_name, const char* vt,
|
|
int xinitrc)
|
|
{
|
|
FILE* file;
|
|
pid_t child;
|
|
int status;
|
|
char cmd[256];
|
|
|
|
// updates cookie
|
|
snprintf(cmd,
|
|
sizeof(cmd),
|
|
"exec xauth add %s . `%s`",
|
|
display_name,
|
|
config.mcookie_cmd);
|
|
|
|
file = fopen(getenv("XAUTHORITY"), "ab+");
|
|
fclose(file);
|
|
|
|
// generates the cookie
|
|
child = fork();
|
|
|
|
if(child == 0)
|
|
{
|
|
execl(pwd->pw_shell, pwd->pw_shell, "-c", cmd, NULL);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
waitpid(child, &status, 0);
|
|
|
|
// starts x
|
|
snprintf(cmd, sizeof(cmd),
|
|
"exec xinit %s %s%s -- %s %s %s %s %s %s -auth %s",
|
|
config.x_cmd_setup,
|
|
xinitrc ? "" : "/usr/bin/",
|
|
de_command, config.x_cmd,
|
|
config.hide_x ? "-keeptty >" : "",
|
|
config.hide_x ? config.hide_x_save_log : "",
|
|
config.hide_x ? "2>&1" : "",
|
|
display_name, vt, getenv("XAUTHORITY"));
|
|
reset_terminal(pwd);
|
|
execl(pwd->pw_shell, pwd->pw_shell, "-l", "-c", cmd, NULL);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
enum err login_desktop(struct desktop* desktop,
|
|
struct input* login,
|
|
struct input* password)
|
|
{
|
|
int display_id;
|
|
char display_name[3];
|
|
pid_t display_pid;
|
|
int display_status;
|
|
|
|
const char* creds[2] = {login->text, password->text};
|
|
struct pam_conv conv = {login_conv, creds};
|
|
struct passwd* pwd = NULL;
|
|
pam_handle_t* handle;
|
|
int pam_result;
|
|
|
|
enum display_server display_server = desktop->display_server[desktop->cur];
|
|
char tty_id [3];
|
|
char vt[5];
|
|
|
|
display_id = get_free_display();
|
|
snprintf(display_name, sizeof(display_name), ":%d", display_id);
|
|
snprintf(tty_id, sizeof(tty_id), "%d", config.tty_id);
|
|
snprintf(vt, sizeof(vt), "vt%d", config.tty_id);
|
|
|
|
// starting pam transations
|
|
pam_result = pam_start(config.service_name, login->text, &conv, &handle);
|
|
|
|
if (pam_result != PAM_SUCCESS)
|
|
{
|
|
pam_diagnose(pam_result);
|
|
pam_end(handle, pam_result);
|
|
return ERR;
|
|
}
|
|
|
|
pam_result = pam_authenticate(handle, 0);
|
|
|
|
if (pam_result != PAM_SUCCESS)
|
|
{
|
|
pam_diagnose(pam_result);
|
|
pam_end(handle, pam_result);
|
|
return ERR;
|
|
}
|
|
|
|
pam_result = pam_acct_mgmt(handle, 0);
|
|
|
|
if (pam_result != PAM_SUCCESS)
|
|
{
|
|
pam_diagnose(pam_result);
|
|
pam_end(handle, pam_result);
|
|
return ERR;
|
|
}
|
|
|
|
// initializes user groups
|
|
struct passwd* pw = getpwnam(login->text);
|
|
|
|
if (!pw)
|
|
{
|
|
pam_end(handle, pam_result);
|
|
return ERR;
|
|
}
|
|
|
|
int grp_result = initgroups(login->text, pw->pw_gid);
|
|
|
|
if (grp_result == -1)
|
|
{
|
|
pam_end(handle, pam_result);
|
|
return ERR;
|
|
}
|
|
|
|
// back to pam transactions
|
|
pam_result = pam_setcred(handle, PAM_ESTABLISH_CRED);
|
|
|
|
if (pam_result != PAM_SUCCESS)
|
|
{
|
|
pam_diagnose(pam_result);
|
|
pam_end(handle, pam_result);
|
|
return ERR;
|
|
}
|
|
|
|
pam_result = pam_open_session(handle, 0);
|
|
|
|
if (pam_result != PAM_SUCCESS)
|
|
{
|
|
pam_diagnose(pam_result);
|
|
pam_end(handle, pam_result);
|
|
return ERR;
|
|
}
|
|
|
|
// login error
|
|
if (handle == NULL)
|
|
{
|
|
return ERR;
|
|
}
|
|
|
|
pwd = getpwnam(login->text);
|
|
|
|
// clears the password in memory
|
|
widget_input_free(password);
|
|
widget_input(password, config.max_password_len);
|
|
|
|
// launches the DE
|
|
display_pid = fork();
|
|
|
|
if (display_pid == 0)
|
|
{
|
|
// restores regular terminal mode
|
|
// doing this here to enable post-return cleanup
|
|
tb_clear();
|
|
tb_present();
|
|
tb_shutdown();
|
|
|
|
// initialization
|
|
clearenv();
|
|
init_xdg(tty_id, display_name, display_server);
|
|
|
|
// downgrades group permissions
|
|
if ((pwd == NULL) || (setgid(pwd->pw_gid) < 0))
|
|
{
|
|
set_error(ERR_PERM_GROUP);
|
|
pam_end(handle, pam_result);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
init_env(handle, pwd);
|
|
|
|
// downgrades user permissions
|
|
if (setuid(pwd->pw_uid) < 0)
|
|
{
|
|
set_error(ERR_PERM_USER);
|
|
pam_end(handle, pam_result);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (chdir(pwd->pw_dir) < 0)
|
|
{
|
|
set_error(ERR_PERM_DIR);
|
|
pam_end(handle, pam_result);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
switch (display_server)
|
|
{
|
|
case DS_SHELL:
|
|
launch_shell(pwd, handle);
|
|
break;
|
|
case DS_WAYLAND:
|
|
launch_wayland(pwd, handle, desktop->cmd[desktop->cur]);
|
|
break;
|
|
case DS_XORG:
|
|
launch_xorg(pwd, handle, desktop->cmd[desktop->cur],
|
|
display_name, vt, 0);
|
|
break;
|
|
case DS_XINITRC:
|
|
launch_xorg(pwd, handle, desktop->cmd[desktop->cur],
|
|
display_name, vt, 1);
|
|
break;
|
|
}
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
// waits for the de/shell to exit
|
|
waitpid(display_pid, &display_status, 0);
|
|
|
|
tb_init();
|
|
tb_select_output_mode(TB_OUTPUT_TRUECOLOR);
|
|
|
|
// reloads the desktop environment list on logout
|
|
widget_desktop_free(desktop);
|
|
widget_desktop(desktop);
|
|
desktop_load(desktop);
|
|
|
|
info_line = lang.logout;
|
|
pam_result = pam_close_session(handle, 0);
|
|
|
|
if (pam_result != PAM_SUCCESS)
|
|
{
|
|
pam_diagnose(pam_result);
|
|
pam_end(handle, pam_result);
|
|
return ERR;
|
|
}
|
|
|
|
pam_result = pam_setcred(handle, PAM_DELETE_CRED);
|
|
|
|
if (pam_result != PAM_SUCCESS)
|
|
{
|
|
pam_diagnose(pam_result);
|
|
pam_end(handle, pam_result);
|
|
return ERR;
|
|
}
|
|
|
|
pam_result = pam_end(handle, pam_result);
|
|
|
|
if (pam_result != PAM_SUCCESS)
|
|
{
|
|
pam_diagnose(pam_result);
|
|
pam_end(handle, pam_result);
|
|
return ERR;
|
|
}
|
|
|
|
return OK;
|
|
}
|