647

I have a script that automates a process that needs access to a password-protected system. The system is accessed via a command-line program that accepts the user password as an argument.

I would like to prompt the user to type in their password, assign it to a shell variable, and then use that variable to construct the command line of the accessing program (which will of course produce stream output that I will process).

I am a reasonably competent shell programmer in Bourne/Bash, but I don't know how to accept the user input without having it echo to the terminal (or maybe having it echoed using '*' characters).

Can anyone help with this?

2

9 Answers 9

928

Here is another way to do it:

#!/bin/bash # Read Password echo -n Password: read -s password echo # Run Command echo $password 

The read -s will turn off echo for you. Just replace the echo on the last line with the command you want to run.

In some shells (e.g. Bash) read supports -p prompt-string which will allow the echo and read commands to be combined:

read -s -p "Password: " password 
Sign up to request clarification or add additional context in comments.

11 Comments

Some shells allow you to specify the prompt for the read command: read -s -p "Password:" password
Please note that read -s is not in POSIX, your script depends on bash if you use it. If you want to be POSIX-compliant, you should instead use the stty -echo solution suggested below, because stty and its echo parameter are defined in POSIX.
Oh, and echo -n isn't in POSIX either. Use printf instead.
According to my tries: Only works with /bin/bash and not with /bin/sh, just to make this clear.
I follow it up with echo "$REPLY" | sed -r 's/./*/g' (or even an arbitrary number of asterisks) instead of an empty echo to let them know their password input has been seen (if they typed one. I have a default password if they choose to skip over the prompt in my particular script)
|
323

A POSIX compliant answer. Notice the use of /bin/sh instead of /bin/bash. (It does work with bash, but it does not require bash.)

#!/bin/sh stty -echo printf "Password: " read PASSWORD stty echo printf "\n" 

11 Comments

No, really, do use stty if you want to be POSIX compliant. The code in this answer runs perfectly not even on bash, but actually on all shells that conform to POSIX.
unfortunately, breaking with a CTRL+C after stty -echo has occured will leave your terminal broken. Some better error catching might be needed here.
It seems better for read to handle hiding the password, if possible. echo -n "Password: " && read -s password || { stty -echo; read password; stty echo; }
@PypeBros You can just trap the interrupt to turn echoing back on: trap 'stty echo' INT. This would go at the beginning of the script or function wrapping this functionality.
@AndreyKaipov so a minimal POSIX-compliant password script would look something like this (given that I can't put linefeeds in comments): printf "Password: "; trap 'stty echo' INT EXIT; stty -echo; read PASSWORD; printf "\n"
|
127

One liner:

read -s -p "Password: " password 

Under Linux (and cygwin) this form works in bash and sh. It may not be standard Unix sh, though.

For more info and options, in bash, type "help read".

$ help read read: read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...] Read a line from the standard input and split it into fields. ... -p prompt output the string PROMPT without a trailing newline before attempting to read ... -s do not echo input coming from a terminal 

3 Comments

If the command generates "read: Illegal option -s" it means the script needs to be executed directly (./script vs sh ./script) ...see stackoverflow.com/questions/30554353/…
@shao.lo No it just means you need to use bash, not sh. Your comment only applies if the shebang header in the script is using bash.
It is not well defined what sh will be in "Linux" generally, each distribution can make its own choice. It's reasonable to assume bash is bash itself or a bash-compatible shell, but sh is not required to be bash or even bash-compatible and may be e.g. dash, which doesn't support read -s.
82

The -s option of read is not defined in the POSIX standard. See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html. I wanted something that would work for any POSIX shell, so I wrote a little function that uses stty to disable echo.

# Read secret string read_secret() { # Set up trap to ensure echo is enabled before exiting if the script # is terminated while echo is disabled. trap 'stty echo' EXIT # Disable echo. stty -echo # Read secret. read "$@" # Enable echo. stty echo trap - EXIT # Print a newline because the newline entered by the user after # entering the passcode is not echoed. This ensures that the # next line of output begins at a new line. echo } 

This function behaves quite similar to the read command. Here is a simple usage of read followed by similar usage of read_secret. The input to read_secret appears empty because it was not echoed to the terminal.

[susam@cube ~]$ read a b c foo \bar baz \qux [susam@cube ~]$ echo a=$a b=$b c=$c a=foo b=bar c=baz qux [susam@cube ~]$ unset a b c [susam@cube ~]$ read_secret a b c [susam@cube ~]$ echo a=$a b=$b c=$c a=foo b=bar c=baz qux [susam@cube ~]$ unset a b c 

Here is another that uses the -r option to preserve the backslashes in the input. This works because the read_secret function defined above passes all arguments it receives to the read command.

[susam@cube ~]$ read -r a b c foo \bar baz \qux [susam@cube ~]$ echo a=$a b=$b c=$c a=foo b=\bar c=baz \qux [susam@cube ~]$ unset a b c [susam@cube ~]$ read_secret -r a b c [susam@cube ~]$ echo a=$a b=$b c=$c a=foo b=\bar c=baz \qux [susam@cube ~]$ unset a b c 

Finally, here is an example that shows how to use the read_secret function to read a password in a POSIX compliant manner.

printf "Password: " read_secret password # Do something with $password here ... 

10 Comments

Should probably make sure that echo is not already disabled in the shell and not enable it if it wasn't before... Can probably use oldtty = stty -g and then at the end stty $oldtty to restore the previous settings. Otherwise, it's great.
got my vote due to failing read: Illegal option -s
@Perkins - Should it be oldtty=$(stty -g)? @SusamPal - +1 nice trick.
There is a problem with this script (or I have some misunderstandings). First I assume this script is sourced by . script_name then call the function read_secret. If Ctrl+c is pressed during the read command, the trap is not triggered as this script is sourced rather than called.
The trap should be set before the stty, otherwise, if it's interrupted right in between, there would be no restore.
|
18

I found to be the the askpass command useful

password=$(/lib/cryptsetup/askpass "Give a password") 

Every input character is replaced by *. See: Give a password ****

5 Comments

Where's this /lib/cryptsetup/askpass from? It's certainly not a standard *nix (or GNU/Linux) tool.
It's the best solution I've seen so far. It's from cryptsetup which is the standard for encrypting hard-drives, so pretty commonly used. sudo apt-get install cryptsetup.
I tried using this and the stars didn't show up, plus it messed up my terminal big time until I closed it. Any further shell input is hidden!
It works if I pass in the password, but if I try to abort and kill it with Ctrl+C it messes up the terminal
I use this method as a oneliner to ask for a password and write it to file (yes I know what I do ^^): /lib/cryptsetup/askpass "Give a password" > pass.txt Very useful, thanks!
16

While there are plenty of answers already, there is one more way to ask for passwords from terminal in virtually any modern linux that runs systemd (yes, booo, I know, but what can you do about it). systemd-ask-password command is included with standard core systemd package and can be used like this:

#!/bin/sh PASSWORD="$(systemd-ask-password "Enter your password:")" 

Use --emoji=no switch to suppress that stupid unicode lock character. It works great inside terminal but requires tweaking if you need it to pop up a GUI dialog, but that is out of scope here.

Comments

9

You can also prompt for a password without setting a variable in the current shell by doing something like this:

$(read -s;echo $REPLY) 

For instance:

my-command --set password=$(read -sp "Password: ";echo $REPLY) 

You can add several of these prompted values with line break, doing this:

my-command --set user=$(read -sp "`echo $'\n '`User: ";echo $REPLY) --set password=$(read -sp "`echo $'\n '`Password: ";echo $REPLY) 

Comments

5

Turn echo off using stty, then back on again after.

Comments

2

For anyone needing to prompt for a password, you may be interested in using encpass.sh. This is a script I wrote for similar purposes of capturing a secret at runtime and then encrypting it for subsequent occasions. Subsequent runs do not prompt for the password as it will just use the encrypted value from disk.

It stores the encrypted passwords in a hidden folder under the user's home directory or in a custom folder that you can define through the environment variable ENCPASS_HOME_DIR. It is designed to be POSIX compliant and has an MIT License, so it can be used even in corporate enterprise environments. My company, Plyint LLC, maintains the script and occasionally releases updates. Pull requests are also welcome, if you find an issue. :)

To use it in your scripts simply source encpass.sh in your script and call the get_secret function. I'm including a copy of the script below for easy visibility.

#!/bin/sh ################################################################################ # Copyright (c) 2020 Plyint, LLC <[email protected]>. All Rights Reserved. # This file is licensed under the MIT License (MIT). # Please see LICENSE.txt for more information. # # DESCRIPTION: # This script allows a user to encrypt a password (or any other secret) at # runtime and then use it, decrypted, within a script. This prevents shoulder # surfing passwords and avoids storing the password in plain text, which could # inadvertently be sent to or discovered by an individual at a later date. # # This script generates an AES 256 bit symmetric key for each script (or user- # defined bucket) that stores secrets. This key will then be used to encrypt # all secrets for that script or bucket. encpass.sh sets up a directory # (.encpass) under the user's home directory where keys and secrets will be # stored. # # For further details, see README.md or run "./encpass ?" from the command line. # ################################################################################ encpass_checks() { if [ -n "$ENCPASS_CHECKS" ]; then return fi if [ ! -x "$(command -v openssl)" ]; then echo "Error: OpenSSL is not installed or not accessible in the current path." \ "Please install it and try again." >&2 exit 1 fi if [ -z "$ENCPASS_HOME_DIR" ]; then ENCPASS_HOME_DIR=$(encpass_get_abs_filename ~)/.encpass fi if [ ! -d "$ENCPASS_HOME_DIR" ]; then mkdir -m 700 "$ENCPASS_HOME_DIR" mkdir -m 700 "$ENCPASS_HOME_DIR/keys" mkdir -m 700 "$ENCPASS_HOME_DIR/secrets" fi if [ "$(basename "$0")" != "encpass.sh" ]; then encpass_include_init "$1" "$2" fi ENCPASS_CHECKS=1 } # Initializations performed when the script is included by another script encpass_include_init() { if [ -n "$1" ] && [ -n "$2" ]; then ENCPASS_BUCKET=$1 ENCPASS_SECRET_NAME=$2 elif [ -n "$1" ]; then ENCPASS_BUCKET=$(basename "$0") ENCPASS_SECRET_NAME=$1 else ENCPASS_BUCKET=$(basename "$0") ENCPASS_SECRET_NAME="password" fi } encpass_generate_private_key() { ENCPASS_KEY_DIR="$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET" if [ ! -d "$ENCPASS_KEY_DIR" ]; then mkdir -m 700 "$ENCPASS_KEY_DIR" fi if [ ! -f "$ENCPASS_KEY_DIR/private.key" ]; then (umask 0377 && printf "%s" "$(openssl rand -hex 32)" >"$ENCPASS_KEY_DIR/private.key") fi } encpass_get_private_key_abs_name() { ENCPASS_PRIVATE_KEY_ABS_NAME="$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.key" if [ "$1" != "nogenerate" ]; then if [ ! -f "$ENCPASS_PRIVATE_KEY_ABS_NAME" ]; then encpass_generate_private_key fi fi } encpass_get_secret_abs_name() { ENCPASS_SECRET_ABS_NAME="$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET/$ENCPASS_SECRET_NAME.enc" if [ "$3" != "nocreate" ]; then if [ ! -f "$ENCPASS_SECRET_ABS_NAME" ]; then set_secret "$1" "$2" fi fi } get_secret() { encpass_checks "$1" "$2" encpass_get_private_key_abs_name encpass_get_secret_abs_name "$1" "$2" encpass_decrypt_secret } set_secret() { encpass_checks "$1" "$2" if [ "$3" != "reuse" ] || { [ -z "$ENCPASS_SECRET_INPUT" ] && [ -z "$ENCPASS_CSECRET_INPUT" ]; }; then echo "Enter $ENCPASS_SECRET_NAME:" >&2 stty -echo read -r ENCPASS_SECRET_INPUT stty echo echo "Confirm $ENCPASS_SECRET_NAME:" >&2 stty -echo read -r ENCPASS_CSECRET_INPUT stty echo fi if [ "$ENCPASS_SECRET_INPUT" = "$ENCPASS_CSECRET_INPUT" ]; then encpass_get_private_key_abs_name ENCPASS_SECRET_DIR="$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET" if [ ! -d "$ENCPASS_SECRET_DIR" ]; then mkdir -m 700 "$ENCPASS_SECRET_DIR" fi printf "%s" "$(openssl rand -hex 16)" >"$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc" ENCPASS_OPENSSL_IV="$(cat "$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc")" echo "$ENCPASS_SECRET_INPUT" | openssl enc -aes-256-cbc -e -a -iv \ "$ENCPASS_OPENSSL_IV" -K \ "$(cat "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.key")" 1>> \ "$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc" else echo "Error: secrets do not match. Please try again." >&2 exit 1 fi } encpass_get_abs_filename() { # $1 : relative filename filename="$1" parentdir="$(dirname "${filename}")" if [ -d "${filename}" ]; then cd "${filename}" && pwd elif [ -d "${parentdir}" ]; then echo "$(cd "${parentdir}" && pwd)/$(basename "${filename}")" fi } encpass_decrypt_secret() { if [ -f "$ENCPASS_PRIVATE_KEY_ABS_NAME" ]; then ENCPASS_DECRYPT_RESULT="$(dd if="$ENCPASS_SECRET_ABS_NAME" ibs=1 skip=32 2> /dev/null | openssl enc -aes-256-cbc \ -d -a -iv "$(head -c 32 "$ENCPASS_SECRET_ABS_NAME")" -K "$(cat "$ENCPASS_PRIVATE_KEY_ABS_NAME")" 2> /dev/null)" if [ ! -z "$ENCPASS_DECRYPT_RESULT" ]; then echo "$ENCPASS_DECRYPT_RESULT" else # If a failed unlock command occurred and the user tries to show the secret # Present either locked or decrypt command if [ -f "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.lock" ]; then echo "**Locked**" else # The locked file wasn't present as expected. Let's display a failure echo "Error: Failed to decrypt" fi fi elif [ -f "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.lock" ]; then echo "**Locked**" else echo "Error: Unable to decrypt. The key file \"$ENCPASS_PRIVATE_KEY_ABS_NAME\" is not present." fi } ########################################################## # COMMAND LINE MANAGEMENT SUPPORT # ------------------------------- # If you don't need to manage the secrets for the scripts # with encpass.sh you can delete all code below this point # in order to significantly reduce the size of encpass.sh. # This is useful if you want to bundle encpass.sh with # your existing scripts and just need the retrieval # functions. ########################################################## encpass_show_secret() { encpass_checks ENCPASS_BUCKET=$1 encpass_get_private_key_abs_name "nogenerate" if [ ! -z "$2" ]; then ENCPASS_SECRET_NAME=$2 encpass_get_secret_abs_name "$1" "$2" "nocreate" if [ -z "$ENCPASS_SECRET_ABS_NAME" ]; then echo "No secret named $2 found for bucket $1." exit 1 fi encpass_decrypt_secret else ENCPASS_FILE_LIST=$(ls -1 "$ENCPASS_HOME_DIR"/secrets/"$1") for ENCPASS_F in $ENCPASS_FILE_LIST; do ENCPASS_SECRET_NAME=$(basename "$ENCPASS_F" .enc) encpass_get_secret_abs_name "$1" "$ENCPASS_SECRET_NAME" "nocreate" if [ -z "$ENCPASS_SECRET_ABS_NAME" ]; then echo "No secret named $ENCPASS_SECRET_NAME found for bucket $1." exit 1 fi echo "$ENCPASS_SECRET_NAME = $(encpass_decrypt_secret)" done fi } encpass_getche() { old=$(stty -g) stty raw min 1 time 0 printf '%s' "$(dd bs=1 count=1 2>/dev/null)" stty "$old" } encpass_remove() { if [ ! -n "$ENCPASS_FORCE_REMOVE" ]; then if [ ! -z "$ENCPASS_SECRET" ]; then printf "Are you sure you want to remove the secret \"%s\" from bucket \"%s\"? [y/N]" "$ENCPASS_SECRET" "$ENCPASS_BUCKET" else printf "Are you sure you want to remove the bucket \"%s?\" [y/N]" "$ENCPASS_BUCKET" fi ENCPASS_CONFIRM="$(encpass_getche)" printf "\n" if [ "$ENCPASS_CONFIRM" != "Y" ] && [ "$ENCPASS_CONFIRM" != "y" ]; then exit 0 fi fi if [ ! -z "$ENCPASS_SECRET" ]; then rm -f "$1" printf "Secret \"%s\" removed from bucket \"%s\".\n" "$ENCPASS_SECRET" "$ENCPASS_BUCKET" else rm -Rf "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET" rm -Rf "$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET" printf "Bucket \"%s\" removed.\n" "$ENCPASS_BUCKET" fi } encpass_save_err() { if read -r x; then { printf "%s\n" "$x"; cat; } > "$1" elif [ "$x" != "" ]; then printf "%s" "$x" > "$1" fi } encpass_help() { less << EOF NAME: encpass.sh - Use encrypted passwords in shell scripts DESCRIPTION: A lightweight solution for using encrypted passwords in shell scripts using OpenSSL. It allows a user to encrypt a password (or any other secret) at runtime and then use it, decrypted, within a script. This prevents shoulder surfing passwords and avoids storing the password in plain text, within a script, which could inadvertently be sent to or discovered by an individual at a later date. This script generates an AES 256 bit symmetric key for each script (or user-defined bucket) that stores secrets. This key will then be used to encrypt all secrets for that script or bucket. Subsequent calls to retrieve a secret will not prompt for a secret to be entered as the file with the encrypted value already exists. Note: By default, encpass.sh sets up a directory (.encpass) under the user's home directory where keys and secrets will be stored. This directory can be overridden by setting the environment variable ENCPASS_HOME_DIR to a directory of your choice. ~/.encpass (or the directory specified by ENCPASS_HOME_DIR) will contain the following subdirectories: - keys (Holds the private key for each script/bucket) - secrets (Holds the secrets stored for each script/bucket) USAGE: To use the encpass.sh script in an existing shell script, source the script and then call the get_secret function. Example: #!/bin/sh . encpass.sh password=\$(get_secret) When no arguments are passed to the get_secret function, then the bucket name is set to the name of the script and the secret name is set to "password". There are 2 other ways to call get_secret: Specify the secret name: Ex: \$(get_secret user) - bucket name = <script name> - secret name = "user" Specify both the secret name and bucket name: Ex: \$(get_secret personal user) - bucket name = "personal" - secret name = "user" encpass.sh also provides a command line interface to manage the secrets. To invoke a command, pass it as an argument to encpass.sh from the shell. $ encpass.sh [COMMAND] See the COMMANDS section below for a list of available commands. Wildcard handling is implemented for secret and bucket names. This enables performing operations like adding/removing a secret to/from multiple buckets at once. COMMANDS: add [-f] <bucket> <secret> Add a secret to the specified bucket. The bucket will be created if it does not already exist. If a secret with the same name already exists for the specified bucket, then the user will be prompted to confirm overwriting the value. If the -f option is passed, then the add operation will perform a forceful overwrite of the value. (i.e. no prompt) list|ls [<bucket>] Display the names of the secrets held in the bucket. If no bucket is specified, then the names of all existing buckets will be displayed. lock Locks all keys used by encpass.sh using a password. The user will be prompted to enter a password and confirm it. A user should take care to securely store the password. If the password is lost then keys can not be unlocked. When keys are locked, secrets can not be retrieved. (e.g. the output of the values in the "show" command will be encrypted/garbage) remove|rm [-f] <bucket> [<secret>] Remove a secret from the specified bucket. If only a bucket is specified then the entire bucket (i.e. all secrets and keys) will be removed. By default the user is asked to confirm the removal of the secret or the bucket. If the -f option is passed then a forceful removal will be performed. (i.e. no prompt) show [<bucket>] [<secret>] Show the unencrypted value of the secret from the specified bucket. If no secret is specified then all secrets for the bucket are displayed. update <bucket> <secret> Updates a secret in the specified bucket. This command is similar to using an "add -f" command, but it has a safety check to only proceed if the specified secret exists. If the secret, does not already exist, then an error will be reported. There is no forceable update implemented. Use "add -f" for any required forceable update scenarios. unlock Unlocks all the keys for encpass.sh. The user will be prompted to enter the password and confirm it. dir Prints out the current value of the ENCPASS_HOME_DIR environment variable. help|--help|usage|--usage|? Display this help message. EOF } # Subcommands for cli support case "$1" in add ) shift while getopts ":f" ENCPASS_OPTS; do case "$ENCPASS_OPTS" in f ) ENCPASS_FORCE_ADD=1;; esac done encpass_checks if [ -n "$ENCPASS_FORCE_ADD" ]; then shift $((OPTIND-1)) fi if [ ! -z "$1" ] && [ ! -z "$2" ]; then # Allow globbing # shellcheck disable=SC2027,SC2086 ENCPASS_ADD_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)" if [ -z "$ENCPASS_ADD_LIST" ]; then ENCPASS_ADD_LIST="$1" fi for ENCPASS_ADD_F in $ENCPASS_ADD_LIST; do ENCPASS_ADD_DIR="$(basename "$ENCPASS_ADD_F")" ENCPASS_BUCKET="$ENCPASS_ADD_DIR" if [ ! -n "$ENCPASS_FORCE_ADD" ] && [ -f "$ENCPASS_ADD_F/$2.enc" ]; then echo "Warning: A secret with the name \"$2\" already exists for bucket $ENCPASS_BUCKET." echo "Would you like to overwrite the value? [y/N]" ENCPASS_CONFIRM="$(encpass_getche)" if [ "$ENCPASS_CONFIRM" != "Y" ] && [ "$ENCPASS_CONFIRM" != "y" ]; then continue fi fi ENCPASS_SECRET_NAME="$2" echo "Adding secret \"$ENCPASS_SECRET_NAME\" to bucket \"$ENCPASS_BUCKET\"..." set_secret "$ENCPASS_BUCKET" "$ENCPASS_SECRET_NAME" "reuse" done else echo "Error: A bucket name and secret name must be provided when adding a secret." exit 1 fi ;; update ) shift encpass_checks if [ ! -z "$1" ] && [ ! -z "$2" ]; then ENCPASS_SECRET_NAME="$2" # Allow globbing # shellcheck disable=SC2027,SC2086 ENCPASS_UPDATE_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)" for ENCPASS_UPDATE_F in $ENCPASS_UPDATE_LIST; do # Allow globbing # shellcheck disable=SC2027,SC2086 if [ -f "$ENCPASS_UPDATE_F/"$2".enc" ]; then ENCPASS_UPDATE_DIR="$(basename "$ENCPASS_UPDATE_F")" ENCPASS_BUCKET="$ENCPASS_UPDATE_DIR" echo "Updating secret \"$ENCPASS_SECRET_NAME\" to bucket \"$ENCPASS_BUCKET\"..." set_secret "$ENCPASS_BUCKET" "$ENCPASS_SECRET_NAME" "reuse" else echo "Error: A secret with the name \"$2\" does not exist for bucket $1." exit 1 fi done else echo "Error: A bucket name and secret name must be provided when updating a secret." exit 1 fi ;; rm|remove ) shift encpass_checks while getopts ":f" ENCPASS_OPTS; do case "$ENCPASS_OPTS" in f ) ENCPASS_FORCE_REMOVE=1;; esac done if [ -n "$ENCPASS_FORCE_REMOVE" ]; then shift $((OPTIND-1)) fi if [ -z "$1" ]; then echo "Error: A bucket must be specified for removal." fi # Allow globbing # shellcheck disable=SC2027,SC2086 ENCPASS_REMOVE_BKT_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)" if [ ! -z "$ENCPASS_REMOVE_BKT_LIST" ]; then for ENCPASS_REMOVE_B in $ENCPASS_REMOVE_BKT_LIST; do ENCPASS_BUCKET="$(basename "$ENCPASS_REMOVE_B")" if [ ! -z "$2" ]; then # Removing secrets for a specified bucket # Allow globbing # shellcheck disable=SC2027,SC2086 ENCPASS_REMOVE_LIST="$(ls -1p "$ENCPASS_REMOVE_B/"$2".enc" 2>/dev/null)" if [ -z "$ENCPASS_REMOVE_LIST" ]; then echo "Error: No secrets found for $2 in bucket $ENCPASS_BUCKET." exit 1 fi for ENCPASS_REMOVE_F in $ENCPASS_REMOVE_LIST; do ENCPASS_SECRET="$2" encpass_remove "$ENCPASS_REMOVE_F" done else # Removing a specified bucket encpass_remove fi done else echo "Error: The bucket named $1 does not exist." exit 1 fi ;; show ) shift encpass_checks if [ -z "$1" ]; then ENCPASS_SHOW_DIR="*" else ENCPASS_SHOW_DIR=$1 fi if [ ! -z "$2" ]; then # Allow globbing # shellcheck disable=SC2027,SC2086 if [ -f "$(encpass_get_abs_filename "$ENCPASS_HOME_DIR/secrets/$ENCPASS_SHOW_DIR/"$2".enc")" ]; then encpass_show_secret "$ENCPASS_SHOW_DIR" "$2" fi else # Allow globbing # shellcheck disable=SC2027,SC2086 ENCPASS_SHOW_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$ENCPASS_SHOW_DIR"" 2>/dev/null)" if [ -z "$ENCPASS_SHOW_LIST" ]; then if [ "$ENCPASS_SHOW_DIR" = "*" ]; then echo "Error: No buckets exist." else echo "Error: Bucket $1 does not exist." fi exit 1 fi for ENCPASS_SHOW_F in $ENCPASS_SHOW_LIST; do ENCPASS_SHOW_DIR="$(basename "$ENCPASS_SHOW_F")" echo "$ENCPASS_SHOW_DIR:" encpass_show_secret "$ENCPASS_SHOW_DIR" echo " " done fi ;; ls|list ) shift encpass_checks if [ ! -z "$1" ]; then # Allow globbing # shellcheck disable=SC2027,SC2086 ENCPASS_FILE_LIST="$(ls -1p "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)" if [ -z "$ENCPASS_FILE_LIST" ]; then # Allow globbing # shellcheck disable=SC2027,SC2086 ENCPASS_DIR_EXISTS="$(ls -d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)" if [ ! -z "$ENCPASS_DIR_EXISTS" ]; then echo "Bucket $1 is empty." else echo "Error: Bucket $1 does not exist." fi exit 1 fi ENCPASS_NL="" for ENCPASS_F in $ENCPASS_FILE_LIST; do if [ -d "${ENCPASS_F%:}" ]; then printf "$ENCPASS_NL%s\n" "$(basename "$ENCPASS_F")" ENCPASS_NL="\n" else printf "%s\n" "$(basename "$ENCPASS_F" .enc)" fi done else # Allow globbing # shellcheck disable=SC2027,SC2086 ENCPASS_BUCKET_LIST="$(ls -1p "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)" for ENCPASS_C in $ENCPASS_BUCKET_LIST; do if [ -d "${ENCPASS_C%:}" ]; then printf "\n%s" "\n$(basename "$ENCPASS_C")" else basename "$ENCPASS_C" .enc fi done fi ;; lock ) shift encpass_checks echo "************************!!!WARNING!!!*************************" >&2 echo "* You are about to lock your keys with a password. *" >&2 echo "* You will not be able to use your secrets again until you *" >&2 echo "* unlock the keys with the same password. It is important *" >&2 echo "* that you securely store the password, so you can recall it *" >&2 echo "* in the future. If you forget your password you will no *" >&2 echo "* longer be able to access your secrets. *" >&2 echo "************************!!!WARNING!!!*************************" >&2 printf "\n%s\n" "About to lock keys held in directory $ENCPASS_HOME_DIR/keys/" printf "\nEnter Password to lock keys:" >&2 stty -echo read -r ENCPASS_KEY_PASS printf "\nConfirm Password:" >&2 read -r ENCPASS_CKEY_PASS printf "\n" stty echo if [ -z "$ENCPASS_KEY_PASS" ]; then echo "Error: You must supply a password value." exit 1 fi if [ "$ENCPASS_KEY_PASS" = "$ENCPASS_CKEY_PASS" ]; then ENCPASS_NUM_KEYS_LOCKED=0 ENCPASS_KEYS_LIST="$(ls -1d "$ENCPASS_HOME_DIR/keys/"*"/" 2>/dev/null)" for ENCPASS_KEY_F in $ENCPASS_KEYS_LIST; do if [ -d "${ENCPASS_KEY_F%:}" ]; then ENCPASS_KEY_NAME="$(basename "$ENCPASS_KEY_F")" ENCPASS_KEY_VALUE="" if [ -f "$ENCPASS_KEY_F/private.key" ]; then ENCPASS_KEY_VALUE="$(cat "$ENCPASS_KEY_F/private.key")" if [ ! -f "$ENCPASS_KEY_F/private.lock" ]; then echo "Locking key $ENCPASS_KEY_NAME..." else echo "Error: The key $ENCPASS_KEY_NAME appears to have been previously locked." echo " The current key file may hold a bad value. Exiting to avoid encrypting" echo " a bad value and overwriting the lock file." exit 1 fi else echo "Error: Private key file ${ENCPASS_KEY_F}private.key missing for bucket $ENCPASS_KEY_NAME." exit 1 fi if [ ! -z "$ENCPASS_KEY_VALUE" ]; then openssl enc -aes-256-cbc -pbkdf2 -iter 10000 -salt -in "$ENCPASS_KEY_F/private.key" -out "$ENCPASS_KEY_F/private.lock" -k "$ENCPASS_KEY_PASS" if [ -f "$ENCPASS_KEY_F/private.key" ] && [ -f "$ENCPASS_KEY_F/private.lock" ]; then # Both the key and lock file exist. We can remove the key file now rm -f "$ENCPASS_KEY_F/private.key" echo "Locked key $ENCPASS_KEY_NAME." ENCPASS_NUM_KEYS_LOCKED=$(( ENCPASS_NUM_KEYS_LOCKED + 1 )) else echo "Error: The key fle and/or lock file were not found as expected for key $ENCPASS_KEY_NAME." fi else echo "Error: No key value found for the $ENCPASS_KEY_NAME key." exit 1 fi fi done echo "Locked $ENCPASS_NUM_KEYS_LOCKED keys." else echo "Error: Passwords do not match." fi ;; unlock ) shift encpass_checks printf "%s\n" "About to unlock keys held in the $ENCPASS_HOME_DIR/keys/ directory." printf "\nEnter Password to unlock keys: " >&2 stty -echo read -r ENCPASS_KEY_PASS printf "\n" stty echo if [ ! -z "$ENCPASS_KEY_PASS" ]; then ENCPASS_NUM_KEYS_UNLOCKED=0 ENCPASS_KEYS_LIST="$(ls -1d "$ENCPASS_HOME_DIR/keys/"*"/" 2>/dev/null)" for ENCPASS_KEY_F in $ENCPASS_KEYS_LIST; do if [ -d "${ENCPASS_KEY_F%:}" ]; then ENCPASS_KEY_NAME="$(basename "$ENCPASS_KEY_F")" echo "Unlocking key $ENCPASS_KEY_NAME..." if [ -f "$ENCPASS_KEY_F/private.key" ] && [ ! -f "$ENCPASS_KEY_F/private.lock" ]; then echo "Error: Key $ENCPASS_KEY_NAME appears to be unlocked already." exit 1 fi if [ -f "$ENCPASS_KEY_F/private.lock" ]; then # Remove the failed file in case previous decryption attempts were unsuccessful rm -f "$ENCPASS_KEY_F/failed" 2>/dev/null # Decrypt key. Log any failure to the "failed" file. openssl enc -aes-256-cbc -d -pbkdf2 -iter 10000 -salt \ -in "$ENCPASS_KEY_F/private.lock" -out "$ENCPASS_KEY_F/private.key" \ -k "$ENCPASS_KEY_PASS" 2>&1 | encpass_save_err "$ENCPASS_KEY_F/failed" if [ ! -f "$ENCPASS_KEY_F/failed" ]; then # No failure has occurred. if [ -f "$ENCPASS_KEY_F/private.key" ] && [ -f "$ENCPASS_KEY_F/private.lock" ]; then # Both the key and lock file exist. We can remove the lock file now. rm -f "$ENCPASS_KEY_F/private.lock" echo "Unlocked key $ENCPASS_KEY_NAME." ENCPASS_NUM_KEYS_UNLOCKED=$(( ENCPASS_NUM_KEYS_UNLOCKED + 1 )) else echo "Error: The key file and/or lock file were not found as expected for key $ENCPASS_KEY_NAME." fi else printf "Error: Failed to unlock key %s.\n" "$ENCPASS_KEY_NAME" printf " Please view %sfailed for details.\n" "$ENCPASS_KEY_F" fi else echo "Error: No lock file found for the $ENCPASS_KEY_NAME key." fi fi done echo "Unlocked $ENCPASS_NUM_KEYS_UNLOCKED keys." else echo "No password entered." fi ;; dir ) shift encpass_checks echo "ENCPASS_HOME_DIR=$ENCPASS_HOME_DIR" ;; help|--help|usage|--usage|\? ) encpass_checks encpass_help ;; * ) if [ ! -z "$1" ]; then echo "Command not recognized. See \"encpass.sh help\" for a list commands." exit 1 fi ;; esac 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.