GPG-Based Password Wallet
January 1st, 2008 by Carl Welch in
Like many Internet addicts, I have way too many user name/password accounts to remember: accounts on social-networking sites, rarely used logins at work, on-line banking and so on. One solution to this problem is to use the same user name and password everywhere, but that's clearly not safe; if people get a hold of your account information in one place, they own all your other accounts too.
I wanted a relatively safe, flexible and easy way to store passwords and other useful confidential information. I also wanted it to be easily accessible, which meant that I'd like to get at it over a text-only SSH connection. And, I wanted it to be something that could move around from machine to machine without too much trouble.
A few months ago, I saw an article by Duane Odom on linux.ocm about a shell script that uses GPG to encrypt and decrypt a text file containing the user's list of passwords (or any kind of text). I liked this approach, as it met the following requirements:
It stores passwords in a well-encrypted text file (protected by a master password). The text file could contain anything and be formatted any way I want.
The entire interface is text (an ncurses password interface, followed by less or a text editor like vim), so you can access it over a nongraphical SSH session (see the Accessing Your Password Wallet from the Computer at Your Friend's House sidebar).
The script is built on standard utilities common to most Linux distributions (gpg and dialog).
Although I liked the way the original script worked, I wanted to add several features. So I made some alterations to the original, and the result is shown in Listing 1.
It's pretty easy to install; simply save the script somewhere in your $PATH and make it executable. Then, you just need to tell it where your encrypted password file should be. There are three ways to do this:
Set the $WALLET_FILENAME environment variable.
Set $WALLET_FILENAME in ~/.walletrc.
Specify the filename with the -c command-line option.
The second method (which overrides the first method) is my preference—I have the following line in ~/.walletrc:
WALLET_FILENAME=~/docs/wallet.gpg
But, if I needed to use a different wallet file, I could override either of the first two methods with the command-line option by calling the script like this:
wallet -c ~/docs/other_wallet.gpg
wallet defaults to its read-only mode, in which it displays the decrypted version of your wallet file using less. But, if you include the -e command-line option (edit mode), the script decrypts your wallet file to a temporary location and opens it in a text editor (the script defaults to using vi, but you can set the $VISUAL variable in the environment or in your ~/.walletrc file). When you close the editor, wallet encrypts the file and saves it to the original location.
The first time you run wallet, you won't have a wallet file, so wallet creates it for you and runs in edit mode.
Let's dig in to the script to see how it works. The first thing it does is use the dot operator to source a file called functions, which appears as shown in Listing 2. Having wallet source an external file (with the dot operator) is essentially equivalent to inserting the contents of the sourced file (~/bin/functions) at line 3 of wallet. Doing it this way allows other scripts to use the same code (a code library for shell scripts).
The functions file includes a function called is_installed, which uses the bash built-in type to see whether a program is installed. If is_installed doesn't find the program in your $PATH, is_installed prints an error message and calls exit, which terminates wallet. So, if you run wallet and it quits with an error like “cannot locate dialog in path”, you probably haven't installed the dialog package. Use your distribution's package management system (yum, apt-get, whatever) to install dialog and try again.
Lines 18 through 28 of the wallet script parse the command-line arguments using the getopts bash built-in. The while loop loops through the options specified by the string ec:. This means that wallet can accept the -e and -c options, and that the -c option requires an argument. As the while loop moves through the command-line arguments, the current option is assigned to the variable $OPTION, and any argument to the current option is assigned to the variable $OPTARG. Any unrecognized option results in an error message, and wallet exits. After the while loop completes, it's important to reset the $OPTIND variable (this is necessary after any getopts call).
Lines 37 through 45 of the wallet script verify that the encrypted file exists, and create the file if it doesn't exist already. The -f test checks to see whether $WALLET_FILENAME exists as a normal file. If not, the test fails, and wallet assumes you are running wallet for the first time and that wallet needs to set up the working environment. wallet uses the command substitution syntax for creating the directory in which the encrypted file should exist (line 40):
mkdir -p $( dirname $WALLET_FILENAME )
The command inside the $(...) runs first, and the result becomes the argument to mkdir. The dirname command returns the encrypted file's directory, and mkdir -p creates that directory (and any necessary parent directories).
Next, wallet needs to create the encrypted file (even though the unencrypted version will be empty). Line 41 uses mktemp to create an empty file in /tmp whose name ends in six randomly chosen characters. mktemp prints the name of the file it creates, so running this in a command substitution shell and assigning the result to $TEMPFILE puts the name of the temporary file in $TEMPFILE.
Now we see the first use of gpg. Line 42 uses gpg to encrypt the (empty) temporary file ($TEMPFILE) via symmetric encryption (gpg's -c option) and to write the encrypted file to $WALLET_FILENAME. wallet then deletes the temporary file. Because this is the first time wallet has run, it assumes that edit mode is appropriate and sets the $EDIT_PWFILE flag.
Line 52 uses the command substitution trick again, this time to prompt the user for the master password (used to encrypt the wallet file). The dialog man page describes the many ways that scripts using dialog can retrieve input from the user. This example uses dialog to create a simple password box. The --stdout option tells dialog to print the user's input (the master password) to standard output, so that it may be assigned to $PASSWORD.
Line 55 inspects the bash variable $?, which contains the exit code of the previous process (dialog, in this case). The convention is that an exit code of 0 indicates success (and wallet follows this convention in its own exit calls). If $? differs from 0 on line 55, this indicates that dialog encountered an error, and wallet terminates with an error message.
If $EDIT_PWFILE is 0 (line 65), then wallet is running in read-only mode:
echo $PASSWORD | gpg --decrypt --passphrase-fd 0 ↪$WALLET_FILENAME | less
This tells gpg to decrypt $WALLET_FILENAME and to read the password from standard input (fd 0). Piping $PASSWORD into gpg enables gpg to decrypt the wallet file without interactively asking the user for the master password. The output (the decrypted wallet file) is printed to standard output, which is piped into less, allowing the user to page through the passwords, run searches and so on. When the user closes less, wallet clears the screen and exits.
The rest of the script assumes that $EDIT_PWFILE is nonzero (that wallet is running in edit mode).
In edit mode, wallet needs to decrypt the wallet file, open the decrypted file in a text editor, and then encrypt the edited file back to the original location. Line 74 uses mktemp to create a temporary directory, into which the wallet file will be decrypted. Line 75 sets $CLEARTEXT_WALLET_FILENAME to be the name of a file inside the temporary directory.
Line 79 runs trap, a bash built-in. The first argument to trap is a command, and this is followed by a list of signals (for example, if someone runs kill on wallet). If wallet receives any of these signals after line 79, wallet will run the trapped command (deleting the decrypted wallet file) prior to exiting. This is an attempt to ensure that the decrypted file isn't left sitting around if wallet terminates unexpectedly.
Line 83 is like what we saw in read-only mode, with the addition of the -o option to gpg. This instructs gpg to write the decrypted file to $CLEARTEXT_WALLET_FILENAME.
If gpg's exit code was 0, wallet renames the encrypted wallet file with a .bak extension (thus preserving a copy, in case something goes wrong) and opens the decrypted file in the text editor $VISUAL. After the editor exits, wallet tells gpg to encrypt the edited plain-text file at $CLEARTEXT_WALLET_FILENAME and to write the encrypted wallet file back to $WALLET_FILENAME. A nonzero exit status from this gpg call means that something went wrong in re-encrypting the wallet file, so wallet makes a copy of the plain-text file in your home directory and prints an error message.
wallet is a bash script for managing a password wallet. It's written to be usable over a text-only interface. Hopefully, this description of the code has helped you add an item or two to your bag of scripting tricks.
Special Magazine Offer -- 2 Free Trial Issues!
Receive 2 free trial issues of Linux Journal as well as instant online access to current and past issues. There's NO RISK and NO OBLIGATION to buy. CLICK HERE for offer
Linux Journal: delivering readers the advice and inspiration they need to get the most out of their Linux systems since 1994.
Sorry, offer available in the US only. International orders, click here.
Subscribe now!
The Latest
Featured Videos
Linux Journal Gadget Guy, Shawn Powers, reviews the Flip Video Ultra, a small portable video camera, and shows us how easy it is to edit the video with Kino.
Thanks to our sponsor: Silicon Mechanics
Webcams are notorious for their lack of support under Linux. But thanks to GSPCA, many webcams now have functional V4L drivers. This tutorial covers the building, installation, and configuration of the GSPCA drivers, including how to adjust color balance and brightness directly at the kernel module level.
Recently Popular
| What do you use to run Windows applications on your Linux desktop? | Aug-19-08 |
| Music Education With Linux Sound Tools, Redux | Aug-18-08 |
| Building a Call Center with LTSP and Soft Phones | Aug-25-05 |
| Having Fun on ViewSurf | Jul-01-98 |
| Chapter 16: Ubuntu and Your iPod | Aug-30-06 |
| Why Python? | May-01-00 |
From the Magazine
September 2008, #173
Feeling a bit like a Thermian? Never give up, never surrender! Someday, you could go from underdog to top dog. Just take a look at a few of the underdogs we highlight in this issue: Mutt, djbdns, Nginix, Gentoo, Xara and the program voted mostly likely to fail just a few years back—Firefox. If Firefox not radical enough for you, check out Chef Marcel's column for some more alternatives. Having trouble mapping your program data to your relational database? If so, Rueven Lerner shows you some tricks in his At The Forge column.
Need to run GUI applications on your server in the next state? In his Paranoid Penguin column, Mick Bauer shows you how to do it securely. Kyle Rankin keeps hacking and slashing and shows you a few split screen secrets you may not be familiar with. Finally, we all know what happens next February, but only Doc knows what happens afterward.
Delicious
Digg
Reddit
Newsvine
Technorati






