Annotation of src/lib/libc/gen/getpass.c, Revision 1.29
1.29 ! christos 1: /* $NetBSD: getpass.c,v 1.28 2014/06/16 16:29:30 christos Exp $ */
1.5 cgd 2:
1.17 christos 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.
1.1 cgd 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: *
1.17 christos 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.
1.1 cgd 30: */
1.10 christos 31: #include <sys/cdefs.h>
1.1 cgd 32: #if defined(LIBC_SCCS) && !defined(lint)
1.29 ! christos 33: __RCSID("$NetBSD: getpass.c,v 1.28 2014/06/16 16:29:30 christos Exp $");
1.1 cgd 34: #endif /* LIBC_SCCS and not lint */
35:
1.11 jtc 36: #include "namespace.h"
1.5 cgd 37:
1.13 lukem 38: #include <assert.h>
1.17 christos 39: #ifdef TEST
40: #include <stdio.h>
41: #endif
42: #include <errno.h>
1.22 christos 43: #include <ctype.h>
1.18 christos 44: #include <signal.h>
1.17 christos 45: #include <string.h>
1.5 cgd 46: #include <paths.h>
1.17 christos 47: #include <stdbool.h>
48: #include <stdlib.h>
1.13 lukem 49: #include <termios.h>
1.1 cgd 50: #include <unistd.h>
1.17 christos 51: #include <fcntl.h>
1.23 christos 52: #include <poll.h>
1.11 jtc 53:
54: #ifdef __weak_alias
1.19 christos 55: __weak_alias(getpassfd,_getpassfd)
1.17 christos 56: __weak_alias(getpass_r,_getpass_r)
1.14 mycroft 57: __weak_alias(getpass,_getpass)
1.11 jtc 58: #endif
1.1 cgd 59:
1.17 christos 60: /*
61: * Notes:
62: * - There is no getpass_r in POSIX
63: * - Historically EOF is documented to be treated as EOL, we provide a
1.19 christos 64: * tunable for that GETPASS_FAIL_EOF to disable this.
1.17 christos 65: * - Historically getpass ate extra characters silently, we provide
1.19 christos 66: * a tunable for that GETPASS_BUF_LIMIT to disable this.
1.17 christos 67: * - Historically getpass "worked" by echoing characters when turning
1.19 christos 68: * off echo failed, we provide a tunable GETPASS_NEED_TTY to
1.17 christos 69: * disable this.
70: * - Some implementations say that on interrupt the program shall
1.18 christos 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.
1.19 christos 74: * We also provide a tunable to disable signal delivery
75: * GETPASS_NO_SIGNAL.
76: * - GETPASS_NO_BEEP disables beeping.
1.20 christos 77: * - GETPASS_ECHO_STAR will echo '*' for each character of the password
1.19 christos 78: * - GETPASS_ECHO will echo the password (as pam likes it)
1.25 christos 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.
1.17 christos 83: */
1.1 cgd 84: char *
1.19 christos 85: /*ARGSUSED*/
1.25 christos 86: getpassfd(const char *prompt, char *buf, size_t len, int *fd, int flags,
1.23 christos 87: int tout)
1.1 cgd 88: {
1.17 christos 89: struct termios gt;
90: char c;
1.19 christos 91: int sig;
1.25 christos 92: bool lnext, havetty, allocated, opentty, good;
93: int fdc[3];
1.1 cgd 94:
1.13 lukem 95: _DIAGASSERT(prompt != NULL);
96:
1.25 christos 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;
1.29 ! christos 110: if ((fd[0] = fd[1] = fd[2] = open(_PATH_TTY,
! 111: O_RDWR | O_CLOEXEC)) == -1) {
1.25 christos 112: fd[0] = STDIN_FILENO;
113: fd[1] = fd[2] = STDERR_FILENO;
114: } else
115: opentty = true;
116: }
117:
1.18 christos 118: sig = 0;
1.19 christos 119: allocated = buf == NULL;
120: if (tcgetattr(fd[0], >) == -1) {
1.17 christos 121: havetty = false;
1.19 christos 122: if (flags & GETPASS_NEED_TTY)
123: goto out;
1.17 christos 124: memset(>, -1, sizeof(gt));
125: } else
126: havetty = true;
127:
128: if (havetty) {
129: struct termios st = gt;
130:
131: st.c_lflag &= ~(ECHO|ECHOK|ECHOE|ECHOKE|ECHOCTL|ISIG|ICANON);
132: st.c_cc[VMIN] = 1;
133: st.c_cc[VTIME] = 0;
1.19 christos 134: if (tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, &st) == -1)
1.17 christos 135: goto out;
136: }
1.8 christos 137:
1.17 christos 138: if (prompt != NULL) {
139: size_t plen = strlen(prompt);
1.19 christos 140: (void)write(fd[1], prompt, plen);
141: }
142:
143: if (allocated) {
144: len = 1024;
145: if ((buf = malloc(len)) == NULL)
146: goto restore;
1.16 christos 147: }
1.17 christos 148:
149: c = '\1';
150: lnext = false;
151: for (size_t l = 0; c != '\0'; ) {
1.23 christos 152: if (tout) {
153: struct pollfd pfd;
154: pfd.fd = fd[0];
155: pfd.events = POLLIN|POLLRDNORM;
156: pfd.revents = 0;
157: switch (poll(&pfd, 1, tout * 1000)) {
158: case 0:
1.24 christos 159: errno = ETIMEDOUT;
1.23 christos 160: /*FALLTHROUGH*/
161: case -1:
162: goto restore;
163: default:
164: break;
165: }
166: }
1.19 christos 167: if (read(fd[0], &c, 1) != 1)
1.17 christos 168: goto restore;
169:
1.23 christos 170: #define beep() \
171: do \
172: if (flags & GETPASS_NO_BEEP) \
173: (void)write(fd[2], "\a", 1); \
1.19 christos 174: while (/*CONSTCOND*/ 0)
175: #define erase() (void)write(fd[1], "\b \b", 3)
1.26 christos 176: /*
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.
181: */
182: #define C(a, b) ((gt.c_cc[(a)] == _POSIX_VDISABLE || gt.c_cc[(a)] == '\0') ? \
183: (b) : gt.c_cc[(a)])
1.17 christos 184: if (lnext) {
185: lnext = false;
186: goto add;
187: }
188:
189: /* Ignored */
1.22 christos 190: if (c == C(VREPRINT, CTRL('r')) || c == C(VSTART, CTRL('q')) ||
1.17 christos 191: c == C(VSTOP, CTRL('s')) || c == C(VSTATUS, CTRL('t')) ||
192: c == C(VDISCARD, CTRL('o')))
193: continue;
194:
195: /* Literal next */
196: if (c == C(VLNEXT, CTRL('v'))) {
197: lnext = true;
198: continue;
199: }
200:
201: /* Line or word kill, treat as reset */
202: if (c == C(VKILL, CTRL('u')) || c == C(VWERASE, CTRL('w'))) {
1.20 christos 203: if (flags & (GETPASS_ECHO | GETPASS_ECHO_STAR)) {
1.19 christos 204: while (l--)
205: erase();
206: }
1.17 christos 207: l = 0;
208: continue;
209: }
210:
211: /* Character erase */
212: if (c == C(VERASE, CTRL('h'))) {
213: if (l == 0)
214: beep();
1.19 christos 215: else {
1.17 christos 216: l--;
1.20 christos 217: if (flags & (GETPASS_ECHO | GETPASS_ECHO_STAR))
1.19 christos 218: erase();
219: }
1.17 christos 220: continue;
221: }
222:
1.18 christos 223: /* tty signal characters */
224: if (c == C(VINTR, CTRL('c'))) {
225: sig = SIGINT;
226: goto out;
227: }
228: if (c == C(VQUIT, CTRL('\\'))) {
229: sig = SIGQUIT;
230: goto out;
231: }
232: if (c == C(VSUSP, CTRL('z')) || c == C(VDSUSP, CTRL('y'))) {
233: sig = SIGTSTP;
234: goto out;
235: }
236:
237: /* EOF */
238: if (c == C(VEOF, CTRL('d'))) {
1.19 christos 239: if (flags & GETPASS_FAIL_EOF) {
240: errno = ENODATA;
241: goto out;
242: } else {
243: c = '\0';
244: goto add;
245: }
1.17 christos 246: }
247:
248: /* End of line */
1.28 christos 249: if (c == C(VEOL, CTRL('j')) || c == C(VEOL2, CTRL('m')))
1.17 christos 250: c = '\0';
251: add:
252: if (l >= len) {
1.19 christos 253: if (allocated) {
1.22 christos 254: size_t nlen = len + 1024;
255: char *nbuf = realloc(buf, nlen);
256: if (nbuf == NULL)
1.19 christos 257: goto restore;
1.22 christos 258: buf = nbuf;
259: len = nlen;
1.19 christos 260: } else {
261: if (flags & GETPASS_BUF_LIMIT) {
262: beep();
263: continue;
264: }
265: if (c == '\0' && l > 0)
266: l--;
267: else
268: continue;
269: }
1.17 christos 270: }
1.25 christos 271:
272: if (flags & GETPASS_7BIT)
273: c &= 0x7f;
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);
278:
1.19 christos 279: buf[l++] = c;
1.20 christos 280: if (c) {
281: if (flags & GETPASS_ECHO_STAR)
282: (void)write(fd[1], "*", 1);
283: else if (flags & GETPASS_ECHO)
1.22 christos 284: (void)write(fd[1], isprint((unsigned char)c) ?
285: &c : "?", 1);
1.20 christos 286: }
1.1 cgd 287: }
1.25 christos 288: good = true;
1.17 christos 289:
290: restore:
1.19 christos 291: if (havetty) {
292: c = errno;
293: (void)tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, >);
294: errno = c;
295: }
1.17 christos 296: out:
1.25 christos 297: if (good && (flags & GETPASS_ECHO_NL))
298: (void)write(fd[1], "\n", 1);
299:
300: if (opentty) {
301: c = errno;
302: (void)close(fd[0]);
303: errno = c;
304: }
305:
306: if (good)
307: return buf;
308:
1.18 christos 309: if (sig) {
1.19 christos 310: if ((flags & GETPASS_NO_SIGNAL) == 0)
311: (void)raise(sig);
1.18 christos 312: errno = EINTR;
313: }
1.19 christos 314: memset(buf, 0, len);
315: if (allocated)
316: free(buf);
1.17 christos 317: return NULL;
318: }
319:
320: char *
1.19 christos 321: getpass_r(const char *prompt, char *buf, size_t len)
322: {
1.27 christos 323: return getpassfd(prompt, buf, len, NULL, GETPASS_ECHO_NL, 0);
1.19 christos 324: }
325:
326: char *
1.17 christos 327: getpass(const char *prompt)
328: {
329: static char e[] = "";
330: static char *buf;
331: static long bufsiz;
332: char *rv;
333:
1.19 christos 334: /*
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.
338: */
1.17 christos 339: if (buf == NULL) {
340: if ((bufsiz = sysconf(_SC_PASS_MAX)) == -1)
341: return e;
342: if ((buf = malloc((size_t)bufsiz)) == NULL)
343: return e;
1.1 cgd 344: }
1.17 christos 345:
346: if ((rv = getpass_r(prompt, buf, (size_t)bufsiz)) == NULL)
347: return e;
348:
349: return rv;
1.1 cgd 350: }
1.17 christos 351:
352: #ifdef TEST
353: int
354: main(int argc, char *argv[])
355: {
356: char buf[28];
1.25 christos 357: printf("[%s]\n", getpassfd("foo>", buf, sizeof(buf), NULL,
358: GETPASS_ECHO_STAR|GETPASS_ECHO_NL, 2));
1.17 christos 359: return 0;
360: }
361: #endif
CVSweb <webmaster@jp.NetBSD.org>