/* $OpenBSD: cli.c,v 1.11 2001/03/06 00:33:04 deraadt Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. */ #include "includes.h" RCSID("$OpenBSD: cli.c,v 1.11 2001/03/06 00:33:04 deraadt Exp $"); #include "xmalloc.h" #include "log.h" #include "cli.h" static int cli_input = -1; static int cli_output = -1; static int cli_from_stdin = 0; sigset_t oset; sigset_t nset; struct sigaction nsa; struct sigaction osa; struct termios ntio; struct termios otio; int echo_modified; volatile int intr; static int cli_open(int from_stdin) { if (cli_input >= 0 && cli_output >= 0 && cli_from_stdin == from_stdin) return 1; if (from_stdin) { if (!cli_from_stdin && cli_input >= 0) { (void)close(cli_input); } cli_input = STDIN_FILENO; cli_output = STDERR_FILENO; } else { cli_input = cli_output = open(_PATH_TTY, O_RDWR); if (cli_input < 0) fatal("You have no controlling tty. Cannot read passphrase."); } cli_from_stdin = from_stdin; return cli_input >= 0 && cli_output >= 0 && cli_from_stdin == from_stdin; } static void cli_close(void) { if (!cli_from_stdin && cli_input >= 0) close(cli_input); cli_input = -1; cli_output = -1; cli_from_stdin = 0; return; } void intrcatch(int sig) { intr = 1; } static void cli_echo_disable(void) { sigemptyset(&nset); sigaddset(&nset, SIGTSTP); (void) sigprocmask(SIG_BLOCK, &nset, &oset); intr = 0; memset(&nsa, 0, sizeof(nsa)); nsa.sa_handler = intrcatch; (void) sigaction(SIGINT, &nsa, &osa); echo_modified = 0; if (tcgetattr(cli_input, &otio) == 0 && (otio.c_lflag & ECHO)) { echo_modified = 1; ntio = otio; ntio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); (void) tcsetattr(cli_input, TCSANOW, &ntio); } return; } static void cli_echo_restore(void) { if (echo_modified != 0) { tcsetattr(cli_input, TCSANOW, &otio); echo_modified = 0; } (void) sigprocmask(SIG_SETMASK, &oset, NULL); (void) sigaction(SIGINT, &osa, NULL); if (intr != 0) { kill(getpid(), SIGINT); sigemptyset(&nset); /* XXX tty has not neccessarily drained by now? */ sigsuspend(&nset); intr = 0; } return; } static int cli_read(char* buf, int size, int echo) { char ch = 0; int i = 0; int n; if (!echo) cli_echo_disable(); while (ch != '\n') { n = read(cli_input, &ch, 1); if (n == -1 && (errno == EAGAIN || errno == EINTR)) continue; if (n != 1) break; if (ch == '\n' || intr != 0) break; if (i < size) buf[i++] = ch; } buf[i] = '\0'; if (!echo) cli_echo_restore(); if (!intr && !echo) (void) write(cli_output, "\n", 1); return i; } static int cli_write(const char* buf, int size) { int i, len, pos, ret = 0; char *output, *p; output = xmalloc(4*size); for (p = output, i = 0; i < size; i++) { if (buf[i] == '\n' || buf[i] == '\r') *p++ = buf[i]; else p = vis(p, buf[i], 0, 0); } len = p - output; for (pos = 0; pos < len; pos += ret) { ret = write(cli_output, output + pos, len - pos); if (ret == -1) { xfree(output); return -1; } } xfree(output); return 0; } /* * Presents a prompt and returns the response allocated with xmalloc(). * Uses /dev/tty or stdin/out depending on arg. Optionally disables echo * of response depending on arg. Tries to ensure that no other userland * buffer is storing the response. */ char* cli_read_passphrase(const char* prompt, int from_stdin, int echo_enable) { char buf[BUFSIZ]; char* p; if (!cli_open(from_stdin)) fatal("Cannot read passphrase."); fflush(stdout); cli_write(prompt, strlen(prompt)); cli_read(buf, sizeof buf, echo_enable); cli_close(); p = xstrdup(buf); memset(buf, 0, sizeof(buf)); return (p); } char* cli_prompt(char* prompt, int echo_enable) { return cli_read_passphrase(prompt, 0, echo_enable); } void cli_mesg(char* mesg) { cli_open(0); cli_write(mesg, strlen(mesg)); cli_write("\n", strlen("\n")); cli_close(); return; }