2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A#include "k5-int.h"
2N/A#if !defined(_WIN32) || (defined(_WIN32) && defined(__CYGWIN32__))
2N/A#include <stdio.h>
2N/A#include <errno.h>
2N/A#include <signal.h>
2N/A#include <limits.h>
2N/A/* Is vxworks broken w.r.t. termios? --tlyu */
2N/A#ifdef __vxworks
2N/A#define ECHO_PASSWORD
2N/A#endif
2N/A
2N/A#include <termios.h>
2N/A
2N/A#ifdef POSIX_SIGNALS
2N/Atypedef struct sigaction osiginfo;
2N/A#else
2N/Atypedef struct krb5_sigtype (*osiginfo)();
2N/A#endif
2N/A
2N/Astatic void catch_signals(osiginfo *);
2N/Astatic void restore_signals(osiginfo *);
2N/Astatic krb5_sigtype intrfunc(int sig);
2N/A
2N/Astatic krb5_error_code setup_tty(FILE*, int, struct termios *, osiginfo *);
2N/Astatic krb5_error_code restore_tty(FILE*, struct termios *, osiginfo *);
2N/A
2N/Astatic volatile int got_int; /* should be sig_atomic_t */
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_prompter_posix(
2N/A krb5_context context,
2N/A void *data,
2N/A const char *name,
2N/A const char *banner,
2N/A int num_prompts,
2N/A krb5_prompt prompts[])
2N/A{
2N/A int fd, i, scratchchar;
2N/A FILE *fp;
2N/A char *retp;
2N/A krb5_error_code errcode;
2N/A struct termios saveparm;
2N/A osiginfo osigint;
2N/A
2N/A errcode = KRB5_LIBOS_CANTREADPWD;
2N/A
2N/A if (name) {
2N/A fputs(name, stdout);
2N/A fputs("\n", stdout);
2N/A }
2N/A if (banner) {
2N/A fputs(banner, stdout);
2N/A fputs("\n", stdout);
2N/A }
2N/A
2N/A /*
2N/A * Get a non-buffered stream on stdin.
2N/A */
2N/A fp = NULL;
2N/A fd = dup(STDIN_FILENO);
2N/A if (fd < 0)
2N/A return KRB5_LIBOS_CANTREADPWD;
2N/A set_cloexec_fd(fd);
2N/A fp = fdopen(fd, "r");
2N/A if (fp == NULL)
2N/A goto cleanup;
2N/A if (setvbuf(fp, NULL, _IONBF, 0))
2N/A goto cleanup;
2N/A
2N/A for (i = 0; i < num_prompts; i++) {
2N/A errcode = KRB5_LIBOS_CANTREADPWD;
2N/A /* fgets() takes int, but krb5_data.length is unsigned. */
2N/A if (prompts[i].reply->length > INT_MAX)
2N/A goto cleanup;
2N/A
2N/A errcode = setup_tty(fp, prompts[i].hidden, &saveparm, &osigint);
2N/A if (errcode)
2N/A break;
2N/A
2N/A /* put out the prompt */
2N/A (void)fputs(prompts[i].prompt, stdout);
2N/A (void)fputs(": ", stdout);
2N/A (void)fflush(stdout);
2N/A (void)memset(prompts[i].reply->data, 0, prompts[i].reply->length);
2N/A
2N/A got_int = 0;
2N/A retp = fgets(prompts[i].reply->data, (int)prompts[i].reply->length,
2N/A fp);
2N/A if (prompts[i].hidden) {
2N/A putchar('\n');
2N/A /* Solaris Kerberos */
2N/A fflush(stdout);
2N/A }
2N/A if (retp == NULL) {
2N/A if (got_int)
2N/A errcode = KRB5_LIBOS_PWDINTR;
2N/A else
2N/A errcode = KRB5_LIBOS_CANTREADPWD;
2N/A restore_tty(fp, &saveparm, &osigint);
2N/A break;
2N/A }
2N/A
2N/A /* replace newline with null */
2N/A retp = strchr(prompts[i].reply->data, '\n');
2N/A if (retp != NULL)
2N/A *retp = '\0';
2N/A else {
2N/A /* flush rest of input line */
2N/A do {
2N/A scratchchar = getc(fp);
2N/A } while (scratchchar != EOF && scratchchar != '\n');
2N/A }
2N/A
2N/A errcode = restore_tty(fp, &saveparm, &osigint);
2N/A if (errcode)
2N/A break;
2N/A prompts[i].reply->length = strlen(prompts[i].reply->data);
2N/A }
2N/Acleanup:
2N/A if (fp != NULL)
2N/A fclose(fp);
2N/A else if (fd >= 0)
2N/A close(fd);
2N/A
2N/A return errcode;
2N/A}
2N/A
2N/Astatic krb5_sigtype
2N/Aintrfunc(int sig)
2N/A{
2N/A got_int = 1;
2N/A}
2N/A
2N/Astatic void
2N/Acatch_signals(osiginfo *osigint)
2N/A{
2N/A#ifdef POSIX_SIGNALS
2N/A struct sigaction sa;
2N/A
2N/A sigemptyset(&sa.sa_mask);
2N/A sa.sa_flags = 0;
2N/A sa.sa_handler = intrfunc;
2N/A sigaction(SIGINT, &sa, osigint);
2N/A#else
2N/A *osigint = signal(SIGINT, intrfunc);
2N/A#endif
2N/A}
2N/A
2N/Astatic void
2N/Arestore_signals(osiginfo *osigint)
2N/A{
2N/A#ifdef POSIX_SIGNALS
2N/A sigaction(SIGINT, osigint, NULL);
2N/A#else
2N/A signal(SIGINT, *osigint);
2N/A#endif
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Asetup_tty(FILE *fp, int hidden, struct termios *saveparm, osiginfo *osigint)
2N/A{
2N/A krb5_error_code ret;
2N/A int fd;
2N/A struct termios tparm;
2N/A
2N/A ret = KRB5_LIBOS_CANTREADPWD;
2N/A catch_signals(osigint);
2N/A fd = fileno(fp);
2N/A do {
2N/A if (!isatty(fd)) {
2N/A ret = 0;
2N/A break;
2N/A }
2N/A if (tcgetattr(fd, &tparm) < 0)
2N/A break;
2N/A *saveparm = tparm;
2N/A#ifndef ECHO_PASSWORD
2N/A if (hidden)
2N/A tparm.c_lflag &= ~(ECHO|ECHONL);
2N/A#endif
2N/A tparm.c_lflag |= ISIG|ICANON;
2N/A if (tcsetattr(STDIN_FILENO, TCSANOW, &tparm) < 0)
2N/A break;
2N/A ret = 0;
2N/A } while (0);
2N/A /* If we're losing, restore signal handlers. */
2N/A if (ret)
2N/A restore_signals(osigint);
2N/A return ret;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Arestore_tty(FILE* fp, struct termios *saveparm, osiginfo *osigint)
2N/A{
2N/A int ret, fd;
2N/A
2N/A ret = 0;
2N/A fd = fileno(fp);
2N/A if (isatty(fd)) {
2N/A ret = tcsetattr(fd, TCSANOW, saveparm);
2N/A if (ret < 0)
2N/A ret = KRB5_LIBOS_CANTREADPWD;
2N/A else
2N/A ret = 0;
2N/A }
2N/A restore_signals(osigint);
2N/A return ret;
2N/A}
2N/A
2N/A#else /* non-Cygwin Windows, or Mac */
2N/A
2N/A#if defined(_WIN32)
2N/A
2N/A#include <io.h>
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_prompter_posix(krb5_context context,
2N/A void *data,
2N/A const char *name,
2N/A const char *banner,
2N/A int num_prompts,
2N/A krb5_prompt prompts[])
2N/A{
2N/A HANDLE handle;
2N/A DWORD old_mode, new_mode;
2N/A char *ptr;
2N/A int scratchchar;
2N/A krb5_error_code errcode = 0;
2N/A int i;
2N/A
2N/A handle = GetStdHandle(STD_INPUT_HANDLE);
2N/A if (handle == INVALID_HANDLE_VALUE)
2N/A return ENOTTY;
2N/A if (!GetConsoleMode(handle, &old_mode))
2N/A return ENOTTY;
2N/A
2N/A new_mode = old_mode;
2N/A new_mode |= ( ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT );
2N/A new_mode &= ~( ENABLE_ECHO_INPUT );
2N/A
2N/A if (!SetConsoleMode(handle, new_mode))
2N/A return ENOTTY;
2N/A
2N/A if (!SetConsoleMode(handle, old_mode))
2N/A return ENOTTY;
2N/A
2N/A if (name) {
2N/A fputs(name, stdout);
2N/A fputs("\n", stdout);
2N/A }
2N/A
2N/A if (banner) {
2N/A fputs(banner, stdout);
2N/A fputs("\n", stdout);
2N/A }
2N/A
2N/A for (i = 0; i < num_prompts; i++) {
2N/A if (prompts[i].hidden) {
2N/A if (!SetConsoleMode(handle, new_mode)) {
2N/A errcode = ENOTTY;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A fputs(prompts[i].prompt,stdout);
2N/A fputs(": ", stdout);
2N/A fflush(stdout);
2N/A memset(prompts[i].reply->data, 0, prompts[i].reply->length);
2N/A
2N/A if (fgets(prompts[i].reply->data, prompts[i].reply->length, stdin)
2N/A == NULL) {
2N/A if (prompts[i].hidden)
2N/A putchar('\n');
2N/A errcode = KRB5_LIBOS_CANTREADPWD;
2N/A goto cleanup;
2N/A }
2N/A if (prompts[i].hidden)
2N/A putchar('\n');
2N/A /* fgets always null-terminates the returned string */
2N/A
2N/A /* replace newline with null */
2N/A if ((ptr = strchr(prompts[i].reply->data, '\n')))
2N/A *ptr = '\0';
2N/A else /* flush rest of input line */
2N/A do {
2N/A scratchchar = getchar();
2N/A } while (scratchchar != EOF && scratchchar != '\n');
2N/A
2N/A prompts[i].reply->length = strlen(prompts[i].reply->data);
2N/A
2N/A if (!SetConsoleMode(handle, old_mode)) {
2N/A errcode = ENOTTY;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/Acleanup:
2N/A if (errcode) {
2N/A for (i = 0; i < num_prompts; i++) {
2N/A memset(prompts[i].reply->data, 0, prompts[i].reply->length);
2N/A }
2N/A }
2N/A return errcode;
2N/A}
2N/A
2N/A#else /* !_WIN32 */
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_prompter_posix(krb5_context context,
2N/A void *data,
2N/A const char *name,
2N/A const char *banner,
2N/A int num_prompts,
2N/A krb5_prompt prompts[])
2N/A{
2N/A return(EINVAL);
2N/A}
2N/A#endif /* !_WIN32 */
2N/A#endif /* Windows or Mac */
2N/A
2N/Avoid
2N/Akrb5int_set_prompt_types(krb5_context context, krb5_prompt_type *types)
2N/A{
2N/A context->prompt_types = types;
2N/A}
2N/A
2N/Akrb5_prompt_type*
2N/AKRB5_CALLCONV
2N/Akrb5_get_prompt_types(krb5_context context)
2N/A{
2N/A return context->prompt_types;
2N/A}