summaryrefslogtreecommitdiffstats
path: root/contrib/openpam/lib/libpam/openpam_ttyconv.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/openpam/lib/libpam/openpam_ttyconv.c')
-rw-r--r--contrib/openpam/lib/libpam/openpam_ttyconv.c401
1 files changed, 401 insertions, 0 deletions
diff --git a/contrib/openpam/lib/libpam/openpam_ttyconv.c b/contrib/openpam/lib/libpam/openpam_ttyconv.c
new file mode 100644
index 0000000..01e6181
--- /dev/null
+++ b/contrib/openpam/lib/libpam/openpam_ttyconv.c
@@ -0,0 +1,401 @@
+/*-
+ * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
+ * Copyright (c) 2004-2011 Dag-Erling Smørgrav
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by ThinkSec AS and
+ * Network Associates Laboratories, the Security Research Division of
+ * Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: openpam_ttyconv.c 688 2013-07-11 16:40:08Z des $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <security/pam_appl.h>
+
+#include "openpam_impl.h"
+
+int openpam_ttyconv_timeout = 0;
+
+static volatile sig_atomic_t caught_signal;
+
+/*
+ * Handle incoming signals during tty conversation
+ */
+static void
+catch_signal(int signo)
+{
+
+ switch (signo) {
+ case SIGINT:
+ case SIGQUIT:
+ case SIGTERM:
+ caught_signal = signo;
+ break;
+ }
+}
+
+/*
+ * Accept a response from the user on a tty
+ */
+static int
+prompt_tty(int ifd, int ofd, const char *message, char *response, int echo)
+{
+ struct sigaction action;
+ struct sigaction saction_sigint, saction_sigquit, saction_sigterm;
+ struct termios tcattr;
+ struct timeval now, target, remaining;
+ int remaining_ms;
+ tcflag_t slflag;
+ struct pollfd pfd;
+ int serrno;
+ int pos, ret;
+ char ch;
+
+ /* write prompt */
+ if (write(ofd, message, strlen(message)) < 0) {
+ openpam_log(PAM_LOG_ERROR, "write(): %m");
+ return (-1);
+ }
+
+ /* turn echo off if requested */
+ slflag = 0; /* prevent bogus uninitialized variable warning */
+ if (!echo) {
+ if (tcgetattr(ifd, &tcattr) != 0) {
+ openpam_log(PAM_LOG_ERROR, "tcgetattr(): %m");
+ return (-1);
+ }
+ slflag = tcattr.c_lflag;
+ tcattr.c_lflag &= ~ECHO;
+ if (tcsetattr(ifd, TCSAFLUSH, &tcattr) != 0) {
+ openpam_log(PAM_LOG_ERROR, "tcsetattr(): %m");
+ return (-1);
+ }
+ }
+
+ /* install signal handlers */
+ caught_signal = 0;
+ action.sa_handler = &catch_signal;
+ action.sa_flags = 0;
+ sigfillset(&action.sa_mask);
+ sigaction(SIGINT, &action, &saction_sigint);
+ sigaction(SIGQUIT, &action, &saction_sigquit);
+ sigaction(SIGTERM, &action, &saction_sigterm);
+
+ /* compute timeout */
+ if (openpam_ttyconv_timeout > 0) {
+ (void)gettimeofday(&now, NULL);
+ remaining.tv_sec = openpam_ttyconv_timeout;
+ remaining.tv_usec = 0;
+ timeradd(&now, &remaining, &target);
+ } else {
+ /* prevent bogus uninitialized variable warning */
+ now.tv_sec = now.tv_usec = 0;
+ remaining.tv_sec = remaining.tv_usec = 0;
+ target.tv_sec = target.tv_usec = 0;
+ }
+
+ /* input loop */
+ pos = 0;
+ ret = -1;
+ serrno = 0;
+ while (!caught_signal) {
+ pfd.fd = ifd;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+ if (openpam_ttyconv_timeout > 0) {
+ gettimeofday(&now, NULL);
+ if (timercmp(&now, &target, >))
+ break;
+ timersub(&target, &now, &remaining);
+ remaining_ms = remaining.tv_sec * 1000 +
+ remaining.tv_usec / 1000;
+ } else {
+ remaining_ms = -1;
+ }
+ if ((ret = poll(&pfd, 1, remaining_ms)) < 0) {
+ serrno = errno;
+ if (errno == EINTR)
+ continue;
+ openpam_log(PAM_LOG_ERROR, "poll(): %m");
+ break;
+ } else if (ret == 0) {
+ /* timeout */
+ write(ofd, " timed out", 10);
+ openpam_log(PAM_LOG_NOTICE, "timed out");
+ break;
+ }
+ if ((ret = read(ifd, &ch, 1)) < 0) {
+ serrno = errno;
+ openpam_log(PAM_LOG_ERROR, "read(): %m");
+ break;
+ } else if (ret == 0 || ch == '\n') {
+ response[pos] = '\0';
+ ret = pos;
+ break;
+ }
+ if (pos + 1 < PAM_MAX_RESP_SIZE)
+ response[pos++] = ch;
+ /* overflow is discarded */
+ }
+
+ /* restore tty state */
+ if (!echo) {
+ tcattr.c_lflag = slflag;
+ if (tcsetattr(ifd, 0, &tcattr) != 0) {
+ /* treat as non-fatal, since we have our answer */
+ openpam_log(PAM_LOG_NOTICE, "tcsetattr(): %m");
+ }
+ }
+
+ /* restore signal handlers and re-post caught signal*/
+ sigaction(SIGINT, &saction_sigint, NULL);
+ sigaction(SIGQUIT, &saction_sigquit, NULL);
+ sigaction(SIGTERM, &saction_sigterm, NULL);
+ if (caught_signal != 0) {
+ openpam_log(PAM_LOG_ERROR, "caught signal %d",
+ (int)caught_signal);
+ raise((int)caught_signal);
+ /* if raise() had no effect... */
+ serrno = EINTR;
+ ret = -1;
+ }
+
+ /* done */
+ write(ofd, "\n", 1);
+ errno = serrno;
+ return (ret);
+}
+
+/*
+ * Accept a response from the user on a non-tty stdin.
+ */
+static int
+prompt_notty(const char *message, char *response)
+{
+ struct timeval now, target, remaining;
+ int remaining_ms;
+ struct pollfd pfd;
+ int ch, pos, ret;
+
+ /* show prompt */
+ fputs(message, stdout);
+ fflush(stdout);
+
+ /* compute timeout */
+ if (openpam_ttyconv_timeout > 0) {
+ (void)gettimeofday(&now, NULL);
+ remaining.tv_sec = openpam_ttyconv_timeout;
+ remaining.tv_usec = 0;
+ timeradd(&now, &remaining, &target);
+ } else {
+ /* prevent bogus uninitialized variable warning */
+ now.tv_sec = now.tv_usec = 0;
+ remaining.tv_sec = remaining.tv_usec = 0;
+ target.tv_sec = target.tv_usec = 0;
+ }
+
+ /* input loop */
+ pos = 0;
+ for (;;) {
+ pfd.fd = STDIN_FILENO;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+ if (openpam_ttyconv_timeout > 0) {
+ gettimeofday(&now, NULL);
+ if (timercmp(&now, &target, >))
+ break;
+ timersub(&target, &now, &remaining);
+ remaining_ms = remaining.tv_sec * 1000 +
+ remaining.tv_usec / 1000;
+ } else {
+ remaining_ms = -1;
+ }
+ if ((ret = poll(&pfd, 1, remaining_ms)) < 0) {
+ /* interrupt is ok, everything else -> bail */
+ if (errno == EINTR)
+ continue;
+ perror("\nopenpam_ttyconv");
+ return (-1);
+ } else if (ret == 0) {
+ /* timeout */
+ break;
+ } else {
+ /* input */
+ if ((ch = getchar()) == EOF && ferror(stdin)) {
+ perror("\nopenpam_ttyconv");
+ return (-1);
+ }
+ if (ch == EOF || ch == '\n') {
+ response[pos] = '\0';
+ return (pos);
+ }
+ if (pos + 1 < PAM_MAX_RESP_SIZE)
+ response[pos++] = ch;
+ /* overflow is discarded */
+ }
+ }
+ fputs("\nopenpam_ttyconv: timeout\n", stderr);
+ return (-1);
+}
+
+/*
+ * Determine whether stdin is a tty; if not, try to open the tty; in
+ * either case, call the appropriate method.
+ */
+static int
+prompt(const char *message, char *response, int echo)
+{
+ int ifd, ofd, ret;
+
+ if (isatty(STDIN_FILENO)) {
+ fflush(stdout);
+#ifdef HAVE_FPURGE
+ fpurge(stdin);
+#endif
+ ifd = STDIN_FILENO;
+ ofd = STDOUT_FILENO;
+ } else {
+ if ((ifd = open("/dev/tty", O_RDWR)) < 0)
+ /* no way to prevent echo */
+ return (prompt_notty(message, response));
+ ofd = ifd;
+ }
+ ret = prompt_tty(ifd, ofd, message, response, echo);
+ if (ifd != STDIN_FILENO)
+ close(ifd);
+ return (ret);
+}
+
+/*
+ * OpenPAM extension
+ *
+ * Simple tty-based conversation function
+ */
+
+int
+openpam_ttyconv(int n,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *data)
+{
+ char respbuf[PAM_MAX_RESP_SIZE];
+ struct pam_response *aresp;
+ int i;
+
+ ENTER();
+ (void)data;
+ if (n <= 0 || n > PAM_MAX_NUM_MSG)
+ RETURNC(PAM_CONV_ERR);
+ if ((aresp = calloc(n, sizeof *aresp)) == NULL)
+ RETURNC(PAM_BUF_ERR);
+ for (i = 0; i < n; ++i) {
+ aresp[i].resp_retcode = 0;
+ aresp[i].resp = NULL;
+ switch (msg[i]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+ if (prompt(msg[i]->msg, respbuf, 0) < 0 ||
+ (aresp[i].resp = strdup(respbuf)) == NULL)
+ goto fail;
+ break;
+ case PAM_PROMPT_ECHO_ON:
+ if (prompt(msg[i]->msg, respbuf, 1) < 0 ||
+ (aresp[i].resp = strdup(respbuf)) == NULL)
+ goto fail;
+ break;
+ case PAM_ERROR_MSG:
+ fputs(msg[i]->msg, stderr);
+ if (strlen(msg[i]->msg) > 0 &&
+ msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
+ fputc('\n', stderr);
+ break;
+ case PAM_TEXT_INFO:
+ fputs(msg[i]->msg, stdout);
+ if (strlen(msg[i]->msg) > 0 &&
+ msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
+ fputc('\n', stdout);
+ break;
+ default:
+ goto fail;
+ }
+ }
+ *resp = aresp;
+ memset(respbuf, 0, sizeof respbuf);
+ RETURNC(PAM_SUCCESS);
+fail:
+ for (i = 0; i < n; ++i) {
+ if (aresp[i].resp != NULL) {
+ memset(aresp[i].resp, 0, strlen(aresp[i].resp));
+ FREE(aresp[i].resp);
+ }
+ }
+ memset(aresp, 0, n * sizeof *aresp);
+ FREE(aresp);
+ *resp = NULL;
+ memset(respbuf, 0, sizeof respbuf);
+ RETURNC(PAM_CONV_ERR);
+}
+
+/*
+ * Error codes:
+ *
+ * PAM_SYSTEM_ERR
+ * PAM_BUF_ERR
+ * PAM_CONV_ERR
+ */
+
+/**
+ * The =openpam_ttyconv function is a standard conversation function
+ * suitable for use on TTY devices.
+ * It should be adequate for the needs of most text-based interactive
+ * programs.
+ *
+ * The =openpam_ttyconv function allows the application to specify a
+ * timeout for user input by setting the global integer variable
+ * :openpam_ttyconv_timeout to the length of the timeout in seconds.
+ *
+ * >openpam_nullconv
+ * >pam_prompt
+ * >pam_vprompt
+ */
OpenPOWER on IntegriCloud