Annotation of src/lib/libc/gen/getpass.c, Revision 1.21
1.21 ! christos 1: /* $NetBSD: getpass.c,v 1.20 2012/04/12 23:16:38 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.21 ! christos 33: __RCSID("$NetBSD: getpass.c,v 1.20 2012/04/12 23:16:38 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.18 christos 43: #include <signal.h>
1.17 christos 44: #include <string.h>
1.5 cgd 45: #include <paths.h>
1.17 christos 46: #include <stdbool.h>
47: #include <stdlib.h>
1.13 lukem 48: #include <termios.h>
1.1 cgd 49: #include <unistd.h>
1.17 christos 50: #include <fcntl.h>
1.11 jtc 51:
52: #ifdef __weak_alias
1.19 christos 53: __weak_alias(getpassfd,_getpassfd)
1.17 christos 54: __weak_alias(getpass_r,_getpass_r)
1.14 mycroft 55: __weak_alias(getpass,_getpass)
1.11 jtc 56: #endif
1.1 cgd 57:
1.17 christos 58: /*
59: * Notes:
60: * - There is no getpass_r in POSIX
61: * - Historically EOF is documented to be treated as EOL, we provide a
1.19 christos 62: * tunable for that GETPASS_FAIL_EOF to disable this.
1.17 christos 63: * - Historically getpass ate extra characters silently, we provide
1.19 christos 64: * a tunable for that GETPASS_BUF_LIMIT to disable this.
1.17 christos 65: * - Historically getpass "worked" by echoing characters when turning
1.19 christos 66: * off echo failed, we provide a tunable GETPASS_NEED_TTY to
1.17 christos 67: * disable this.
68: * - Some implementations say that on interrupt the program shall
1.18 christos 69: * receive an interrupt signal before the function returns. We
70: * send all the tty signals before we return, but we don't expect
71: * suspend to do something useful unless the caller calls us again.
1.19 christos 72: * We also provide a tunable to disable signal delivery
73: * GETPASS_NO_SIGNAL.
74: * - GETPASS_NO_BEEP disables beeping.
1.20 christos 75: * - GETPASS_ECHO_STAR will echo '*' for each character of the password
1.19 christos 76: * - GETPASS_ECHO will echo the password (as pam likes it)
1.17 christos 77: */
1.1 cgd 78: char *
1.19 christos 79: /*ARGSUSED*/
80: getpassfd(const char *prompt, char *buf, size_t len, int fd[], int flags)
1.1 cgd 81: {
1.17 christos 82: struct termios gt;
83: char c;
1.19 christos 84: int sig;
85: bool lnext, havetty, allocated;
1.1 cgd 86:
1.13 lukem 87: _DIAGASSERT(prompt != NULL);
88:
1.18 christos 89: sig = 0;
1.17 christos 90:
1.19 christos 91: allocated = buf == NULL;
92: if (tcgetattr(fd[0], >) == -1) {
1.17 christos 93: havetty = false;
1.19 christos 94: if (flags & GETPASS_NEED_TTY)
95: goto out;
1.17 christos 96: memset(>, -1, sizeof(gt));
97: } else
98: havetty = true;
99:
100:
101: if (havetty) {
102: struct termios st = gt;
103:
104: st.c_lflag &= ~(ECHO|ECHOK|ECHOE|ECHOKE|ECHOCTL|ISIG|ICANON);
105: st.c_cc[VMIN] = 1;
106: st.c_cc[VTIME] = 0;
1.19 christos 107: if (tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, &st) == -1)
1.17 christos 108: goto out;
109: }
1.8 christos 110:
1.17 christos 111: if (prompt != NULL) {
112: size_t plen = strlen(prompt);
1.19 christos 113: (void)write(fd[1], prompt, plen);
114: }
115:
116: if (allocated) {
117: len = 1024;
118: if ((buf = malloc(len)) == NULL)
119: goto restore;
1.16 christos 120: }
1.17 christos 121:
122: c = '\1';
123: lnext = false;
124: for (size_t l = 0; c != '\0'; ) {
1.19 christos 125: if (read(fd[0], &c, 1) != 1)
1.17 christos 126: goto restore;
127:
1.19 christos 128: #define beep() do \
129: if (flags & GETPASS_NO_BEEP) \
130: (void)write(fd[2], "\a", 1); \
131: while (/*CONSTCOND*/ 0)
132: #define erase() (void)write(fd[1], "\b \b", 3)
133:
1.17 christos 134: #define C(a, b) (gt.c_cc[(a)] == _POSIX_VDISABLE ? (b) : gt.c_cc[(a)])
135:
136: if (lnext) {
137: lnext = false;
138: goto add;
139: }
140:
141: /* Ignored */
142: if (c == C(VREPRINT, CTRL('r')) || c == C(VSTART, CTRL('q')) ||
143: c == C(VSTOP, CTRL('s')) || c == C(VSTATUS, CTRL('t')) ||
144: c == C(VDISCARD, CTRL('o')))
145: continue;
146:
147: /* Literal next */
148: if (c == C(VLNEXT, CTRL('v'))) {
149: lnext = true;
150: continue;
151: }
152:
153: /* Line or word kill, treat as reset */
154: if (c == C(VKILL, CTRL('u')) || c == C(VWERASE, CTRL('w'))) {
1.20 christos 155: if (flags & (GETPASS_ECHO | GETPASS_ECHO_STAR)) {
1.19 christos 156: while (l--)
157: erase();
158: }
1.17 christos 159: l = 0;
160: continue;
161: }
162:
163: /* Character erase */
164: if (c == C(VERASE, CTRL('h'))) {
165: if (l == 0)
166: beep();
1.19 christos 167: else {
1.17 christos 168: l--;
1.20 christos 169: if (flags & (GETPASS_ECHO | GETPASS_ECHO_STAR))
1.19 christos 170: erase();
171: }
1.17 christos 172: continue;
173: }
174:
1.18 christos 175: /* tty signal characters */
176: if (c == C(VINTR, CTRL('c'))) {
177: sig = SIGINT;
178: goto out;
179: }
180: if (c == C(VQUIT, CTRL('\\'))) {
181: sig = SIGQUIT;
182: goto out;
183: }
184: if (c == C(VSUSP, CTRL('z')) || c == C(VDSUSP, CTRL('y'))) {
185: sig = SIGTSTP;
186: goto out;
187: }
188:
189: /* EOF */
190: if (c == C(VEOF, CTRL('d'))) {
1.19 christos 191: if (flags & GETPASS_FAIL_EOF) {
192: errno = ENODATA;
193: goto out;
194: } else {
195: c = '\0';
196: goto add;
197: }
1.17 christos 198: }
199:
200: /* End of line */
1.18 christos 201: if (c == C(VEOL, CTRL('j')) || c == C(VEOL2, CTRL('l')))
1.17 christos 202: c = '\0';
203: add:
204: if (l >= len) {
1.19 christos 205: if (allocated) {
1.21 ! christos 206: char *b;
1.19 christos 207: len += 1024;
1.21 ! christos 208: b = realloc(buf, len);
1.19 christos 209: if (b == NULL)
210: goto restore;
211: buf = b;
212: } else {
213: if (flags & GETPASS_BUF_LIMIT) {
214: beep();
215: continue;
216: }
217: if (c == '\0' && l > 0)
218: l--;
219: else
220: continue;
221: }
1.17 christos 222: }
1.19 christos 223: buf[l++] = c;
1.20 christos 224: if (c) {
225: if (flags & GETPASS_ECHO_STAR)
226: (void)write(fd[1], "*", 1);
227: else if (flags & GETPASS_ECHO)
228: (void)write(fd[1], &c, 1);
229: }
1.1 cgd 230: }
1.17 christos 231:
232: if (havetty)
1.19 christos 233: (void)tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, >);
234: return buf;
1.17 christos 235: restore:
1.19 christos 236: if (havetty) {
237: c = errno;
238: (void)tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, >);
239: errno = c;
240: }
1.17 christos 241: out:
1.18 christos 242: if (sig) {
1.19 christos 243: if ((flags & GETPASS_NO_SIGNAL) == 0)
244: (void)raise(sig);
1.18 christos 245: errno = EINTR;
246: }
1.19 christos 247: memset(buf, 0, len);
248: if (allocated)
249: free(buf);
1.17 christos 250: return NULL;
251: }
252:
253: char *
1.19 christos 254: getpass_r(const char *prompt, char *buf, size_t len)
255: {
256: bool opentty;
257: int fd[3];
258: char *rv;
259:
260: /*
261: * Try to use /dev/tty if possible; otherwise read from stdin and
262: * write to stderr.
263: */
264: if ((fd[0] = fd[1] = fd[2] = open(_PATH_TTY, O_RDWR)) == -1) {
265: opentty = false;
266: fd[0] = STDIN_FILENO;
267: fd[1] = fd[2] = STDERR_FILENO;
268: } else
269: opentty = true;
270:
271: rv = getpassfd(prompt, buf, len, fd, 0);
272:
273: if (opentty) {
274: int serrno = errno;
275: (void)close(fd[0]);
276: errno = serrno;
277: }
278: return rv;
279: }
280:
281: char *
1.17 christos 282: getpass(const char *prompt)
283: {
284: static char e[] = "";
285: static char *buf;
286: static long bufsiz;
287: char *rv;
288:
1.19 christos 289: /*
290: * Strictly speaking we could double allocate here, if we get
291: * called at the same time, but this function is not re-entrant
292: * anyway and it is not supposed to work if called concurrently.
293: */
1.17 christos 294: if (buf == NULL) {
295: if ((bufsiz = sysconf(_SC_PASS_MAX)) == -1)
296: return e;
297: if ((buf = malloc((size_t)bufsiz)) == NULL)
298: return e;
1.1 cgd 299: }
1.17 christos 300:
301: if ((rv = getpass_r(prompt, buf, (size_t)bufsiz)) == NULL)
302: return e;
303:
304: return rv;
1.1 cgd 305: }
1.17 christos 306:
307: #ifdef TEST
308: int
309: main(int argc, char *argv[])
310: {
311: char buf[28];
1.19 christos 312: int fd[3] = { 0, 1, 2 };
1.20 christos 313: printf("[%s]\n", getpassfd("foo>", buf, sizeof(buf), fd,
314: GETPASS_ECHO_STAR));
1.17 christos 315: return 0;
316: }
317: #endif
CVSweb <webmaster@jp.NetBSD.org>