1 /* $NetBSD: getpass.c,v 1.29 2014/09/18 13:58:20 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.29 2014/09/18 13:58:20 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
,
111 O_RDWR
| O_CLOEXEC
)) == -1) {
112 fd
[0] = STDIN_FILENO
;
113 fd
[1] = fd
[2] = STDERR_FILENO
;
119 allocated
= buf
== NULL
;
120 if (tcgetattr(fd
[0], >
) == -1) {
122 if (flags
& GETPASS_NEED_TTY
)
124 memset(>
, -1, sizeof(gt
));
129 struct termios st
= gt
;
131 st
.c_lflag
&= ~(ECHO
|ECHOK
|ECHOE
|ECHOKE
|ECHOCTL
|ISIG
|ICANON
);
134 if (tcsetattr(fd
[0], TCSAFLUSH
|TCSASOFT
, &st
) == -1)
138 if (prompt
!= NULL
) {
139 size_t plen
= strlen(prompt
);
140 (void)write(fd
[1], prompt
, plen
);
145 if ((buf
= malloc(len
)) == NULL
)
151 for (size_t l
= 0; c
!= '\0'; ) {
155 pfd
.events
= POLLIN
|POLLRDNORM
;
157 switch (poll(&pfd
, 1, tout
* 1000)) {
167 if (read(fd
[0], &c
, 1) != 1)
172 if (flags & GETPASS_NO_BEEP) \
173 (void)write(fd[2], "\a", 1); \
174 while (/*CONSTCOND*/ 0)
175 #define erase() (void)write(fd[1], "\b \b", 3)
177 * We test for both _POSIX_VDISABLE and NUL here because _POSIX_VDISABLE
178 * propagation does not seem to be very consistent on multiple daemon hops
179 * between different OS's. Perhaps we should not even bother with
180 * _POSIX_VDISABLE and use ~0 and 0 directly.
182 #define C(a, b) ((gt.c_cc[(a)] == _POSIX_VDISABLE || gt.c_cc[(a)] == '\0') ? \
190 if (c
== C(VREPRINT
, CTRL('r')) || c
== C(VSTART
, CTRL('q')) ||
191 c
== C(VSTOP
, CTRL('s')) || c
== C(VSTATUS
, CTRL('t')) ||
192 c
== C(VDISCARD
, CTRL('o')))
196 if (c
== C(VLNEXT
, CTRL('v'))) {
201 /* Line or word kill, treat as reset */
202 if (c
== C(VKILL
, CTRL('u')) || c
== C(VWERASE
, CTRL('w'))) {
203 if (flags
& (GETPASS_ECHO
| GETPASS_ECHO_STAR
)) {
211 /* Character erase */
212 if (c
== C(VERASE
, CTRL('h'))) {
217 if (flags
& (GETPASS_ECHO
| GETPASS_ECHO_STAR
))
223 /* tty signal characters */
224 if (c
== C(VINTR
, CTRL('c'))) {
228 if (c
== C(VQUIT
, CTRL('\\'))) {
232 if (c
== C(VSUSP
, CTRL('z')) || c
== C(VDSUSP
, CTRL('y'))) {
238 if (c
== C(VEOF
, CTRL('d'))) {
239 if (flags
& GETPASS_FAIL_EOF
) {
249 if (c
== C(VEOL
, CTRL('j')) || c
== C(VEOL2
, CTRL('m')))
254 size_t nlen
= len
+ 1024;
255 char *nbuf
= realloc(buf
, nlen
);
261 if (flags
& GETPASS_BUF_LIMIT
) {
265 if (c
== '\0' && l
> 0)
272 if (flags
& GETPASS_7BIT
)
274 if ((flags
& GETPASS_FORCE_LOWER
) && isupper((unsigned char)c
))
275 c
= tolower((unsigned char)c
);
276 if ((flags
& GETPASS_FORCE_UPPER
) && islower((unsigned char)c
))
277 c
= toupper((unsigned char)c
);
281 if (flags
& GETPASS_ECHO_STAR
)
282 (void)write(fd
[1], "*", 1);
283 else if (flags
& GETPASS_ECHO
)
284 (void)write(fd
[1], isprint((unsigned char)c
) ?
293 (void)tcsetattr(fd
[0], TCSAFLUSH
|TCSASOFT
, >
);
297 if (good
&& (flags
& GETPASS_ECHO_NL
))
298 (void)write(fd
[1], "\n", 1);
310 if ((flags
& GETPASS_NO_SIGNAL
) == 0)
321 getpass_r(const char *prompt
, char *buf
, size_t len
)
323 return getpassfd(prompt
, buf
, len
, NULL
, GETPASS_ECHO_NL
, 0);
327 getpass(const char *prompt
)
329 static char e
[] = "";
335 * Strictly speaking we could double allocate here, if we get
336 * called at the same time, but this function is not re-entrant
337 * anyway and it is not supposed to work if called concurrently.
340 if ((bufsiz
= sysconf(_SC_PASS_MAX
)) == -1)
342 if ((buf
= malloc((size_t)bufsiz
)) == NULL
)
346 if ((rv
= getpass_r(prompt
, buf
, (size_t)bufsiz
)) == NULL
)
354 main(int argc
, char *argv
[])
357 printf("[%s]\n", getpassfd("foo>", buf
, sizeof(buf
), NULL
,
358 GETPASS_ECHO_STAR
|GETPASS_ECHO_NL
, 2));