------------------------------------------------------------------------------- Password Reading and Handling For Terminal password reading scroll down to... "Plain TTY Prompted Password Reading" This includes a lot of information on using "systemd-ask-password" For Programs see below... "Password Reading from a Program" For development of a BASH password reading helper... https://antofthy.gitlab.io/info/crypto/passwd_askpass_stars.txt =============================================================================== GUI Password Reading Programs. Why re-invent the wheel. There are lots of programs that have already been written for getting password from users, and which then pipe the result to stdout, ready to feed into the program that needs it, or to be buffered in a variable. Examples include... /usr/libexec/openssh/x11-ssh-askpass Prompt /usr/libexec/openssh/ssh-askpass Prompt /usr/bin/ssh-askpass Prompt /usr/libexec/openssh/gnome-ssh-askpass Prompt /usr/lib/openssh/gnome-ssh-askpass Prompt /usr/lib64/seahorse/seahorse-ssh-askpass Prompt /usr/lib/git-core/git-gui--askpass Prompt zenity --title=Title --entry --text=Prompt --hide-text kdialog --title Title --password Prompt Xdialog --title Title --stdout --password --inputbox "Prompt" 0x0 yad --title Title --mouse --on-top \ --entry --hide-text --entry-label "Password:" # Note "pinentry" has a very weird interface, designed for program use... # BUT also has some good options for positioning of the window # Quite a few programs actually use it too! { echo "SETDESC Title" # Window Title echo "SETDESC title" # main descriptive text (above prompt) echo "SETPROMPT Password" # the prompt before input window echo "SETQUALITYBAR" # enable a passphrase quality bar echo "GETPIN" # Now DOIT } | pinentry | sed -n 's/^D //p' These programs can be used for general password entry, but often do not provide any configuration options to let you re-purpose them to your application. Many systems actually specify the password reading program to use in the SSH_ASSPASS environment variable (set during system login). This lets the user override the method by which passwords will be entered, and let them use other password entry systems, sources, and key rings. However only programs needing passwords will make use that environment variable :-( Of course if X windows is not enabled you will need to fall back to some form of TTY method, or even a curses method such as "dialog" (see below). My views on the above list... "YAD" looks to be the cleanest and most configurable, especially with its general overall clean look and popup position. Then "x11-ssh-askpass" which is a little more configurable (and even has a manpage!), than other 'askpass' type programs. Though I would include a little X windows resource modification to make it cleaner. title="Decrypt Data" prompt="Password" /usr/libexec/openssh/x11-ssh-askpass \ -xrm "*Dialog.title:$title" \ "$prompt" PinEntry is not bad, and is active development as part of GPG. However it has a VERY different application interface than most other password input helpers. And likes to grab the display. See "pinentry.txt" After that "Zenity" would be a good choice. And finally "Xdialog" is pretty 'ugly' looking, in its widget placement. DIY solutions are also posible using things like "qtkdialog" and "Perl::Tk". ------------------------------------------------------------------------------- GUI Double Password Entry... When you are encrypting data, (essentially entering a new password) you want to read a password twice, and then allow the user to try again, or cancel, if the two passwords don't match. However very few GUI password helpers provides this capability! --- pinentry See "pinentry.txt" sed -n '/^ *|/!d; s///; s/^ //; p' <<<' | SETDESC New Password | SETREPEAT | GETPIN ' | pinentry-gtk | sed -n 's/^D //p' This will only return (outputing a password) if the user enters the same password twice. If you don't get any output (the "sed" did not find 'D' output) the user pressed the "cancel" ('ERR' output), or helper was killed. --- zenity The "zenity" 'forms' allow you to input the password twice... However "zenity" does not check that the password is the same, it just outputs the input with the specified seperator (like a newline). For example this uses "readarray" to read the two output lines. into an array variable, then checks the two values matchs. readarray -t password < <( zenity --forms --text "Enter Password Twice" \ --add-password="Password" \ --add-password="Pwd Again" \ --separator=$'\n' ) # Check results... (( $? )) && echo Zenity Cancled [[ -z "$password" ]] && echo "Empty Password or Aborted" if [[ "$password" = "${password[1]}" ]] then echo Passwords Match else echo Passwords DO NOT Match fi printf "DEBUG:"; printf " '%s'" "${password[@]}"; printf "\n" --- XDialog XDialog can create a form of 2 input elements! Output is the two passwords (presumably of the same length) with a '/' seperating them. The script below check the middle char is a '/' and each string on both sides match (same text and length). title='New Password' set - "new password:" result=$( Xdialog --title "$title" --stdout --password --password \ --2inputsbox "$*" 0x0 password: '' "Password Again": '' ) len=$(( ${#result} /2 )) if [[ ${result:len:1} == '/' ]] && [[ "${result:0:len}" == "${result:len+1}" ]]; then echo "${result:0:len}"; exit 0 else exit 1 fi --- qtkdialog As this is a XML generated GUI, making shell calls, I am certian it can, ask the password twice, and even check the password is the same before returning the password, or a cancel/failure status. However I have not played with it, so as to DIY a XML program to do this. It also does not appear to be widely available (DIY compilation/install). --- I am certain other DIY solutions such as "qtkdialog" also exist for things like Perl::Tk. Perhaps you would like to contribute! ------------------------------------------------------------------------------- TTY Curses input... This method generally takes over the whole TTY window (like text editors do). Which is typically very annoying for getting a password and generally not what is wanted. But if you do want to use this... dialog --stdout --insecure --title Password --passwordbox 'Prompt' 0 0 --insecure will output stars for the typed characters --stdout makes the result output to stdout instead of stderr. You can also add a --timeout before the 'box' option whiptail --title "passwd" --passwordbox 'Password' 5 50 2>&1 >/dev/tty Very simular to dialog but with better clean up (use of alternate screen) Note the result is on stderr, while stdout is assumed to be the TTY. The stars are output by default, no --timeout option available. The 'Prompt' option string is not displayed. ------------------------------------------------------------------------------- Plain TTY Prompted Password Reading... (Tools & Helpers) There is not a lot of easilly obtainable 'helper' programs for TTY password input. Almost every program rolls its own... ------ askpass_stars A shell script to try and read a password while outputting stars so that the user can see that what they are typing is being relieved. https://antofthy.gitlab.io/software/#askpass_stars This will use "system-ask-password" if available (see below), but with a TTY bug fix for use with sudo. The script also makes use of kernel cache if wanted, in a way that allows you to use it for editing files, something direct use of "system-ask-password" does not provide. For the initial development notes of this script see https://antofthy.gitlab.io/info/crypto/passwd_askpass_stars.txt -------- httpd-ssl-pass-dialog /usr/libexec/httpd-ssl-pass-dialog 'site' 'type' /opt/rh/httpd*/root/usr/libexec/httpd-ssl-pass-dialog 'site' 'type' This is used by web server "mod_ssl' package to get a password to decode the private SSL keys, when an apache webserver is starting up. You do not however have good control of the prompt, and it is not always available. Given the arguments above the prompts (in bold) becomes... Enter SSL pass phrase for site (type) : Internally all it does is define a prompt and calls "systemd-ask-password" So don't bother, and just use "systemd-ask-password" directly (see next) ------ systemd-ask-password HIGHLY RECOMMENDED This is an excellent program with full prompt control. systemd-ask-password "Password:" It also has the ability to interact with other methods of password input, and can even cache passwords for re-use later. For more info, see 'caching' below. A later upgrade added some extras to the basic use above... + Initially types out "(press TAB for no echo)", in the color grey on start. This is then removed when the user presses the first character (use stars). If 'no echo' mode is enabled then grey part is replaced with "(no echo)" + TAB at any time will remove any stars already output and turn on 'no echo'. Later TABs after this will be included in the final output! Of course, password containing tabs will now need to press tab once first, before pressing the TAB's in the password. Tabs in password are NOT recommended. + DELETE or BACKSPACE as first character also turns on 'no echo' mode. + It understands Backspace (^H), kill-line (^U) to edit the password. + Outputs an emoji padlock character, followed by a space before the prompt. Emoji characters do not work for many terminals (like XTerms), so comes out as a dotted box in my current unicode font. This is VERY annoying. Newer versons of the command can disable this using "--emoji=off". + New versions now output bullet '•' (U+2022) instead of ASCII stars '*' but provide no means to disable or change this char. NOTE: Very few of these 'features' can be turned off or disabled There is a few more options too... + A useful new feature is to not output the password (--no-output) at all. In this mode you would tell it to save the password in the cache, and retrieve it from that cache (see 'caching' below). + You can now use "-e" or "--echo" to echo user output, so you could use it for normal prompted input. In this case the padlock emoji is also removed from prompt. BIGGER PROBLEM... If this program is used for "sudo -A" (eg: via the "SUDO_ASKPASS" environment variable) and the user interrupts it (by typing ^C) the program tends to leave the TTY in a bad state, requiring you to run "reset" to restore things. Later versions seems to have fixed this issue. Here is wrapper script to ensure there are no TTY problems... =======8<-------- #!/bin/sh # use the systemd to ask for a password with echoed 'stars' # Warning "sudo" with interrupt - can leave the TTY in a bad state! # Thus I save and restore the TTY settings stty_save=`stty -g` trap 'stty "$stty_save"' EXIT /bin/systemd-ask-password --timeout=0 "${1:-Password: }" exit $? =======8<-------- WARNING: If a password is piped in (no input TTY), it does NOT stop reading on a End-Of-Line, but instead waits for the pipe to close. Later version aborts with an error "Failed to query password: Permission denied". This can be bad for "co-processing" situations. My "askpass_stars" script (v2 an on) solves this by switching to a plain bash read, when input is not from a TTY. It also includes the above wrapper to handle older versions of the program. See my "askpass_stars" script https://antofthy.gitlab.io/software/#askpass_stars --- NO TTY systemd-ask-password... This works but is trickier, and unfortunatally also reqires sudo privalages, which is a pain. The man pages about this is clear-as-mud. Initiate a NO-TTY password request... sudo systemd-ask-password --no-tty "Testing No-TTY Password" A wall request (via systemd service 'systemd-ask-password-wall.service' will be sent to all TTY's on the system asking you to enter a password. But it does not tell you how to do this, not even in the man pages! TO answer the system password request, type.... sudo systemd-tty-ask-password-agent No arguments or other options. However 'sudo' privalages are again needed! The password will then be prompted for, and answer will be returned to calling process. A X windows popup ask-password-agent for this would be a VERY nice addition! But alas, you will need to DIY one, if wanted. --- Linux Password caching by "systemd-ask-password"... The "systemd-ask-password" program also has the ability to cache passwords into a user kernel keyring. This is different to the gnome keyring in that it is in kernel memory only, and will also 'timeout' after a defined period, making it a much better way of providing general password caching for a specific user. For more detail see... https://antofthy.gitlab.io/info/crypto/keyring_linux_kernal.txt The "systemd-ask-password" does not provides a 'retrieve only' mode for password handling, which is required if you want to use this for encrypting files with password caching. That is because you need to read the password twice when encrypting a file, which it was never designed to handle. For details see.. https://antofthy.gitlab.io/info/crypto/passwd_caching.txt ASIDE: Password caching has been incorperated into version 3 of my "askpass_stars" helper script, with a options that works (retrieve but don't cache if user typed), while still using "systemd-ask-password" (if availble). https://antofthy.gitlab.io/software/#askpass_stars Password Caching has also been built into my "keepout" script... https://antofthy.gitlab.io/software/#keepout =============================================================================== Password Reading from a Program OR: How to allow use of a password helper, with fallback to direct TTY input... When you ask a password in a script you generally want to keep it as simple as posible (that is a simple a 'no-echo' TTY reader). But you need to ensure that the variable used is not an environment variable (the reason it is unset). If you don't the password will become visible in the process table of any child process, such as the final "tty" or "stty" call, fleeting though those commands are. Bourne Shell... =======8<-------- read_noecho() { # A 'no-echo' TTY Reader (bourne sh) # Prepare Terminal if tty >/dev/null; then echo -n "$1 " >/dev/tty # output prompt stty_save=`stty -g` # save the terminal state trap 'stty "$stty_save"' EXIT # restore it on exit trap 'echo "===INTERUPT==="; exit 10' HUP INT QUIT ABRT TERM stty -echo # turn off echo fi # Do the password read unset passwd # ensure it is not an environment variable! read passwd # read password echo '' # echo the users return # Return TTY to normal, cancel exit trap if tty >/dev/null; then stty "$stty_save" trap - EXIT HUP INT QUIT TERM fi } read_noecho "Password:" =======8<-------- BASH makes this a lot simpler... A "-s" option that can be used to ensure no-echo regardless of any current 'stty' settings. You can add a "-p prompt" which will automatically be disabled if the input is not from a TTY. This means you don't need the 'stty' tests above. If using bash you should also ALWAYS add an "-r" option to ensure correct handling of a final backslash. This option is needed even when you are reading the input one character at a time. =======8<-------- read_password_noecho() { # A 'no-echo' TTY Password Reader (BASH) unset passwd # ensure it is not an environment variable! read -r -s -p "$1" passwd tty >/dev/null && echo '' } =======8<-------- For python solutions (which can get complex fast) see https://pymotw.com/2/getpass/ Now that we have a 'fallback' password reader... We can wrap that with another function to use a password helper if the user has provided one (via $TTY_ASKPASS environment variable). Or optionally fall back to using the linux "systemd-ask-password" program, if available. Or if needed fall back to the above bash read function. This is Bourne shell version =======8<-------- read_password() { # Read password from helper, or no-echo fallback unset passwd # ensure it is not an environment variable! if [ "X$TTY_ASKPASS" != 'X' ]; then # User defined password reader passwd=`$TTY_ASKPASS "$1"` elif [ -x /usr/bin/systemd-ask-password ]; then # Linux systemd password reader passwd=`/usr/bin/systemd-ask-password "$1"` else read_password_noecho "$1" fi # Final checks -- adjust to suit password standards of your application [ "X$passwd" = 'X' ] && Error "Zero length password not allowed." } =======8<-------- And this BASH version is not that much different... =======8<-------- read_password() { # Read password from helper, or no-echo fallback unset passwd # ensure it is not an environment variable! if [[ "$TTY_ASKPASS" ]]; then # User defined password reader passwd=$("$TTY_ASKPASS" "$1" /dev/null); then # key_id found in cache, read password from cache passwd=$(keyctl pipe "$key_id" 3>/dev/null) else # read passwd and cache it read_password "$1" key_id=$(echo -n "$passwd" | keyctl padd user "$KEY" @u 2>/dev/null) fi # reset password cache timeout (even if it came from cache) keyctl timeout "$key_id" $key_timeout 2>/dev/null unset key_id else # read password (no cache) read_password "$1" fi } =======8<-------- Replace "read_password" with "read_password_twice" in the above, to use cached passwords when encrypting! For a practical example take a look at the latest "keepout" script... Keepout A wrapper around the 'OpenSSL enc' command to encrypt and decrypt openssl encrypted files. It saves the options that were used to do the encryption as an additional (text) header to the encrypted file so that the exact requirements needed to decrypt the file is remembered, even if the default options used by openssl encryption are changed later. https://antofthy.gitlab.io/software/#keepout OR ------------------------------------------------------------------------------- Python TTY Password Reading WARNING: This needs to be improved to make use of any password helpers that may be defined by the user in environment variables. --- Read password, No Echo, and without a stdin fallback when no TTY us available. =======8<-------- import getpass import sys if sys.stdin.isatty(): p = getpass.getpass('Using getpass: ') else: print 'Using readline' p = sys.stdin.readline().rstrip() print 'Read: ', p =======8<-------- --- Read Password with stars (requires "stdiomask" module) pip install stdiomask stdiomask.getpass(prompt='PW: ', mask=x'') This shows the following while typing a password.... PW: xxxxxxxxxx ------------------------------------------------------------------------------- Shell TTY Password Helper... (DIY Shell Script) This has been moved to... https://antofthy.gitlab.io/info/crypto/passwd_askpass_stars.txt It explains the evolution and problems faced by a password helper script to read passwords from a TTY while echoing only stars (or some other character). It started from discussion on "password scripts" http://www.linuxquestions.org/questions/showthread.php?p=4484096 That script has evolved into a much more complex script that also provides a lot of other features, including fall back to "systemd-ask-password" and the caching of passwords in the kernel keyring. https://antofthy.gitlab.io/software/#askpass_stars =============================================================================== Passwords, using Variables and Pipelines in scripts... Generally you can assume variables are safe, as only someone with root access to your machine, or your working environment, has any chance of recovering the password, via process debuggers, memory dumps, or searching swap disk. If you need to store a password in a variable it is still is better to store them with some less obvious but simple form of encryption. The password should only be decoded at the last moment before being used. It should be saved in binary if the language allows. As shells cannot handle the binary NUL character, you can use base64 or hexadecimal encoding instead. Then when finished, clear or overwrite the password, before unsetting the variable. Note that some languages (like: perl, python, ruby) just allocate new memory rather than overwrite old memory, which can make clearing old passwords from memory very difficult. Check your language security notes if you are not sure. Also in shells (like bash) never use anything other than built-in commands, such as the shell "echo", or bash "printf", to feed that password other programs, or it could appear briefly in the process table. Check each command to ensure it is a builtin and not a external command (which will appear in the process table). The better idea is not to store a password in a variable, but pipe the password direct from your password input program/helper/function to the program that will use it. Of course if you need to use the password multiple times, or require testing the password for security rules or double input checking, then storing it in a variable may be the only way to handle it. Many commands that need a password will have options to read from a pipe or special file descriptor for the password to use. If these are available they should be used. The "openssl" for example can read passwords from many different input sources. If a command must read the password from a file, try getting it to read it from a named UNIX pipe (BASH has builtin facilities to generate named pipes for this) rather than a plain disk-based file. This lets you feed the password to the program without saving it to disk. I have used this in my "ks" script for passing config files for the mounting of encrypted file systems. If the program insists on reading from a TTY (user input only) you can use programs like "unbuffer" (from the "expect" package) to wrap the program in a TTY so you can properly pipe the password into the program. I have also used the "expect" command directly to do this. A newer method is to use the "socat" command (a advanced version of "netcat") to create a PTY wrapper around such commands. See my notes in https://antofthy.gitlab.io/info/co-processing/general_hints.txt for other ways to get around the TTY reading problem. ===============================================================================