aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rwxr-xr-xlib/keyringer/actions/check29
-rwxr-xr-xlib/keyringer/actions/find2
-rwxr-xr-xlib/keyringer/actions/git9
-rwxr-xr-xlib/keyringer/actions/mv2
-rwxr-xr-xlib/keyringer/actions/options2
-rwxr-xr-xlib/keyringer/actions/preferences2
-rwxr-xr-xlib/keyringer/actions/xclip54
-rw-r--r--lib/keyringer/completions/bash/keyringer2
-rw-r--r--lib/keyringer/completions/zsh/_keyringer2
-rwxr-xr-xlib/keyringer/functions251
10 files changed, 265 insertions, 90 deletions
diff --git a/lib/keyringer/actions/check b/lib/keyringer/actions/check
new file mode 100755
index 0000000..c80fa8f
--- /dev/null
+++ b/lib/keyringer/actions/check
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Check a keyring.
+#
+# See also some useful OpenPGP maintenance scripts:
+#
+# - git://lair.fifthhorseman.net/~mjgoins/cur
+# - https://gitorious.org/key-report
+# - https://github.com/ilf/gpg-maintenance.git
+# - https://github.com/EtiennePerot/parcimonie.sh
+# - https://gaffer.ptitcanardnoir.org/intrigeri/code/parcimonie/
+#
+# This script can run from a crontab, client or server side to check
+# keyringer health status.
+
+# Load functions
+LIB="`dirname $0`/../functions"
+source "$LIB" || exit 1
+
+# The following should run automatically from keyringer_check_recipients
+# and keyringer_check_repository:
+#
+# Pull the keyring repository.
+# Git maintenance operations.
+# Fetch absent keys from all recipients.
+# Check key expirations
+
+# This should be done here:
+# TODO: Check canaries' timestamps, warning by mail if configured by user preferences.
diff --git a/lib/keyringer/actions/find b/lib/keyringer/actions/find
index 21afc7a..9b18d66 100755
--- a/lib/keyringer/actions/find
+++ b/lib/keyringer/actions/find
@@ -15,5 +15,5 @@ shift
ARGS="`echo "$*" | sed -e "s|^/*||"`"
# Run find command
-cd "$KEYDIR/$RELATIVE_PATH" && find -iname "*$ARGS*" | sed -e 's|^./||g'
+cd "$KEYDIR/$RELATIVE_PATH" && find | grep -i "$ARGS" | sed -e 's|^./||g'
cd "$CWD"
diff --git a/lib/keyringer/actions/git b/lib/keyringer/actions/git
index 108ccea..d4e7aa4 100755
--- a/lib/keyringer/actions/git
+++ b/lib/keyringer/actions/git
@@ -13,12 +13,5 @@ CWD="`pwd`"
# Run git command
shift
-# Set working folder
-if [ ! -z "$RELATIVE_PATH" ]; then
- WORK="$KEYDIR/$RELATIVE_PATH"
-else
- WORK="$BASEDIR"
-fi
-
-mkdir -p "$WORK" && cd "$WORK" && git $*
+mkdir -p "$BASEDIR" && cd "$BASEDIR" && git $*
cd "$CWD"
diff --git a/lib/keyringer/actions/mv b/lib/keyringer/actions/mv
index aaf6772..daac7b0 100755
--- a/lib/keyringer/actions/mv
+++ b/lib/keyringer/actions/mv
@@ -25,4 +25,4 @@ if ! echo "$ORIG" | grep -q '*' && [ ! -e "$KEYDIR/$RELATIVE_PATH/$ORIG" ]; then
fi
# Run move command
-keyringer_exec git "$BASEDIR" mv $ORIG $FILE
+keyringer_exec git "$BASEDIR" mv "keys/$RELATIVE_PATH/$ORIG" "keys/$FILE"
diff --git a/lib/keyringer/actions/options b/lib/keyringer/actions/options
index 3bf0e43..b210e1a 100755
--- a/lib/keyringer/actions/options
+++ b/lib/keyringer/actions/options
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Recipient management.
+# Repository options management.
#
# Load functions
diff --git a/lib/keyringer/actions/preferences b/lib/keyringer/actions/preferences
index f7507a7..114f9ac 100755
--- a/lib/keyringer/actions/preferences
+++ b/lib/keyringer/actions/preferences
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Manipulate preferences.
+# Manipulate user preferences.
#
# Load functions
diff --git a/lib/keyringer/actions/xclip b/lib/keyringer/actions/xclip
index b28984f..7afdf05 100755
--- a/lib/keyringer/actions/xclip
+++ b/lib/keyringer/actions/xclip
@@ -7,31 +7,33 @@
# Function thanks to Password Store by Jason A. Donenfeld <Jason@zx2c4.com>
# distributed under GPLv2+: http://www.zx2c4.com/projects/password-store/
clip() {
- # This base64 business is a disgusting hack to deal with newline inconsistancies
- # in shell. There must be a better way to deal with this, but because I'm a dolt,
- # we're going with this for now.
-
- before="$(xclip -o -selection clipboard | base64)"
- echo -n "$1" | xclip -selection clipboard
- (
- sleep 45
- now="$(xclip -o -selection clipboard | base64)"
- if [[ $now != $(echo -n "$1" | base64) ]]; then
- before="$now"
- fi
-
- # It might be nice to programatically check to see if klipper exists,
- # as well as checking for other common clipboard managers. But for now,
- # this works fine -- if qdbus isn't there or if klipper isn't running,
- # this essentially becomes a no-op.
- #
- # Clipboard managers frequently write their history out in plaintext,
- # so we axe it here:
- qdbus org.kde.klipper /klipper org.kde.klipper.klipper.clearClipboardHistory &>/dev/null
-
- echo "$before" | base64 -d | xclip -selection clipboard
- ) & disown
- echo "Copied $2 to clipboard. Will clear in 45 seconds."
+ # This base64 business is a disgusting hack to deal with newline inconsistancies
+ # in shell. There must be a better way to deal with this, but because I'm a dolt,
+ # we're going with this for now.
+
+ #local xclip="xclip -selection clipboard"
+ local xclip="xclip"
+ before="$($xclip -o | base64)"
+ echo -n "$1" | $xclip
+ (
+ sleep 45
+ now="$($xclip -o | base64)"
+ if [[ $now != $(echo -n "$1" | base64) ]]; then
+ before="$now"
+ fi
+
+ # It might be nice to programatically check to see if klipper exists,
+ # as well as checking for other common clipboard managers. But for now,
+ # this works fine -- if qdbus isn't there or if klipper isn't running,
+ # this essentially becomes a no-op.
+ #
+ # Clipboard managers frequently write their history out in plaintext,
+ # so we axe it here:
+ qdbus org.kde.klipper /klipper org.kde.klipper.klipper.clearClipboardHistory &>/dev/null
+
+ echo "$before" | base64 -d | $xclip
+ ) & disown
+ echo "Copied $2 to clipboard. Will clear in 45 seconds."
}
# Load functions
@@ -39,7 +41,7 @@ LIB="`dirname $0`/../functions"
source "$LIB" || exit 1
# Check for xclip
-if ! which xclip; then
+if ! which xclip &> /dev/null; then
echo "fatal: xclip not found"
exit 1
fi
diff --git a/lib/keyringer/completions/bash/keyringer b/lib/keyringer/completions/bash/keyringer
index a640583..0f2cb2b 100644
--- a/lib/keyringer/completions/bash/keyringer
+++ b/lib/keyringer/completions/bash/keyringer
@@ -94,7 +94,7 @@ _keyringer() {
recipients)
opts="ls edit"
;;
- ls|tree|mkdir|encrypt|encrypt-batch|decrypt|edit|append|append-batch|del|rm|recrypt|open|clip|xclip)
+ ls|tree|mkdir|encrypt|encrypt-batch|decrypt|edit|append|append-batch|del|rm|recrypt|open|clip|xclip|find)
cur="`echo ${cur} | sed -e "s|^/*||"`" # avoid leading slash
opts="$(bash -c "set -f && export KEYRINGER_CHECK_VERSION=false && keyringer $instance ls -p -d ${cur}*" 2> /dev/null)"
;;
diff --git a/lib/keyringer/completions/zsh/_keyringer b/lib/keyringer/completions/zsh/_keyringer
index 1a6d8c6..b4ccdd4 100644
--- a/lib/keyringer/completions/zsh/_keyringer
+++ b/lib/keyringer/completions/zsh/_keyringer
@@ -50,7 +50,7 @@ _keyringer() {
recipients)
compadd "$@" ls edit
;;
- ls|tree|mkdir|encrypt|encrypt-batch|decrypt|edit|append|append-batch|del|rm|recrypt|open|clip|xclip)
+ ls|tree|mkdir|encrypt|encrypt-batch|decrypt|edit|append|append-batch|del|rm|recrypt|open|clip|xclip|find)
words[4]="`echo $words[4] | sed -e "s|^/*||"`" # avoid leading slash
compadd "$@" $(KEYRINGER_CHECK_VERSION=false keyringer $words[2] ls -p -d $words[4]'*' 2> /dev/null)
;;
diff --git a/lib/keyringer/functions b/lib/keyringer/functions
index 014c2c9..0b084af 100755
--- a/lib/keyringer/functions
+++ b/lib/keyringer/functions
@@ -301,6 +301,11 @@ function keyringer_set_env {
# Upgrade configuration
keyringer_upgrade
+ # Check repository integrity
+ if [ "$BASENAME" == "check" ]; then
+ keyringer_check_repository
+ fi
+
# Check recipients file
keyringer_check_recipients $SUBCOMMAND
@@ -403,6 +408,17 @@ function keyringer_upgrade {
fi
}
+# Get an option
+#
+# Given that options are shared among users through the
+# repository, we can't just "source $OPTIONS" as we would
+# be opening a simple arbitrary code execution hole.
+#
+# TODO
+function keyringer_get_option {
+ false
+}
+
# Get a file argument
function keyringer_get_file {
FILE="$(keyringer_filename "$RELATIVE_PATH/$1")"
@@ -413,11 +429,10 @@ function keyringer_get_file {
elif [ ! -f "$KEYDIR/$FILE" ]; then
# Try to find a similar file
count=0
- candidates=(`keyringer_exec find "$BASEDIR" "$1" | grep -e '.asc$'`)
+ candidates=(`keyringer_exec find "$BASEDIR" | grep -i "$1" | grep -e '.asc$'`)
if [ ! -z "$candidates" ]; then
- echo "Could not find exact match \"$1\", please chose one"
- echo "of the following secrets:"
+ echo "Could not find exact match \"$1\", please chose one of the following secrets:"
echo ""
for candidate in ${candidates[@]}; do
@@ -426,7 +441,7 @@ function keyringer_get_file {
done
echo ""
- read -p "Enter option: " option
+ read -p "Enter option (Ctrl-C to abort): " option
if [[ "$option" =~ ^[0-9]+$ ]] && [ ! -z "${candidates[$option]}" ]; then
FILE="$(keyringer_filename "$RELATIVE_PATH/${candidates[$option]}")"
@@ -504,7 +519,7 @@ function keyringer_usage {
printf "Keyringer $KEYRINGER_VERSION\n"
printf "Usage: %s <keyring> <action> [arguments]\n\n" "$BASENAME"
- printf "Available commands: \n\n"
+ printf "Available actions: \n\n"
keyringer_show_actions | sed -e 's/^/\t/'
# Display only when not in a keyring context
@@ -514,12 +529,85 @@ function keyringer_usage {
fi
}
+# Check repository integrity
+function keyringer_check_repository {
+ # Check if it's a git repository
+ if [ ! -d "$BASEDIR/.git" ]; then
+ echo "Fatal: not a git repository: $BASEDIR"
+ exit 1
+ fi
+
+ # Git maintenance operations
+ echo "Running git maintenance operations..."
+ keyringer_exec git "$BASEDIR" fsck
+ keyringer_exec git "$BASEDIR" gc --prune=all
+ echo ""
+
+ # Sync the repository
+ if [ "`keyringer_exec git "$BASEDIR" remote | wc -l`" != "0" ]; then
+ echo "Syncing git repository..."
+ keyringer_exec git "$BASEDIR" pull
+ echo ""
+ fi
+}
+
+# Receive keys from keyservers
+# TODO: gpg-maintenance trickery
+# TODO: should be controlled by user preference
+function keyringer_recv_keys {
+ local recipient="$1"
+
+ echo "Trying to receive missing key $recipient..."
+ gpg --batch --recv-keys "$recipient"
+}
+
+# Refresh keys from keyserver
+# TODO: gpg-maintenance trickery
+# TODO: should be controlled by user preference
+function keyringer_refresh_keys {
+ local recipient="$1"
+
+ echo "Trying to refresh key $recipient..."
+ gpg --batch --refresh-keys "$recipient"
+}
+
+# Check recipient size
+function keyringer_check_recipient_size {
+ local recipient="$1"
+ local size=$(echo "$recipient" | wc -c)
+
+ if (( $size < 41 )); then
+ echo "Fatal: please set the full GPG signature hash for key ID $recipient:"
+ cat <<-EOF
+
+Please provide a full OpenPGP fingerprint, for example:
+
+ john@doe.com ABCD1234ABCD12345678ABCD1234ABCD12345678
+
+Short key ids (for example, DEADBEEF or DECAF123) are not allowed in
+recipient files because they are easy to spoof. Researchers have proven
+that it is possible to build fake keys to match any possible short key
+id by using a few gigabytes of disk space, and a day of computation on
+common hardware.
+
+Otherwise, the encryption can be broken, if someone spoofs a short key
+id, and causes a participant in a keyringer repository to encrypt
+secrets to a fake key.
+EOF
+ exit 1
+ fi
+}
+
# Check recipients
function keyringer_check_recipients {
+ # Shall we check recipients?
if [ "$KEYRINGER_CHECK_RECIPIENTS" == "false" ]; then
return
fi
+ # Local variables
+ local processed=":"
+
# Check if recipients file is empty.
if [ "`grep -vE "^#|^$" "$RECIPIENTS"/* | wc -l`" == 0 ] && [ "$SUBCOMMAND" != "edit" ]; then
echo "Fatal: no recipients configured for this keyring."
@@ -539,60 +627,98 @@ function keyringer_check_recipients {
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
+ # Process a recipient just once
+ if echo $processed | grep -q "$recipient:"; then
+ continue
+ else
+ processed="$processed$recipient:"
+ fi
-Please provide a full OpenPGP fingerprint, for example:
+ # Check recipient size
+ keyringer_check_recipient_size "$recipient"
- john@doe.com ABCD1234ABCD12345678ABCD1234ABCD12345678
+ # Check if key is present
+ keyringer_check_recipient_key "$recipient"
-Short key ids (for example, DEADBEEF or DECAF123) are not allowed in
-recipient files because they are easy to spoof. Researchers have proven
-that it is possible to build fake keys to match any possible short key
-id by using a few gigabytes of disk space, and a day of computation on
-common hardware.
+ # Refresh keys
+ if [ "$BASENAME" == "check" ] && [ "$refresh" != "no" ]; then
+ keyringer_refresh_keys "$recipient"
+ echo ""
+ fi
-Otherwise, the encryption can be broken, if someone spoofs a short key
-id, and causes a participant in a keyringer repository to encrypt
-secrets to a fake key.
-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
+ # Check key expiration
+ keyringer_check_expiration "$recipient"
+
+ done
+}
+
+# Check if a key is present
+function keyringer_check_recipient_key {
+ local recipient="$1"
+
+ gpg --list-key "$recipient" &> /dev/null
+ if [ "$?" != "0" ]; then
+ if [ "$BASENAME" == "check" ]; then
+ refresh="no"
+ keyringer_recvs_keys "$recipient"
+ if [ "$?" != 0 ]; then
+ echo "Error fetching $recipient from keyservers."
+ continue
fi
+ echo ""
+ else
+ echo "Fatal: no such key $recipient on your GPG keyring."
+ echo "Please check for this key or fix the recipient file."
- # Current date
- seconds="`date +%s`"
+ exit 1
+ fi
+ fi
+}
- # Check the main key
- expiry="`gpg --with-colons --fixed-list-mode --list-keys "$recipient" | grep ^pub | cut -d : -f 7`"
+# Check key expiration
+function keyringer_check_expiration {
+ # Variables
+ local recipient="$1"
+ local not_expired="0"
- # Check if key is expired
- if [ ! -z "$expiry" ] && [[ "$seconds" -gt "$expiry" ]]; then
- echo "Fatal: primary key for $recipient expired on `date --date="@$expiry"`"
- exit 1
- else
- # Check the subkeys
- for expiry in `gpg --with-colons --fixed-list-mode --list-keys "$recipient" | grep ^sub | cut -d : -f 7`; do
- if [[ "$seconds" -lt "$expiry" ]]; then
- not_expired="1"
- fi
+ # Current date
+ seconds="`date +%s`"
- if [ "$not_expired" != "1" ]; then
- echo "Fatal: key $recipient has no keys suitable for encryption: all subkeys expired."
- exit 1
- fi
- done
- fi
+ # Check the main key
+ expiry="`gpg --with-colons --fixed-list-mode --list-keys "$recipient" | grep ^pub | cut -d : -f 7`"
+
+ # TODO: Time to expire can be configured via repository options.
+ ahead="$((86400 * 30 + $seconds))"
+ # Check if key is expired
+ if [ ! -z "$expiry" ] && [[ "$seconds" -gt "$expiry" ]]; then
+ echo "Fatal: primary key for $recipient expired on `date --date="@$expiry"`"
+ exit 1
+ fi
+
+ # Check if key is about to expire
+ # TODO: Users can be alerted by mail if configured by user preferences.
+ # TODO: Outgoing emails can be encrypted.
+ if [ "$BASENAME" == "check" ] && [ ! -z "$expiry" ] && [[ "$ahead" -gt "$expiry" ]]; then
+ echo "Warning: key $recipient will expire soon, on `date --date="@$expiry"`"
+ fi
+
+ # Check the subkeys
+ for expiry in `gpg --with-colons --fixed-list-mode --list-keys "$recipient" | grep ^sub | cut -d : -f 7`; do
+ if [[ "$seconds" -lt "$expiry" ]]; then
+ not_expired="1"
+ fi
+
+ if [[ "$ahead" -gt "$expiry" ]] && [ "$BASENAME" == "check" ]; then
+ echo "Warning: subkey from $recipient will expire soon, on `date --date="@$expiry"`"
fi
done
+
+ # All subkeys are expired
+ if [ ! -z "$expiry" ] && [ "$not_expired" != "1" ]; then
+ echo "Fatal: key $recipient has no keys suitable for encryption: all subkeys expired."
+ exit 1
+ fi
}
# Set recipients
@@ -646,10 +772,35 @@ function keyringer_set_new_recipients {
# Create a new recipients file
function keyringer_create_new_recipients {
- if [ ! -e "$1" ]; then
- mkdir -p "`dirname $1`"
+ local recipients="$1"
+ local recipient
+ local key
+ local uid
+ local fpr
+
+ if [ ! -e "$recipients" ]; then
+ mkdir -p "`dirname $recipients`"
echo "# Use entries in the form of 'john@doe.com XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'" > "$1"
- echo "" >> "$1"
+ echo "" >> "$recipients"
+
+ # Try to get an initial recipient
+ if [ -e "$HOME/.gnupg/gpg.conf" ]; then
+ recipient="`grep -e "^default-key" ~/.gnupg/gpg.conf | cut -d ' ' -f 2`"
+
+ if [ ! -z "$recipient" ]; then
+ key="`gpg --fingerprint --with-colons $recipient 2> /dev/null`"
+
+ if [ "$?" == "0" ]; then
+ fpr="`echo "$key" | grep -e '^fpr:' | head -1 | cut -d : -f 10`"
+ uid="`echo "$key" | grep -e '^uid:' | head -1 | cut -d : -f 10 | sed -e 's|^[^<]*<||' -e 's|>$||'`"
+
+ if [ ! -z "$uid" ] && [ ! -z "$fpr" ]; then
+ echo "Default key $fpr ($uid) found at ~/.gnupg/gpg.conf, using as initial recipient."
+ echo "$uid $fpr" >> "$recipients"
+ fi
+ fi
+ fi
+ fi
fi
}