aboutsummaryrefslogtreecommitdiff
blob: d19f0ad5165fdd2c98c8685e00f15f5549875f2d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/bin/bash

DEV_BASE='ou=devs,dc=gentoo,dc=org'
SYSTEM_BASE='ou=system,dc=gentoo,dc=org'

COMMIT_RULE='(&(gentooAccess=git.gentoo.org/repo/gentoo.git)(gentooStatus=active))'
NONCOMMIT_RULE='(&(!(gentooAccess=git.gentoo.org/repo/gentoo.git))(gentooStatus=active))'
RETIRED_RULE='(!(gentooStatus=active))'

export KS_GENTOO=hkps://keys.gentoo.org/
#export KS_SKS=hkps://hkps.pool.sks-keyservers.net/ # Disabled pending security announcement
export KS_OPENPGP=hkps://keys.openpgp.org/ # runs Hagrid
export KEYSERVERS=( ) # empty by default
export COMMITTING_DEVS=( )
export NONCOMMITTING_DEVS=( )
export RETIRED_DEVS=( )
export SYSTEM_KEYS=( )

# grab_ldap_fingerprints <ldap-rule>
grab_ldap_fingerprints() {
	ldapsearch "${@}" -x -Z gpgfingerprint -LLL |
		sed -n -e '/: undefined/d' -e '/^gpgfingerprint: /{s/^.*://;s/ //g;p}' |
		sort -u
}

# grab_keys <fingerprint>...
grab_keys() {
	local retries=0
	local missing=()
	local remaining=( "${@}" )

	KEYSERVER_TIMEOUT=${KEYSERVER_TIMEOUT:=1m}
	# quickly handle empty keyservers set
	[ "${#KEYSERVERS[@]}" -eq 0 ] && return
	while :; do
		for ks in "${KEYSERVERS[@]}" ; do
			timeout ${KEYSERVER_TIMEOUT} gpg \
				--keyserver-options no-import-clean,no-self-sigs-only \
				--keyserver "$ks" -q --recv-keys "${remaining[@]}" || :
		done
		missing=()
		for key in "${remaining[@]}"; do
			gpg --list-public "${key}" &>/dev/null || missing+=( "${key}" )
		done

		[[ ${#missing[@]} -ne 0 ]] || break

		# if we did not make progress, give it a few seconds and retry
		if [[ ${#missing[@]} -eq ${#remaining[@]} ]]; then
			if [[ $(( retries++ )) -gt 3 ]]; then
				echo "Unable to fetch the following keys:"
				printf '%s\n' "${missing[@]}"
				break # if we hard-exit, the entire export will fail
			fi
			sleep 5
		fi

		remaining=( "${missing[@]}" )
	done
}

# push_keys <fingerprint>...
push_keys() {
	# quickly handle empty keyservers set
	[ "${#KEYSERVERS[@]}" -eq 0 ] && return
	# Only send keys that we have
	local remaining=( $(gpg --with-colon --list-public "${@}" | sed -n '/^pub/{n; /fpr/p }' |cut -d: -f10) )
	KEYSERVER_TIMEOUT=${KEYSERVER_TIMEOUT:=1m}
	for ks in "${KEYSERVERS[@]}" ; do
		timeout ${KEYSERVER_TIMEOUT} gpg --keyserver "$ks" -q --send-keys "${remaining[@]}" || :
	done
}

export GPG_TMPDIR=''
clean_tmp() {
	[ -n "$GPG_TMPDIR" ] && [ -d "$GPG_TMPDIR" ] && rm -rf "$GPG_TMPDIR"
}
setup_tmp() {
	if [ -z "${GPG_TMPDIR}" ]; then
		GPG_TMPDIR=$(mktemp -d)
		export GPG_TMPDIR
		trap clean_tmp EXIT
	fi
}

export_keys() {
	DST="$1"
	shift
	setup_tmp
	TMP="${GPG_TMPDIR}"/$(basename "${DST}")
	# Must not exist, otherwise GPG will give error
	[[ -f "${TMP}" ]] && rm -f "${TMP}"
	# 'gpg --export' returns zero if there was no error with the command itself
	# If there are no keys in the export set, then it ALSO does not write the destination file
	# and prints 'gpg: WARNING: nothing exported' to stderr
	if ! gpg --output "$TMP" --export "${@}"; then
		echo "Unable to export keys to $DST: GPG returned non-zero"
		exit 1
	fi
	if ! test -s "${TMP}"; then
		echo "Unable to export keys to $DST: GPG returned zero but generated empty file"
		exit 1
	fi
	# We have a non-empty output now!
	# Capture it in a textual format that can be compared for changes, but make sure it exports correctly
	if ! gpg --list-packets "${TMP}" >"${TMP}.packets.txt"; then
		echo "Unable to export keys to $DST: GPG failed to list packets"
		exit 1
	fi
	# Check if the textual format has changed at all, and emit the new version
	# if there are ANY changes at all.
	if ! cmp -s "${DST}.packets.txt" "${TMP}.packets.txt"; then
		chmod a+r "${TMP}"
		mv -f "${TMP}" "${DST}"
		mv -f "${TMP}.packets.txt" "${DST}.packets.txt"
	fi
	# Cleanup anyway
	rm -f "${TMP}.packets.txt" "${TMP}"
}

# populate common variables
# TODO: for unclear reason this does not populate correctly inside a function
export_ldap_data_to_env() {
	export -a COMMITTING_DEVS=( $(grab_ldap_fingerprints -b "${DEV_BASE}" "${COMMIT_RULE}") )
	export -a NONCOMMITTING_DEVS=( $(grab_ldap_fingerprints -b "${DEV_BASE}" "${NONCOMMIT_RULE}") )
	export -a RETIRED_DEVS=( $(grab_ldap_fingerprints -b "${DEV_BASE}" "${RETIRED_RULE}") )
	export -a SYSTEM_KEYS=( $(grab_ldap_fingerprints -b "${SYSTEM_BASE}" "${NONCOMMIT_RULE}") )
}