From 51cb45499311851fe97bcf148c4e55d9e4960c61 Mon Sep 17 00:00:00 2001 From: "Carsten Haitzler (Rasterman)" Date: Mon, 26 Feb 2018 19:01:46 +0900 Subject: [PATCH] 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 --- src/bin/e_auth.c | 234 +----------------------------------- src/bin/e_auth.h | 1 - src/bin/e_ckpasswd_main.c | 242 +++++++++++++++++++++++++++++--------- src/bin/e_desklock.c | 10 -- src/bin/e_sys_main.c | 60 ---------- src/bin/meson.build | 29 +++-- 6 files changed, 207 insertions(+), 369 deletions(-) diff --git a/src/bin/e_auth.c b/src/bin/e_auth.c index 2fabc25a6..00b0e5d84 100644 --- a/src/bin/e_auth.c +++ b/src/bin/e_auth.c @@ -1,137 +1,6 @@ #include "e.h" -#if defined(HAVE_PAM) && !defined(__FreeBSD__) && !defined(__OpenBSD__) -# include -# include - - -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"); -} diff --git a/src/bin/e_auth.h b/src/bin/e_auth.h index a91f064d1..2ca283763 100644 --- a/src/bin/e_auth.h +++ b/src/bin/e_auth.h @@ -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) diff --git a/src/bin/e_ckpasswd_main.c b/src/bin/e_ckpasswd_main.c index c2508a738..8b6479a1c 100644 --- a/src/bin/e_ckpasswd_main.c +++ b/src/bin/e_ckpasswd_main.c @@ -1,81 +1,217 @@ -#include +#include "config.h" + +#define __USE_MISC +#define _SVID_SOURCE +#define _DEFAULT_SOURCE -#include -#include #include #include -#include #include +#include +#include +#include +#include +#ifdef HAVE_ALLOCA_H +#include +#endif +#if defined(__OpenBSD__) + +static int +_check_auth(uid_t uid, const char *guess) +{ + struct passwd *pwent; + + pwent = getpwuid_shadow(uid); + if (!pwent) return -1; + if (!pwent->pw_passwd) return -1; + + return crypt_checkpass(guess, pw_ent->pw_passwd); +} + + + + + +#elif defined(__FreeBSD__) #include -// Exit codes, per src/modules/lokker/lokker.c: -// 0: success (unlock) -// 1-128: PAM error but also unlock (!!!) -// else: failed. - -static char pw[4096]; -struct passwd *pwent; - -static void -zeropw(void) +static int +_check_auth(uid_t uid, const char *pw) { - /* security - null out passwd string once we are done with it */ - memset(pw, 0, sizeof(pw)); - if (pw[0] || pw[3]) printf("ACK!\n"); + struct passwd *pwent = getpwuid(uid); - if (pwent == NULL) return; - if (pwent->pw_passwd == NULL) return; + if (!pwent) return -1; + if (!pwent->pw_passwd) return -1; - /* 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"); + if (!strcmp(crypt(pw, pwent->pw_passwd), pwent->pw_passwd)) return 0; + return -1; } + + + + +#elif defined(HAVE_PAM) +# include + +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; - int i; + char pw[4096], *p; - 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); - } - } if (argc != 1) - exit(130); + { + int i; + for (i = 1; i < argc; i++) + fprintf(stderr, "Unknown option %s\n", argv[i]); + fprintf(stderr, + "This is an internal tool for Enlightenment\n"); + goto err; + } + + // 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; } diff --git a/src/bin/e_desklock.c b/src/bin/e_desklock.c index 8a1214ac6..7b99fccfa 100644 --- a/src/bin/e_desklock.c +++ b/src/bin/e_desklock.c @@ -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" - "desk locking is disabled.")); - return 0; - } -#endif - if (e_desklock_is_personal()) { if (!e_config->desklock_passwd) diff --git a/src/bin/e_sys_main.c b/src/bin/e_sys_main.c index 9043b5854..e5e4e7a7a 100644 --- a/src/bin/e_sys_main.c +++ b/src/bin/e_sys_main.c @@ -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"))) diff --git a/src/bin/meson.build b/src/bin/meson.build index 3f328b2ec..a8f0793d5 100644 --- a/src/bin/meson.build +++ b/src/bin/meson.build @@ -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 ], - 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 +executable('enlightenment_ckpasswd', + 'e_ckpasswd_main.c', + 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') if config_h.has('HAVE_WAYLAND') == true shared_library('loader',