2014-08-28 20:47:32 +00:00
|
|
|
#!/usr/bin/env bash
|
2014-07-07 20:30:16 -04:00
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Common constants and functions used by the blackbox_* utilities.
|
|
|
|
|
#
|
|
|
|
|
|
2014-08-28 20:47:32 +00:00
|
|
|
# Usage:
|
2014-09-08 20:25:38 +00:00
|
|
|
#
|
|
|
|
|
# set -e
|
2014-08-28 20:47:32 +00:00
|
|
|
# . _blackbox_common.sh
|
2014-07-07 20:30:16 -04:00
|
|
|
|
2014-08-28 20:47:32 +00:00
|
|
|
# Where in the VCS repo should the blackbox data be found?
|
|
|
|
|
: ${BLACKBOXDATA:=keyrings/live} ; # If BLACKBOXDATA not set, set it.
|
2014-08-29 20:21:02 +00:00
|
|
|
|
2014-08-28 20:47:32 +00:00
|
|
|
# Outputs a string that is the base directory of this VCS repo.
|
|
|
|
|
# By side-effect, sets the variable VCS_TYPE to either 'git', 'hg',
|
2014-10-15 11:01:52 -07:00
|
|
|
# 'svn' or 'unknown'.
|
2014-08-28 20:47:32 +00:00
|
|
|
function _determine_vcs_base_and_type() {
|
2014-09-08 20:09:04 +00:00
|
|
|
if git rev-parse --show-toplevel 2>/dev/null ; then
|
2014-08-28 20:47:32 +00:00
|
|
|
VCS_TYPE=git
|
2014-10-15 11:01:52 -07:00
|
|
|
elif [ -d ".svn" ] ; then
|
2014-10-18 23:30:38 -07:00
|
|
|
#find topmost dir with .svn sub-dir
|
|
|
|
|
parent=""
|
|
|
|
|
grandparent="."
|
|
|
|
|
mydir=`pwd`
|
|
|
|
|
while [ -d "$grandparent/.svn" ]; do
|
|
|
|
|
parent=$grandparent
|
|
|
|
|
grandparent="$parent/.."
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
if [ ! -z "$parent" ]; then
|
|
|
|
|
cd $parent
|
|
|
|
|
echo `pwd`
|
|
|
|
|
else
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
cd $mydir
|
2014-10-15 11:01:52 -07:00
|
|
|
VCS_TYPE=svn
|
2014-09-08 20:09:04 +00:00
|
|
|
elif hg root 2>/dev/null ; then
|
|
|
|
|
# NOTE: hg has to be tested last because it always "succeeds".
|
|
|
|
|
VCS_TYPE=hg
|
2014-08-28 20:47:32 +00:00
|
|
|
else
|
|
|
|
|
echo /dev/null
|
|
|
|
|
VCS_TYPE=unknown
|
|
|
|
|
fi
|
2014-09-09 20:32:48 +00:00
|
|
|
export VCS_TYPE
|
2014-09-08 20:09:04 +00:00
|
|
|
# FIXME: Verify this function by checking for .hg or .git
|
|
|
|
|
# after determining what we believe to be the answer.
|
2014-07-07 20:30:16 -04:00
|
|
|
}
|
|
|
|
|
|
2014-08-28 20:47:32 +00:00
|
|
|
REPOBASE=$(_determine_vcs_base_and_type)
|
|
|
|
|
KEYRINGDIR="$REPOBASE/$BLACKBOXDATA"
|
2014-08-29 20:21:02 +00:00
|
|
|
BB_ADMINS_FILE="blackbox-admins.txt"
|
|
|
|
|
BB_ADMINS="${KEYRINGDIR}/${BB_ADMINS_FILE}"
|
|
|
|
|
BB_FILES_FILE="blackbox-files.txt"
|
|
|
|
|
BB_FILES="${KEYRINGDIR}/${BB_FILES_FILE}"
|
2014-08-28 20:47:32 +00:00
|
|
|
SECRING="${KEYRINGDIR}/secring.gpg"
|
|
|
|
|
PUBRING="${KEYRINGDIR}/pubring.gpg"
|
2014-09-23 23:44:48 -04:00
|
|
|
: ${DECRYPT_UMASK:=o=} ;
|
2014-08-28 20:47:32 +00:00
|
|
|
|
2014-09-09 20:32:48 +00:00
|
|
|
# Return error if not on cryptlist.
|
|
|
|
|
function is_on_cryptlist() {
|
|
|
|
|
# Assumes $1 does NOT have the .gpg extension
|
|
|
|
|
local rname=$(vcs_relative_path "$1")
|
|
|
|
|
grep -F -x -s -q "$rname" "$BB_FILES"
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 20:30:16 -04:00
|
|
|
# Exit with error if a file exists.
|
|
|
|
|
function fail_if_exists() {
|
|
|
|
|
if [[ -f "$1" ]]; then
|
|
|
|
|
echo ERROR: "$1" exists. "$2"
|
|
|
|
|
echo Exiting...
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Exit with error if a file is missing.
|
|
|
|
|
function fail_if_not_exists() {
|
|
|
|
|
if [[ ! -f "$1" ]]; then
|
|
|
|
|
echo ERROR: "$1" not found. "$2"
|
|
|
|
|
echo Exiting...
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-29 20:21:02 +00:00
|
|
|
# Exit we we aren't in a VCS repo.
|
|
|
|
|
function fail_if_not_in_repo() {
|
|
|
|
|
_determine_vcs_base_and_type
|
|
|
|
|
if [[ $VCS_TYPE = "unknown" ]]; then
|
2014-10-15 11:01:52 -07:00
|
|
|
echo "ERROR: This must be run in a VCS repo: git, hg, or svn."
|
2014-08-29 20:21:02 +00:00
|
|
|
echo Exiting...
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 20:30:16 -04:00
|
|
|
# Exit with error if filename is not registered on blackbox list.
|
|
|
|
|
function fail_if_not_on_cryptlist() {
|
2014-09-08 20:09:04 +00:00
|
|
|
# Assumes $1 does NOT have the .gpg extension
|
|
|
|
|
|
2014-09-09 20:32:48 +00:00
|
|
|
local name="$1"
|
2014-09-08 20:09:04 +00:00
|
|
|
|
2014-09-09 20:32:48 +00:00
|
|
|
if ! is_on_cryptlist "$name" ; then
|
|
|
|
|
echo "ERROR: $name not found in $BB_FILES"
|
|
|
|
|
echo "PWD="$(/bin/pwd)
|
2014-07-07 20:30:16 -04:00
|
|
|
echo 'Exiting...'
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Exit with error if keychain contains secret keys.
|
|
|
|
|
function fail_if_keychain_has_secrets() {
|
|
|
|
|
if [[ -s ${SECRING} ]]; then
|
|
|
|
|
echo 'ERROR: The file' "$SECRING" 'should be empty.'
|
|
|
|
|
echo 'Did someone accidentally add this private key to the ring?'
|
|
|
|
|
echo 'Exiting...'
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Output the unencrypted filename.
|
|
|
|
|
function get_unencrypted_filename() {
|
|
|
|
|
echo $(dirname "$1")/$(basename "$1" .gpg) | sed -e 's#^\./##'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Output the encrypted filename.
|
|
|
|
|
function get_encrypted_filename() {
|
|
|
|
|
echo $(dirname "$1")/$(basename "$1" .gpg).gpg | sed -e 's#^\./##'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Prepare keychain for use.
|
|
|
|
|
function prepare_keychain() {
|
|
|
|
|
echo '========== Importing keychain: START'
|
|
|
|
|
gpg --import "${PUBRING}" 2>&1 | egrep -v 'not changed$'
|
|
|
|
|
echo '========== Importing keychain: DONE'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Add file to list of encrypted files.
|
|
|
|
|
function add_filename_to_cryptlist() {
|
|
|
|
|
# If the name is already on the list, this is a no-op.
|
|
|
|
|
# However no matter what the datestamp is updated.
|
2014-09-09 20:32:48 +00:00
|
|
|
local name=$(vcs_relative_path "$1")
|
2014-07-07 20:30:16 -04:00
|
|
|
|
|
|
|
|
if grep -s -q "$name" "$BB_FILES" ; then
|
|
|
|
|
echo ========== File is registered. No need to add to list.
|
|
|
|
|
else
|
|
|
|
|
echo ========== Adding file to list.
|
|
|
|
|
touch "$BB_FILES"
|
|
|
|
|
sort -u -o "$BB_FILES" <(echo "$name") "$BB_FILES"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Print out who the current BB ADMINS are:
|
|
|
|
|
function disclose_admins() {
|
|
|
|
|
echo ========== blackbox administrators are:
|
|
|
|
|
cat "$BB_ADMINS"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Encrypt file, overwriting .gpg if it exists.
|
|
|
|
|
function encrypt_file() {
|
|
|
|
|
local unencrypted
|
|
|
|
|
local encrypted
|
|
|
|
|
unencrypted="$1"
|
|
|
|
|
encrypted="$2"
|
|
|
|
|
|
|
|
|
|
echo "========== Encrypting: $unencrypted"
|
|
|
|
|
gpg --yes --trust-model=always --encrypt -o "$encrypted" $(awk '{ print "-r" $1 }' < "$BB_ADMINS") "$unencrypted"
|
|
|
|
|
echo '========== Encrypting: DONE'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Decrypt .gpg file, asking "yes/no" before overwriting unencrypted file.
|
|
|
|
|
function decrypt_file() {
|
|
|
|
|
local encrypted
|
|
|
|
|
local unencrypted
|
2014-09-23 23:44:48 -04:00
|
|
|
local old_umask
|
2014-07-07 20:30:16 -04:00
|
|
|
encrypted="$1"
|
|
|
|
|
unencrypted="$2"
|
|
|
|
|
|
|
|
|
|
echo "========== EXTRACTING $unencrypted"
|
2014-09-23 23:44:48 -04:00
|
|
|
|
|
|
|
|
old_umask=$(umask)
|
|
|
|
|
umask $DECRYPT_UMASK
|
2014-07-07 20:30:16 -04:00
|
|
|
gpg -q --decrypt -o "$unencrypted" "$encrypted"
|
2014-09-23 23:44:48 -04:00
|
|
|
umask $old_umask
|
2014-07-07 20:30:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Decrypt .gpg file, overwriting unencrypted file if it exists.
|
|
|
|
|
function decrypt_file_overwrite() {
|
|
|
|
|
local encrypted
|
|
|
|
|
local unencrypted
|
|
|
|
|
local old_hash
|
|
|
|
|
local new_hash
|
2014-09-23 23:44:48 -04:00
|
|
|
local old_umask
|
2014-07-07 20:30:16 -04:00
|
|
|
encrypted="$1"
|
|
|
|
|
unencrypted="$2"
|
|
|
|
|
|
|
|
|
|
if [[ -f "$unencrypted" ]]; then
|
2014-09-01 18:59:22 +00:00
|
|
|
old_hash=$(md5sum_file "$unencrypted")
|
2014-07-07 20:30:16 -04:00
|
|
|
else
|
|
|
|
|
old_hash=unmatchable
|
|
|
|
|
fi
|
2014-09-23 23:44:48 -04:00
|
|
|
|
|
|
|
|
old_umask=$(umask)
|
|
|
|
|
umask $DECRYPT_UMASK
|
2014-07-07 20:30:16 -04:00
|
|
|
gpg --yes -q --decrypt -o "$unencrypted" "$encrypted"
|
2014-09-23 23:44:48 -04:00
|
|
|
umask $old_umask
|
|
|
|
|
|
2014-09-01 18:59:22 +00:00
|
|
|
new_hash=$(md5sum_file "$unencrypted")
|
2014-07-07 20:30:16 -04:00
|
|
|
if [[ $old_hash != $new_hash ]]; then
|
|
|
|
|
echo "========== EXTRACTED $unencrypted"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Shred a file. If shred binary does not exist, delete it.
|
|
|
|
|
function shred_file() {
|
|
|
|
|
local name
|
|
|
|
|
local CMD
|
|
|
|
|
local OPT
|
|
|
|
|
name="$1"
|
|
|
|
|
|
|
|
|
|
if which shred >/dev/null ; then
|
|
|
|
|
CMD=shred
|
|
|
|
|
OPT=-u
|
2014-10-02 16:55:48 -07:00
|
|
|
elif which srm >/dev/null ; then
|
|
|
|
|
#NOTE: srm by default uses 35-pass Gutmann algorithm
|
|
|
|
|
CMD=srm
|
|
|
|
|
OPT=-f
|
2014-07-07 20:30:16 -04:00
|
|
|
else
|
|
|
|
|
CMD=rm
|
|
|
|
|
OPT=-f
|
|
|
|
|
fi
|
|
|
|
|
|
2014-10-03 16:25:13 +00:00
|
|
|
$CMD $OPT -- "$name"
|
2014-07-07 20:30:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# $1 is the name of a file that contains a list of files.
|
|
|
|
|
# For each filename, output the individual subdirectories
|
|
|
|
|
# leading up to that file. i.e. one one/two one/two/three
|
|
|
|
|
function enumerate_subdirs() {
|
|
|
|
|
local listfile
|
|
|
|
|
local dir
|
|
|
|
|
local filename
|
|
|
|
|
listfile="$1"
|
|
|
|
|
|
|
|
|
|
while read filename; do
|
|
|
|
|
dir=$(dirname "$filename")
|
|
|
|
|
while [[ $dir != '.' && $dir != '/' ]]; do
|
|
|
|
|
echo $dir
|
|
|
|
|
dir=$(dirname $dir)
|
|
|
|
|
done
|
|
|
|
|
done <"$listfile" | sort -u
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-13 17:26:41 +00:00
|
|
|
# Output the path of a file relative to the repo base
|
|
|
|
|
function vcs_relative_path() {
|
|
|
|
|
# Usage: vcs_relative_path file
|
|
|
|
|
local name="$1"
|
|
|
|
|
python -c 'import os ; print(os.path.relpath("'$(pwd -P)'/'"$name"'", "'"$REPOBASE"'"))'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Portability Section:
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Abstract the difference between Linux and Mac OS X:
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
function md5sum_file() {
|
|
|
|
|
# Portably generate the MD5 hash of file $1.
|
|
|
|
|
case $(uname -s) in
|
|
|
|
|
Darwin )
|
|
|
|
|
md5 -r "$1" | awk '{ print $1 }'
|
|
|
|
|
;;
|
|
|
|
|
Linux )
|
|
|
|
|
md5sum "$1" | awk '{ print $1 }'
|
|
|
|
|
;;
|
2014-10-31 15:15:33 -07:00
|
|
|
CYGWIN* )
|
|
|
|
|
md5sum "$1" | awk '{ print $1 }'
|
|
|
|
|
;;
|
2014-10-13 17:26:41 +00:00
|
|
|
* )
|
|
|
|
|
echo 'ERROR: Unknown OS. Exiting.'
|
|
|
|
|
exit 1
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Abstract the difference between git and hg:
|
|
|
|
|
#
|
|
|
|
|
|
2014-08-28 20:47:32 +00:00
|
|
|
# Are we in git, hg, or unknown repo?
|
2014-07-07 20:30:16 -04:00
|
|
|
function which_vcs() {
|
2014-08-29 20:21:02 +00:00
|
|
|
if [[ $VCS_TYPE = '' ]]; then
|
|
|
|
|
_determine_vcs_base_and_type >/dev/null
|
|
|
|
|
fi
|
|
|
|
|
echo "$VCS_TYPE"
|
2014-07-07 20:30:16 -04:00
|
|
|
}
|
|
|
|
|
|
2014-08-29 20:21:02 +00:00
|
|
|
|
2014-07-07 20:30:16 -04:00
|
|
|
# Is this file in the current repo?
|
|
|
|
|
function is_in_vcs() {
|
|
|
|
|
is_in_$(which_vcs) """$@"""
|
|
|
|
|
}
|
2014-08-29 20:21:02 +00:00
|
|
|
# Mercurial
|
2014-07-07 20:30:16 -04:00
|
|
|
function is_in_hg() {
|
|
|
|
|
local filename
|
|
|
|
|
filename="$1"
|
|
|
|
|
|
|
|
|
|
if hg locate "$filename" ; then
|
|
|
|
|
echo true
|
|
|
|
|
else
|
|
|
|
|
echo false
|
|
|
|
|
fi
|
|
|
|
|
}
|
2014-08-29 20:21:02 +00:00
|
|
|
# Git:
|
2014-07-07 20:30:16 -04:00
|
|
|
function is_in_git() {
|
|
|
|
|
local filename
|
|
|
|
|
filename="$1"
|
|
|
|
|
|
|
|
|
|
if git ls-files --error-unmatch >/dev/null 2>&1 -- "$filename" ; then
|
|
|
|
|
echo true
|
|
|
|
|
else
|
|
|
|
|
echo false
|
|
|
|
|
fi
|
|
|
|
|
}
|
2014-10-15 11:01:52 -07:00
|
|
|
# Subversion
|
|
|
|
|
function is_in_svn() {
|
|
|
|
|
local filename
|
|
|
|
|
filename="$1"
|
|
|
|
|
|
|
|
|
|
if svn list "$filename" ; then
|
|
|
|
|
echo true
|
|
|
|
|
else
|
|
|
|
|
echo false
|
|
|
|
|
fi
|
|
|
|
|
}
|
2014-08-13 15:16:35 -04:00
|
|
|
|
2014-08-29 20:21:02 +00:00
|
|
|
|
|
|
|
|
# Add a file to the repo (but don't commit it).
|
|
|
|
|
function vcs_add() {
|
|
|
|
|
vcs_add_$(which_vcs) """$@"""
|
|
|
|
|
}
|
|
|
|
|
# Mercurial
|
|
|
|
|
function vcs_add_hg() {
|
|
|
|
|
hg add """$@"""
|
|
|
|
|
}
|
|
|
|
|
# Git
|
|
|
|
|
function vcs_add_git() {
|
|
|
|
|
git add """$@"""
|
|
|
|
|
}
|
2014-10-15 11:01:52 -07:00
|
|
|
# Subversion
|
|
|
|
|
function vcs_add_svn() {
|
|
|
|
|
svn add --parents """$@"""
|
|
|
|
|
}
|
2014-08-29 20:21:02 +00:00
|
|
|
|
2014-10-13 17:26:41 +00:00
|
|
|
|
2014-08-29 20:21:02 +00:00
|
|
|
# Commit a file to the repo
|
|
|
|
|
function vcs_commit() {
|
|
|
|
|
vcs_commit_$(which_vcs) """$@"""
|
|
|
|
|
}
|
|
|
|
|
# Mercurial
|
|
|
|
|
function vcs_commit_hg() {
|
|
|
|
|
hg commit -m"""$@"""
|
|
|
|
|
}
|
|
|
|
|
# Git
|
|
|
|
|
function vcs_commit_git() {
|
|
|
|
|
git commit -m"""$@"""
|
|
|
|
|
}
|
2014-10-15 11:01:52 -07:00
|
|
|
# Subversion
|
|
|
|
|
function vcs_commit_svn() {
|
|
|
|
|
svn commit -m"""$@"""
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-29 20:21:02 +00:00
|
|
|
|
|
|
|
|
|
2014-08-13 15:16:35 -04:00
|
|
|
# Remove file from repo, even if it was deleted locally already.
|
|
|
|
|
# If it doesn't exist yet in the repo, it should be a no-op.
|
2014-08-29 20:21:02 +00:00
|
|
|
function vcs_remove() {
|
2014-09-01 14:26:56 -04:00
|
|
|
vcs_remove_$(which_vcs) """$@"""
|
2014-08-13 15:16:35 -04:00
|
|
|
}
|
2014-08-29 20:21:02 +00:00
|
|
|
# Mercurial
|
|
|
|
|
function vcs_remove_hg() {
|
2014-10-03 16:25:13 +00:00
|
|
|
hg rm -A -- """$@"""
|
2014-08-13 15:16:35 -04:00
|
|
|
}
|
2014-08-29 20:21:02 +00:00
|
|
|
# Git
|
|
|
|
|
function vcs_remove_git() {
|
2014-08-13 15:16:35 -04:00
|
|
|
git rm --ignore-unmatch -f -- """$@"""
|
|
|
|
|
}
|
2014-10-15 11:01:52 -07:00
|
|
|
# Subversion
|
|
|
|
|
function vcs_remove_svn() {
|
|
|
|
|
svn delete """$@"""
|
|
|
|
|
}
|