Initial commit
[authen-passphrase-scrypt.git] / scrypt-1.2.1 / libcperciva / util / readpass.c
CommitLineData
0c1f3509
MG
1#include <signal.h>
2#include <stdio.h>
3#include <string.h>
4#include <termios.h>
5#include <unistd.h>
6
7#include "insecure_memzero.h"
8#include "warnp.h"
9
10#include "readpass.h"
11
12#define MAXPASSLEN 2048
13
14/* Signals we need to block. */
15static const int badsigs[] = {
16 SIGALRM, SIGHUP, SIGINT,
17 SIGPIPE, SIGQUIT, SIGTERM,
18 SIGTSTP, SIGTTIN, SIGTTOU
19};
20#define NSIGS sizeof(badsigs)/sizeof(badsigs[0])
21
22/* Highest signal number we care about. */
23#define MAX2(a, b) ((a) > (b) ? (a) : (b))
24#define MAX4(a, b, c, d) MAX2(MAX2(a, b), MAX2(c, d))
25#define MAX8(a, b, c, d, e, f, g, h) MAX2(MAX4(a, b, c, d), MAX4(e, f, g, h))
26#define MAXBADSIG MAX2(SIGALRM, MAX8(SIGHUP, SIGINT, SIGPIPE, SIGQUIT, \
27 SIGTERM, SIGTSTP, SIGTTIN, SIGTTOU))
28
29/* Has a signal of this type been received? */
30static volatile sig_atomic_t gotsig[MAXBADSIG + 1];
31
32/* Signal handler. */
33static void
34handle(int sig)
35{
36
37 gotsig[sig] = 1;
38}
39
40/* Restore old signals and re-issue intercepted signals. */
41static void
42resetsigs(struct sigaction savedsa[NSIGS])
43{
44 size_t i;
45
46 /* Restore old signals. */
47 for (i = 0; i < NSIGS; i++)
48 sigaction(badsigs[i], &savedsa[i], NULL);
49
50 /* If we intercepted a signal, re-issue it. */
51 for (i = 0; i < NSIGS; i++) {
52 if (gotsig[badsigs[i]])
53 raise(badsigs[i]);
54 }
55}
56
57/**
58 * readpass(passwd, prompt, confirmprompt, devtty)
59 * If ${devtty} is non-zero, read a password from /dev/tty if possible; if
60 * not, read from stdin. If reading from a tty (either /dev/tty or stdin),
61 * disable echo and prompt the user by printing ${prompt} to stderr. If
62 * ${confirmprompt} is non-NULL, read a second password (prompting if a
63 * terminal is being used) and repeat until the user enters the same password
64 * twice. Return the password as a malloced NUL-terminated string via
65 * ${passwd}.
66 */
67int
68readpass(char ** passwd, const char * prompt,
69 const char * confirmprompt, int devtty)
70{
71 FILE * readfrom;
72 char passbuf[MAXPASSLEN];
73 char confpassbuf[MAXPASSLEN];
74 struct sigaction sa, savedsa[NSIGS];
75 struct termios term, term_old;
76 size_t i;
77 int usingtty;
78
79 /*
80 * If devtty != 0, try to open /dev/tty; if that fails, or if devtty
81 * is zero, we'll read the password from stdin instead.
82 */
83 if ((devtty == 0) || ((readfrom = fopen("/dev/tty", "r")) == NULL))
84 readfrom = stdin;
85
86 /* We have not received any signals yet. */
87 for (i = 0; i <= MAXBADSIG; i++)
88 gotsig[i] = 0;
89
90 /*
91 * If we receive a signal while we're reading the password, we might
92 * end up with echo disabled; to prevent this, we catch the signals
93 * here, and we'll re-send them to ourselves later after we re-enable
94 * terminal echo.
95 */
96 sa.sa_handler = handle;
97 sa.sa_flags = 0;
98 sigemptyset(&sa.sa_mask);
99 for (i = 0; i < NSIGS; i++)
100 sigaction(badsigs[i], &sa, &savedsa[i]);
101
102 /* If we're reading from a terminal, try to disable echo. */
103 if ((usingtty = isatty(fileno(readfrom))) != 0) {
104 if (tcgetattr(fileno(readfrom), &term_old)) {
105 warnp("Cannot read terminal settings");
106 goto err2;
107 }
108 memcpy(&term, &term_old, sizeof(struct termios));
109 term.c_lflag = (term.c_lflag & ~((tcflag_t)ECHO)) | ECHONL;
110 if (tcsetattr(fileno(readfrom), TCSANOW, &term)) {
111 warnp("Cannot set terminal settings");
112 goto err2;
113 }
114 }
115
116retry:
117 /* If we have a terminal, prompt the user to enter the password. */
118 if (usingtty)
119 fprintf(stderr, "%s: ", prompt);
120
121 /* Read the password. */
122 if (fgets(passbuf, MAXPASSLEN, readfrom) == NULL) {
123 if (feof(readfrom))
124 warn0("EOF reading password");
125 else
126 warnp("Cannot read password");
127 goto err3;
128 }
129
130 /* Confirm the password if necessary. */
131 if (confirmprompt != NULL) {
132 if (usingtty)
133 fprintf(stderr, "%s: ", confirmprompt);
134 if (fgets(confpassbuf, MAXPASSLEN, readfrom) == NULL) {
135 if (feof(readfrom))
136 warn0("EOF reading password");
137 else
138 warnp("Cannot read password");
139 goto err3;
140 }
141 if (strcmp(passbuf, confpassbuf)) {
142 fprintf(stderr,
143 "Passwords mismatch, please try again\n");
144 goto retry;
145 }
146 }
147
148 /* Terminate the string at the first "\r" or "\n" (if any). */
149 passbuf[strcspn(passbuf, "\r\n")] = '\0';
150
151 /* If we changed terminal settings, reset them. */
152 if (usingtty)
153 tcsetattr(fileno(readfrom), TCSANOW, &term_old);
154
155 /* Restore old signals and re-issue intercepted signals. */
156 resetsigs(savedsa);
157
158 /* Close /dev/tty if we opened it. */
159 if (readfrom != stdin)
160 fclose(readfrom);
161
162 /* Copy the password out. */
163 if ((*passwd = strdup(passbuf)) == NULL) {
164 warnp("Cannot allocate memory");
165 goto err1;
166 }
167
168 /*
169 * Zero any stored passwords. This is not guaranteed to work, since a
170 * "sufficiently intelligent" compiler can optimize these out due to
171 * the values not being accessed again; and even if we outwitted the
172 * compiler, all we can do is ensure that *a* buffer is zeroed but
173 * not that it is the only buffer containing the data in question.
174 * Unfortunately the C standard does not provide any way to mark data
175 * as "sensitive" in order to prevent extra copies being sprinkled
176 * around the implementation address space.
177 */
178 insecure_memzero(passbuf, MAXPASSLEN);
179 insecure_memzero(confpassbuf, MAXPASSLEN);
180
181 /* Success! */
182 return (0);
183
184err3:
185 /* Reset terminal settings if necessary. */
186 if (usingtty)
187 tcsetattr(fileno(readfrom), TCSAFLUSH, &term_old);
188err2:
189 /* Close /dev/tty if we opened it. */
190 if (readfrom != stdin)
191 fclose(readfrom);
192
193 /* Restore old signals and re-issue intercepted signals. */
194 resetsigs(savedsa);
195err1:
196 /* Zero any stored passwords. */
197 insecure_memzero(passbuf, MAXPASSLEN);
198 insecure_memzero(confpassbuf, MAXPASSLEN);
199
200 /* Failure! */
201 return (-1);
202}
This page took 0.021833 seconds and 4 git commands to generate.