[pacman-dev] [PATCH] pacman-key: rework importing distro/repo provided keyrings
The current --reload option, apart from being non-clear in its naming, is very limited in that only one keyring can be provided. A distribution may want to provide multiple keyrings for various subsets of its organisation or custom repo providers may also want to provide a keyring. This patch adds a --populate option that reads keyrings from (by default) /usr/share/pacman/keyrings. A keyring is named foo.gpg, with optional foo-revoked file providing a list of revoked key ids. These files are required to be signed (detached) by a key trusted by pacman-key, in practise probably by the key that signed the package providing these files. The --populate flag either updates the pacman keyring using all keyrings in the directory or individual keyrings can be specified. Signed-off-by: Allan McRae <allan@archlinux.org> --- doc/pacman-key.8.txt | 8 ++- scripts/Makefile.am | 1 + scripts/pacman-key.sh.in | 149 +++++++++++++++++++++++----------------------- 3 files changed, 82 insertions(+), 76 deletions(-) diff --git a/doc/pacman-key.8.txt b/doc/pacman-key.8.txt index c8ce026..98ee6f6 100644 --- a/doc/pacman-key.8.txt +++ b/doc/pacman-key.8.txt @@ -80,8 +80,12 @@ Options *-r, \--receive* <keyserver> <keyid(s)>:: Fetch the specified keyids from the specified key server URL. -*\--reload*:: - Reloads the keys from the keyring package. +*\--populate* [<keyring(s)>]:: + Reload the default keys from the (given) keyrings in +{pkgdatadir}/keyrings+. + Each keyring is provided in a file foo.gpg that contains the keys for the foo + keyring. Optionally the file foo-revoked contains a list of revoked key ids + for that keyring. These files are required to be signed (detached) by a + trusted PGP key. *-u, \--updatedb*:: Equivalent to \--check-trustdb in GnuPG. diff --git a/scripts/Makefile.am b/scripts/Makefile.am index adb259a..30b6ad8 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -46,6 +46,7 @@ edit = sed \ -e 's|@localedir[@]|$(localedir)|g' \ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' \ + -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' \ -e 's|@prefix[@]|$(prefix)|g' \ -e '1s|!/bin/bash|!$(BASH_SHELL)|g' \ -e 's|@PACKAGE_VERSION[@]|$(REAL_PACKAGE_VERSION)|g' \ diff --git a/scripts/pacman-key.sh.in b/scripts/pacman-key.sh.in index 74ecfcf..c14d9bb 100644 --- a/scripts/pacman-key.sh.in +++ b/scripts/pacman-key.sh.in @@ -37,8 +37,8 @@ IMPORT_TRUSTDB=0 INIT=0 LISTKEYS=0 LISTSIGS=0 +POPULATE=0 RECEIVE=0 -RELOAD=0 UPDATEDB=0 VERIFY=0 @@ -73,7 +73,8 @@ usage() { echo "$(gettext " --import-trustdb <dir(s)> Imports ownertrust values from trustdb.gpg in dir(s)")" echo "$(gettext " --init Ensure the keyring is properly initialized")" echo "$(gettext " --list-sigs [<keyid(s)>] List keys and their signatures")" - echo "$(gettext " --reload Reload the default keys")" + printf "$(gettext " --populate [<keyring(s)] Reload the default keys from the (given) keyrings\n\ + in '%s'")\n" "@pkgdatadir@/keyrings" } version() { @@ -139,75 +140,87 @@ check_keyring() { verify_keyring_input() { local ret=0; + local KEYRING_IMPORT_DIR='@pkgdatadir@/keyrings' - # Verify signatures of related files, if they exist - if [[ -r "${ADDED_KEYS}" ]]; then - msg "$(gettext "Verifying official keys file signature...")" - if ! "${GPG_PACMAN[@]}" --verify "${ADDED_KEYS}.sig" &>/dev/null; then + # Verify signatures of keyring files and association revocation files if they exist + msg "$(gettext "Verifying keyring file signatures...")" + local keyring + for keyring in ${KEYRINGIDS[@]}; do + if ! "${GPG_PACMAN[@]}" --verify "${KEYRING_IMPORT_DIR}/${keyring}.gpg.sig" &>/dev/null; then error "$(gettext "The signature of file %s is not valid.")" "${ADDED_KEYS}" ret=1 fi - fi - if [[ -r "${DEPRECATED_KEYS}" ]]; then - msg "$(gettext "Verifying deprecated keys file signature...")" - if ! "${GPG_PACMAN[@]}" --verify "${DEPRECATED_KEYS}.sig" &>/dev/null; then - error "$(gettext "The signature of file %s is not valid.")" "${DEPRECATED_KEYS}" - ret=1 + if [[ -f "${KEYRING_IMPORT_DIR}/${keyring}-revoked" ]]; then + if ! "${GPG_PACMAN[@]}" --verify "${KEYRING_IMPORT_DIR}/${keyring}-revoked.sig" &>/dev/null; then + error "$(gettext "The signature of file %s is not valid.")" "${KEYRING_IMPORT_DIR}/${keyring}-revoked" + ret=1 + fi fi - fi + done - if [[ -r "${REMOVED_KEYS}" ]]; then - msg "$(gettext "Verifying deleted keys file signature...")" - if ! "${GPG_PACMAN[@]}" --verify "${REMOVED_KEYS}.sig" &>/dev/null; then - error "$(gettext "The signature of file %s is not valid.")" "${REMOVED_KEYS}" + return $ret +} + +populate_keyring() { + local KEYRING_IMPORT_DIR='@pkgdatadir@/keyrings' + local GPG_NOKEYRING=(gpg --batch --quiet --ignore-time-conflict --no-options --no-default-keyring --homedir ${PACMAN_KEYRING_DIR} --no-permission-warning) + + local keyring + local ret=0 + if [[ -z ${KEYRINGIDS[@]} ]]; then + # get list of all available keyrings + shopt -s nullglob + KEYRINGIDS=("$KEYRING_IMPORT_DIR"/*.gpg) + shopt -u nullglob + KEYRINGIDS=("${KEYRINGIDS[@]##*/}") + KEYRINGIDS=("${KEYRINGIDS[@]%.gpg}") + if [[ -z ${KEYRINGIDS[@]} ]]; then + error "$(gettext "No keyring files exist in %s.")" "$KEYRING_IMPORT_DIR" ret=1 fi + else + # verify listed keyrings exist + for keyring in ${KEYRINGIDS[@]}; do + if [[ ! -f "$KEYRING_IMPORT_DIR/$keyring.gpg" ]]; then + error "$(gettext "The keyring file %s does not exist.")" "$KEYRING_IMPORT_DIR/$keyring.gpg" + ret=1 + fi + done fi - return $ret -} + if (( ret )); then + exit 1 + fi -reload_keyring() { - local PACMAN_SHARE_DIR='@prefix@/share/pacman' - local GPG_NOKEYRING=(gpg --batch --quiet --ignore-time-conflict --no-options --no-default-keyring --homedir ${PACMAN_KEYRING_DIR}) + verify_keyring_input || exit 1 # Variable used for iterating on keyrings local key local key_id - # Keyring with keys to be added to the keyring - local ADDED_KEYS="${PACMAN_SHARE_DIR}/addedkeys.gpg" - - # Keyring with keys that were deprecated and will eventually be deleted - local DEPRECATED_KEYS="${PACMAN_SHARE_DIR}/deprecatedkeys.gpg" - - # List of keys removed from the keyring. This file is not a keyring, unlike the others. - # It is a textual list of values that gpg recogniezes as identifiers for keys. - local REMOVED_KEYS="${PACMAN_SHARE_DIR}/removedkeys" - - verify_keyring_input || exit 1 - # Read the key ids to an array. The conversion from whatever is inside the file # to key ids is important, because key ids are the only guarantee of identification # for the keys. local -A removed_ids - if [[ -r "${REMOVED_KEYS}" ]]; then - while read key; do - local key_values name - key_values="$("${GPG_PACMAN[@]}" --quiet --with-colons --list-key "${key}" | grep ^pub | cut -d: -f5,10 --output-delimiter=' ')" - if [[ -n $key_values ]]; then - # The first word is the key_id - key_id="${key_values%% *}" - # the rest if the name of the owner - name="${key_values#* }" - if [[ -n ${key_id} ]]; then - # Mark this key to be deleted - removed_ids[$key_id]="$name" + for keyring in ${KEYRINGIDS[@]}; do + if [[ -f "${KEYRING_IMPORT_DIR}/${keyring}-revoked" ]]; then + while read key; do + local key_values name + key_values="$("${GPG_PACMAN[@]}" --quiet --with-colons --list-key "${key}" 2>/dev/null | grep ^pub | cut -d: -f5,10 --output-delimiter=' ')" + if [[ -n $key_values ]]; then + # The first word is the key_id + key_id="${key_values%% *}" + # the rest if the name of the owner + name="${key_values#* }" + if [[ -n ${key_id} ]]; then + # Mark this key to be deleted + removed_ids[$key_id]="$name" + fi fi - fi - done < "${REMOVED_KEYS}" - fi + done < "${KEYRING_IMPORT_DIR}/${keyring}-revoked" + fi + done # List of keys that must be kept installed, even if in the list of keys to be removed local HOLD_KEYS="$(get_from "$CONFIG" "HoldKeys")" @@ -222,33 +235,21 @@ reload_keyring() { done fi - # Add keys from the current set of keys from pacman-keyring package. The web of trust will - # be updated automatically. - if [[ -r "${ADDED_KEYS}" ]]; then - msg "$(gettext "Appending official keys...")" - local add_keys="$("${GPG_NOKEYRING[@]}" --keyring "${ADDED_KEYS}" --with-colons --list-keys | grep ^pub | cut -d: -f5)" - for key_id in ${add_keys}; do - # There is no point in adding a key that will be deleted right after - if [[ -z "${removed_ids[$key_id]}" ]]; then - "${GPG_NOKEYRING[@]}" --keyring "${ADDED_KEYS}" --export "${key_id}" | "${GPG_PACMAN[@]}" --import - fi - done - fi - - if [[ -r "${DEPRECATED_KEYS}" ]]; then - msg "$(gettext "Appending deprecated keys...")" - local add_keys="$("${GPG_NOKEYRING[@]}" --keyring "${DEPRECATED_KEYS}" --with-colons --list-keys | grep ^pub | cut -d: -f5)" + # Add keys from requested keyrings + for keyring in ${KEYRINGIDS[@]}; do + msg "$(gettext "Appending keys from %s.gpg...")" "$keyring" + local add_keys="$("${GPG_NOKEYRING[@]}" --keyring "${KEYRING_IMPORT_DIR}/${keyring}.gpg" --with-colons --list-keys | grep ^pub | cut -d: -f5)" for key_id in ${add_keys}; do # There is no point in adding a key that will be deleted right after if [[ -z "${removed_ids[$key_id]}" ]]; then - "${GPG_NOKEYRING[@]}" --keyring "${DEPRECATED_KEYS}" --export "${key_id}" | "${GPG_PACMAN[@]}" --import + "${GPG_NOKEYRING[@]}" --keyring "${KEYRING_IMPORT_DIR}/${keyring}.gpg" --export "${key_id}" | "${GPG_PACMAN[@]}" --import fi done - fi + done # Remove the keys not marked to keep if (( ${#removed_ids[@]} > 0 )); then - msg "$(gettext "Removing deleted keys from keyring...")" + msg "$(gettext "Removing revoked keys from keyring...")" for key_id in "${!removed_ids[@]}"; do echo " removing key $key_id - ${removed_ids[$key_id]}" "${GPG_PACMAN[@]}" --quiet --batch --yes --delete-key "${key_id}" @@ -321,8 +322,8 @@ fi OPT_SHORT="a::d:e:f::hl::r:uv:V" OPT_LONG="add::,config:,delete:,edit-key:,export::,finger::,gpgdir:" -OPT_LONG+=",help,import:,import-trustdb:,init,list-keys::,list-sigs::,,receive:" -OPT_LONG+=",reload,updatedb,verify:,version" +OPT_LONG+=",help,import:,import-trustdb:,init,list-keys::,list-sigs::" +OPT_LONG+=",populate::,receive:,updatedb,verify:,version" if ! OPT_TEMP="$(parse_options $OPT_SHORT $OPT_LONG "$@")"; then echo; usage; exit 1 # E_INVALID_OPTION; fi @@ -348,8 +349,8 @@ while true; do --init) INIT=1 ;; -l|--list-keys) LISTKEYS=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; --list-sigs) LISTSIGS=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; + --populate) POPULATE=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYRINGIDS=($1) ;; -r|--receive) RECEIVE=1; shift; TMP=($1); KEYSERVER=${TMP[0]}; KEYIDS=(${TMP[@]:1}); unset TMP;; - --reload) RELOAD=1 ;; -u|--updatedb) UPDATEDB=1 ;; -v|--verify) VERIFY=1; shift; SIGNATURE=$1 ;; @@ -368,7 +369,7 @@ if ! type -p gpg >/dev/null; then exit 1 fi -if (( (ADD || DELETE || EDITKEY || IMPORT || IMPORT_TRUSTDB || INIT || RECEIVE || RELOAD || UPDATEDB) && EUID != 0 )); then +if (( (ADD || DELETE || EDITKEY || IMPORT || IMPORT_TRUSTDB || INIT || POPULATE || RECEIVE || UPDATEDB) && EUID != 0 )); then error "$(gettext "%s needs to be run as root for this operation.")" "pacman-key" exit 1 fi @@ -387,7 +388,7 @@ GPG_PACMAN=(gpg --homedir ${PACMAN_KEYRING_DIR} --no-permission-warning) # check only a single operation has been given numopt=$(( ADD + DELETE + EDITKEY + EXPORT + FINGER + IMPORT + IMPORT_TRUSTDB + - INIT + LISTKEYS + LISTSIGS + RECEIVE + RELOAD + UPDATEDB + VERIFY )) + INIT + LISTKEYS + LISTSIGS + POPULATE + RECEIVE + UPDATEDB + VERIFY )) case $numopt in 0) @@ -413,8 +414,8 @@ esac (( INIT )) && initialize (( LISTKEYS )) && "${GPG_PACMAN[@]}" --batch --list-keys "${KEYIDS[@]}" (( LISTSIGS )) && "${GPG_PACMAN[@]}" --batch --list-sigs "${KEYIDS[@]}" +(( POPULATE )) && populate_keyring (( RECEIVE )) && receive_keys -(( RELOAD )) && reload_keyring (( UPDATEDB )) && "${GPG_PACMAN[@]}" --batch --check-trustdb (( VERIFY )) && "${GPG_PACMAN[@]}" --verify $SIGNATURE -- 1.7.6
On 15/08/11 23:10, Allan McRae wrote:
The current --reload option, apart from being non-clear in its naming, is very limited in that only one keyring can be provided. A distribution may want to provide multiple keyrings for various subsets of its organisation or custom repo providers may also want to provide a keyring.
This patch adds a --populate option that reads keyrings from (by default) /usr/share/pacman/keyrings. A keyring is named foo.gpg, with optional foo-revoked file providing a list of revoked key ids. These files are required to be signed (detached) by a key trusted by pacman-key, in practise probably by the key that signed the package providing these files. The --populate flag either updates the pacman keyring using all keyrings in the directory or individual keyrings can be specified.
Signed-off-by: Allan McRae<allan@archlinux.org>
As an example of this in action:
sudo ./scripts/pacman-key --populate ==> Verifying keyring file signatures... ==> Appending keys from allanbrokeit.gpg... gpg: key EAE999BD: "Allan McRae <me@allanmcrae.com>" not changed gpg: Total number processed: 1 gpg: unchanged: 1 ==> Removing revoked keys from keyring... removing key 5C2E46A0F53A76ED - Dan McGee <dpmcgee@gmail.com> ==> Updating trust database... gpg: no need for a trustdb check
Allan
On Mon, Aug 15, 2011 at 8:10 AM, Allan McRae <allan@archlinux.org> wrote:
The current --reload option, apart from being non-clear in its naming, is very limited in that only one keyring can be provided. A distribution may want to provide multiple keyrings for various subsets of its organisation or custom repo providers may also want to provide a keyring. Interesting thought. What happens here if a key is marked as OK in foo.gpg, but "revoked" in bar-revoked, as might happen if someone changes organizations?
This patch adds a --populate option that reads keyrings from (by default) /usr/share/pacman/keyrings. A keyring is named foo.gpg, with optional foo-revoked file providing a list of revoked key ids. These files are required to be signed (detached) by a key trusted by pacman-key, in practise probably by the key that signed the package providing these
Keeping arch and the TU and Dev groups in mind, you could see such a situation. However, I don't believe we would ever put a key in the revoked keyring unless we had really good reason to- this wouldn't just happen if someone left the dev team for instance. Not that timestamps are trustable in signatures themselves if the key used to sign is trusted, but is there any way to say "this key is valid but not if sigs using it were created after X time"? Clearly we can get bogged down in this but something worth thinking about. practice
files. The --populate flag either updates the pacman keyring using all keyrings in the directory or individual keyrings can be specified.
Signed-off-by: Allan McRae <allan@archlinux.org> --- doc/pacman-key.8.txt | 8 ++- scripts/Makefile.am | 1 + scripts/pacman-key.sh.in | 149 +++++++++++++++++++++++----------------------- 3 files changed, 82 insertions(+), 76 deletions(-)
diff --git a/doc/pacman-key.8.txt b/doc/pacman-key.8.txt index c8ce026..98ee6f6 100644 --- a/doc/pacman-key.8.txt +++ b/doc/pacman-key.8.txt @@ -80,8 +80,12 @@ Options *-r, \--receive* <keyserver> <keyid(s)>:: Fetch the specified keyids from the specified key server URL.
-*\--reload*:: - Reloads the keys from the keyring package. +*\--populate* [<keyring(s)>]:: + Reload the default keys from the (given) keyrings in +{pkgdatadir}/keyrings+. (optionally provided) ?
+ Each keyring is provided in a file foo.gpg that contains the keys for the foo filenames in `foo.gpg` backticks usually is the convention. + keyring. Optionally the file foo-revoked contains a list of revoked key ids Optionally, <comma> `foo-revoked.gpg` (forgot the extension here too). Or did I misunderstand and this is a text file with IDs rather than an actual keyring file with binary data? This might require some thought, it looks like I did misunderstand. "IDs" is the capitalization used in the gpg manpage.
+ for that keyring. These files are required to be signed (detached) by a + trusted PGP key.
*-u, \--updatedb*:: Equivalent to \--check-trustdb in GnuPG. diff --git a/scripts/Makefile.am b/scripts/Makefile.am index adb259a..30b6ad8 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -46,6 +46,7 @@ edit = sed \ -e 's|@localedir[@]|$(localedir)|g' \ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' \ + -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' \ -e 's|@prefix[@]|$(prefix)|g' \ -e '1s|!/bin/bash|!$(BASH_SHELL)|g' \ -e 's|@PACKAGE_VERSION[@]|$(REAL_PACKAGE_VERSION)|g' \ diff --git a/scripts/pacman-key.sh.in b/scripts/pacman-key.sh.in index 74ecfcf..c14d9bb 100644 --- a/scripts/pacman-key.sh.in +++ b/scripts/pacman-key.sh.in @@ -37,8 +37,8 @@ IMPORT_TRUSTDB=0 INIT=0 LISTKEYS=0 LISTSIGS=0 +POPULATE=0 RECEIVE=0 -RELOAD=0 UPDATEDB=0 VERIFY=0
@@ -73,7 +73,8 @@ usage() { echo "$(gettext " --import-trustdb <dir(s)> Imports ownertrust values from trustdb.gpg in dir(s)")" echo "$(gettext " --init Ensure the keyring is properly initialized")" echo "$(gettext " --list-sigs [<keyid(s)>] List keys and their signatures")" - echo "$(gettext " --reload Reload the default keys")" + printf "$(gettext " --populate [<keyring(s)] Reload the default keys from the (given) keyrings\n\ + in '%s'")\n" "@pkgdatadir@/keyrings" }
version() { @@ -139,75 +140,87 @@ check_keyring() {
verify_keyring_input() { local ret=0; + local KEYRING_IMPORT_DIR='@pkgdatadir@/keyrings'
- # Verify signatures of related files, if they exist - if [[ -r "${ADDED_KEYS}" ]]; then - msg "$(gettext "Verifying official keys file signature...")" - if ! "${GPG_PACMAN[@]}" --verify "${ADDED_KEYS}.sig" &>/dev/null; then + # Verify signatures of keyring files and association revocation files if they exist + msg "$(gettext "Verifying keyring file signatures...")" + local keyring + for keyring in ${KEYRINGIDS[@]}; do + if ! "${GPG_PACMAN[@]}" --verify "${KEYRING_IMPORT_DIR}/${keyring}.gpg.sig" &>/dev/null; then So how do we do the initial bootstrap? Maybe we need a way (e.g. command line option) to override the verification?
error "$(gettext "The signature of file %s is not valid.")" "${ADDED_KEYS}" ret=1 fi - fi
- if [[ -r "${DEPRECATED_KEYS}" ]]; then - msg "$(gettext "Verifying deprecated keys file signature...")" - if ! "${GPG_PACMAN[@]}" --verify "${DEPRECATED_KEYS}.sig" &>/dev/null; then - error "$(gettext "The signature of file %s is not valid.")" "${DEPRECATED_KEYS}" - ret=1 + if [[ -f "${KEYRING_IMPORT_DIR}/${keyring}-revoked" ]]; then + if ! "${GPG_PACMAN[@]}" --verify "${KEYRING_IMPORT_DIR}/${keyring}-revoked.sig" &>/dev/null; then + error "$(gettext "The signature of file %s is not valid.")" "${KEYRING_IMPORT_DIR}/${keyring}-revoked" + ret=1 + fi fi - fi + done
- if [[ -r "${REMOVED_KEYS}" ]]; then - msg "$(gettext "Verifying deleted keys file signature...")" - if ! "${GPG_PACMAN[@]}" --verify "${REMOVED_KEYS}.sig" &>/dev/null; then - error "$(gettext "The signature of file %s is not valid.")" "${REMOVED_KEYS}" + return $ret +} + +populate_keyring() { + local KEYRING_IMPORT_DIR='@pkgdatadir@/keyrings' + local GPG_NOKEYRING=(gpg --batch --quiet --ignore-time-conflict --no-options --no-default-keyring --homedir ${PACMAN_KEYRING_DIR} --no-permission-warning) + + local keyring + local ret=0 + if [[ -z ${KEYRINGIDS[@]} ]]; then + # get list of all available keyrings + shopt -s nullglob + KEYRINGIDS=("$KEYRING_IMPORT_DIR"/*.gpg) + shopt -u nullglob + KEYRINGIDS=("${KEYRINGIDS[@]##*/}") + KEYRINGIDS=("${KEYRINGIDS[@]%.gpg}") + if [[ -z ${KEYRINGIDS[@]} ]]; then + error "$(gettext "No keyring files exist in %s.")" "$KEYRING_IMPORT_DIR" ret=1 fi + else + # verify listed keyrings exist + for keyring in ${KEYRINGIDS[@]}; do + if [[ ! -f "$KEYRING_IMPORT_DIR/$keyring.gpg" ]]; then + error "$(gettext "The keyring file %s does not exist.")" "$KEYRING_IMPORT_DIR/$keyring.gpg" + ret=1 + fi + done fi
- return $ret -} + if (( ret )); then + exit 1 + fi
-reload_keyring() { - local PACMAN_SHARE_DIR='@prefix@/share/pacman' - local GPG_NOKEYRING=(gpg --batch --quiet --ignore-time-conflict --no-options --no-default-keyring --homedir ${PACMAN_KEYRING_DIR}) + verify_keyring_input || exit 1
# Variable used for iterating on keyrings local key local key_id
- # Keyring with keys to be added to the keyring - local ADDED_KEYS="${PACMAN_SHARE_DIR}/addedkeys.gpg" - - # Keyring with keys that were deprecated and will eventually be deleted - local DEPRECATED_KEYS="${PACMAN_SHARE_DIR}/deprecatedkeys.gpg" - - # List of keys removed from the keyring. This file is not a keyring, unlike the others. - # It is a textual list of values that gpg recogniezes as identifiers for keys. - local REMOVED_KEYS="${PACMAN_SHARE_DIR}/removedkeys" - - verify_keyring_input || exit 1 - # Read the key ids to an array. The conversion from whatever is inside the file # to key ids is important, because key ids are the only guarantee of identification # for the keys. local -A removed_ids - if [[ -r "${REMOVED_KEYS}" ]]; then - while read key; do - local key_values name - key_values="$("${GPG_PACMAN[@]}" --quiet --with-colons --list-key "${key}" | grep ^pub | cut -d: -f5,10 --output-delimiter=' ')" - if [[ -n $key_values ]]; then - # The first word is the key_id - key_id="${key_values%% *}" - # the rest if the name of the owner - name="${key_values#* }" - if [[ -n ${key_id} ]]; then - # Mark this key to be deleted - removed_ids[$key_id]="$name" + for keyring in ${KEYRINGIDS[@]}; do + if [[ -f "${KEYRING_IMPORT_DIR}/${keyring}-revoked" ]]; then + while read key; do + local key_values name + key_values="$("${GPG_PACMAN[@]}" --quiet --with-colons --list-key "${key}" 2>/dev/null | grep ^pub | cut -d: -f5,10 --output-delimiter=' ')" A comment describing this madness line would be nice.
+ if [[ -n $key_values ]]; then + # The first word is the key_id + key_id="${key_values%% *}" + # the rest if the name of the owner + name="${key_values#* }" + if [[ -n ${key_id} ]]; then + # Mark this key to be deleted + removed_ids[$key_id]="$name" + fi fi - fi - done < "${REMOVED_KEYS}" - fi + done < "${KEYRING_IMPORT_DIR}/${keyring}-revoked" + fi + done
# List of keys that must be kept installed, even if in the list of keys to be removed local HOLD_KEYS="$(get_from "$CONFIG" "HoldKeys")" This is something I hadn't noticed before. How exactly does this hold keys stuff work? man pacman-key, /hold shows me nothing.
@@ -222,33 +235,21 @@ reload_keyring() { done fi
- # Add keys from the current set of keys from pacman-keyring package. The web of trust will - # be updated automatically. - if [[ -r "${ADDED_KEYS}" ]]; then - msg "$(gettext "Appending official keys...")" - local add_keys="$("${GPG_NOKEYRING[@]}" --keyring "${ADDED_KEYS}" --with-colons --list-keys | grep ^pub | cut -d: -f5)" - for key_id in ${add_keys}; do - # There is no point in adding a key that will be deleted right after - if [[ -z "${removed_ids[$key_id]}" ]]; then - "${GPG_NOKEYRING[@]}" --keyring "${ADDED_KEYS}" --export "${key_id}" | "${GPG_PACMAN[@]}" --import - fi - done - fi - - if [[ -r "${DEPRECATED_KEYS}" ]]; then - msg "$(gettext "Appending deprecated keys...")" - local add_keys="$("${GPG_NOKEYRING[@]}" --keyring "${DEPRECATED_KEYS}" --with-colons --list-keys | grep ^pub | cut -d: -f5)" + # Add keys from requested keyrings + for keyring in ${KEYRINGIDS[@]}; do + msg "$(gettext "Appending keys from %s.gpg...")" "$keyring" + local add_keys="$("${GPG_NOKEYRING[@]}" --keyring "${KEYRING_IMPORT_DIR}/${keyring}.gpg" --with-colons --list-keys | grep ^pub | cut -d: -f5)" for key_id in ${add_keys}; do # There is no point in adding a key that will be deleted right after if [[ -z "${removed_ids[$key_id]}" ]]; then - "${GPG_NOKEYRING[@]}" --keyring "${DEPRECATED_KEYS}" --export "${key_id}" | "${GPG_PACMAN[@]}" --import + "${GPG_NOKEYRING[@]}" --keyring "${KEYRING_IMPORT_DIR}/${keyring}.gpg" --export "${key_id}" | "${GPG_PACMAN[@]}" --import fi done - fi + done
# Remove the keys not marked to keep if (( ${#removed_ids[@]} > 0 )); then - msg "$(gettext "Removing deleted keys from keyring...")" + msg "$(gettext "Removing revoked keys from keyring...")" for key_id in "${!removed_ids[@]}"; do echo " removing key $key_id - ${removed_ids[$key_id]}" "${GPG_PACMAN[@]}" --quiet --batch --yes --delete-key "${key_id}" @@ -321,8 +322,8 @@ fi
OPT_SHORT="a::d:e:f::hl::r:uv:V" OPT_LONG="add::,config:,delete:,edit-key:,export::,finger::,gpgdir:" -OPT_LONG+=",help,import:,import-trustdb:,init,list-keys::,list-sigs::,,receive:" -OPT_LONG+=",reload,updatedb,verify:,version" +OPT_LONG+=",help,import:,import-trustdb:,init,list-keys::,list-sigs::" +OPT_LONG+=",populate::,receive:,updatedb,verify:,version" if ! OPT_TEMP="$(parse_options $OPT_SHORT $OPT_LONG "$@")"; then echo; usage; exit 1 # E_INVALID_OPTION; fi @@ -348,8 +349,8 @@ while true; do --init) INIT=1 ;; -l|--list-keys) LISTKEYS=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; --list-sigs) LISTSIGS=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; + --populate) POPULATE=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYRINGIDS=($1) ;; -r|--receive) RECEIVE=1; shift; TMP=($1); KEYSERVER=${TMP[0]}; KEYIDS=(${TMP[@]:1}); unset TMP;; - --reload) RELOAD=1 ;; -u|--updatedb) UPDATEDB=1 ;; -v|--verify) VERIFY=1; shift; SIGNATURE=$1 ;;
@@ -368,7 +369,7 @@ if ! type -p gpg >/dev/null; then exit 1 fi
-if (( (ADD || DELETE || EDITKEY || IMPORT || IMPORT_TRUSTDB || INIT || RECEIVE || RELOAD || UPDATEDB) && EUID != 0 )); then +if (( (ADD || DELETE || EDITKEY || IMPORT || IMPORT_TRUSTDB || INIT || POPULATE || RECEIVE || UPDATEDB) && EUID != 0 )); then error "$(gettext "%s needs to be run as root for this operation.")" "pacman-key" exit 1 fi @@ -387,7 +388,7 @@ GPG_PACMAN=(gpg --homedir ${PACMAN_KEYRING_DIR} --no-permission-warning)
# check only a single operation has been given numopt=$(( ADD + DELETE + EDITKEY + EXPORT + FINGER + IMPORT + IMPORT_TRUSTDB + - INIT + LISTKEYS + LISTSIGS + RECEIVE + RELOAD + UPDATEDB + VERIFY )) + INIT + LISTKEYS + LISTSIGS + POPULATE + RECEIVE + UPDATEDB + VERIFY ))
case $numopt in 0) @@ -413,8 +414,8 @@ esac (( INIT )) && initialize (( LISTKEYS )) && "${GPG_PACMAN[@]}" --batch --list-keys "${KEYIDS[@]}" (( LISTSIGS )) && "${GPG_PACMAN[@]}" --batch --list-sigs "${KEYIDS[@]}" +(( POPULATE )) && populate_keyring (( RECEIVE )) && receive_keys -(( RELOAD )) && reload_keyring (( UPDATEDB )) && "${GPG_PACMAN[@]}" --batch --check-trustdb (( VERIFY )) && "${GPG_PACMAN[@]}" --verify $SIGNATURE
-- 1.7.6
On 18/08/11 08:48, Dan McGee wrote:
On Mon, Aug 15, 2011 at 8:10 AM, Allan McRae<allan@archlinux.org> wrote:
The current --reload option, apart from being non-clear in its naming, is very limited in that only one keyring can be provided. A distribution may want to provide multiple keyrings for various subsets of its organisation or custom repo providers may also want to provide a keyring. Interesting thought. What happens here if a key is marked as OK in foo.gpg, but "revoked" in bar-revoked, as might happen if someone changes organizations?
Yeah.. that could be interesting. At the moment I believe revoking anywhere takes precedence, but I will need to double check that. I think this is something that entirely undocmuented HoldKey option can be used for.
Keeping arch and the TU and Dev groups in mind, you could see such a situation. However, I don't believe we would ever put a key in the revoked keyring unless we had really good reason to- this wouldn't just happen if someone left the dev team for instance.
Not that timestamps are trustable in signatures themselves if the key used to sign is trusted, but is there any way to say "this key is valid but not if sigs using it were created after X time"? Clearly we can get bogged down in this but something worth thinking about.
I have absolutely no idea if that is even possible with gpg...
This patch adds a --populate option that reads keyrings from (by default) /usr/share/pacman/keyrings. A keyring is named foo.gpg, with optional foo-revoked file providing a list of revoked key ids. These files are required to be signed (detached) by a key trusted by pacman-key, in practise probably by the key that signed the package providing these practice files. The --populate flag either updates the pacman keyring using all keyrings in the directory or individual keyrings can be specified.
Signed-off-by: Allan McRae<allan@archlinux.org> --- doc/pacman-key.8.txt | 8 ++- scripts/Makefile.am | 1 + scripts/pacman-key.sh.in | 149 +++++++++++++++++++++++----------------------- 3 files changed, 82 insertions(+), 76 deletions(-)
diff --git a/doc/pacman-key.8.txt b/doc/pacman-key.8.txt index c8ce026..98ee6f6 100644 --- a/doc/pacman-key.8.txt +++ b/doc/pacman-key.8.txt @@ -80,8 +80,12 @@ Options *-r, \--receive*<keyserver> <keyid(s)>:: Fetch the specified keyids from the specified key server URL.
-*\--reload*:: - Reloads the keys from the keyring package. +*\--populate* [<keyring(s)>]:: + Reload the default keys from the (given) keyrings in +{pkgdatadir}/keyrings+. (optionally provided) ?
+ Each keyring is provided in a file foo.gpg that contains the keys for the foo filenames in `foo.gpg` backticks usually is the convention. + keyring. Optionally the file foo-revoked contains a list of revoked key ids Optionally,<comma> `foo-revoked.gpg` (forgot the extension here too). Or did I misunderstand and this is a text file with IDs rather than an actual keyring file with binary data? This might require some thought, it looks like I did misunderstand. "IDs" is the capitalization used in the gpg manpage.
Yes - the foo-revoked file is a list of key IDs rather than a keyring. I just went with extending what was already implemented previously so I guess this is how it is done in apt-key. When you say "requires more thought" do you mean in the description or implementation?
+ for that keyring. These files are required to be signed (detached) by a + trusted PGP key.
*-u, \--updatedb*:: Equivalent to \--check-trustdb in GnuPG. diff --git a/scripts/Makefile.am b/scripts/Makefile.am index adb259a..30b6ad8 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -46,6 +46,7 @@ edit = sed \ -e 's|@localedir[@]|$(localedir)|g' \ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' \ + -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' \ -e 's|@prefix[@]|$(prefix)|g' \ -e '1s|!/bin/bash|!$(BASH_SHELL)|g' \ -e 's|@PACKAGE_VERSION[@]|$(REAL_PACKAGE_VERSION)|g' \ diff --git a/scripts/pacman-key.sh.in b/scripts/pacman-key.sh.in index 74ecfcf..c14d9bb 100644 --- a/scripts/pacman-key.sh.in +++ b/scripts/pacman-key.sh.in @@ -37,8 +37,8 @@ IMPORT_TRUSTDB=0 INIT=0 LISTKEYS=0 LISTSIGS=0 +POPULATE=0 RECEIVE=0 -RELOAD=0 UPDATEDB=0 VERIFY=0
@@ -73,7 +73,8 @@ usage() { echo "$(gettext " --import-trustdb<dir(s)> Imports ownertrust values from trustdb.gpg in dir(s)")" echo "$(gettext " --init Ensure the keyring is properly initialized")" echo "$(gettext " --list-sigs [<keyid(s)>] List keys and their signatures")" - echo "$(gettext " --reload Reload the default keys")" + printf "$(gettext " --populate [<keyring(s)] Reload the default keys from the (given) keyrings\n\ + in '%s'")\n" "@pkgdatadir@/keyrings" }
version() { @@ -139,75 +140,87 @@ check_keyring() {
verify_keyring_input() { local ret=0; + local KEYRING_IMPORT_DIR='@pkgdatadir@/keyrings'
- # Verify signatures of related files, if they exist - if [[ -r "${ADDED_KEYS}" ]]; then - msg "$(gettext "Verifying official keys file signature...")" - if ! "${GPG_PACMAN[@]}" --verify "${ADDED_KEYS}.sig"&>/dev/null; then + # Verify signatures of keyring files and association revocation files if they exist + msg "$(gettext "Verifying keyring file signatures...")" + local keyring + for keyring in ${KEYRINGIDS[@]}; do + if ! "${GPG_PACMAN[@]}" --verify "${KEYRING_IMPORT_DIR}/${keyring}.gpg.sig"&>/dev/null; then So how do we do the initial bootstrap? Maybe we need a way (e.g. command line option) to override the verification?
The initial bootstrap is going to require a user to manually add a key(s) and give it some trust. That key(s) would then be used to sign the keyring (and the package it is distributed in) for the rest of the repo. I can not see any sane way around that step. <snip>
+ for keyring in ${KEYRINGIDS[@]}; do + if [[ -f "${KEYRING_IMPORT_DIR}/${keyring}-revoked" ]]; then + while read key; do + local key_values name + key_values="$("${GPG_PACMAN[@]}" --quiet --with-colons --list-key "${key}" 2>/dev/null | grep ^pub | cut -d: -f5,10 --output-delimiter=' ')" A comment describing this madness line would be nice.
Sure... note that is just an indent change so I will need to figure it out too!
+ if [[ -n $key_values ]]; then + # The first word is the key_id + key_id="${key_values%% *}" + # the rest if the name of the owner + name="${key_values#* }" + if [[ -n ${key_id} ]]; then + # Mark this key to be deleted + removed_ids[$key_id]="$name" + fi fi - fi - done< "${REMOVED_KEYS}" - fi + done< "${KEYRING_IMPORT_DIR}/${keyring}-revoked" + fi + done
# List of keys that must be kept installed, even if in the list of keys to be removed local HOLD_KEYS="$(get_from "$CONFIG" "HoldKeys")" This is something I hadn't noticed before. How exactly does this hold keys stuff work? man pacman-key, /hold shows me nothing.
Looks like this was never documented anywhere... My understanding is that you can provide a list of keys to never be removed from the keyring in using "HoldKey = " in pacman.conf. Thinking about this, I'm not sure that is really an option for pacman.conf, but it does seem like something that is a useful feature. Suggestions for how it should be adjusted? Allan
On Wed, Aug 17, 2011 at 9:49 PM, Allan McRae <allan@archlinux.org> wrote:
On 18/08/11 08:48, Dan McGee wrote:
Hi, guys. I would like to add some notes on what Allan replied.
Optionally,<comma> `foo-revoked.gpg` (forgot the extension here too). Or did I misunderstand and this is a text file with IDs rather than an actual keyring file with binary data? This might require some thought, it looks like I did misunderstand. "IDs" is the capitalization used in the gpg manpage.
Yes - the foo-revoked file is a list of key IDs rather than a keyring. I just went with extending what was already implemented previously so I guess this is how it is done in apt-key. When you say "requires more thought" do you mean in the description or implementation?
I don't remember exactly if it was in apt-get, but even if it isn't, it's not that useful to have a real keyring for the revoked keys. What is really needed is a way to identify a key to be removed. If we had a real keyring we would just add another command to the pipe to get the key IDs without any real gain. So, using the IDs from the start is all that we need.
# List of keys that must be kept installed, even if in the list of keys to be removed local HOLD_KEYS="$(get_from "$CONFIG" "HoldKeys")"
This is something I hadn't noticed before. How exactly does this hold keys stuff work? man pacman-key, /hold shows me nothing.
Looks like this was never documented anywhere... My understanding is that you can provide a list of keys to never be removed from the keyring in using "HoldKey = " in pacman.conf.
Thinking about this, I'm not sure that is really an option for pacman.conf, but it does seem like something that is a useful feature. Suggestions for how it should be adjusted?
I think I forgot to write the corresponding documentation when I sent the patch [1]. Sorry for that... To summarize, when I made the patch, I worked with 3 sets of keys: current, deprecated and removed. Current and deprecated would still be added by the reload operation (now renamed to populate). Only keys in the removed set would really be dropped from the keyring. In that context, I thought of adding Holdkey as a way to allow the end user to avoid having to re-add a needed key if it was marked to be removed. My initial use case was based on the my personal premise that a ex-developer's key would be removed by the time of his departure. In that situation, maybe his key would still be useful for other personal repositories he could keep maintaining elsewhere. But if a developer's key is kept even after he leaves, the premise is not true anymore and HoldKey would lose a potential use case. But maybe it is a matter of distribution policy, so it could still be useful to have such an option. [1] http://archlinux.2023198.n4.nabble.com/Appendix-to-signature-patchset-td2957... -- A: Because it obfuscates the reading. Q: Why is top posting so bad? ------------------------------------------- Denis A. Altoe Falqueto Linux user #524555 -------------------------------------------
participants (3)
-
Allan McRae
-
Dan McGee
-
Denis A. Altoé Falqueto