diff options
Diffstat (limited to 'helper')
-rw-r--r-- | helper/Makefile.am | 27 | ||||
-rw-r--r-- | helper/helper_proto.c | 167 | ||||
-rw-r--r-- | helper/helper_proto.h | 53 | ||||
-rw-r--r-- | helper/pam-helper.c | 289 |
4 files changed, 536 insertions, 0 deletions
diff --git a/helper/Makefile.am b/helper/Makefile.am new file mode 100644 index 0000000..7a0ab0a --- /dev/null +++ b/helper/Makefile.am @@ -0,0 +1,27 @@ +## We require new-style dependency handling. +AUTOMAKE_OPTIONS = 1.7 + +noinst_LIBRARIES = libhelper-proto.a + +libhelper_proto_a_CFLAGS = \ + $(PAM_HELPER_CFLAGS) \ + -I$(top_srcdir)/src + +libhelper_proto_a_SOURCES = \ + helper_proto.h \ + helper_proto.c + +pkglibexec_PROGRAMS = mate-screensaver-pam-helper + +mate_screensaver_pam_helper_CFLAGS = \ + $(PAM_HELPER_CFLAGS) \ + -I$(top_srcdir)/src + +mate_screensaver_pam_helper_SOURCES = \ + pam-helper.c + +mate_screensaver_pam_helper_LDADD = \ + libhelper-proto.a + +mate_screensaver_pam_helper_LDFLAGS = \ + $(AUTH_LIBS) diff --git a/helper/helper_proto.c b/helper/helper_proto.c new file mode 100644 index 0000000..3abe263 --- /dev/null +++ b/helper/helper_proto.c @@ -0,0 +1,167 @@ +/* Part of mate-screensaver. + * + * Copyright (c) 2019-2021 Paul Wolneykien <[email protected]> + * + * This program 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; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/* Provides functions for two-way communication between the screensaver + * and the helper program. The idea of helper program is to be able to + * run mate-screensaver-dialog without any setuid bits. + */ + +#include "config.h" + +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include "helper_proto.h" + +static ssize_t +read_all (int fd, void *buf, size_t count) +{ + ssize_t rd, t_rd = 0; + + if (0 == count) + return 0; + + while (t_rd < count) + { + rd = read (fd, buf + t_rd, count - t_rd); + if (0 == rd) + break; + if (rd < 0) + return rd; + t_rd += rd; + } + + return t_rd; +} + +ssize_t +read_msg (int fd, char *buf, size_t length) +{ + size_t msg_len; + ssize_t rd; + + rd = read_all (fd, &msg_len, sizeof msg_len); + if (rd < 0) + return HELPER_IO_ERR; + if (rd > 0 && rd != sizeof msg_len) + return HELPER_LENGTH_READ_ERR; + + if (msg_len >= length) + return HELPER_TOO_LONG_ERR; + + if (msg_len > 0) + { + rd = read_all (fd, buf, msg_len); + if (rd < 0) + return HELPER_IO_ERR; + if (rd != msg_len) + return HELPER_MSG_READ_ERR; + } + else + rd = 0; + buf[rd] = '\0'; + + return rd; +} + +int +read_prompt (int fd, char *buf, size_t *length) +{ + int msg_type, rd; + + rd = read_all (fd, &msg_type, sizeof msg_type); + if (0 == rd) + return 0; + if (rd < 0) + return HELPER_IO_ERR; + if (rd > 0 && rd != sizeof msg_type) + return HELPER_TYPE_READ_ERR; + + rd = read_msg (fd, buf, *length); + if (rd < 0) + return rd; + + *length = rd; + return msg_type; +} + +static ssize_t +write_all (int fd, const void *buf, size_t count) +{ + ssize_t wt, t_wt = 0; + + if (0 == count) + return 0; + + while (t_wt < count) + { + wt = write (fd, buf + t_wt, count - t_wt); + if (0 == wt) + break; + if (wt < 0) + return wt; + t_wt += wt; + } + + return t_wt; +} + +ssize_t +write_msg (int fd, const void *buf, size_t length) +{ + ssize_t wt; + + wt = write_all (fd, &length, sizeof length); + if (wt < 0) + return HELPER_IO_ERR; + if (wt > 0 && wt != sizeof length) + return HELPER_LENGTH_WRITE_ERR; + + if (length > 0) + { + wt = write_all (fd, buf, length); + if (wt < 0) + return HELPER_IO_ERR; + if (wt != length) + return HELPER_MSG_WRITE_ERR; + } + else + wt = 0; + + return wt; +} + +int +write_prompt (int fd, int msg_type, const void *buf, size_t length) +{ + ssize_t wt; + + wt = write_all (fd, &msg_type, sizeof msg_type); + if (wt < 0) + return HELPER_IO_ERR; + if (wt > 0 && wt != sizeof msg_type) + return HELPER_TYPE_WRITE_ERR; + + wt = write_msg (fd, buf, length); + + return wt; +} diff --git a/helper/helper_proto.h b/helper/helper_proto.h new file mode 100644 index 0000000..b815b52 --- /dev/null +++ b/helper/helper_proto.h @@ -0,0 +1,53 @@ +/* Part of mate-screensaver. + * + * Copyright (c) 2019-2021 Paul Wolneykien <[email protected]> + * + * This program 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; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/* Provides functions for two-way communication between the screensaver + * and the helper program. The idea of helper program is to be able to + * run mate-screensaver-dialog without any setuid bits. + */ + +#ifndef __HELPER_PROTO_H +#define __HELPER_PROTO_H + +#include "config.h" + +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#define HELPER_IO_ERR -1 + +#define HELPER_LENGTH_READ_ERR -2 +#define HELPER_TOO_LONG_ERR -3 +#define HELPER_MSG_READ_ERR -4 +#define HELPER_TYPE_READ_ERR -5 + +ssize_t read_msg (int fd, char *buf, size_t length); +int read_prompt (int fd, char *buf, size_t *length); + +#define HELPER_LENGTH_WRITE_ERR -6 +#define HELPER_MSG_WRITE_ERR -7 +#define HELPER_TYPE_WRITE_ERR -8 + +ssize_t write_msg (int fd, const void *buf, size_t length); +int write_prompt (int fd, int msg_type, const void *buf, size_t length); + +#endif /* __HELPER_PROTO_H */ diff --git a/helper/pam-helper.c b/helper/pam-helper.c new file mode 100644 index 0000000..c2bd837 --- /dev/null +++ b/helper/pam-helper.c @@ -0,0 +1,289 @@ +/* Part of mate-screensaver. + * + * Copyright (C) 2002 SuSE Linux AG. + * Copyright (c) 2019-2021 Paul Wolneykien <[email protected]> + * + * This program 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; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/* + * Set*id helper program for PAM authentication. + * + * It is supposed to be called from mate-screensaver + * in order to communicate with Linux PAM as a privileged proxy. + * The conversation messages from the PAM stack is transmitted to + * mate-screensaver dialog via stdout and the received user replies + * read from stdin are sent back to PAM. + * + * Based on the helper written by [email protected], loosely based on + * unix_chkpwd by Andrew Morgan. + */ + +#include <security/pam_appl.h> +#include <security/_pam_macros.h> + +#include <sys/types.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <pwd.h> +#include <signal.h> +#include <fcntl.h> +#include <ctype.h> +#include <limits.h> + +#include "helper_proto.h" +#include "gs-auth-pam.h" + +#define MAXLEN 1024 + +enum { + UNIX_PASSED = 0, + UNIX_FAILED = 1 +}; + +static char * program_name; + +/* + * Log error messages + */ +static void +_log_err(int err, const char *format,...) +{ + va_list args; + + va_start(args, format); + openlog(program_name, LOG_CONS | LOG_PID, LOG_AUTH); + vsyslog(err, format, args); + va_end(args); + closelog(); +} + +static void +su_sighandler(int sig) +{ + if (sig > 0) { + _log_err(LOG_NOTICE, "caught signal %d.", sig); + exit(sig); + } +} + +/* + * Setup signal handlers + */ +static void +setup_signals(void) +{ + struct sigaction action; + + memset((void *) &action, 0, sizeof(action)); + action.sa_handler = su_sighandler; + action.sa_flags = SA_RESETHAND; + sigaction(SIGILL, &action, NULL); + sigaction(SIGTRAP, &action, NULL); + sigaction(SIGBUS, &action, NULL); + sigaction(SIGSEGV, &action, NULL); + action.sa_handler = SIG_IGN; + action.sa_flags = 0; + sigaction(SIGTERM, &action, NULL); + sigaction(SIGHUP, &action, NULL); + sigaction(SIGINT, &action, NULL); + sigaction(SIGQUIT, &action, NULL); + sigaction(SIGALRM, &action, NULL); +} + +static int +_converse(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) +{ + struct pam_response *reply = NULL; + char buf[MAXLEN]; + int num; + int ret = PAM_SUCCESS; + + if (!(reply = malloc(sizeof(*reply) * num_msg))) + return PAM_CONV_ERR; + + for (num = 0; num < num_msg; num++) { + ssize_t wt, rd; + size_t msg_len = strlen(msg[num]->msg); + wt = write_prompt (STDOUT_FILENO, + pam_style_to_gs_style (msg[num]->msg_style), + msg[num]->msg, msg_len); + if (wt < 0 || wt != msg_len) { + _log_err(LOG_ERR, "error writing promt"); + ret = PAM_CONV_ERR; + break; + } + + rd = read_msg (STDIN_FILENO, buf, sizeof (buf)); + if (rd < 0) { + _log_err(LOG_ERR, "error reading reply"); + ret = PAM_CONV_ERR; + break; + } + + reply[num].resp = malloc (rd + 1); + if (!reply[num].resp) + ret = PAM_BUF_ERR; + else { + reply[num].resp_retcode = 0; + memcpy (reply[num].resp, buf, rd); + reply[num].resp[rd] = '\0'; + } + } + + if (ret != PAM_SUCCESS && reply != NULL) { + for (num = 0; num < num_msg; num++) + free (reply[num].resp); + free (reply); + *resp = NULL; + } else + *resp = reply; + + return ret; +} + +static int +_authenticate(const char *service, const char *user) +{ + struct pam_conv conv = { _converse, NULL }; + pam_handle_t *pamh; + int err; + + err = pam_start(service, user, &conv, &pamh); + if (err != PAM_SUCCESS) { + _log_err(LOG_ERR, "pam_start(%s, %s) failed (errno %d)", + service, user, err); + return UNIX_FAILED; + } + + err = pam_authenticate(pamh, 0); + if (err != PAM_SUCCESS) + _log_err(LOG_ERR, "pam_authenticate(%s, %s): %s", + service, user, + pam_strerror(pamh, err)); + + if (err == PAM_SUCCESS) + { + int err2 = pam_setcred(pamh, PAM_REFRESH_CRED); + if (err2 != PAM_SUCCESS) + _log_err(LOG_ERR, "pam_setcred(%s, %s): %s", + service, user, + pam_strerror(pamh, err2)); + /* + * ignore errors on refresh credentials. + * If this did not work we use the old once. + */ + } + + pam_end(pamh, err); + + if (err != PAM_SUCCESS) + return UNIX_FAILED; + return UNIX_PASSED; +} + +static char * +getuidname(uid_t uid) +{ + struct passwd *pw; + static char username[32]; + + pw = getpwuid(uid); + if (pw == NULL) + return NULL; + + strncpy(username, pw->pw_name, sizeof(username)); + username[sizeof(username) - 1] = '\0'; + + endpwent(); + return username; +} + +int +main(int argc, char *argv[]) +{ + const char *program_name; + char *service, *user; + int fd; + uid_t uid; + + uid = getuid(); + + /* + * Make sure standard file descriptors are connected. + */ + while ((fd = open("/dev/null", O_RDWR)) <= 2) + ; + close(fd); + + /* + * Get the program name + */ + if ((program_name = strrchr(argv[0], '/')) != NULL) + program_name++; + else + program_name = argv[0]; + + /* + * Catch or ignore as many signal as possible. + */ + setup_signals(); + + /* + * Check argument list + */ + if (argc < 2 || argc > 3) { + _log_err(LOG_NOTICE, "Bad number of arguments (%d)", argc); + return UNIX_FAILED; + } + + /* + * Get the service name. + */ + service = argv[1]; + + /* + * Discourage users messing around (fat chance) + */ + if (isatty(STDIN_FILENO) && uid != 0) { + _log_err(LOG_NOTICE, + "Inappropriate use of Unix helper binary [UID=%d]", + uid); + fprintf(stderr, + "This binary is not designed for running in this way\n" + "-- the system administrator has been informed\n"); + sleep(10); /* this should discourage/annoy the user */ + return UNIX_FAILED; + } + + /* + * determine the caller's user name + */ + user = getuidname(uid); + if (argc == 3 && strcmp(user, argv[2])) { + user = argv[2]; + /* Discourage use of this program as a + * password cracker */ + if (uid != 0) + sleep(5); + } + return _authenticate(service, user); +} |