e auth - move all auth to child process only (e_ckpasswd).

this should fix T6211 ensuring no drivers can cause a segfault at exit
time. this also happens to remove the enlightenment_sys -z option for
openbsd and unifies all the passwd checking into the single
enlightenment_ckpasswd binary util (that has ifdefs for openbsd,
freebsd and linux pam in it).

this simplifies code removing a mess of auth being done in multiple
places, removes special fork vs run 1 exe or a different exe in
different cases making it more maintainable. yes - this requires
enlightenment_ckpasswd to be setuid root, but it already was when it
was built.

@fix
This commit is contained in:
Carsten Haitzler 2018-02-26 19:01:46 +09:00
parent 46764d5975
commit 51cb454993
6 changed files with 207 additions and 369 deletions

View File

@ -1,137 +1,6 @@
#include "e.h"
#if defined(HAVE_PAM) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
# include <security/pam_appl.h>
# include <pwd.h>
typedef struct E_Auth
{
struct
{
struct pam_conv conv;
pam_handle_t *handle;
} pam;
char user[4096];
char passwd[4096];
} E_Auth;
static pid_t _e_auth_child_pid = -1;
static char *
_auth_auth_get_current_user(void)
{
char *user;
struct passwd *pwent = NULL;
pwent = getpwuid(getuid());
if (!pwent) return NULL;
user = strdup(pwent->pw_name);
return user;
}
static int
_auth_auth_pam_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
{
int replies = 0;
E_Auth *da = (E_Auth *)appdata_ptr;
struct pam_response *reply = NULL;
reply = (struct pam_response *)malloc(sizeof(struct pam_response) * num_msg);
if (!reply) return PAM_CONV_ERR;
for (replies = 0; replies < num_msg; replies++)
{
switch (msg[replies]->msg_style)
{
case PAM_PROMPT_ECHO_ON:
reply[replies].resp_retcode = PAM_SUCCESS;
reply[replies].resp = strdup(da->user);
break;
case PAM_PROMPT_ECHO_OFF:
reply[replies].resp_retcode = PAM_SUCCESS;
reply[replies].resp = strdup(da->passwd);
break;
case PAM_ERROR_MSG:
case PAM_TEXT_INFO:
reply[replies].resp_retcode = PAM_SUCCESS;
reply[replies].resp = NULL;
break;
default:
free(reply);
return PAM_CONV_ERR;
}
}
*resp = reply;
return PAM_SUCCESS;
}
static int
_auth_pam_init(E_Auth *da)
{
int pamerr;
const char *pam_prof;
char *current_host;
char *current_user;
if (!da) return -1;
da->pam.conv.conv = _auth_auth_pam_conv;
da->pam.conv.appdata_ptr = da;
da->pam.handle = NULL;
/* try other pam profiles - and system-auth (login for fbsd users) is a fallback */
pam_prof = "login";
if (ecore_file_exists("/etc/pam.d/enlightenment"))
pam_prof = "enlightenment";
else if (ecore_file_exists("/etc/pam.d/xscreensaver"))
pam_prof = "xscreensaver";
else if (ecore_file_exists("/etc/pam.d/kscreensaver"))
pam_prof = "kscreensaver";
else if (ecore_file_exists("/etc/pam.d/system-auth"))
pam_prof = "system-auth";
else if (ecore_file_exists("/etc/pam.d/system"))
pam_prof = "system";
else if (ecore_file_exists("/etc/pam.d/xdm"))
pam_prof = "xdm";
else if (ecore_file_exists("/etc/pam.d/gdm"))
pam_prof = "gdm";
else if (ecore_file_exists("/etc/pam.d/kdm"))
pam_prof = "kdm";
if ((pamerr = pam_start(pam_prof, da->user, &(da->pam.conv),
&(da->pam.handle))) != PAM_SUCCESS)
return pamerr;
current_user = _auth_auth_get_current_user();
if ((pamerr = pam_set_item(da->pam.handle, PAM_USER, current_user)) != PAM_SUCCESS)
{
free(current_user);
return pamerr;
}
current_host = e_auth_hostname_get();
if ((pamerr = pam_set_item(da->pam.handle, PAM_RHOST, current_host)) != PAM_SUCCESS)
{
free(current_user);
free(current_host);
return pamerr;
}
free(current_user);
free(current_host);
return 0;
}
#endif // HAVE_PAM && !__FreeBSD__ && !_OpenBSD__
E_API int
#if defined(__FreeBSD__)
e_auth_begin(char *passwd)
{
char buf[PATH_MAX], *p;
@ -159,109 +28,8 @@ out:
if (exe) ecore_exe_free(exe);
/* security - null out passwd string once we are done with it */
for (p = passwd; *p; p++)
*p = 0;
for (p = passwd; *p; p++) *p = 0;
if (passwd[0] || passwd[3]) fprintf(stderr, "ACK!\n");
return ret;
}
#elif defined(__OpenBSD__)
e_auth_begin(char *passwd)
{
char exe_path[PATH_MAX], *p;
Ecore_Exe *exe = NULL;
int ret = 0;
int len = strlen(passwd);
if (len == 0) goto out;
snprintf(exe_path, sizeof(exe_path), "%s/enlightenment/utils/enlightenment_sys -z",
e_prefix_lib_get());
exe = ecore_exe_pipe_run(exe_path, ECORE_EXE_PIPE_WRITE, NULL);
if (!exe) goto out;
if (ecore_exe_send(exe, passwd, len) != EINA_TRUE) goto out;
if (ecore_exe_send(exe, "\n", 1) != EINA_TRUE) goto out;
ecore_exe_close_stdin(exe);
ret = ecore_exe_pid_get(exe);
if (ret == -1)
{
ret = 0;
goto out;
}
exe = NULL;
out:
if (exe) ecore_exe_free(exe);
for (p = passwd; *p; p++)
*p = 0;
return ret;
}
#elif defined(HAVE_PAM)
e_auth_begin(char *passwd)
{
/* child */
int pamerr;
E_Auth da;
char *current_user;
struct sigaction action;
_e_auth_child_pid = fork();
if (_e_auth_child_pid > 0) return _e_auth_child_pid;
if (_e_auth_child_pid < 0) return -1;
action.sa_handler = SIG_DFL;
action.sa_flags = SA_ONSTACK | SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
sigemptyset(&action.sa_mask);
sigaction(SIGSEGV, &action, NULL);
sigaction(SIGILL, &action, NULL);
sigaction(SIGFPE, &action, NULL);
sigaction(SIGBUS, &action, NULL);
sigaction(SIGABRT, &action, NULL);
current_user = _auth_auth_get_current_user();
eina_strlcpy(da.user, current_user, sizeof(da.user));
eina_strlcpy(da.passwd, passwd, sizeof(da.passwd));
/* security - null out passwd string once we are done with it */
e_util_memclear(passwd, strlen(passwd));
da.pam.handle = NULL;
da.pam.conv.conv = NULL;
da.pam.conv.appdata_ptr = NULL;
pamerr = _auth_pam_init(&da);
if (pamerr != PAM_SUCCESS)
{
free(current_user);
_exit(1);
}
pamerr = pam_authenticate(da.pam.handle, 0);
pam_end(da.pam.handle, pamerr);
/* security - null out passwd string once we are done with it */
e_util_memclear(da.passwd, sizeof(da.passwd));
if (pamerr == PAM_SUCCESS)
{
free(current_user);
_exit(0);
}
free(current_user);
_exit(-1);
return 0;
}
#else
e_auth_begin(char *passwd EINA_UNUSED)
{
return 0;
}
#endif
E_API char *
e_auth_hostname_get(void)
{
return strdup("localhost");
}

View File

@ -2,7 +2,6 @@
#define E_AUTH_H
E_API int e_auth_begin(char *passwd);
E_API char *e_auth_hostname_get(void);
static inline int
e_auth_hash_djb2(const char *key, int len)

View File

@ -1,81 +1,217 @@
#include <sys/types.h>
#include "config.h"
#define __USE_MISC
#define _SVID_SOURCE
#define _DEFAULT_SOURCE
#include <err.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#include <security/pam_constants.h>
#if defined(__OpenBSD__)
// Exit codes, per src/modules/lokker/lokker.c:
// 0: success (unlock)
// 1-128: PAM error but also unlock (!!!)
// else: failed.
static char pw[4096];
static int
_check_auth(uid_t uid, const char *guess)
{
struct passwd *pwent;
static void
zeropw(void)
{
/* security - null out passwd string once we are done with it */
memset(pw, 0, sizeof(pw));
if (pw[0] || pw[3]) printf("ACK!\n");
pwent = getpwuid_shadow(uid);
if (!pwent) return -1;
if (!pwent->pw_passwd) return -1;
if (pwent == NULL) return;
if (pwent->pw_passwd == NULL) return;
/* security - null out passwd string once we are done with it */
memset(pwent->pw_passwd, 0, strlen(pwent->pw_passwd));
if (pwent->pw_passwd[0]) printf("ACK!\n");
return crypt_checkpass(guess, pw_ent->pw_passwd);
}
#elif defined(__FreeBSD__)
#include <security/pam_constants.h>
static int
_check_auth(uid_t uid, const char *pw)
{
struct passwd *pwent = getpwuid(uid);
if (!pwent) return -1;
if (!pwent->pw_passwd) return -1;
if (!strcmp(crypt(pw, pwent->pw_passwd), pwent->pw_passwd)) return 0;
return -1;
}
#elif defined(HAVE_PAM)
# include <security/pam_appl.h>
typedef struct
{
const char *user;
const char *pw;
} Authinfo;
static int
_conv_cb(int num, const struct pam_message **msg, struct pam_response **resp, void *data)
{
Authinfo *ai = data;
int replies;
struct pam_response *reply = NULL;
reply = malloc(sizeof(struct pam_response) * num);
if (!reply) return PAM_CONV_ERR;
for (replies = 0; replies < num; replies++)
{
switch (msg[replies]->msg_style)
{
case PAM_PROMPT_ECHO_ON:
reply[replies].resp_retcode = PAM_SUCCESS;
reply[replies].resp = strdup(ai->user);
break;
case PAM_PROMPT_ECHO_OFF:
reply[replies].resp_retcode = PAM_SUCCESS;
reply[replies].resp = strdup(ai->pw);
break;
case PAM_ERROR_MSG:
case PAM_TEXT_INFO:
reply[replies].resp_retcode = PAM_SUCCESS;
reply[replies].resp = NULL;
break;
default:
free(reply);
return PAM_CONV_ERR;
}
}
*resp = reply;
return PAM_SUCCESS;
}
static int
_check_auth(uid_t uid, const char *pw)
{
Authinfo ai;
struct passwd *pwent;
const char *user;
const char *prof;
const char *host;
struct stat st;
pam_handle_t *handle;
int pamerr;
struct pam_conv conv;
pwent = getpwuid(uid);
if (!pwent) return -1;
user = pwent->pw_name;
if (!user) return -1;
host = "localhost";
prof = "login";
if (!stat("/etc/pam.d/enlightenment", &st)) prof = "enlightenment";
else if (!stat("/etc/pam.d/xscreensaver", &st)) prof = "xscreensaver";
else if (!stat("/etc/pam.d/kscreensaver", &st)) prof = "kscreensaver";
else if (!stat("/etc/pam.d/system-auth", &st)) prof = "system-auth";
else if (!stat("/etc/pam.d/system", &st)) prof = "system";
else if (!stat("/etc/pam.d/xdm", &st)) prof = "xdm";
else if (!stat("/etc/pam.d/gdm", &st)) prof = "gdm";
else if (!stat("/etc/pam.d/kdm", &st)) prof = "kdm";
ai.user = user;
ai.pw = pw;
conv.conv = _conv_cb;
conv.appdata_ptr = &ai;
if (pam_start(prof, user, &conv, &handle) != PAM_SUCCESS) return -1;
if (pam_set_item(handle, PAM_USER, user) != PAM_SUCCESS) return -1;
if (pam_set_item(handle, PAM_RHOST, host) != PAM_SUCCESS) return -1;
pamerr = pam_authenticate(handle, 0);
pam_end(handle, pamerr);
if (pamerr != PAM_SUCCESS) return -1;
return 0;
}
#else
static int
_check_auth(uid_t uid, const char *pw)
{
return -1;
}
#endif
int
main(int argc, char **argv)
{
ssize_t rd;
uid_t id;
char pw[4096], *p;
if (argc != 1)
{
int i;
for (i = 1; i < argc; i++)
{
if ((!strcmp(argv[i], "-h")) ||
(!strcmp(argv[i], "-help")) ||
(!strcmp(argv[i], "--help")))
{
printf("This is an internal tool for Enlightenment.\n"
"do not use it.\n");
exit(129);
fprintf(stderr, "Unknown option %s\n", argv[i]);
fprintf(stderr,
"This is an internal tool for Enlightenment\n");
goto err;
}
}
if (argc != 1)
exit(130);
// get uid who ran this
id = getuid();
if (atexit(zeropw)) err(131, "atexit");
// read passwd from stdin
rd = read(0, pw, sizeof(pw) - 1);
if (rd < 0) err(132, "read");
if (rd < 0)
{
fprintf(stderr,
"Error. Can't read passwd on stdin\n");
goto err;
}
pw[rd] = 0;
for (p = pw; *p; p++)
{
if ((*p == '\r') || (*p == '\n'))
{
*p = 0;
break;
}
}
// If we are setuid root then try become root - we can work without though
// if pam etc. can work without being root
if (setuid(0) != 0)
{
printf("ERROR: UNABLE TO ASSUME ROOT PRIVILEGES\n");
exit(133);
}
fprintf(stderr,
"Warning. Can't become user root. If password auth requires root then this will fail\n");
if (setgid(0) != 0)
{
printf("ERROR: UNABLE TO ASSUME ROOT GROUP PRIVILEGES\n");
exit(134);
}
pwent = getpwuid(id);
if (pwent == NULL) return -2;
if (strcmp(crypt(pw, pwent->pw_passwd), pwent->pw_passwd) == 0)
return 0;
fprintf(stderr,
"Warning. Can't become group root. If password auth requires root then this will fail\n");
if (_check_auth(id, pw) == 0) return 0;
err:
fprintf(stderr,
"Password auth fail\n");
return -1;
}

View File

@ -268,16 +268,6 @@ e_desklock_show(Eina_Bool suspend)
return 1;
}
#if ! defined(HAVE_PAM) && ! defined(__OpenBSD__)
if (e_desklock_is_system())
{
e_util_dialog_show(_("Error - no PAM support"),
_("No PAM support was built into Enlightenment, so<ps/>"
"desk locking is disabled."));
return 0;
}
#endif
if (e_desklock_is_personal())
{
if (!e_config->desklock_passwd)

View File

@ -49,57 +49,6 @@ static int auth_etc_enlightenment_sysactions(char *a,
static void auth_etc_enlightenment_sysactions_perm(char *path);
static char *get_word(char *s,
char *d);
#if defined(__OpenBSD__)
static void
_exit_backoff(void)
{
sleep(3);
exit(1 << 7);
}
static int
_check_auth(const char *guess)
{
struct passwd *pw_ent;
uid_t uid = getuid();
pw_ent = getpwuid_shadow(uid);
if (!pw_ent)
_exit_backoff();
return crypt_checkpass(guess, pw_ent->pw_passwd);
}
static int
auth_generic_enlightenment_desklock(void)
{
char buf[4096];
char byte[1];
int res = -1;
int i = 0;
while (read(STDIN_FILENO, byte, sizeof(byte)) > 0)
{
if (byte[0] == '\n') break;
buf[i++] = byte[0];
if (i == sizeof(buf) -1) break;
}
buf[i] = '\0';
if (!i)
_exit_backoff();
res = _check_auth(buf);
if (res) _exit_backoff();
return res;
}
#endif
/* local subsystem globals */
static Eina_Hash *actions = NULL;
static uid_t uid = -1;
@ -132,15 +81,6 @@ main(int argc,
exit(0);
}
}
#if defined(__OpenBSD__)
if (argc >= 2)
{
if (!strcmp(argv[1], "-z"))
{
exit(auth_generic_enlightenment_desklock());
}
}
#endif
if (argc >= 3)
{
if ((argc == 3) && (!strcmp(argv[1], "-t")))

View File

@ -35,8 +35,14 @@ deps_e = [
dep_intl
]
deps_ckpass = [ ]
if freebsd == true
deps_ckpass += dep_crypt
endif
if config_h.has('HAVE_PAM') == true
deps_e += dep_pam
deps_ckpass += dep_pam
endif
requires_e = ' '.join([
@ -555,17 +561,16 @@ executable('enlightenment_sys',
)
suid_exes += join_paths(dir_e_utils, 'enlightenment_sys')
if freebsd == true
executable('enlightenment_ckpasswd',
'e_ckpasswd_main.c',
dependencies : [ dep_crypt ],
include_directories: include_directories('../..'),
dependencies : deps_ckpass,
c_args : suid_cflags,
link_args : suid_ldflags,
install_dir : dir_e_utils,
install : true
)
suid_exes += join_paths(dir_e_utils, 'enlightenment_ckpasswd')
endif
if config_h.has('HAVE_WAYLAND') == true
shared_library('loader',