#!/bin/bash # # Common functions. # # Setup main configuration and load preferences function keyringer_config_load { if [ -f "$HOME/.$NAME" ]; then echo "Converting legacy configuration scheme..." mv "$HOME/.$NAME" "$HOME/.$NAME.tmp" mkdir "$HOME/.$NAME" mv "$HOME/.$NAME.tmp" "$CONFIG" fi if [ ! -e "$CONFIG" ]; then echo "Creating $CONFIG..." mkdir -p `dirname $CONFIG` touch "$CONFIG" chmod 600 "$CONFIG" echo "# Keyringer config file." > "$CONFIG" echo "" >> "$CONFIG" fi keyringer_config_load_preferences } # Load config preferences function keyringer_config_load_preferences { # Load custom keyring preferences if [ ! -z "$PREFERENCES" ] && [ -f "$PREFERENCES" ]; then source "$PREFERENCES" fi } # Load a parameter from config function keyringer_config { if [ -z "$CONFIG" ]; then echo "Your have to set CONFIG variable in the code" exit 1 elif [ -e "$CONFIG" ]; then grep -e "^$1=" "$CONFIG" | tail -n 1 | cut -d = -f 2 | sed -e 's/"//g' -e "s/'//g" | sed -e 's/ *#.*$//' else echo "Config file not found: $CONFIG" exit 1 fi } # Return the list of recipients function keyringer_recipients { grep -v '^#' "$1" | grep -v '^$' | awk '{ print "-r " $2 }' | xargs } # Check if keyringer has a given action function keyringer_has_action { if [ -z "$ACTIONS" ]; then echo "Your have to set ACTIONS variable in the code" exit 1 fi if [ -e "$ACTIONS/$1" ]; then true else false fi } # Execute an action function keyringer_exec { # Setup action="$1" basedir="$2" shift 2 # Dispatch if keyringer_has_action "$action"; then "$ACTIONS/$action" "$basedir" $* fi } # Return a filename with correct extension function keyringer_filename { if [ -z "$1" ]; then return else printf "%s/%s.asc\n" "$(dirname "$1")" "$(basename "$1" .asc)" fi } # Check if a folder is inside a git repository function keyringer_is_git { if [ -z "$1" ]; then false elif [ ! -d "$1" ]; then false elif [ -d "$1/.git" ]; then true else ( cd "$1" && git status &> /dev/null ) if [ "$?" != "128" ]; then true else false fi fi } # Setup a temporary file function keyringer_set_tmpfile { if [ -z "$BASEDIR" ]; then echo "Please set BASEDIR before creating a tmp file" exit 1 fi if [ -z "$1" ]; then template="$BASEDIR/tmp/keyringer.XXXXXXXXXX" else template="$BASEDIR/tmp/$1.XXXXXXXXXX" fi mkdir -p "$BASEDIR/tmp" keyringer_git_ignore 'tmp/*' if [ "$2" == "-d" ]; then TMPWORK="$(mktemp -d "$template")" else TMPWORK="$(mktemp "$template")" fi if [ "$?" != "0" ]; then printf "Error: can't set TMPWORK %s\n" "$TMPWORK" exit 1 fi trap "keyringer_unset_tmpfile $TMPWORK; exit" INT TERM EXIT } # Remove a temporary file function keyringer_unset_tmpfile { if [ -z "$1" ]; then echo "No tmp file set" fi rm -f "$1" if [ "$?" != "0" ]; then echo "Warning: could not delete file $1. Please delete it manually as it might have sensitive information." exit 1 fi } # Add a pattern into gitignore function keyringer_git_ignore { if [ ! -z "$BASEDIR/.gitignore" ]; then echo "$1" > "$BASEDIR/.gitignore" keyringer_exec git "$BASEDIR" add .gitignore else if ! grep -q -e "^$1$" "$BASEDIR/.gitignore"; then echo "$1" >> "$BASEDIR/.gitignore" fi fi } # Set needed environment variables and do basic checks. function keyringer_set_env { if [ -z "$1" ]; then echo "Error: missing arguments for keyringer_set_env" exit 1 fi ACTIONS="`dirname $0`" BASENAME="`basename $0`" BASEDIR="$1" SUBCOMMAND="$2" KEYDIR="$BASEDIR/keys" RECIPIENTS_BASE="config/recipients" RECIPIENTS="$BASEDIR/$RECIPIENTS_BASE" OPTIONS="$BASEDIR/config/options" VERSION_INFO="$BASEDIR/config/version" if [ -z "$BASEDIR" ]; then keyringer_action_usage exit 1 fi if [ ! -e "$RECIPIENTS" ]; then echo "No recipient config was found" exit 1 fi if [ -z "$EDITOR" ]; then if type sensible-editor > /dev/null 2>&1 ; then EDITOR=sensible-editor elif type editor > /dev/null 2>&1 ; then EDITOR=editor else echo "You have to set EDITOR env variable" exit 1 fi fi if [ ! -f "$OPTIONS" ]; then echo "No option config was found" exit 1 fi if [ ! -z "$KEYID" ]; then GPG="gpg -u $KEYID" else GPG="gpg" fi # Check keyring config version keyringer_check_version # Upgrade configuration keyringer_upgrade # Check recipients file keyringer_check_recipients $SUBCOMMAND # Ensure that keydir exists mkdir -p "$KEYDIR" && chmod 700 "$KEYDIR" } # Configuration version tracking to help keyring upgrades function keyringer_check_version { if [ "$KEYRINGER_CHECK_VERSION" == "false" ]; then return fi if [ ! -f "$VERSION_INFO" ]; then echo "Configuration version file not found, trying to pull from remotes..." # Do not use keyringer_exec as it would trigger keyringer_check_version again ( cd "$BASEDIR" && git pull ) if [ ! -f "$VERSION_INFO" ]; then echo "Creating configuration version file..." echo 0 > "$VERSION_INFO" if keyringer_is_git "$BASEDIR"; then keyringer_exec git "$BASEDIR" add config/version echo "Pushing configuration version file to remotes..." for remote in "$BASEDIR/.git/refs/remotes/*"; do keyringer_exec git "$BASEDIR" push $remote master done fi fi fi VERSION="`cat $VERSION_INFO`" # Check if config version is supported by keyringer if [ "$VERSION" != "$KEYRINGER_VERSION" ]; then echo "Configuration version differs from keyringer version, trying to pull from remotes" # Do not use keyringer_exec as it would trigger keyringer_check_version again ( cd "$BASEDIR" && git pull ) if [ "$VERSION" != "$KEYRINGER_VERSION" ]; then NEWEST="`echo -e "$VERSION\n$KEYRINGER_VERSION" | sort -V | tail -n 1`" if [ "$NEWEST" == "$VERSION" ]; then echo "Fatal: keyringer version: $KEYRINGER_VERSION / config version: $VERSION" echo "Please upgrade your keyringer application" exit 1 fi fi fi } # Configuration upgrades function keyringer_upgrade { # Upgrade 0.1 if [ "$VERSION" == "0" ]; then if [ ! -d "$RECIPIENTS" ]; then echo "Converting recipients to the new scheme..." mv $RECIPIENTS $RECIPIENTS.tmp mkdir $RECIPIENTS mv $RECIPIENTS.tmp $RECIPIENTS/default keyringer_exec git "$BASEDIR" add $RECIPIENTS_BASE/default keyringer_exec git "$BASEDIR" add config/version keyringer_exec git "$BASEDIR" commit -m "Config-upgrade-0.1" echo "Upgrade to version 0.1 completed, pushing to remotes..." for remote in "$BASEDIR/.git/refs/remotes/*"; do keyringer_exec git "$BASEDIR" push $remote master done fi # Update version information echo 0.1 > $VERSION_INFO fi } # Get a file argument function keyringer_get_file { FILE="$(keyringer_filename "$1")" if [ -z "$FILE" ]; then keyringer_action_usage exit 1 elif [ ! -f "$KEYDIR/$FILE" ]; then echo "File not found: $KEYDIR/$FILE" exit 1 fi } # Get a new file argument function keyringer_get_new_file { FILE="$(keyringer_filename "$1")" if [ -z "$FILE" ]; then keyringer_action_usage exit 1 fi } # Get a command argument function keyringer_get_command { # Aditional parameters COMMAND="$1" if [ -z "$COMMAND" ]; then keyringer_action_usage command exit 1 fi } # Run the action usage function keyringer_action_usage { if [ "`type -t "keyringer_usage_$BASENAME"`" == "function" ]; then # Use custom action usage "keyringer_usage_$BASENAME" else # Default usage if [ "$1" == "command" ]; then echo "Usage: keyringer $BASENAME [arguments]" else echo "Usage: keyringer $BASENAME " fi fi } # Check recipients function keyringer_check_recipients { if [ "$KEYRINGER_CHECK_RECIPIENTS" == "false" ]; then return fi # Check if recipients file is empty. if [ "`grep -vE "^#|^$" "$RECIPIENTS"/* | wc -l`" == 0 ] && [ "$SUBCOMMAND" != "edit" ]; then echo "Fatal: no recipients configured for this keyring." echo "Please edit your recipients file first." exit 1 fi # Check recipients header for updates. if grep -qe ' XXXXXXXX$' "$RECIPIENTS"/*; then echo "Updating recipients file..." sed -i -e 's/ XXXXXXXX$/ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/' "$RECIPIENTS"/* fi if [ "$1" == "edit" ]; then # Don't do the other checks at edit mode. return fi for recipient in $(cat "$RECIPIENTS"/* | grep -v '^#' | awk '{ print $2 }'); do size=$(echo "$recipient" | wc -c) if (( $size < 41 )); then echo "Fatal: please set the full GPG signature hash for key ID $recipient:" cat <<-EOF Recipients file can't have 32-bit keyids (e.g. DEADBEEF or DECAF123). These are trivial to spoof. With a few gigs of disk space and a day of time on cheap, readily-available hardware, it's possible to build keys to match every possible 32-bit keyid. The search space just isn't big enough. If you're going to specify keys by keyid, they should be specified by full 160-bit OpenPGP fingerprint. It would be very bad if someone spoofed a keyID and caused another participant in a keyringer instance to reencrypt a secret store to the spoofed key in addition to your own. EOF exit 1 else gpg --list-key "$recipient" &> /dev/null if [ "$?" != "0" ]; then echo "Fatal: no such key $recipient on your GPG keyring." echo "Please check for this key or fix the recipient file." exit 1 fi fi done } # Set recipients function keyringer_set_recipients { if [ -z "$1" ]; then keyringer_set_default_recipients else candidate="$1" candidate_no_extension="`echo $1 | sed -e 's/.asc$//'`" # Find the first matching recipient while [ ! -z "$candidate" ] && [ "$candidate" != "." ] && [ "$candidate" != "/" ]; do if [ -e "$RECIPIENTS/$candidate" ]; then RECIPIENTS_FILE="$RECIPIENTS/$candidate" RECIPIENTS_FILE_BASE="$RECIPIENTS_BASE/$candidate" return elif [ -e "$RECIPIENTS/$candidate_no_extension" ]; then RECIPIENTS_FILE="$RECIPIENTS/$candidate_no_extension" RECIPIENTS_FILE_BASE="$RECIPIENTS_BASE/$candidate_no_extension" return fi candidate="`dirname $candidate`" done keyringer_set_default_recipients "$1" fi } # Set default recipients function keyringer_set_default_recipients { if [ -e "$RECIPIENTS/default" ]; then RECIPIENTS_FILE="$RECIPIENTS/default" RECIPIENTS_FILE_BASE="$RECIPIENTS_BASE/default" else echo "Fatal: no suitable recipient file found for path $1" exit 1 fi } # Set a new recipient, avoid file checks function keyringer_set_new_recipients { if [ -z "$1" ]; then keyringer_set_default_recipients else RECIPIENTS_FILE="$RECIPIENTS/$1" RECIPIENTS_FILE_BASE="$RECIPIENTS_BASE/$1" fi } # Create a new recipients file function keyringer_create_new_recipients { if [ ! -e "$1" ]; then mkdir -p "`dirname $1`" echo "# Use entries in the form of 'john@doe.com XXXXXXXX" > "$1" echo "" >> "$1" fi } # Setup environment if [ "$(basename "$0")" != "keyringer" ]; then keyringer_set_env $* fi