| /* Copyright (C) 1992-2001, 2003-2007, 2009-2026 Free Software Foundation, Inc. | |
| This file is part of the GNU C Library. | |
| This file is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU Lesser General Public License as | |
| published by the Free Software Foundation; either version 2.1 of the | |
| License, or (at your option) any later version. | |
| This file is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU Lesser General Public License for more details. | |
| You should have received a copy of the GNU Lesser General Public License | |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ | |
| #ifndef _LIBC | |
| /* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc | |
| warns for the null checks on 'prompt' below. */ | |
| # define _GL_ARG_NONNULL(params) | |
| # include <config.h> | |
| #endif | |
| #include "getpass.h" | |
| #include <stdio.h> | |
| #if !(defined _WIN32 && !defined __CYGWIN__) | |
| # if HAVE_DECL___FSETLOCKING && HAVE___FSETLOCKING | |
| # if HAVE_STDIO_EXT_H | |
| # include <stdio_ext.h> | |
| # endif | |
| # else | |
| # define __fsetlocking(stream, type) /* empty */ | |
| # endif | |
| # if HAVE_TERMIOS_H | |
| # include <termios.h> | |
| # endif | |
| # if USE_UNLOCKED_IO | |
| # include "unlocked-io.h" | |
| # else | |
| # if !HAVE_DECL_FFLUSH_UNLOCKED | |
| # undef fflush_unlocked | |
| # define fflush_unlocked(x) fflush (x) | |
| # endif | |
| # if !HAVE_DECL_FLOCKFILE | |
| # undef flockfile | |
| # define flockfile(x) ((void) 0) | |
| # endif | |
| # if !HAVE_DECL_FUNLOCKFILE | |
| # undef funlockfile | |
| # define funlockfile(x) ((void) 0) | |
| # endif | |
| # if !HAVE_DECL_FPUTS_UNLOCKED | |
| # undef fputs_unlocked | |
| # define fputs_unlocked(str,stream) fputs (str, stream) | |
| # endif | |
| # if !HAVE_DECL_PUTC_UNLOCKED | |
| # undef putc_unlocked | |
| # define putc_unlocked(c,stream) putc (c, stream) | |
| # endif | |
| # endif | |
| /* It is desirable to use this bit on systems that have it. | |
| The only bit of terminal state we want to twiddle is echoing, which is | |
| done in software; there is no need to change the state of the terminal | |
| hardware. */ | |
| # ifndef TCSASOFT | |
| # define TCSASOFT 0 | |
| # endif | |
| static void | |
| call_fclose (void *arg) | |
| { | |
| if (arg != NULL) | |
| fclose (arg); | |
| } | |
| char * | |
| getpass (const char *prompt) | |
| { | |
| /* Try to write to and read from the terminal if we can. | |
| If we can't open the terminal, use stderr and stdin. */ | |
| FILE *tty = fopen ("/dev/tty", "w+e"); | |
| FILE *in, *out; | |
| if (tty == NULL) | |
| { | |
| in = stdin; | |
| out = stderr; | |
| } | |
| else | |
| { | |
| /* We do the locking ourselves. */ | |
| __fsetlocking (tty, FSETLOCKING_BYCALLER); | |
| out = in = tty; | |
| } | |
| flockfile (out); | |
| bool tty_changed = false; | |
| /* Turn echoing off if it is on now. */ | |
| # if HAVE_TCGETATTR | |
| struct termios s, t; | |
| if (tcgetattr (fileno (in), &t) == 0) | |
| { | |
| /* Save the old one. */ | |
| s = t; | |
| /* Tricky, tricky. */ | |
| t.c_lflag &= ~(ECHO | ISIG); | |
| tty_changed = (tcsetattr (fileno (in), TCSAFLUSH | TCSASOFT, &t) == 0); | |
| } | |
| # endif | |
| if (prompt) | |
| { | |
| /* Write the prompt. */ | |
| fputs_unlocked (prompt, out); | |
| fflush_unlocked (out); | |
| } | |
| /* Read the password. */ | |
| static char *buf; | |
| static size_t bufsize; | |
| ssize_t nread = getline (&buf, &bufsize, in); | |
| /* According to the C standard, input may not be followed by output | |
| on the same stream without an intervening call to a file | |
| positioning function. Suppose in == out; then without this fseek | |
| call, on Solaris, HP-UX, AIX, the previous input gets echoed, | |
| whereas on IRIX, the following newline is not output as | |
| it should be. POSIX imposes similar restrictions if fileno (in) | |
| == fileno (out). The POSIX restrictions are tricky and change | |
| from POSIX version to POSIX version, so play it safe and invoke | |
| fseek even if in != out. */ | |
| fseeko (out, 0, SEEK_CUR); | |
| if (buf != NULL) | |
| { | |
| if (nread < 0) | |
| buf[0] = '\0'; | |
| else if (buf[nread - 1] == '\n') | |
| { | |
| /* Remove the newline. */ | |
| buf[nread - 1] = '\0'; | |
| if (tty_changed) | |
| { | |
| /* Write the newline that was not echoed. */ | |
| putc_unlocked ('\n', out); | |
| } | |
| } | |
| } | |
| /* Restore the original setting. */ | |
| # if HAVE_TCSETATTR | |
| if (tty_changed) | |
| tcsetattr (fileno (in), TCSAFLUSH | TCSASOFT, &s); | |
| # endif | |
| funlockfile (out); | |
| call_fclose (tty); | |
| return buf; | |
| } | |
| #else /* W32 native */ | |
| /* Windows implementation by Martin Lambers <marlam@marlam.de>, | |
| improved by Simon Josefsson. */ | |
| /* For PASS_MAX. */ | |
| # include <limits.h> | |
| /* For _getch(). */ | |
| # include <conio.h> | |
| /* For strdup(). */ | |
| # include <string.h> | |
| # ifndef PASS_MAX | |
| # define PASS_MAX 512 | |
| # endif | |
| char * | |
| getpass (const char *prompt) | |
| { | |
| if (prompt) | |
| { | |
| fputs (prompt, stderr); | |
| fflush (stderr); | |
| } | |
| char getpassbuf[PASS_MAX + 1]; | |
| { | |
| size_t i = 0; | |
| for (;;) | |
| { | |
| int c = _getch (); | |
| if (c == '\r') | |
| { | |
| getpassbuf[i] = '\0'; | |
| break; | |
| } | |
| else if (i < PASS_MAX) | |
| { | |
| getpassbuf[i++] = c; | |
| } | |
| if (i >= PASS_MAX) | |
| { | |
| getpassbuf[i] = '\0'; | |
| break; | |
| } | |
| } | |
| } | |
| if (prompt) | |
| { | |
| fputs ("\r\n", stderr); | |
| fflush (stderr); | |
| } | |
| return strdup (getpassbuf); | |
| } | |
| #endif |