(root)/
coreutils-9.4/
lib/
getpass.c
       1  /* Copyright (C) 1992-2001, 2003-2007, 2009-2023 Free Software Foundation, Inc.
       2  
       3     This file is part of the GNU C Library.
       4  
       5     This file is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU Lesser General Public License as
       7     published by the Free Software Foundation; either version 2.1 of the
       8     License, or (at your option) any later version.
       9  
      10     This file is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #ifndef _LIBC
      19  /* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
      20     warns for the null checks on 'prompt' below.  */
      21  # define _GL_ARG_NONNULL(params)
      22  # include <config.h>
      23  #endif
      24  
      25  #include "getpass.h"
      26  
      27  #include <stdio.h>
      28  
      29  #if !(defined _WIN32 && !defined __CYGWIN__)
      30  
      31  # if HAVE_DECL___FSETLOCKING && HAVE___FSETLOCKING
      32  #  if HAVE_STDIO_EXT_H
      33  #   include <stdio_ext.h>
      34  #  endif
      35  # else
      36  #  define __fsetlocking(stream, type)    /* empty */
      37  # endif
      38  
      39  # if HAVE_TERMIOS_H
      40  #  include <termios.h>
      41  # endif
      42  
      43  # if USE_UNLOCKED_IO
      44  #  include "unlocked-io.h"
      45  # else
      46  #  if !HAVE_DECL_FFLUSH_UNLOCKED
      47  #   undef fflush_unlocked
      48  #   define fflush_unlocked(x) fflush (x)
      49  #  endif
      50  #  if !HAVE_DECL_FLOCKFILE
      51  #   undef flockfile
      52  #   define flockfile(x) ((void) 0)
      53  #  endif
      54  #  if !HAVE_DECL_FUNLOCKFILE
      55  #   undef funlockfile
      56  #   define funlockfile(x) ((void) 0)
      57  #  endif
      58  #  if !HAVE_DECL_FPUTS_UNLOCKED
      59  #   undef fputs_unlocked
      60  #   define fputs_unlocked(str,stream) fputs (str, stream)
      61  #  endif
      62  #  if !HAVE_DECL_PUTC_UNLOCKED
      63  #   undef putc_unlocked
      64  #   define putc_unlocked(c,stream) putc (c, stream)
      65  #  endif
      66  # endif
      67  
      68  /* It is desirable to use this bit on systems that have it.
      69     The only bit of terminal state we want to twiddle is echoing, which is
      70     done in software; there is no need to change the state of the terminal
      71     hardware.  */
      72  
      73  # ifndef TCSASOFT
      74  #  define TCSASOFT 0
      75  # endif
      76  
      77  static void
      78  call_fclose (void *arg)
      79  {
      80    if (arg != NULL)
      81      fclose (arg);
      82  }
      83  
      84  char *
      85  getpass (const char *prompt)
      86  {
      87    FILE *tty;
      88    FILE *in, *out;
      89  # if HAVE_TCGETATTR
      90    struct termios s, t;
      91  # endif
      92    bool tty_changed = false;
      93    static char *buf;
      94    static size_t bufsize;
      95    ssize_t nread;
      96  
      97    /* Try to write to and read from the terminal if we can.
      98       If we can't open the terminal, use stderr and stdin.  */
      99  
     100    tty = fopen ("/dev/tty", "w+e");
     101    if (tty == NULL)
     102      {
     103        in = stdin;
     104        out = stderr;
     105      }
     106    else
     107      {
     108        /* We do the locking ourselves.  */
     109        __fsetlocking (tty, FSETLOCKING_BYCALLER);
     110  
     111        out = in = tty;
     112      }
     113  
     114    flockfile (out);
     115  
     116    /* Turn echoing off if it is on now.  */
     117  # if HAVE_TCGETATTR
     118    if (tcgetattr (fileno (in), &t) == 0)
     119      {
     120        /* Save the old one. */
     121        s = t;
     122        /* Tricky, tricky. */
     123        t.c_lflag &= ~(ECHO | ISIG);
     124        tty_changed = (tcsetattr (fileno (in), TCSAFLUSH | TCSASOFT, &t) == 0);
     125      }
     126  # endif
     127  
     128    if (prompt)
     129      {
     130        /* Write the prompt.  */
     131        fputs_unlocked (prompt, out);
     132        fflush_unlocked (out);
     133      }
     134  
     135    /* Read the password.  */
     136    nread = getline (&buf, &bufsize, in);
     137  
     138    /* According to the C standard, input may not be followed by output
     139       on the same stream without an intervening call to a file
     140       positioning function.  Suppose in == out; then without this fseek
     141       call, on Solaris, HP-UX, AIX, OSF/1, the previous input gets
     142       echoed, whereas on IRIX, the following newline is not output as
     143       it should be.  POSIX imposes similar restrictions if fileno (in)
     144       == fileno (out).  The POSIX restrictions are tricky and change
     145       from POSIX version to POSIX version, so play it safe and invoke
     146       fseek even if in != out.  */
     147    fseeko (out, 0, SEEK_CUR);
     148  
     149    if (buf != NULL)
     150      {
     151        if (nread < 0)
     152          buf[0] = '\0';
     153        else if (buf[nread - 1] == '\n')
     154          {
     155            /* Remove the newline.  */
     156            buf[nread - 1] = '\0';
     157            if (tty_changed)
     158              {
     159                /* Write the newline that was not echoed.  */
     160                putc_unlocked ('\n', out);
     161              }
     162          }
     163      }
     164  
     165    /* Restore the original setting.  */
     166  # if HAVE_TCSETATTR
     167    if (tty_changed)
     168      tcsetattr (fileno (in), TCSAFLUSH | TCSASOFT, &s);
     169  # endif
     170  
     171    funlockfile (out);
     172  
     173    call_fclose (tty);
     174  
     175    return buf;
     176  }
     177  
     178  #else /* W32 native */
     179  
     180  /* Windows implementation by Martin Lambers <marlam@marlam.de>,
     181     improved by Simon Josefsson. */
     182  
     183  /* For PASS_MAX. */
     184  # include <limits.h>
     185  /* For _getch(). */
     186  # include <conio.h>
     187  /* For strdup(). */
     188  # include <string.h>
     189  
     190  # ifndef PASS_MAX
     191  #  define PASS_MAX 512
     192  # endif
     193  
     194  char *
     195  getpass (const char *prompt)
     196  {
     197    char getpassbuf[PASS_MAX + 1];
     198    size_t i = 0;
     199    int c;
     200  
     201    if (prompt)
     202      {
     203        fputs (prompt, stderr);
     204        fflush (stderr);
     205      }
     206  
     207    for (;;)
     208      {
     209        c = _getch ();
     210        if (c == '\r')
     211          {
     212            getpassbuf[i] = '\0';
     213            break;
     214          }
     215        else if (i < PASS_MAX)
     216          {
     217            getpassbuf[i++] = c;
     218          }
     219  
     220        if (i >= PASS_MAX)
     221          {
     222            getpassbuf[i] = '\0';
     223            break;
     224          }
     225      }
     226  
     227    if (prompt)
     228      {
     229        fputs ("\r\n", stderr);
     230        fflush (stderr);
     231      }
     232  
     233    return strdup (getpassbuf);
     234  }
     235  #endif