automatically create/extend groups if user requests it

This commit is contained in:
Lance Vick 2020-11-17 15:32:41 -08:00
parent a56a9c477e
commit d0f7f9e199
Signed by: lrvick
GPG Key ID: 8E47A1EC35A1551D
3 changed files with 157 additions and 61 deletions

View File

@ -1,16 +1,16 @@
-----BEGIN PGP SIGNATURE----- -----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEZ1U/vaRrtxq9LgsLjkeh7DWhVR0FAl+zF58ACgkQjkeh7DWh iQIzBAABCgAdFiEEZ1U/vaRrtxq9LgsLjkeh7DWhVR0FAl+0XVoACgkQjkeh7DWh
VR2DbQ//fLBe5OUcrT7O4TWguYPOYmQJSNM7NWG2tNS4roiJFaJUPhx0djnEIhqI VR3+gxAAnPUlZ7zVrk0DuTGpozySfgh2GL0oekFqtQqy5XwC7A5Etq4VznGmpkOJ
4Ui8FJfXyowHYDyxbtzL165bl2LcjUVlp2+xyTZxZwqh/TGVSqSy0tioepiWiTO3 PIvFjv1GYdpVFFlLEmf/rgLg2e15K0gkxtOKqGZ8w9zih87gA71N1GXe9SpvkHtu
f/aVki7j7TkGwi/fPb1t7WpwbGlMxgXDGFsYYZ8+MWi7XQEg72bl+dOHUy8EsDBd n5mPziYHZVU/ESQoMbGAO+/Hn8ctP/gSITo+br7IqwOOuxD1H/MCYOFqqO4ZGpF6
jZKfWuc6NcpUwip814WAQBTWQbRrNdyTPTn/jJ974Ba75trEY9RMbMbaDw80Aw98 JSacNuGD4hdEhLZXjSWM+mx67J3WbbIR6NHJW/iq4iTNq+Snc/OGl3hjCZEk935o
KoRSo6zDu/xT9UBoxWfRO+aMahtYqKNO+F8PmXFxXGI0Gi3iwciv82H5h3KH52Cx 9LjuKEdaqgpaEDUuQKBtLl2HOYgSR0NlV0OkrTAfNPZhFYHwcoxH12JBHmSU+MnI
LHAeUApApI5xD0A7s/UfOei48ed34XiSymbJG6ppIwaEsXNIgbe3qhdx4YvGQ9X0 0vMmL9TL1JyTSiC2nNuhbd07t9bTV1WeTZe9Iu8qGpCuOrSiKnptef/cRUDseZhc
euvRGxRGbOZjCPalqQfu4Z7CWywTPdX2q/usyPe7u+D8SzD0ICdhBk0A86qi4J84 PyoYRW8rY8u54mvQJmepTMNy6pRY5OlTvGysWXTmmQE/TyTGHzvz0llFKcO6nJdz
/1cWHmtotssN9tNNLGGoGKmDkxawqElQWeHTlNvxzFzW5CI9KyYPD5cxaFtr+jsY SbuCi9lF8tz/Po5XwF6Be3YS6wO0cS53A+tAq05nUryxGAI4TaS6OTvE9Fj9medp
yc2Av6PDnKOLH3HFUo03eoeq9p7ov5NGQswDFxdlTEDcbpaPZDcYiV+Z5zJPMJOs oWUD49xb4dB2sNEye0Lg83HKjqBbEAIUA5wOejYeLFxfLFuNYT1Pjt8PAwPJmnKA
ve2otw9WcximzSs9mryNgLEFSlRTG31oNzLI5E92oHS3pkYRNgW0Wr1SpzetRk+6 Bzc1H7M36pcpgj2i/FvLmtGn3BpkvtmShO904BNNCpV8ymwKFYBAbZmlpNwCg2ST
JhCp7f3C2dwUx3/TxeNX/F5j/vQvYrbS0Km83a7CzgEIvfrJEYs= +Px+Eplb2EijzBsGZSAikOaVjoxbEsucI7VFRvmPzJrmOSZRpWs=
=8zXb =8C5n
-----END PGP SIGNATURE----- -----END PGP SIGNATURE-----

View File

@ -1,3 +1,3 @@
64263feac7b00952e9ec3b6c1fd11316faa58ff673c6bd085fac9f6f8d8389f6 .gitignore 64263feac7b00952e9ec3b6c1fd11316faa58ff673c6bd085fac9f6f8d8389f6 .gitignore
c5649b242a88aec7617c627b75d3d6c6057123fdd9f1ebd8fee1d7001a827da0 README.md c5649b242a88aec7617c627b75d3d6c6057123fdd9f1ebd8fee1d7001a827da0 README.md
bbe6f757d0f17cd0eab5b4cafbd7351e2cd403a7386ac47a2a894115b6f29683 sig e02b0ef74b361497249967dd13477c988c602a187d76d610c05f9193eaec6cb4 sig

186
sig
View File

@ -40,6 +40,32 @@ die_pkg() {
exit 1 exit 1
} }
### Ask user to make a binary decision
ask() {
local prompt default
while true; do
prompt=""
default=""
if [ "${2}" = "Y" ]; then
prompt="Y/n"
default=Y
elif [ "${2}" = "N" ]; then
prompt="y/N"
default=N
else
prompt="y/n"
default=
fi
printf "\\n%s [%s] " "$1" "$prompt"
read -r reply
[ -z "$reply" ] && reply=$default
case "$reply" in
Y*|y*) return 0 ;;
N*|n*) return 1 ;;
esac
done
}
### Check if actual binary version is >= minimum version ### Check if actual binary version is >= minimum version
check_version(){ check_version(){
local pkg="${1?}" local pkg="${1?}"
@ -96,23 +122,100 @@ get_files(){
fi fi
} }
### Get signer name/email from key fingerprint ### Get primary UID for a given fingerprint
get_signer(){ get_uid(){
local fingerprint="${1?}" local fp="${1?}"
gpg \ gpg --list-keys --with-colons "${fp}" 2>&1 \
--list-keys \
--with-colons "${fingerprint}" 2>&1 \
| awk -F: '$1 == "uid" {print $10}' \ | awk -F: '$1 == "uid" {print $10}' \
| head -n1 | head -n1
} }
get_group_config(){ ### Get primary fingerprint for given search
local group=${1?} get_primary_fp(){
gpg --with-colons --list-config group \ local search="${1?}"
| grep -i "^cfg:group:${group}:" \ gpg --list-keys --with-colons "${search}" 2>&1 \
|| die "Error: group \"${group}\" not found in ~/.gnupg/gpg.conf" | awk -F: '$1 == "fpr" {print $10}' \
| head -n1
} }
### Get fingerprint for a given pgp file
get_file_fp(){
local filename="${1?}"
gpg --list-packets "${filename}" \
| grep keyid \
| sed 's/.*keyid //g'
}
### Get raw gpgconf group config
group_get_config(){
local -r config=$(gpgconf --list-options gpg | grep ^group)
printf '%s' "${config##*:}"
}
### Add fingerprint to a given group
group_add_fp(){
local fp=${1?}
local group_name=${2?}
local group_names=()
local member_lists=()
local name member_list config i data
config=$(group_get_config)
while IFS=' =' read -rd, name member_list; do
group_names+=("${name:1}")
member_lists+=("$member_list")
done <<< "$config,"
printf '%s\n' "${group_names[@]}" \
| grep -w "${group_name}" \
|| group_names+=("${group_name}")
for i in "${!group_names[@]}"; do
[ "${group_names[$i]}" == "${group_name}" ] \
&& member_lists[$i]="${member_lists[$i]} ${fp}"
data+=$(printf '"%s = %s,' "${group_names[$i]}" "${member_lists[$i]}")
done
echo "Adding key \"${fp}\" to group \"${group_name}\""
printf 'group:0:%s' "${data%?}" \
| gpgconf --change-options gpg >/dev/null 2>&1
}
### Get fingerprints for a given group
group_get_fps(){
local group_name=${1?}
gpg --with-colons --list-config group \
| grep -i "^cfg:group:${group_name}:" \
| cut -d ':' -f4
}
### Check if fingerprint belongs to a given group
### Give user option to add it if they wish
group_check_fp(){
local fp=${1?}
local group_name=${2?}
local group_fps; group_fps=$( group_get_fps "${group_name}" )
local uid; uid=$(get_uid "${fp}")
if [ -z "$group_fps" ] \
|| [[ "${group_fps}" != *"${fp}"* ]]; then
cat <<-_EOF
The following key is not a member of group "${group_name}":
Fingerprint: ${fp}
Primary UID: ${uid}
_EOF
if ask "Add key to group \"${group_name}\" ?" "N"; then
group_add_fp "${fp}" "${group_name}"
else
return 1
fi
fi
}
### Verify a file has 0-N unique valid detached signatures ### Verify a file has 0-N unique valid detached signatures
### Optionally verify all signatures belong to keys in gpg alias group ### Optionally verify all signatures belong to keys in gpg alias group
verify_detached() { verify_detached() {
@ -120,39 +223,34 @@ verify_detached() {
local threshold="${1}" local threshold="${1}"
local group="${2}" local group="${2}"
local filename="${3}" local filename="${3}"
local group_config=""
local sig_count=0 local sig_count=0
local seen_fingerprints="" local seen_fps=""
local fingerprint local fp
local signer local uid
for sig_filename in "${filename%.*}".*.asc; do for sig_filename in "${filename%.*}".*.asc; do
gpg --verify "${sig_filename}" "${filename}" >/dev/null 2>&1 || { gpg --verify "${sig_filename}" "${filename}" >/dev/null 2>&1 || {
echo "Invalid signature: ${sig_filename}"; echo "Invalid signature: ${sig_filename}";
exit 1; exit 1;
} }
fingerprint=$( \ file_fp=$( get_file_fp "${sig_filename}" )
gpg --list-packets "${sig_filename}" \ fp=$( get_primary_fp "${file_fp}" )
| grep keyid \ uid=$( get_uid "${fp}" )
| sed 's/.*keyid //g'
)
signer=$( get_signer "${fingerprint}" )
[[ "${seen_fingerprints}" == *"${fingerprint}"* ]] \ [[ "${seen_fps}" == *"${fp}"* ]] \
&& die "Duplicate signature: ${sig_filename}"; && die "Duplicate signature: ${sig_filename}";
if [ ! -z "$group" ]; then echo "Verified detached signature by \"${uid}\""
group_config=$(get_group_config "${group}")
[[ "${group_config}" != *"${fingerprint}"* ]] \ if [ ! -z "${group}" ]; then
&& die "Signature not in group \"${group}\": ${sig_filename}"; group_check_fp "${fp}" "${group}" \
|| die "Detached signing key not in group \"${group}\": ${fp}";
fi fi
echo "Verified detached signature by \"${signer}\"" seen_fps="${seen_fps} ${fp}"
seen_fingerprints="${seen_fingerprints} ${fingerprint}"
((sig_count=sig_count+1)) ((sig_count=sig_count+1))
done done
[[ "$sig_count" -ge "$threshold" ]] || \ [[ "${sig_count}" -ge "${threshold}" ]] || \
die "Minimum detached signatures not found: ${sig_count}/${threshold}"; die "Minimum detached signatures not found: ${sig_count}/${threshold}";
} }
@ -163,30 +261,29 @@ verify_git(){
[ $# -eq 2 ] || die "Usage: verify_git <threshold> <group>" [ $# -eq 2 ] || die "Usage: verify_git <threshold> <group>"
local threshold="${1}" local threshold="${1}"
local group="${2}" local group="${2}"
local group_config="" local seen_fps=""
local seen_fingerprints=""
local sig_count=0 local sig_count=0
local depth=0 local depth=0
while [[ $depth != "$(git rev-list --count HEAD)" ]]; do while [[ $depth != "$(git rev-list --count HEAD)" ]]; do
ref=HEAD~${depth} ref=HEAD~${depth}
commit=$(git log --format="%H" "$ref") commit=$(git log --format="%H" "$ref")
fingerprint=$(git log --format="%GP" "$ref" -n1 ) fp=$(git log --format="%GP" "$ref" -n1 )
signer=$( get_signer "${fingerprint}" ) uid=$( get_uid "${fp}" )
git verify-commit HEAD~${depth} >/dev/null 2>&1\ git verify-commit HEAD~${depth} >/dev/null 2>&1\
|| die "Unsigned commit: ${commit}" || die "Unsigned commit: ${commit}"
if [ ! -z "$group" ]; then if [[ "${seen_fps}" != *"${fp}"* ]]; then
group_config=$(get_group_config "${group}") seen_fps="${seen_fps} ${fp}"
[[ "${group_config}" != *"${fingerprint}"* ]] \ ((sig_count=sig_count+1))
&& die "Git signing key not in group \"${group}\": ${fingerprint}"; echo "Verified git signature at depth ${depth} by \"${uid}\""
fi fi
[[ "${seen_fingerprints}" != *"${fingerprint}"* ]] \ if [ ! -z "$group" ]; then
&& seen_fingerprints="${seen_fingerprints} ${fingerprint}" \ group_check_fp "${fp}" "${group}" \
&& ((sig_count=sig_count+1)) \ || die "Git signing key not in group \"${group}\": ${fp}"
&& echo "Verified git signature at depth ${depth} by \"${signer}\"" fi
[[ "${sig_count}" -ge "${threshold}" ]] && break; [[ "${sig_count}" -ge "${threshold}" ]] && break;
@ -238,15 +335,14 @@ cmd_verify() {
} }
cmd_add(){ cmd_add(){
local fingerprint
cmd_manifest cmd_manifest
gpg --armor --detach-sig ."${PROGRAM}"/manifest.txt >/dev/null 2>&1 gpg --armor --detach-sig ."${PROGRAM}"/manifest.txt >/dev/null 2>&1
fingerprint=$( \ local fp; fp=$( \
gpg --list-packets ."${PROGRAM}"/manifest.txt.asc \ gpg --list-packets ."${PROGRAM}"/manifest.txt.asc \
| grep "issuer key ID" \ | grep "issuer key ID" \
| sed 's/.*\([A-Z0-9]\{16\}\).*/\1/g' \ | sed 's/.*\([A-Z0-9]\{16\}\).*/\1/g' \
) )
mv ."${PROGRAM}"/manifest.{"txt.asc","${fingerprint}.asc"} mv ."${PROGRAM}"/manifest.{"txt.asc","${fp}.asc"}
} }
cmd_version() { cmd_version() {