summaryrefslogtreecommitdiff
path: root/helper/pam-helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'helper/pam-helper.c')
-rw-r--r--helper/pam-helper.c289
1 files changed, 289 insertions, 0 deletions
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);
+}