getpass.c revision 1.16.28.1 1 /* $NetBSD: getpass.c,v 1.16.28.1 2012/04/17 00:05:18 yamt Exp $ */
2
3 /*-
4 * Copyright (c) 2012 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <sys/cdefs.h>
32 #if defined(LIBC_SCCS) && !defined(lint)
33 __RCSID("$NetBSD: getpass.c,v 1.16.28.1 2012/04/17 00:05:18 yamt Exp $");
34 #endif /* LIBC_SCCS and not lint */
35
36 #include "namespace.h"
37
38 #include <assert.h>
39 #ifdef TEST
40 #include <stdio.h>
41 #endif
42 #include <errno.h>
43 #include <ctype.h>
44 #include <signal.h>
45 #include <string.h>
46 #include <paths.h>
47 #include <stdbool.h>
48 #include <stdlib.h>
49 #include <termios.h>
50 #include <unistd.h>
51 #include <fcntl.h>
52 #include <poll.h>
53
54 #ifdef __weak_alias
55 __weak_alias(getpassfd,_getpassfd)
56 __weak_alias(getpass_r,_getpass_r)
57 __weak_alias(getpass,_getpass)
58 #endif
59
60 /*
61 * Notes:
62 * - There is no getpass_r in POSIX
63 * - Historically EOF is documented to be treated as EOL, we provide a
64 * tunable for that GETPASS_FAIL_EOF to disable this.
65 * - Historically getpass ate extra characters silently, we provide
66 * a tunable for that GETPASS_BUF_LIMIT to disable this.
67 * - Historically getpass "worked" by echoing characters when turning
68 * off echo failed, we provide a tunable GETPASS_NEED_TTY to
69 * disable this.
70 * - Some implementations say that on interrupt the program shall
71 * receive an interrupt signal before the function returns. We
72 * send all the tty signals before we return, but we don't expect
73 * suspend to do something useful unless the caller calls us again.
74 * We also provide a tunable to disable signal delivery
75 * GETPASS_NO_SIGNAL.
76 * - GETPASS_NO_BEEP disables beeping.
77 * - GETPASS_ECHO_STAR will echo '*' for each character of the password
78 * - GETPASS_ECHO will echo the password (as pam likes it)
79 * - GETPASS_7BIT strips the 8th bit
80 * - GETPASS_FORCE_UPPER forces to uppercase
81 * - GETPASS_FORCE_LOWER forces to uppercase
82 * - GETPASS_ECHO_NL echo's a new line on success if echo was off.
83 */
84 char *
85 /*ARGSUSED*/
86 getpassfd(const char *prompt, char *buf, size_t len, int *fd, int flags,
87 int tout)
88 {
89 struct termios gt;
90 char c;
91 int sig;
92 bool lnext, havetty, allocated, opentty, good;
93 int fdc[3];
94
95 _DIAGASSERT(prompt != NULL);
96
97 if (buf != NULL && len == 0) {
98 errno = EINVAL;
99 return NULL;
100 }
101
102 good = false;
103 opentty = false;
104 if (fd == NULL) {
105 /*
106 * Try to use /dev/tty if possible; otherwise read from stdin
107 * and write to stderr.
108 */
109 fd = fdc;
110 if ((fd[0] = fd[1] = fd[2] = open(_PATH_TTY, O_RDWR)) == -1) {
111 fd[0] = STDIN_FILENO;
112 fd[1] = fd[2] = STDERR_FILENO;
113 } else
114 opentty = true;
115 }
116
117 sig = 0;
118 allocated = buf == NULL;
119 if (tcgetattr(fd[0], >) == -1) {
120 havetty = false;
121 if (flags & GETPASS_NEED_TTY)
122 goto out;
123 memset(>, -1, sizeof(gt));
124 } else
125 havetty = true;
126
127 if (havetty) {
128 struct termios st = gt;
129
130 st.c_lflag &= ~(ECHO|ECHOK|ECHOE|ECHOKE|ECHOCTL|ISIG|ICANON);
131 st.c_cc[VMIN] = 1;
132 st.c_cc[VTIME] = 0;
133 if (tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, &st) == -1)
134 goto out;
135 }
136
137 if (prompt != NULL) {
138 size_t plen = strlen(prompt);
139 (void)write(fd[1], prompt, plen);
140 }
141
142 if (allocated) {
143 len = 1024;
144 if ((buf = malloc(len)) == NULL)
145 goto restore;
146 }
147
148 c = '\1';
149 lnext = false;
150 for (size_t l = 0; c != '\0'; ) {
151 if (tout) {
152 struct pollfd pfd;
153 pfd.fd = fd[0];
154 pfd.events = POLLIN|POLLRDNORM;
155 pfd.revents = 0;
156 switch (poll(&pfd, 1, tout * 1000)) {
157 case 0:
158 errno = ETIMEDOUT;
159 /*FALLTHROUGH*/
160 case -1:
161 goto restore;
162 default:
163 break;
164 }
165 }
166 if (read(fd[0], &c, 1) != 1)
167 goto restore;
168
169 #define beep() \
170 do \
171 if (flags & GETPASS_NO_BEEP) \
172 (void)write(fd[2], "\a", 1); \
173 while (/*CONSTCOND*/ 0)
174 #define erase() (void)write(fd[1], "\b \b", 3)
175 #define C(a, b) (gt.c_cc[(a)] == _POSIX_VDISABLE ? (b) : gt.c_cc[(a)])
176
177 if (lnext) {
178 lnext = false;
179 goto add;
180 }
181
182 /* Ignored */
183 if (c == C(VREPRINT, CTRL('r')) || c == C(VSTART, CTRL('q')) ||
184 c == C(VSTOP, CTRL('s')) || c == C(VSTATUS, CTRL('t')) ||
185 c == C(VDISCARD, CTRL('o')))
186 continue;
187
188 /* Literal next */
189 if (c == C(VLNEXT, CTRL('v'))) {
190 lnext = true;
191 continue;
192 }
193
194 /* Line or word kill, treat as reset */
195 if (c == C(VKILL, CTRL('u')) || c == C(VWERASE, CTRL('w'))) {
196 if (flags & (GETPASS_ECHO | GETPASS_ECHO_STAR)) {
197 while (l--)
198 erase();
199 }
200 l = 0;
201 continue;
202 }
203
204 /* Character erase */
205 if (c == C(VERASE, CTRL('h'))) {
206 if (l == 0)
207 beep();
208 else {
209 l--;
210 if (flags & (GETPASS_ECHO | GETPASS_ECHO_STAR))
211 erase();
212 }
213 continue;
214 }
215
216 /* tty signal characters */
217 if (c == C(VINTR, CTRL('c'))) {
218 sig = SIGINT;
219 goto out;
220 }
221 if (c == C(VQUIT, CTRL('\\'))) {
222 sig = SIGQUIT;
223 goto out;
224 }
225 if (c == C(VSUSP, CTRL('z')) || c == C(VDSUSP, CTRL('y'))) {
226 sig = SIGTSTP;
227 goto out;
228 }
229
230 /* EOF */
231 if (c == C(VEOF, CTRL('d'))) {
232 if (flags & GETPASS_FAIL_EOF) {
233 errno = ENODATA;
234 goto out;
235 } else {
236 c = '\0';
237 goto add;
238 }
239 }
240
241 /* End of line */
242 if (c == C(VEOL, CTRL('j')) || c == C(VEOL2, CTRL('l')))
243 c = '\0';
244 add:
245 if (l >= len) {
246 if (allocated) {
247 size_t nlen = len + 1024;
248 char *nbuf = realloc(buf, nlen);
249 if (nbuf == NULL)
250 goto restore;
251 buf = nbuf;
252 len = nlen;
253 } else {
254 if (flags & GETPASS_BUF_LIMIT) {
255 beep();
256 continue;
257 }
258 if (c == '\0' && l > 0)
259 l--;
260 else
261 continue;
262 }
263 }
264
265 if (flags & GETPASS_7BIT)
266 c &= 0x7f;
267 if ((flags & GETPASS_FORCE_LOWER) && isupper((unsigned char)c))
268 c = tolower((unsigned char)c);
269 if ((flags & GETPASS_FORCE_UPPER) && islower((unsigned char)c))
270 c = toupper((unsigned char)c);
271
272 buf[l++] = c;
273 if (c) {
274 if (flags & GETPASS_ECHO_STAR)
275 (void)write(fd[1], "*", 1);
276 else if (flags & GETPASS_ECHO)
277 (void)write(fd[1], isprint((unsigned char)c) ?
278 &c : "?", 1);
279 }
280 }
281 good = true;
282
283 restore:
284 if (havetty) {
285 c = errno;
286 (void)tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, >);
287 errno = c;
288 }
289 out:
290 if (good && (flags & GETPASS_ECHO_NL))
291 (void)write(fd[1], "\n", 1);
292
293 if (opentty) {
294 c = errno;
295 (void)close(fd[0]);
296 errno = c;
297 }
298
299 if (good)
300 return buf;
301
302 if (sig) {
303 if ((flags & GETPASS_NO_SIGNAL) == 0)
304 (void)raise(sig);
305 errno = EINTR;
306 }
307 memset(buf, 0, len);
308 if (allocated)
309 free(buf);
310 return NULL;
311 }
312
313 char *
314 getpass_r(const char *prompt, char *buf, size_t len)
315 {
316 return getpassfd(prompt, buf, len, NULL, 0, 0);
317 }
318
319 char *
320 getpass(const char *prompt)
321 {
322 static char e[] = "";
323 static char *buf;
324 static long bufsiz;
325 char *rv;
326
327 /*
328 * Strictly speaking we could double allocate here, if we get
329 * called at the same time, but this function is not re-entrant
330 * anyway and it is not supposed to work if called concurrently.
331 */
332 if (buf == NULL) {
333 if ((bufsiz = sysconf(_SC_PASS_MAX)) == -1)
334 return e;
335 if ((buf = malloc((size_t)bufsiz)) == NULL)
336 return e;
337 }
338
339 if ((rv = getpass_r(prompt, buf, (size_t)bufsiz)) == NULL)
340 return e;
341
342 return rv;
343 }
344
345 #ifdef TEST
346 int
347 main(int argc, char *argv[])
348 {
349 char buf[28];
350 printf("[%s]\n", getpassfd("foo>", buf, sizeof(buf), NULL,
351 GETPASS_ECHO_STAR|GETPASS_ECHO_NL, 2));
352 return 0;
353 }
354 #endif
355