[BACK]Return to getpass.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / lib / libc / gen

Annotation of src/lib/libc/gen/getpass.c, Revision 1.30

1.30    ! christos    1: /*     $NetBSD: getpass.c,v 1.29 2014/09/18 13:58:20 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.30    ! christos   33: __RCSID("$NetBSD: getpass.c,v 1.29 2014/09/18 13:58:20 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], &gt) == -1) {
1.17      christos  121:                havetty = false;
1.19      christos  122:                if (flags & GETPASS_NEED_TTY)
                    123:                        goto out;
1.17      christos  124:                memset(&gt, -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.30    ! christos  291: out:
1.19      christos  292:        if (havetty) {
                    293:                c = errno;
                    294:                (void)tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, &gt);
                    295:                errno = c;
                    296:        }
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>