[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.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], &gt) == -1) {
1.17      christos   93:                havetty = false;
1.19      christos   94:                if (flags & GETPASS_NEED_TTY)
                     95:                        goto out;
1.17      christos   96:                memset(&gt, -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, &gt);
                    234:        return buf;
1.17      christos  235: restore:
1.19      christos  236:        if (havetty) {
                    237:                c = errno;
                    238:                (void)tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, &gt);
                    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>