1 /*	$OpenBSD: readpassphrase.c,v 1.12 2001/12/15 05:41:00 millert Exp $
2  */
3 
4 /*
5  * Copyright (c) 2000 Todd C. Miller <Todd.Miller@courtesan.com>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
21  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
22  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #if defined(LIBC_SCCS) && !defined(lint)
32 static const char rcsid[] =
33     "$OpenBSD: readpassphrase.c,v 1.12 2001/12/15 05:41:00 millert Exp $";
34 #endif /* LIBC_SCCS and not lint */
35 
36 //#include "includes.h"
37 
38 #ifndef HAVE_READPASSPHRASE
39 
40 #include "readpassphrase.h"
41 
42 #include <cctype>
43 #include <cerrno>
44 #include <csignal>
45 #include <cstdio>
46 #include <cstring>
47 #include <fcntl.h>
48 #include <paths.h>
49 #include <sys/types.h>
50 #include <termios.h>
51 #include <unistd.h>
52 
53 #ifdef TCSASOFT
54 #define _T_FLUSH (TCSAFLUSH | TCSASOFT)
55 #else
56 #define _T_FLUSH (TCSAFLUSH)
57 #endif
58 
59 /* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */
60 #if !defined(_POSIX_VDISABLE) && defined(VDISABLE)
61 #define _POSIX_VDISABLE VDISABLE
62 #endif
63 
64 static volatile sig_atomic_t signo;
65 
66 static void handler(int);
67 
readpassphrase(const char * prompt,char * buf,size_t bufsiz,int flags)68 char *readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) {
69   ssize_t nr;
70   int input, output, save_errno;
71   char ch, *p, *end;
72   struct termios term, oterm;
73   struct sigaction sa, saveint, savehup, savequit, saveterm;
74   struct sigaction savetstp, savettin, savettou;
75 
76   /* I suppose we could alloc on demand in this case (XXX). */
77   if (bufsiz == 0) {
78     errno = EINVAL;
79     return (nullptr);
80   }
81 
82 restart:
83   /*
84    * Read and write to /dev/tty if available.  If not, read from
85    * stdin and write to stderr unless a tty is required.
86    */
87   if ((input = output = open(_PATH_TTY, O_RDWR)) == -1) {
88     if ((flags & RPP_REQUIRE_TTY) != 0) {
89       errno = ENOTTY;
90       return (nullptr);
91     }
92     input = STDIN_FILENO;
93     output = STDERR_FILENO;
94   }
95 
96   /*
97    * Catch signals that would otherwise cause the user to end
98    * up with echo turned off in the shell.  Don't worry about
99    * things like SIGALRM and SIGPIPE for now.
100    */
101   sigemptyset(&sa.sa_mask);
102   sa.sa_flags = 0; /* don't restart system calls */
103   sa.sa_handler = handler;
104   (void)sigaction(SIGINT, &sa, &saveint);
105   (void)sigaction(SIGHUP, &sa, &savehup);
106   (void)sigaction(SIGQUIT, &sa, &savequit);
107   (void)sigaction(SIGTERM, &sa, &saveterm);
108   (void)sigaction(SIGTSTP, &sa, &savetstp);
109   (void)sigaction(SIGTTIN, &sa, &savettin);
110   (void)sigaction(SIGTTOU, &sa, &savettou);
111 
112   /* Turn off echo if possible. */
113   if (tcgetattr(input, &oterm) == 0) {
114     memcpy(&term, &oterm, sizeof(term));
115     if ((flags & RPP_ECHO_ON) == 0) {
116       term.c_lflag &= ~(ECHO | ECHONL);
117     }
118 #ifdef VSTATUS
119     if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) {
120       term.c_cc[VSTATUS] = _POSIX_VDISABLE;
121     }
122 #endif
123     (void)tcsetattr(input, _T_FLUSH, &term);
124   } else {
125     memset(&term, 0, sizeof(term));
126     memset(&oterm, 0, sizeof(oterm));
127   }
128 
129   if (write(output, prompt, strlen(prompt)) != -1) {
130     //dummy test to get rid of warn_unused_result compilation warning
131   }
132   end = buf + bufsiz - 1;
133   for (p = buf; (nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r';) {
134     if (p < end) {
135       if ((flags & RPP_SEVENBIT) != 0) {
136         ch &= 0x7f;
137       }
138       if (isalpha(ch) != 0) {
139         if ((flags & RPP_FORCELOWER) != 0) {
140           ch = tolower(ch);
141         }
142         if ((flags & RPP_FORCEUPPER) != 0) {
143           ch = toupper(ch);
144         }
145       }
146       *p++ = ch;
147     }
148   }
149   *p = '\0';
150   save_errno = errno;
151   if ((term.c_lflag & ECHO) == 0u) {
152     if (write(output, "\n", 1) != -1) {
153       //dummy test to get rid of warn_unused_result compilation warning
154     }
155   }
156 
157   /* Restore old terminal settings and signals. */
158   if (memcmp(&term, &oterm, sizeof(term)) != 0) {
159     (void)tcsetattr(input, _T_FLUSH, &oterm);
160   }
161   (void)sigaction(SIGINT, &saveint, nullptr);
162   (void)sigaction(SIGHUP, &savehup, nullptr);
163   (void)sigaction(SIGQUIT, &savequit, nullptr);
164   (void)sigaction(SIGTERM, &saveterm, nullptr);
165   (void)sigaction(SIGTSTP, &savetstp, nullptr);
166   (void)sigaction(SIGTTIN, &savettin, nullptr);
167   (void)sigaction(SIGTTOU, &savettou, nullptr);
168   if (input != STDIN_FILENO) {
169     (void)close(input);
170   }
171 
172   /*
173    * If we were interrupted by a signal, resend it to ourselves
174    * now that we have restored the signal handlers.
175    */
176   if (signo != 0) {
177     kill(getpid(), signo);
178     switch (signo) {
179       case SIGTSTP:
180       case SIGTTIN:
181       case SIGTTOU:
182         signo = 0;
183         goto restart;
184     }
185   }
186 
187   errno = save_errno;
188   return (nr == -1 ? nullptr : buf);
189 }
190 #endif /* HAVE_READPASSPHRASE */
191 
192 #if 0
193 char *
194 getpass(const char *prompt)
195 {
196 	static char buf[_PASSWORD_LEN + 1];
197 
198 	return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF));
199 }
200 #endif
201 
handler(int s)202 static void handler(int s) { signo = s; }
203