mirror of https://github.com/fairyglade/ly.git
620 lines
12 KiB
C
620 lines
12 KiB
C
#define _XOPEN_SOURCE 700
|
|
#define _DEFAULT_SOURCE
|
|
|
|
/* std lib */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
/* linux */
|
|
#include <sys/wait.h>
|
|
#include <paths.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <linux/limits.h>
|
|
#include <grp.h>
|
|
#include <pwd.h>
|
|
#include <signal.h>
|
|
#include <X11/Xlib.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
/* ncurses */
|
|
#include <form.h>
|
|
/* pam */
|
|
#include <security/pam_appl.h>
|
|
#include <security/pam_misc.h>
|
|
#define PAM_SYSTEMD
|
|
#include <security/pam_modules.h>
|
|
#include <security/pam_modutil.h>
|
|
/* ly */
|
|
#include "lang.h"
|
|
#include "config.h"
|
|
#include "utils.h"
|
|
#include "login.h"
|
|
#include "desktop.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 start_env(const char* username, const char* password,
|
|
const char* de_command, enum deserv_t display_server)
|
|
{
|
|
pid_t pid_display;
|
|
int display_status;
|
|
/* login info */
|
|
int pam_result;
|
|
const char* creds[2] = {username, password};
|
|
struct pam_conv conv = {login_conv, creds};
|
|
struct passwd* pwd = NULL;
|
|
pam_handle_t* login_handle;
|
|
/* session info */
|
|
int display_id;
|
|
char display_name[3];
|
|
char tty_id[3];
|
|
char vt[5];
|
|
/* generates console and display id and updates the environment */
|
|
destroy_env();
|
|
display_id = get_free_display();
|
|
snprintf(display_name, sizeof(display_name), ":%d", display_id);
|
|
snprintf(tty_id, sizeof(tty_id), "%d", LY_CONSOLE_TTY);
|
|
snprintf(vt, sizeof(vt), "vt%d", LY_CONSOLE_TTY);
|
|
init_xdg(tty_id, display_name, display_server);
|
|
/* pam_start and error handling */
|
|
pam_result = pam_start(LY_SERVICE_NAME, username, &conv, &login_handle);
|
|
|
|
if(pam_result != PAM_SUCCESS)
|
|
{
|
|
switch(pam_result)
|
|
{
|
|
case PAM_BUF_ERR :
|
|
error_print(LY_ERR_PAM_BUF);
|
|
break;
|
|
|
|
case PAM_SYSTEM_ERR :
|
|
error_print(LY_ERR_PAM_SYSTEM);
|
|
break;
|
|
|
|
case PAM_ABORT :
|
|
default:
|
|
error_print(LY_ERR_PAM_ABORT);
|
|
break;
|
|
}
|
|
|
|
pam_end(login_handle, pam_result);
|
|
return 1;
|
|
}
|
|
|
|
/* pam_authenticate and error handling */
|
|
pam_result = pam_authenticate(login_handle, 0);
|
|
|
|
if(pam_result != PAM_SUCCESS)
|
|
{
|
|
switch(pam_result)
|
|
{
|
|
case PAM_AUTH_ERR :
|
|
error_print(LY_ERR_PAM_AUTH);
|
|
break;
|
|
|
|
case PAM_CRED_INSUFFICIENT :
|
|
error_print(LY_ERR_PAM_CRED_INSUFFICIENT);
|
|
break;
|
|
|
|
case PAM_AUTHINFO_UNAVAIL :
|
|
error_print(LY_ERR_PAM_AUTHINFO_UNAVAIL);
|
|
break;
|
|
|
|
case PAM_MAXTRIES :
|
|
error_print(LY_ERR_PAM_MAXTRIES);
|
|
break;
|
|
|
|
case PAM_USER_UNKNOWN :
|
|
error_print(LY_ERR_PAM_USER_UNKNOWN);
|
|
break;
|
|
|
|
case PAM_ABORT :
|
|
default:
|
|
error_print(LY_ERR_PAM_ABORT);
|
|
break;
|
|
}
|
|
|
|
pam_end(login_handle, pam_result);
|
|
return 1;
|
|
}
|
|
|
|
/* pam_acct_mgmt and error handling */
|
|
pam_result = pam_acct_mgmt(login_handle, 0);
|
|
|
|
if(pam_result != PAM_SUCCESS)
|
|
{
|
|
switch(pam_result)
|
|
{
|
|
case PAM_ACCT_EXPIRED :
|
|
error_print(LY_ERR_PAM_ACCT_EXPIRED);
|
|
break;
|
|
|
|
case PAM_AUTH_ERR :
|
|
error_print(LY_ERR_PAM_AUTH);
|
|
break;
|
|
|
|
case PAM_NEW_AUTHTOK_REQD :
|
|
error_print(LY_ERR_PAM_NEW_AUTHTOK_REQD);
|
|
break;
|
|
|
|
case PAM_PERM_DENIED :
|
|
error_print(LY_ERR_PAM_PERM_DENIED);
|
|
break;
|
|
|
|
case PAM_USER_UNKNOWN :
|
|
error_print(LY_ERR_PAM_USER_UNKNOWN);
|
|
break;
|
|
|
|
default:
|
|
error_print(LY_ERR_PAM_ABORT);
|
|
break;
|
|
}
|
|
|
|
pam_end(login_handle, pam_result);
|
|
return 1;
|
|
}
|
|
|
|
/* Initialise user groups */
|
|
/* Get pwd structure for the user to get his group id */
|
|
struct passwd* pw = getpwnam(username);
|
|
|
|
if(!pw)
|
|
{
|
|
error_print(strerror(errno));
|
|
pam_end(login_handle, pam_result);
|
|
return 1;
|
|
}
|
|
|
|
int grp_result = initgroups(username, pw->pw_gid);
|
|
|
|
if(grp_result == -1)
|
|
{
|
|
error_print(strerror(errno));
|
|
pam_end(login_handle, pam_result);
|
|
return 1;
|
|
}
|
|
|
|
/* pam_setcred and error handling */
|
|
pam_result = pam_setcred(login_handle, PAM_ESTABLISH_CRED);
|
|
|
|
if(pam_result != PAM_SUCCESS)
|
|
{
|
|
switch(pam_result)
|
|
{
|
|
case PAM_BUF_ERR :
|
|
error_print(LY_ERR_PAM_BUF);
|
|
break;
|
|
|
|
case PAM_CRED_ERR :
|
|
error_print(LY_ERR_PAM_CRED);
|
|
break;
|
|
|
|
case PAM_CRED_EXPIRED :
|
|
error_print(LY_ERR_PAM_CRED_EXPIRED);
|
|
break;
|
|
|
|
case PAM_CRED_UNAVAIL :
|
|
error_print(LY_ERR_PAM_CRED_UNAVAIL);
|
|
break;
|
|
|
|
case PAM_SYSTEM_ERR :
|
|
error_print(LY_ERR_PAM_SYSTEM);
|
|
break;
|
|
|
|
case PAM_USER_UNKNOWN :
|
|
error_print(LY_ERR_PAM_USER_UNKNOWN);
|
|
break;
|
|
|
|
default:
|
|
error_print(LY_ERR_PAM_ABORT);
|
|
break;
|
|
}
|
|
|
|
pam_end(login_handle, pam_result);
|
|
return 1;
|
|
}
|
|
|
|
/* pam_open_session and error handling */
|
|
pam_result = pam_open_session(login_handle, 0);
|
|
|
|
if(pam_result != PAM_SUCCESS)
|
|
{
|
|
pam_setcred(login_handle, PAM_DELETE_CRED);
|
|
|
|
switch(pam_result)
|
|
{
|
|
case PAM_BUF_ERR :
|
|
error_print(LY_ERR_PAM_BUF);
|
|
break;
|
|
|
|
case PAM_CRED_ERR :
|
|
error_print(LY_ERR_PAM_CRED);
|
|
break;
|
|
|
|
case PAM_CRED_EXPIRED :
|
|
error_print(LY_ERR_PAM_CRED_EXPIRED);
|
|
break;
|
|
|
|
case PAM_CRED_UNAVAIL :
|
|
error_print(LY_ERR_PAM_CRED_UNAVAIL);
|
|
break;
|
|
|
|
case PAM_SYSTEM_ERR :
|
|
error_print(LY_ERR_PAM_SYSTEM);
|
|
break;
|
|
|
|
case PAM_USER_UNKNOWN :
|
|
error_print(LY_ERR_PAM_USER_UNKNOWN);
|
|
break;
|
|
|
|
default:
|
|
error_print(LY_ERR_PAM_ABORT);
|
|
break;
|
|
}
|
|
|
|
pam_end(login_handle, pam_result);
|
|
return 1;
|
|
}
|
|
|
|
/* login error */
|
|
if(login_handle == NULL)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/* temporarily exits ncurses mode */
|
|
def_prog_mode();
|
|
endwin();
|
|
pwd = getpwnam(username);
|
|
/* launches the DE */
|
|
pid_display = fork();
|
|
|
|
if(pid_display == 0)
|
|
{
|
|
/* downgrades group permissions and checks for an error */
|
|
if(setgid(pwd->pw_gid) < 0)
|
|
{
|
|
error_print(LY_ERR_PERM_GROUP);
|
|
pam_end(login_handle, pam_result);
|
|
return 1;
|
|
}
|
|
|
|
/* initializes environment variables */
|
|
init_env(login_handle, pwd);
|
|
|
|
/* downgrades user permissions and checks for an error */
|
|
if(setuid(pwd->pw_uid) < 0)
|
|
{
|
|
error_print(LY_ERR_PERM_USER);
|
|
pam_end(login_handle, pam_result);
|
|
return 1;
|
|
}
|
|
|
|
/* changes directory and checks for an error */
|
|
if(chdir(pwd->pw_dir) < 0)
|
|
{
|
|
error_print(LY_ERR_PERM_DIR);
|
|
pam_end(login_handle, pam_result);
|
|
return 1;
|
|
}
|
|
|
|
/* starts the chosen environment */
|
|
switch(display_server)
|
|
{
|
|
case shell:
|
|
launch_shell(pwd, login_handle);
|
|
|
|
case wayland:
|
|
launch_wayland(pwd, login_handle, de_command);
|
|
break;
|
|
|
|
case xorg:
|
|
launch_xorg(pwd, login_handle, de_command, display_name, vt, 0);
|
|
break;
|
|
|
|
case xinitrc:
|
|
default :
|
|
launch_xorg(pwd, login_handle, de_command, display_name, vt, 1);
|
|
break;
|
|
}
|
|
|
|
exit(0);
|
|
}
|
|
|
|
/* waits for the de/shell to exit */
|
|
waitpid(pid_display, &display_status, 0);
|
|
/* pam_close_session and error handling */
|
|
pam_result = pam_close_session(login_handle, 0);
|
|
|
|
if(pam_result != PAM_SUCCESS)
|
|
{
|
|
switch(pam_result)
|
|
{
|
|
case PAM_BUF_ERR :
|
|
error_print(LY_ERR_PAM_BUF);
|
|
break;
|
|
|
|
case PAM_SESSION_ERR :
|
|
error_print(LY_ERR_PAM_SESSION);
|
|
break;
|
|
|
|
case PAM_ABORT :
|
|
default:
|
|
error_print(LY_ERR_PAM_ABORT);
|
|
break;
|
|
}
|
|
|
|
pam_end(login_handle, pam_result);
|
|
return 1;
|
|
}
|
|
|
|
/* pam_setcred and error handling */
|
|
pam_result = pam_setcred(login_handle, PAM_DELETE_CRED);
|
|
|
|
if(pam_result != PAM_SUCCESS)
|
|
{
|
|
switch(pam_result)
|
|
{
|
|
case PAM_BUF_ERR :
|
|
error_print(LY_ERR_PAM_BUF);
|
|
break;
|
|
|
|
case PAM_CRED_ERR :
|
|
error_print(LY_ERR_PAM_CRED);
|
|
break;
|
|
|
|
case PAM_CRED_EXPIRED :
|
|
error_print(LY_ERR_PAM_CRED_EXPIRED);
|
|
break;
|
|
|
|
case PAM_CRED_UNAVAIL :
|
|
error_print(LY_ERR_PAM_CRED_UNAVAIL);
|
|
break;
|
|
|
|
case PAM_SYSTEM_ERR :
|
|
error_print(LY_ERR_PAM_SYSTEM);
|
|
break;
|
|
|
|
case PAM_USER_UNKNOWN :
|
|
error_print(LY_ERR_PAM_USER_UNKNOWN);
|
|
break;
|
|
|
|
default:
|
|
error_print(LY_ERR_PAM_ABORT);
|
|
break;
|
|
}
|
|
|
|
pam_end(login_handle, pam_result);
|
|
return 1;
|
|
}
|
|
|
|
/* pam_end and error handling */
|
|
pam_result = pam_end(login_handle, pam_result);
|
|
|
|
if(pam_result != PAM_SUCCESS)
|
|
{
|
|
error_print(LY_ERR_PAM_SYSTEM);
|
|
refresh();
|
|
return 1;
|
|
}
|
|
|
|
error_print(LY_LANG_LOGOUT);
|
|
refresh();
|
|
return 0;
|
|
}
|
|
|
|
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[LY_LIM_CMD];
|
|
char* argv[] = {pwd->pw_shell, "-l", "-c", cmd, NULL};
|
|
extern char** environ;
|
|
/* updates cookie */
|
|
snprintf(cmd, sizeof(cmd), "exec xauth add %s . `%s`", display_name,
|
|
LY_CMD_MCOOKIE);
|
|
/* creates the file if it can't be found */
|
|
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(0);
|
|
}
|
|
|
|
waitpid(child, &status, 0);
|
|
reset_terminal(pwd);
|
|
snprintf(cmd, sizeof(cmd),
|
|
"exec xinit %s %s%s -- %s %s %s -auth %s",
|
|
LY_CMD_XSETUP,
|
|
xinitrc ? "" : "/usr/bin/",
|
|
de_command, LY_CMD_X,
|
|
display_name, vt, getenv("XAUTHORITY"));
|
|
execve(pwd->pw_shell, argv, environ);
|
|
exit(0);
|
|
}
|
|
|
|
void launch_wayland(struct passwd* pwd, pam_handle_t* pam_handle,
|
|
const char* de_command)
|
|
{
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
void launch_shell(struct passwd* pwd, pam_handle_t* pam_handle)
|
|
{
|
|
char* pos;
|
|
char args[PATH_MAX + 2];
|
|
reset_terminal(pwd);
|
|
args[0] = '-';
|
|
strncpy(args + 1, ((pos = strrchr(pwd->pw_shell,
|
|
'/')) ? pos + 1 : pwd->pw_shell), sizeof(args) - 1);
|
|
execl(pwd->pw_shell, args, NULL);
|
|
exit(0);
|
|
}
|
|
|
|
void destroy_env(void)
|
|
{
|
|
/* our environment */
|
|
extern char** environ;
|
|
/* completely destroys environment */
|
|
environ = malloc(sizeof(char*));
|
|
memset(environ, 0, sizeof(char*));
|
|
}
|
|
|
|
void init_xdg(const char* tty_id, const char* display_name,
|
|
enum deserv_t 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 shell:
|
|
setenv("XDG_SESSION_TYPE", "tty", 0);
|
|
|
|
case wayland:
|
|
setenv("XDG_SESSION_TYPE", "wayland", 0);
|
|
break;
|
|
|
|
case xorg:
|
|
default :
|
|
setenv("XDG_SESSION_TYPE", "x11", 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int init_env(pam_handle_t* pam_handle, struct passwd* pw)
|
|
{
|
|
int i;
|
|
int len;
|
|
/* buffers */
|
|
char tmp[PATH_MAX];
|
|
char* buf;
|
|
char** env;
|
|
char* 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);
|
|
snprintf(tmp, sizeof(tmp), "%s/%s", pw->pw_dir, LY_XAUTHORITY);
|
|
setenv("XAUTHORITY", tmp, 0);
|
|
buf = termenv ? strdup(termenv) : NULL;
|
|
setenv("TERM", buf ? buf : "linux", 1);
|
|
free(buf);
|
|
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", LY_PATH, 1))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
env = pam_getenvlist(pam_handle);
|
|
|
|
for(i = 0; env && env[i]; i++)
|
|
{
|
|
putenv(env[i]);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void reset_terminal(struct passwd* pwd)
|
|
{
|
|
pid_t pid;
|
|
int status;
|
|
pid = fork();
|
|
char cmd[LY_LIM_CMD];
|
|
strncpy(cmd, "exec tput reset", sizeof(cmd));
|
|
|
|
if(pid == 0)
|
|
{
|
|
execl(pwd->pw_shell, pwd->pw_shell, "-c", cmd, NULL);
|
|
exit(0);
|
|
}
|
|
|
|
waitpid(pid, &status, 0);
|
|
}
|
|
|
|
int get_free_display(void)
|
|
{
|
|
int i;
|
|
char xlock[LY_LIM_PATH];
|
|
|
|
for(i = 0; i < 200; ++i)
|
|
{
|
|
snprintf(xlock, sizeof(xlock), "/tmp/.X%d-lock", i);
|
|
|
|
if(access(xlock, F_OK) == -1)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|