1 /* $NetBSD: getpass.c,v 1.27 2012/05/26 19:34:16 christos Exp $ */
4 * Copyright (c) 2012 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
31 #include <sys/cdefs.h>
32 #if defined(LIBC_SCCS) && !defined(lint)
33 __RCSID("$NetBSD: getpass.c,v 1.27 2012/05/26 19:34:16 christos Exp $");
34 #endif /* LIBC_SCCS and not lint */
36 #include "namespace.h"
55 __weak_alias(getpassfd
,_getpassfd
)
56 __weak_alias(getpass_r
,_getpass_r
)
57 __weak_alias(getpass
,_getpass
)
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
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
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.
86 getpassfd(const char *prompt
, char *buf
, size_t len
, int *fd
, int flags
,
92 bool lnext
, havetty
, allocated
, opentty
, good
;
95 _DIAGASSERT(prompt
!= NULL
);
97 if (buf
!= NULL
&& len
== 0) {
106 * Try to use /dev/tty if possible; otherwise read from stdin
107 * and write to stderr.
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
;
118 allocated
= buf
== NULL
;
119 if (tcgetattr(fd
[0], >
) == -1) {
121 if (flags
& GETPASS_NEED_TTY
)
123 memset(>
, -1, sizeof(gt
));
128 struct termios st
= gt
;
130 st
.c_lflag
&= ~(ECHO
|ECHOK
|ECHOE
|ECHOKE
|ECHOCTL
|ISIG
|ICANON
);
133 if (tcsetattr(fd
[0], TCSAFLUSH
|TCSASOFT
, &st
) == -1)
137 if (prompt
!= NULL
) {
138 size_t plen
= strlen(prompt
);
139 (void)write(fd
[1], prompt
, plen
);
144 if ((buf
= malloc(len
)) == NULL
)
150 for (size_t l
= 0; c
!= '\0'; ) {
154 pfd
.events
= POLLIN
|POLLRDNORM
;
156 switch (poll(&pfd
, 1, tout
* 1000)) {
166 if (read(fd
[0], &c
, 1) != 1)
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)
176 * We test for both _POSIX_VDISABLE and NUL here because _POSIX_VDISABLE
177 * propagation does not seem to be very consistent on multiple daemon hops
178 * between different OS's. Perhaps we should not even bother with
179 * _POSIX_VDISABLE and use ~0 and 0 directly.
181 #define C(a, b) ((gt.c_cc[(a)] == _POSIX_VDISABLE || gt.c_cc[(a)] == '\0') ? \
189 if (c
== C(VREPRINT
, CTRL('r')) || c
== C(VSTART
, CTRL('q')) ||
190 c
== C(VSTOP
, CTRL('s')) || c
== C(VSTATUS
, CTRL('t')) ||
191 c
== C(VDISCARD
, CTRL('o')))
195 if (c
== C(VLNEXT
, CTRL('v'))) {
200 /* Line or word kill, treat as reset */
201 if (c
== C(VKILL
, CTRL('u')) || c
== C(VWERASE
, CTRL('w'))) {
202 if (flags
& (GETPASS_ECHO
| GETPASS_ECHO_STAR
)) {
210 /* Character erase */
211 if (c
== C(VERASE
, CTRL('h'))) {
216 if (flags
& (GETPASS_ECHO
| GETPASS_ECHO_STAR
))
222 /* tty signal characters */
223 if (c
== C(VINTR
, CTRL('c'))) {
227 if (c
== C(VQUIT
, CTRL('\\'))) {
231 if (c
== C(VSUSP
, CTRL('z')) || c
== C(VDSUSP
, CTRL('y'))) {
237 if (c
== C(VEOF
, CTRL('d'))) {
238 if (flags
& GETPASS_FAIL_EOF
) {
248 if (c
== C(VEOL
, CTRL('j')) || c
== C(VEOL2
, CTRL('l')))
253 size_t nlen
= len
+ 1024;
254 char *nbuf
= realloc(buf
, nlen
);
260 if (flags
& GETPASS_BUF_LIMIT
) {
264 if (c
== '\0' && l
> 0)
271 if (flags
& GETPASS_7BIT
)
273 if ((flags
& GETPASS_FORCE_LOWER
) && isupper((unsigned char)c
))
274 c
= tolower((unsigned char)c
);
275 if ((flags
& GETPASS_FORCE_UPPER
) && islower((unsigned char)c
))
276 c
= toupper((unsigned char)c
);
280 if (flags
& GETPASS_ECHO_STAR
)
281 (void)write(fd
[1], "*", 1);
282 else if (flags
& GETPASS_ECHO
)
283 (void)write(fd
[1], isprint((unsigned char)c
) ?
292 (void)tcsetattr(fd
[0], TCSAFLUSH
|TCSASOFT
, >
);
296 if (good
&& (flags
& GETPASS_ECHO_NL
))
297 (void)write(fd
[1], "\n", 1);
309 if ((flags
& GETPASS_NO_SIGNAL
) == 0)
320 getpass_r(const char *prompt
, char *buf
, size_t len
)
322 return getpassfd(prompt
, buf
, len
, NULL
, GETPASS_ECHO_NL
, 0);
326 getpass(const char *prompt
)
328 static char e
[] = "";
334 * Strictly speaking we could double allocate here, if we get
335 * called at the same time, but this function is not re-entrant
336 * anyway and it is not supposed to work if called concurrently.
339 if ((bufsiz
= sysconf(_SC_PASS_MAX
)) == -1)
341 if ((buf
= malloc((size_t)bufsiz
)) == NULL
)
345 if ((rv
= getpass_r(prompt
, buf
, (size_t)bufsiz
)) == NULL
)
353 main(int argc
, char *argv
[])
356 printf("[%s]\n", getpassfd("foo>", buf
, sizeof(buf
), NULL
,
357 GETPASS_ECHO_STAR
|GETPASS_ECHO_NL
, 2));