getpass.py 5.43 KB
Newer Older
1 2
"""Utilities to get a password and/or the current user name.

Christian Heimes's avatar
Christian Heimes committed
3 4 5 6 7
getpass(prompt[, stream]) - Prompt for a password, with echo turned off.
getuser() - Get the user name from the environment or password database.

GetPassWarning - This UserWarning is issued when getpass() cannot prevent
                 echoing of the password contents while reading.
8

9 10 11
On Windows, the msvcrt module will be used.
On the Mac EasyDialogs.AskPassword is used, if available.

12 13
"""

14 15
# Authors: Piers Lauder (original)
#          Guido van Rossum (Windows support and cleanup)
Christian Heimes's avatar
Christian Heimes committed
16
#          Gregory P. Smith (tty support & GetPassWarning)
17

Christian Heimes's avatar
Christian Heimes committed
18
import os, sys, warnings
19

Christian Heimes's avatar
Christian Heimes committed
20
__all__ = ["getpass","getuser","GetPassWarning"]
Skip Montanaro's avatar
Skip Montanaro committed
21

22

Christian Heimes's avatar
Christian Heimes committed
23
class GetPassWarning(UserWarning): pass
24 25


Christian Heimes's avatar
Christian Heimes committed
26 27
def unix_getpass(prompt='Password: ', stream=None):
    """Prompt for a password, with echo turned off.
28

Christian Heimes's avatar
Christian Heimes committed
29 30 31 32 33 34 35 36 37 38 39 40 41 42
    Args:
      prompt: Written on stream to ask for the input.  Default: 'Password: '
      stream: A writable file object to display the prompt.  Defaults to
              the tty.  If no tty is available defaults to sys.stderr.
    Returns:
      The seKr3t input.
    Raises:
      EOFError: If our input tty or stdin was closed.
      GetPassWarning: When we were unable to turn echo off on the input.

    Always restores terminal settings before returning.
    """
    fd = None
    tty = None
Tim Peters's avatar
Tim Peters committed
43
    try:
Christian Heimes's avatar
Christian Heimes committed
44 45 46 47 48 49 50 51 52 53
        # Always try reading and writing directly on the tty first.
        fd = os.open('/dev/tty', os.O_RDWR|os.O_NOCTTY)
        tty = os.fdopen(fd, 'w+', 1)
        input = tty
        if not stream:
            stream = tty
    except EnvironmentError as e:
        # If that fails, see if stdin can be controlled.
        try:
            fd = sys.stdin.fileno()
Benjamin Peterson's avatar
Benjamin Peterson committed
54
        except (AttributeError, ValueError):
Christian Heimes's avatar
Christian Heimes committed
55 56 57 58 59 60 61 62 63 64
            passwd = fallback_getpass(prompt, stream)
        input = sys.stdin
        if not stream:
            stream = sys.stderr

    if fd is not None:
        passwd = None
        try:
            old = termios.tcgetattr(fd)     # a copy to save
            new = old[:]
65 66 67 68
            new[3] &= ~(termios.ECHO|termios.ISIG)  # 3 == 'lflags'
            tcsetattr_flags = termios.TCSAFLUSH
            if hasattr(termios, 'TCSASOFT'):
                tcsetattr_flags |= termios.TCSASOFT
Christian Heimes's avatar
Christian Heimes committed
69
            try:
70
                termios.tcsetattr(fd, tcsetattr_flags, new)
Christian Heimes's avatar
Christian Heimes committed
71 72
                passwd = _raw_input(prompt, stream, input=input)
            finally:
73 74
                termios.tcsetattr(fd, tcsetattr_flags, old)
                stream.flush()  # issue7208
Christian Heimes's avatar
Christian Heimes committed
75 76 77 78 79 80 81 82 83
        except termios.error as e:
            if passwd is not None:
                # _raw_input succeeded.  The final tcsetattr failed.  Reraise
                # instead of leaving the terminal in an unknown state.
                raise
            # We can't control the tty or stdin.  Give up and use normal IO.
            # fallback_getpass() raises an appropriate warning.
            del input, tty  # clean up unused file objects before blocking
            passwd = fallback_getpass(prompt, stream)
84

85
    stream.write('\n')
Tim Peters's avatar
Tim Peters committed
86
    return passwd
87 88


89
def win_getpass(prompt='Password: ', stream=None):
Tim Peters's avatar
Tim Peters committed
90
    """Prompt for password with echo off, using Windows getch()."""
91
    if sys.stdin is not sys.__stdin__:
Christian Heimes's avatar
Christian Heimes committed
92
        return fallback_getpass(prompt, stream)
Tim Peters's avatar
Tim Peters committed
93 94
    import msvcrt
    for c in prompt:
95
        msvcrt.putwch(c)
Tim Peters's avatar
Tim Peters committed
96 97
    pw = ""
    while 1:
98
        c = msvcrt.getwch()
Tim Peters's avatar
Tim Peters committed
99 100 101 102 103 104 105 106
        if c == '\r' or c == '\n':
            break
        if c == '\003':
            raise KeyboardInterrupt
        if c == '\b':
            pw = pw[:-1]
        else:
            pw = pw + c
107 108
    msvcrt.putwch('\r')
    msvcrt.putwch('\n')
Tim Peters's avatar
Tim Peters committed
109
    return pw
110 111


Christian Heimes's avatar
Christian Heimes committed
112 113 114 115 116 117
def fallback_getpass(prompt='Password: ', stream=None):
    warnings.warn("Can not control echo on the terminal.", GetPassWarning,
                  stacklevel=2)
    if not stream:
        stream = sys.stderr
    print("Warning: Password input may be echoed.", file=stream)
118
    return _raw_input(prompt, stream)
119 120


Christian Heimes's avatar
Christian Heimes committed
121
def _raw_input(prompt="", stream=None, input=None):
122
    # This doesn't save the string in the GNU readline history.
Christian Heimes's avatar
Christian Heimes committed
123 124 125 126
    if not stream:
        stream = sys.stderr
    if not input:
        input = sys.stdin
Tim Peters's avatar
Tim Peters committed
127 128
    prompt = str(prompt)
    if prompt:
129
        stream.write(prompt)
130
        stream.flush()
131
    # NOTE: The Python C API calls flockfile() (and unlock) during readline.
Christian Heimes's avatar
Christian Heimes committed
132
    line = input.readline()
Tim Peters's avatar
Tim Peters committed
133 134 135 136 137
    if not line:
        raise EOFError
    if line[-1] == '\n':
        line = line[:-1]
    return line
138 139


140
def getuser():
Tim Peters's avatar
Tim Peters committed
141
    """Get the username from the environment or password database.
142

Tim Peters's avatar
Tim Peters committed
143 144
    First try various environment variables, then the password
    database.  This works on Windows as long as USERNAME is set.
145

Tim Peters's avatar
Tim Peters committed
146
    """
147

Tim Peters's avatar
Tim Peters committed
148
    import os
149

Tim Peters's avatar
Tim Peters committed
150 151 152 153
    for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'):
        user = os.environ.get(name)
        if user:
            return user
154

Tim Peters's avatar
Tim Peters committed
155 156 157
    # If this fails, the exception will "explain" why
    import pwd
    return pwd.getpwuid(os.getuid())[0]
158 159 160

# Bind the name getpass to the appropriate function
try:
161
    import termios
162 163 164 165
    # it's possible there is an incompatible termios from the
    # McMillan Installer, make sure we have a UNIX-compatible termios
    termios.tcgetattr, termios.tcsetattr
except (ImportError, AttributeError):
Tim Peters's avatar
Tim Peters committed
166 167 168 169 170 171
    try:
        import msvcrt
    except ImportError:
        try:
            from EasyDialogs import AskPassword
        except ImportError:
Christian Heimes's avatar
Christian Heimes committed
172
            getpass = fallback_getpass
Tim Peters's avatar
Tim Peters committed
173 174 175 176
        else:
            getpass = AskPassword
    else:
        getpass = win_getpass
177
else:
Tim Peters's avatar
Tim Peters committed
178
    getpass = unix_getpass