[pacman-dev] New patchset for package signing
So, I changed the code to use gpgme. Here are the patches for your evaluation. By the way, I'm not {angry,upset,crying} :) I really want to see package signing in pacman, but I know that this is a complex issue that will need lots of discussion. Below, follows a little explanation of the general idea of each patch. [PATCH 1/5] pacman-key: keyring management tool The script that helps with management for pacman keyring. It uses gpg, instead of gpg2 and is heavly inpired on apt-key, from debian. It is very straightforward. [PATCH 2/5] Signature verification functions Two functions: one for signatures in memory and another for signatures in files. Signatures of packages are stored in the repository and are copied to memory before verification. The signatures of database files are stored on files, hence the new function. [PATCH 3/5] Verify the signatures of databases and packages The calls for the signature functions. Verification of database updates and package instalations from the repositories. I didn't worry about local instalations, but it doesn't mean they are not there. If it were verified before (as Dan suggests), they are there. :) A point raised by Dan was that the reading of the signature from the repository was too complex. The reaasoning behind it is that signatures grow according to the size of the key used to sign. So, we can't be never sure if some buffer size is really enough. Maybe it is enough now, but in the future it may be not and we'll have a new bug in the bugtracker. My implementation is simple and robust, so it will work with any signature size. [PATCH 4/5] Parameter to select key to sign Just a new parameter to allow the packager to select the key he wants to use. if the key is not specified, his default key will be used. [PATCH 5/5] Document new options related to package signing Just documentation. No imporant comment. As always, comments and suggestions are welcome. -- Denis A. Altoé Falqueto
The script pacman-key will manage pacman's keyring. It imports, exports, fetches from keyservers, helps in the process of trusting and updates the trust database. Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com> --- scripts/.gitignore | 1 + scripts/Makefile.am | 3 + scripts/pacman-key.sh.in | 296 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 300 insertions(+), 0 deletions(-) create mode 100644 scripts/pacman-key.sh.in diff --git a/scripts/.gitignore b/scripts/.gitignore index eafc493..1c662de 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -4,3 +4,4 @@ rankmirrors repo-add repo-remove pkgdelta +pacman-key diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 31e8fb5..c81f703 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -7,6 +7,7 @@ bin_SCRIPTS = \ OURSCRIPTS = \ makepkg \ + pacman-key \ pacman-optimize \ pkgdelta \ rankmirrors \ @@ -14,6 +15,7 @@ OURSCRIPTS = \ EXTRA_DIST = \ makepkg.sh.in \ + pacman-key.sh.in \ pacman-optimize.sh.in \ pkgdelta.sh.in \ rankmirrors.sh.in \ @@ -60,6 +62,7 @@ $(OURSCRIPTS): Makefile @mv $@.tmp $@ makepkg: $(srcdir)/makepkg.sh.in +pacman-key: ${srcdir}/pacman-key.sh.in pacman-optimize: $(srcdir)/pacman-optimize.sh.in pkgdelta: $(srcdir)/pkgdelta.sh.in rankmirrors: $(srcdir)/rankmirrors.sh.in diff --git a/scripts/pacman-key.sh.in b/scripts/pacman-key.sh.in new file mode 100644 index 0000000..5cb4066 --- /dev/null +++ b/scripts/pacman-key.sh.in @@ -0,0 +1,296 @@ +#!/bin/bash -e +# +# pacman-key - manages pacman's keyring +# @configure_input@ +# +# Copyright (c) 2010 - Denis A. Altoé Falqueto <denisfalqueto@gmail.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# gettext initialization +export TEXTDOMAIN='pacman' +export TEXTDOMAINDIR='@localedir@' + +# Based on apt-key, from Debian +# Author: Denis A. Altoé Falqueto <denisfalqueto at gmail dot com> +PACMAN_KEY_VERSION="@PACKAGE_VERSION@" + +# According to apt-key, gpg doesn't like to be called without a secret keyring. +# We will not really need one, because pacman will not sign packages, just verify +# their integrities. +PACMAN_SHARE_DIR="/usr/share/pacman" + +# Default parameters for the command gpg. Some more will be added when needed +GPG="gpg" +GPG_NOKEYRING="${GPG} --ignore-time-conflict --no-options --no-default-keyring" +SIG_EXT=".sig" + +# Read-only keyring with keys to be added to the keyring +ADDED_KEYS="${PACMAN_SHARE_DIR}/addedkeys.gpg" + +# Read-only keyring with keys removed from the keyring. They need to be removed before +# the keys from the added keyring be really imported +REMOVED_KEYS="${PACMAN_SHARE_DIR}/removedkeys.gpg" + +usage() { + echo "pacman-key - Pacman's keyring management utility" + echo "Usage: $(basename $0) [options] command [arguments]" + echo + echo "Manage pacman's list of trusted keys" + echo + echo "Options must be placed before commands. The abailable options are:" + echo " --config - set an alternative configuraton file to use. " + echo " Default is @sysconfdir@/pacman.conf" + echo " --gpgdir - set an alternativ home directory for gnupg. " + echo " Default is @sysconfdir@/pacman.d/gnupg" + echo + echo "The available commands are:" + echo " pacman-key add <file> ... - add the key contained in <file> ('-' for stdin)" + echo " pacman-key del <keyid> ... - remove the key <keyid>" + echo " pacman-key export <keyid> ... - output the key <keyid>" + echo " pacman-key exportall - output all trusted keys" + echo " pacman-key receive <keyserver> <keyid> ... - fetch the keyids from the specified keyserver URL" + echo " pacman-key trust <keyid> ... - set the truslevel of the given key" + echo " pacman-key updatedb - update the trustdb of pacman" + echo " pacman-key reload - reloads the keys from the keyring package" + echo " pacman-key list - list keys" + echo " pacman-key finger <keyid> ... - list fingerprints" + echo " pacman-key adv <params> - pass advanced options to gpg" + echo " pacman-key help - displays this message" + echo " pacman-key version - displays the current version" +} + +prepare_homedir() { + if [[ ! -d ${PACMAN_KEYRING_DIR} ]] ; then + mkdir -p "${PACMAN_KEYRING_DIR}" + [[ ! -f "${PACMAN_KEYRING_DIR}/secring.gpg" ]] && touch "${PACMAN_KEYRING_DIR}/secring.gpg" + [[ ! -f "${PACMAN_KEYRING_DIR}/pubring.gpg" ]] && touch "${PACMAN_KEYRING_DIR}/pubring.gpg" + chmod 700 "${PACMAN_KEYRING_DIR}" + chmod 600 "${PACMAN_KEYRING_DIR}"/* + fi +} + +add_key() { + ${GPG_PACMAN} --quiet --batch --import "$1" +} + +remove_key() { + ${GPG_PACMAN} --quiet --batch --delete-key --yes "$1" +} + +update_trustdb() { + ${GPG_PACMAN} --batch --check-trustdb +} + +list_sigs() { + ${GPG_PACMAN} --batch --list-sigs +} + +list_fingerprints() { + ${GPG_PACMAN} --batch --fingerprint $* +} + +export_key() { + ${GPG_PACMAN} --armor --export "$1" +} + +export_all() { + ${GPG_PACMAN} --armor --export +} + +trust_key() { + # Verify if the key exists in pacman's keyring + if ${GPG_PACMAN} --list-key "$1" > /dev/null 2>&1 ; then + ${GPG_PACMAN} --edit-key "$1" + else + echo >&2 "The key identified by $1 doesn't exist" + exit 1 + fi +} + +reload_keyring() { + # Verify the signature of removed keys file + if [[ -f ${REMOVED_KEYS} ]] && ! ${GPG_PACMAN} --quiet --verify ${REMOVED_KEYS}${SIG_EXT} ; then + echo >&2 "The signature of file ${REMOVED_KEYS} is not valid." + exit 1 + fi + + # Verify the signature of the added keys file + if [[ -f ${ADDED_KEYS} ]] && ! ${GPG_PACMAN} --quiet --verify ${ADDED_KEYS}${SIG_EXT} ; then + echo >&2 "The signature of file ${ADDED_KEYS} is not valid." + exit 1 + fi + + # Remove the keys from REMOVED_KEYS keyring + [[ -r ${REMOVED_KEYS} ]] && cat "${REMOVED_KEYS}" | while read key ; do + ${GPG_PACMAN} --quiet --batch --yes --delete-keys ${key} + done + + # 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 + add_keys=$(${GPG_NOKEYRING} --keyring ${ADDED_KEYS} --with-colons --list-keys | grep ^pub | cut -d: -f5) + for add_key in $add_keys; do + ${GPG_NOKEYRING} --quiet --batch --keyring $ADDED_KEYS --export $add_key | ${GPG_PACMAN} --import + done + fi + + # Update trustdb, just to be sure + update_trustdb +} + +receive() { + keyserver="$1" + shift + ${GPG_PACMAN} --keyserver ${keyserver} $* +} + +# PROGRAM START + +if ! type gettext &>/dev/null; then + gettext() { + echo "$@" + } +fi + +if [[ "$command" != "version" && "$command" != "help" ]] && ! which "${GPG}" >/dev/null 2>&1; then + echo >&2 "Warning: gnupg does not seem to be installed." + echo >&2 "Warning: pacman-key requires gnupg for most operations." + echo >&2 + exit 1 +fi + +# Parse global options +CONFIG="@sysconfdir@/pacman.conf" +PACMAN_KEYRING_DIR="@sysconfdir@/pacman.d/gnupg" +while [[ $1 =~ ^- ]] ; do + case "$1" in + --config) shift; CONFIG="$1" ;; + --gpgdir) shift; PACMAN_KEYRING_DIR="$1" ;; + esac + shift +done + +if [[ ! -r "$CONFIG" ]] ; then + echo >&2 "It is not possible to read $CONFIG." + exit 1 +fi +# Read GPGDIR from $CONFIG +GPGDIR=$(grep "^ *GPGDir *=" "$CONFIG" | cut -d= -f2) +if [[ "$GPGDIR" ]] ; then + PACMAN_KEYRING_DIR="$GPGDIR" +fi +GPG_PACMAN="${GPG} --homedir ${PACMAN_KEYRING_DIR}" + +prepare_homedir + +# Parse and execute command +command="$1" +if [[ -z "$command" ]]; then + usage + exit 1 +fi +shift + +case "$command" in + add) + if (( $# == 0 )) ; then + echo >&2 "You need to specify at least one key identifier" + usage + exit 1 + fi + while (( $# > 0 )) ; do + add_key $1 + shift + done + ;; + del|rm|remove) + if (( $# == 0 )) ; then + echo >&2 "You need to specify at least one key identifier" + usage + exit 1 + fi + while (( $# > 0 )) ; do + remove_key $1 + shift + done + ;; + updatedb) + update_trustdb + ;; + reload) + reload_keyring + ;; + list) + list_sigs + ;; + finger*) + if (( $# == 0 )) ; then + echo >&2 "You need to specify at least one key identifier" + usage + exit 1 + fi + list_fingerprints $* + ;; + export) + if (( $# == 0 )) ; then + echo >&2 "You need to specify at least one key identifier" + usage + exit 1 + fi + while (( $# > 0 )) ; do + export_key $1 + shift + done + ;; + exportall) + export_all + ;; + receive) + if (( $# < 2 )) ; then + echo >&2 "You need to specify the keyserver and at least one key identifier" + usage + exit 1 + fi + receive $* + ;; + trust) + if (( $# == 0 )) ; then + echo >&2 "You need to specify at least one key identifier" + usage + exit 1 + fi + while (( $# > 0 )) ; do + trust_key $1 + shift + done + ;; + adv*) + echo "Executing: ${GPG_PACMAN} $*" + ${GPG_PACMAN} $* || ret=$? + exit $ret + ;; + --help) + usage + ;; + --version) + echo "pacman-key v${PACMAN_KEY_VERSION}" + echo " This program can be freely distributed under the GPL v2" + ;; + *) + usage + exit 1 + ;; +esac -- 1.7.1.1
On Mon, Jul 26, 2010 at 16:26, Denis A. Altoé Falqueto <denisfalqueto@gmail.com> wrote:
The script pacman-key will manage pacman's keyring. It imports, exports, fetches from keyservers, helps in the process of trusting and updates the trust database.
Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com> --- I'd have the script use getopt from the start rather than a manual loop over the args. You can see makepkg for an example.
Also, make sure the trust db and any other important files are added to $backup in the PKGBUILD.
On Mon, Jul 26, 2010 at 5:33 PM, Daenyth Blank <daenyth+arch@gmail.com> wrote:
On Mon, Jul 26, 2010 at 16:26, Denis A. Altoé Falqueto <denisfalqueto@gmail.com> wrote:
The script pacman-key will manage pacman's keyring. It imports, exports, fetches from keyservers, helps in the process of trusting and updates the trust database.
Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com> --- I'd have the script use getopt from the start rather than a manual loop over the args. You can see makepkg for an example.
Also, make sure the trust db and any other important files are added to $backup in the PKGBUILD.
Noted. I'll change it ASAP. -- A: Because it obfuscates the reading. Q: Why is top posting so bad? ------------------------------------------- Denis A. Altoe Falqueto -------------------------------------------
On 27/07/10 06:33, Daenyth Blank wrote:
On Mon, Jul 26, 2010 at 16:26, Denis A. Altoé Falqueto <denisfalqueto@gmail.com> wrote:
The script pacman-key will manage pacman's keyring. It imports, exports, fetches from keyservers, helps in the process of trusting and updates the trust database.
Signed-off-by: Denis A. Altoé Falqueto<denisfalqueto@gmail.com> --- I'd have the script use getopt from the start rather than a manual loop over the args. You can see makepkg for an example.
Just to be clear, makepkg does not use getopt due to portability issue. It uses its own bash implementation instead. Also, repo-add uses a case statement. Not saying which is the best way forward in this case, but something to think about. Allan
On Tue, Jul 27, 2010 at 08:12, Allan McRae <allan@archlinux.org> wrote:
Just to be clear, makepkg does not use getopt due to portability issue. It uses its own bash implementation instead. Also, repo-add uses a case statement.
Not saying which is the best way forward in this case, but something to think about.
Allan
Did it ever? Weird, I could have sworn I copied the getopt code from makepkg to use it for pkgfile...
On Tue, Jul 27, 2010 at 9:12 AM, Allan McRae <allan@archlinux.org> wrote:
Just to be clear, makepkg does not use getopt due to portability issue. It uses its own bash implementation instead. Also, repo-add uses a case statement.
Not saying which is the best way forward in this case, but something to think about.
The way that pacman-key expects its parameters is based on apt-key, but I don't know if it is the best way for Arch. Currently, it expects two optional global parameters (configuration file and gpg home dir). After that, it expects only one "command" (an operation over pacman's keyring) and its parameters per call. Is it better to change it to allow multiple commands (I mean, in the same script call, adding keys and removing or listing, for example)? It is not the way gpg works, for example. If the second way is better, getopt can be of help. But if the current style is preferred, I think getopt would not enforce the restriction of one operation per script call. Am I wrong? I can make it either way, just did it like that so we could start discussing. -- A: Because it obfuscates the reading. Q: Why is top posting so bad? ------------------------------------------- Denis A. Altoe Falqueto -------------------------------------------
On Mon, Jul 26, 2010 at 3:26 PM, Denis A. Altoé Falqueto <denisfalqueto@gmail.com> wrote:
The script pacman-key will manage pacman's keyring. It imports, exports, fetches from keyservers, helps in the process of trusting and updates the trust database.
Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com> --- scripts/.gitignore | 1 + scripts/Makefile.am | 3 + scripts/pacman-key.sh.in | 296 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 300 insertions(+), 0 deletions(-) create mode 100644 scripts/pacman-key.sh.in
diff --git a/scripts/.gitignore b/scripts/.gitignore index eafc493..1c662de 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -4,3 +4,4 @@ rankmirrors repo-add repo-remove pkgdelta +pacman-key diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 31e8fb5..c81f703 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -7,6 +7,7 @@ bin_SCRIPTS = \
OURSCRIPTS = \ makepkg \ + pacman-key \ pacman-optimize \ pkgdelta \ rankmirrors \ @@ -14,6 +15,7 @@ OURSCRIPTS = \
EXTRA_DIST = \ makepkg.sh.in \ + pacman-key.sh.in \ pacman-optimize.sh.in \ pkgdelta.sh.in \ rankmirrors.sh.in \ @@ -60,6 +62,7 @@ $(OURSCRIPTS): Makefile @mv $@.tmp $@
makepkg: $(srcdir)/makepkg.sh.in +pacman-key: ${srcdir}/pacman-key.sh.in pacman-optimize: $(srcdir)/pacman-optimize.sh.in pkgdelta: $(srcdir)/pkgdelta.sh.in rankmirrors: $(srcdir)/rankmirrors.sh.in diff --git a/scripts/pacman-key.sh.in b/scripts/pacman-key.sh.in new file mode 100644 index 0000000..5cb4066 --- /dev/null +++ b/scripts/pacman-key.sh.in @@ -0,0 +1,296 @@ +#!/bin/bash -e +# +# pacman-key - manages pacman's keyring +# @configure_input@ +# +# Copyright (c) 2010 - Denis A. Altoé Falqueto <denisfalqueto@gmail.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# gettext initialization +export TEXTDOMAIN='pacman' +export TEXTDOMAINDIR='@localedir@' You don't end up actually using gettext anywhere. A script like this actually should.
+ +# Based on apt-key, from Debian +# Author: Denis A. Altoé Falqueto <denisfalqueto at gmail dot com> These quickly become outdated- I'm fine with the copyright, but if you want this you also need to fix every single problem that comes up with the script. git does a perfectly fine job at telling you who the original author is.
+PACMAN_KEY_VERSION="@PACKAGE_VERSION@" + +# According to apt-key, gpg doesn't like to be called without a secret keyring. +# We will not really need one, because pacman will not sign packages, just verify +# their integrities. +PACMAN_SHARE_DIR="/usr/share/pacman" This needs to be autoconf-ed. See doc/Makefile.am and pkgdatadir and use that like @sysconfdir@ in makepkg.sh.in.
+ +# Default parameters for the command gpg. Some more will be added when needed +GPG="gpg" +GPG_NOKEYRING="${GPG} --ignore-time-conflict --no-options --no-default-keyring" +SIG_EXT=".sig" Would we ever change this? I'd rather have .sig hardcoded just like we killed other things that will never really change because they are convention outside of just this package.
+ +# Read-only keyring with keys to be added to the keyring +ADDED_KEYS="${PACMAN_SHARE_DIR}/addedkeys.gpg" + +# Read-only keyring with keys removed from the keyring. They need to be removed before +# the keys from the added keyring be really imported +REMOVED_KEYS="${PACMAN_SHARE_DIR}/removedkeys.gpg" + +usage() { + echo "pacman-key - Pacman's keyring management utility" + echo "Usage: $(basename $0) [options] command [arguments]" + echo + echo "Manage pacman's list of trusted keys" + echo + echo "Options must be placed before commands. The abailable options are:" + echo " --config - set an alternative configuraton file to use. " + echo " Default is @sysconfdir@/pacman.conf" + echo " --gpgdir - set an alternativ home directory for gnupg. " + echo " Default is @sysconfdir@/pacman.d/gnupg" + echo + echo "The available commands are:" + echo " pacman-key add <file> ... - add the key contained in <file> ('-' for stdin)" + echo " pacman-key del <keyid> ... - remove the key <keyid>" + echo " pacman-key export <keyid> ... - output the key <keyid>" + echo " pacman-key exportall - output all trusted keys" + echo " pacman-key receive <keyserver> <keyid> ... - fetch the keyids from the specified keyserver URL" + echo " pacman-key trust <keyid> ... - set the truslevel of the given key" + echo " pacman-key updatedb - update the trustdb of pacman" + echo " pacman-key reload - reloads the keys from the keyring package" + echo " pacman-key list - list keys" + echo " pacman-key finger <keyid> ... - list fingerprints" + echo " pacman-key adv <params> - pass advanced options to gpg" + echo " pacman-key help - displays this message" + echo " pacman-key version - displays the current version" +} I forgot we did this in makepkg; I don't really like it there. This stuff seems like it belongs in a manpage with a shorter usage() such as the one in repo-add. Anyone else have thoughts?
Either way, for a essential tool, we will need a manpage.
+ +prepare_homedir() { + if [[ ! -d ${PACMAN_KEYRING_DIR} ]] ; then + mkdir -p "${PACMAN_KEYRING_DIR}" + [[ ! -f "${PACMAN_KEYRING_DIR}/secring.gpg" ]] && touch "${PACMAN_KEYRING_DIR}/secring.gpg" + [[ ! -f "${PACMAN_KEYRING_DIR}/pubring.gpg" ]] && touch "${PACMAN_KEYRING_DIR}/pubring.gpg" If the dir didn't exist, why this -f check? + chmod 700 "${PACMAN_KEYRING_DIR}" + chmod 600 "${PACMAN_KEYRING_DIR}"/* Why the glob? Can't we just explicitly list the two files previously touched? Minor nitpick but seems more sensible to me. + fi +} + +add_key() { + ${GPG_PACMAN} --quiet --batch --import "$1" +} + +remove_key() { + ${GPG_PACMAN} --quiet --batch --delete-key --yes "$1" +} + +update_trustdb() { + ${GPG_PACMAN} --batch --check-trustdb +} + +list_sigs() { + ${GPG_PACMAN} --batch --list-sigs +} + +list_fingerprints() { + ${GPG_PACMAN} --batch --fingerprint $* +} + +export_key() { + ${GPG_PACMAN} --armor --export "$1" +} + +export_all() { + ${GPG_PACMAN} --armor --export +} If these are all one-liners, it would seem to make more sense to inline them if they are only called from one place. Ignore me if they are not.
+ +trust_key() { + # Verify if the key exists in pacman's keyring + if ${GPG_PACMAN} --list-key "$1" > /dev/null 2>&1 ; then + ${GPG_PACMAN} --edit-key "$1" + else + echo >&2 "The key identified by $1 doesn't exist" I'd rather find a way to reuse msg/msg2/warning/error from repo-add then do this. We might want to move those into a sourced common utilities file.
+ exit 1 + fi +} + +reload_keyring() { + # Verify the signature of removed keys file + if [[ -f ${REMOVED_KEYS} ]] && ! ${GPG_PACMAN} --quiet --verify ${REMOVED_KEYS}${SIG_EXT} ; then You quoted paths elsewhere but then didn't quote here or many of the following places.
+ echo >&2 "The signature of file ${REMOVED_KEYS} is not valid." + exit 1 + fi + + # Verify the signature of the added keys file + if [[ -f ${ADDED_KEYS} ]] && ! ${GPG_PACMAN} --quiet --verify ${ADDED_KEYS}${SIG_EXT} ; then + echo >&2 "The signature of file ${ADDED_KEYS} is not valid." + exit 1 + fi + + # Remove the keys from REMOVED_KEYS keyring + [[ -r ${REMOVED_KEYS} ]] && cat "${REMOVED_KEYS}" | while read key ; do + ${GPG_PACMAN} --quiet --batch --yes --delete-keys ${key} + done + + # 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 + add_keys=$(${GPG_NOKEYRING} --keyring ${ADDED_KEYS} --with-colons --list-keys | grep ^pub | cut -d: -f5) + for add_key in $add_keys; do + ${GPG_NOKEYRING} --quiet --batch --keyring $ADDED_KEYS --export $add_key | ${GPG_PACMAN} --import + done + fi + + # Update trustdb, just to be sure + update_trustdb +} + +receive() { + keyserver="$1" + shift + ${GPG_PACMAN} --keyserver ${keyserver} $* +} + +# PROGRAM START + +if ! type gettext &>/dev/null; then + gettext() { + echo "$@" + } +fi + +if [[ "$command" != "version" && "$command" != "help" ]] && ! which "${GPG}" >/dev/null 2>&1; then + echo >&2 "Warning: gnupg does not seem to be installed." + echo >&2 "Warning: pacman-key requires gnupg for most operations." + echo >&2 + exit 1 +fi + +# Parse global options +CONFIG="@sysconfdir@/pacman.conf" +PACMAN_KEYRING_DIR="@sysconfdir@/pacman.d/gnupg" +while [[ $1 =~ ^- ]] ; do + case "$1" in + --config) shift; CONFIG="$1" ;; + --gpgdir) shift; PACMAN_KEYRING_DIR="$1" ;; + esac + shift +done + +if [[ ! -r "$CONFIG" ]] ; then + echo >&2 "It is not possible to read $CONFIG." + exit 1 +fi +# Read GPGDIR from $CONFIG +GPGDIR=$(grep "^ *GPGDir *=" "$CONFIG" | cut -d= -f2) +if [[ "$GPGDIR" ]] ; then + PACMAN_KEYRING_DIR="$GPGDIR" +fi Hmm, this is kinda ugly but not sure what to suggest just yet. +GPG_PACMAN="${GPG} --homedir ${PACMAN_KEYRING_DIR}" + +prepare_homedir + +# Parse and execute command +command="$1" +if [[ -z "$command" ]]; then + usage + exit 1 +fi +shift + +case "$command" in + add) + if (( $# == 0 )) ; then + echo >&2 "You need to specify at least one key identifier" + usage + exit 1 + fi + while (( $# > 0 )) ; do + add_key $1 + shift + done + ;; + del|rm|remove) + if (( $# == 0 )) ; then + echo >&2 "You need to specify at least one key identifier" + usage + exit 1 + fi + while (( $# > 0 )) ; do + remove_key $1 + shift + done + ;; + updatedb) + update_trustdb + ;; + reload) + reload_keyring + ;; + list) + list_sigs + ;; + finger*) + if (( $# == 0 )) ; then + echo >&2 "You need to specify at least one key identifier" + usage + exit 1 + fi + list_fingerprints $* + ;; + export) + if (( $# == 0 )) ; then + echo >&2 "You need to specify at least one key identifier" + usage + exit 1 + fi + while (( $# > 0 )) ; do + export_key $1 + shift + done + ;; + exportall) + export_all + ;; + receive) + if (( $# < 2 )) ; then + echo >&2 "You need to specify the keyserver and at least one key identifier" + usage + exit 1 + fi + receive $* + ;; + trust) + if (( $# == 0 )) ; then + echo >&2 "You need to specify at least one key identifier" + usage + exit 1 + fi + while (( $# > 0 )) ; do + trust_key $1 + shift + done + ;; + adv*) + echo "Executing: ${GPG_PACMAN} $*" + ${GPG_PACMAN} $* || ret=$? + exit $ret + ;; + --help) + usage + ;; + --version) + echo "pacman-key v${PACMAN_KEY_VERSION}" + echo " This program can be freely distributed under the GPL v2" + ;; Please use a similar version() function to makepkg/repo-add.
You also have help/version above, and --help/--version here.
+ *) + usage + exit 1 + ;; +esac -- 1.7.1.1
General thoughts: 1. We don't use the non-dashed option type commands in any other utility. I'm not necessarily against it but thought it was worth pointing out. 2. A lot of times our style in shell scripts has tended towards omitting braces, e.g. ${foobar} would be just $foobar if possible. 3. This was a static code analysis, I need to actually run this and give it a shot. 4. Don't get discouraged at my review here, I'm just trying to make sure when we introduce something new we don't have to spend the next few iterations making it a solid core piece of code. I'd rather get it right the first time. -Dan -Dan
On Monday 26 July 2010 18:46:27 Dan McGee wrote:
+# gettext initialization +export TEXTDOMAIN='pacman' +export TEXTDOMAINDIR='@localedir@'
You don't end up actually using gettext anywhere. A script like this actually should.
Yes, totally forgot. Done.
+ +# Based on apt-key, from Debian +# Author: Denis A. Altoé Falqueto <denisfalqueto at gmail dot com>
These quickly become outdated- I'm fine with the copyright, but if you want this you also need to fix every single problem that comes up with the script. git does a perfectly fine job at telling you who the original author is.
I'm fine with handing off the copyright to Pacman Developers too, if you don't mind. i didn't do it at first because i'm not a pacman developer, so didn't to put me in a position I'm not. I'm not applying for it, this is just an occasional help :)
+PACMAN_SHARE_DIR="/usr/share/pacman"
This needs to be autoconf-ed. See doc/Makefile.am and pkgdatadir and use that like @sysconfdir@ in makepkg.sh.in.
Done. Corrected to @pkgdatadir@, which is in Makefile.am, in source root.
+ +# Default parameters for the command gpg. Some more will be added when needed +GPG="gpg" +GPG_NOKEYRING="${GPG} --ignore-time-conflict --no-options --no-default-keyring" +SIG_EXT=".sig"
Would we ever change this? I'd rather have .sig hardcoded just like we killed other things that will never really change because they are convention outside of just this package.
Yes, I was going to discuss in that other reply. In fact, the variables are there not because they can change. I just don't feel comfortable to use strings literals that mean something to the script. If I mistype them in one place and nobody sees it, it is a fool bug that slips off. That way, bash will make sure that the right values are on the right places.
+usage() { + echo "pacman-key - Pacman's keyring management utility" + echo "Usage: $(basename $0) [options] command [arguments]" + echo + echo "Manage pacman's list of trusted keys" + echo + echo "Options must be placed before commands. The abailable options are:" + echo " --config - set an alternative configuraton file to use. " + echo " Default is @sysconfdir@/pacman.conf" + echo " --gpgdir - set an alternativ home directory for gnupg. " + echo " Default is @sysconfdir@/pacman.d/gnupg" + echo + echo "The available commands are:" + echo " pacman-key add <file> ... - add the key contained in <file> ('-' for stdin)" + echo " pacman-key del <keyid> ... - remove the key <keyid>" + echo " pacman-key export <keyid> ... - output the key <keyid>" + echo " pacman-key exportall - output all trusted keys" + echo " pacman-key receive <keyserver> <keyid> ... - fetch the keyids from the specified keyserver URL" + echo " pacman-key trust <keyid> ... - set the truslevel of the given key" + echo " pacman-key updatedb - update the trustdb of pacman" + echo " pacman-key reload - reloads the keys from the keyring package" + echo " pacman-key list - list keys" + echo " pacman-key finger <keyid> ... - list fingerprints" + echo " pacman-key adv <params> - pass advanced options to gpg" + echo " pacman-key help - displays this message" + echo " pacman-key version - displays the current version" +}
I forgot we did this in makepkg; I don't really like it there. This stuff seems like it belongs in a manpage with a shorter usage() such as the one in repo-add. Anyone else have thoughts?
Either way, for a essential tool, we will need a manpage.
The man page will be done for sure, I just wanted to share my work soon :) About the usage message, I don't think it is too long. It just gives a quick explanation. But I can shorten it, stripping the details of each command.
+ +prepare_homedir() { + if [[ ! -d ${PACMAN_KEYRING_DIR} ]] ; then + mkdir -p "${PACMAN_KEYRING_DIR}" + [[ ! -f "${PACMAN_KEYRING_DIR}/secring.gpg" ]] && touch "${PACMAN_KEYRING_DIR}/secring.gpg" + [[ ! -f "${PACMAN_KEYRING_DIR}/pubring.gpg" ]] && touch "${PACMAN_KEYRING_DIR}/pubring.gpg"
If the dir didn't exist, why this -f check?
Yes, you're right. Corrected.
+ chmod 700 "${PACMAN_KEYRING_DIR}" + chmod 600 "${PACMAN_KEYRING_DIR}"/*
Why the glob? Can't we just explicitly list the two files previously touched? Minor nitpick but seems more sensible to me.
Done too.
+ fi +} + +add_key() { + ${GPG_PACMAN} --quiet --batch --import "$1" +} + +remove_key() { + ${GPG_PACMAN} --quiet --batch --delete-key --yes "$1" +} + +update_trustdb() { + ${GPG_PACMAN} --batch --check-trustdb +} + +list_sigs() { + ${GPG_PACMAN} --batch --list-sigs +} + +list_fingerprints() { + ${GPG_PACMAN} --batch --fingerprint $* +} + +export_key() { + ${GPG_PACMAN} --armor --export "$1" +} + +export_all() { + ${GPG_PACMAN} --armor --export +}
If these are all one-liners, it would seem to make more sense to inline them if they are only called from one place. Ignore me if they are not.
I did that way because of homogeneity. I moved them for the calling places. just update_trustdb can be called from two places, so it stayed.
+ +trust_key() { + # Verify if the key exists in pacman's keyring + if ${GPG_PACMAN} --list-key "$1" > /dev/null 2>&1 ; then + ${GPG_PACMAN} --edit-key "$1" + else + echo >&2 "The key identified by $1 doesn't exist"
I'd rather find a way to reuse msg/msg2/warning/error from repo-add then do this. We might want to move those into a sourced common utilities file.
Right. When the decision about the file is made, I'll change it.
+ if [[ -f ${REMOVED_KEYS} ]] && ! ${GPG_PACMAN} --quiet --verify ${REMOVED_KEYS}${SIG_EXT} ; then
You quoted paths elsewhere but then didn't quote here or many of the following places.
Yes, I corrected the quotes in the other places. If there's still some missing, please let me know.
+if [[ ! -r "$CONFIG" ]] ; then + echo >&2 "It is not possible to read $CONFIG." + exit 1 +fi +# Read GPGDIR from $CONFIG +GPGDIR=$(grep "^ *GPGDir *=" "$CONFIG" | cut -d= -f2) +if [[ "$GPGDIR" ]] ; then + PACMAN_KEYRING_DIR="$GPGDIR" +fi
Hmm, this is kinda ugly but not sure what to suggest just yet.
Agreed :) But pacman.conf is a kind of ini file, so the correct parsing would give too much trouble. Looking now, it doesn't work if there is a tab before GPGDir... I'll fix it tomorow.
+ --help) + usage + ;; + --version) + echo "pacman-key v${PACMAN_KEY_VERSION}" + echo " This program can be freely distributed under the GPL v2" + ;;
Please use a similar version() function to makepkg/repo-add.
You also have help/version above, and --help/--version here.
Done.
+ *) + usage + exit 1 + ;; +esac -- 1.7.1.1
General thoughts: 1. We don't use the non-dashed option type commands in any other utility. I'm not necessarily against it but thought it was worth pointing out.
Agreed. I already changed it to short and long options. I just got influenced by apt-key, because they always use non-dashed commands, as in apt-get install, etc.
2. A lot of times our style in shell scripts has tended towards omitting braces, e.g. ${foobar} would be just $foobar if possible.
I think I've seen some patches in pacman-dev changing variables that way (${} insttead of just $). But I can change it too, no big deal.
3. This was a static code analysis, I need to actually run this and give it a shot. 4. Don't get discouraged at my review here, I'm just trying to make sure when we introduce something new we don't have to spend the next few iterations making it a solid core piece of code. I'd rather get it right the first time.
Don't worry about that. We both want the same goal. I'll try to send the new patch in reply to this email, as soon as I learn how to do it with git send-email, but i'll leave that for tomorrow. 3 am here and I'm barely able to write this message :) -- A: Because it obfuscates the reading. Q: Why is top posting so bad? ------------------------------------------- Denis A. Altoe Falqueto -------------------------------------------
The script pacman-key will manage pacman's keyring. It imports, exports, fetches from keyservers, helps in the process of trusting and updates the trust database. Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com> --- scripts/.gitignore | 1 + scripts/Makefile.am | 3 + scripts/pacman-key.sh.in | 280 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 284 insertions(+), 0 deletions(-) create mode 100644 scripts/pacman-key.sh.in diff --git a/scripts/.gitignore b/scripts/.gitignore index eafc493..1c662de 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -4,3 +4,4 @@ rankmirrors repo-add repo-remove pkgdelta +pacman-key diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 31e8fb5..c81f703 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -7,6 +7,7 @@ bin_SCRIPTS = \ OURSCRIPTS = \ makepkg \ + pacman-key \ pacman-optimize \ pkgdelta \ rankmirrors \ @@ -14,6 +15,7 @@ OURSCRIPTS = \ EXTRA_DIST = \ makepkg.sh.in \ + pacman-key.sh.in \ pacman-optimize.sh.in \ pkgdelta.sh.in \ rankmirrors.sh.in \ @@ -60,6 +62,7 @@ $(OURSCRIPTS): Makefile @mv $@.tmp $@ makepkg: $(srcdir)/makepkg.sh.in +pacman-key: ${srcdir}/pacman-key.sh.in pacman-optimize: $(srcdir)/pacman-optimize.sh.in pkgdelta: $(srcdir)/pkgdelta.sh.in rankmirrors: $(srcdir)/rankmirrors.sh.in diff --git a/scripts/pacman-key.sh.in b/scripts/pacman-key.sh.in new file mode 100644 index 0000000..05745a9 --- /dev/null +++ b/scripts/pacman-key.sh.in @@ -0,0 +1,280 @@ +#!/bin/bash -e +# +# pacman-key - manages pacman's keyring +# @configure_input@ +# +# Copyright (c) 2010 - Pacman Development Team <pacman-dev@archlinux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# gettext initialization +export TEXTDOMAIN='pacman' +export TEXTDOMAINDIR='@localedir@' + +# Based on apt-key, from Debian +myver="@PACKAGE_VERSION@" + +# According to apt-key, gpg doesn't like to be called without a secret keyring. +# We will not really need one, because pacman will not sign packages, just verify +# their integrities. +PACMAN_SHARE_DIR="@pkgdatadir@" + +# Default parameters for the command gpg. Some more will be added when needed +GPG="gpg" +GPG_NOKEYRING="${GPG} --ignore-time-conflict --no-options --no-default-keyring" +SIG_EXT=".sig" + +# Read-only keyring with keys to be added to the keyring +ADDED_KEYS="${PACMAN_SHARE_DIR}/addedkeys.gpg" + +# Read-only keyring with keys removed from the keyring. They need to be removed before +# the keys from the added keyring be really imported +REMOVED_KEYS="${PACMAN_SHARE_DIR}/removedkeys.gpg" + +usage() { + printf "pacman-key (pacman) %s\n" ${myver} + echo + printf $(gettext "Usage: %s [options] command [arguments]") $(basename $0) + echo + echo $(gettext "Manage pacman's list of trusted keys") + echo + echo $(gettext "Options must be placed before commands. The abailable options are:") + echo $(gettext " --config - set an alternative configuraton file to use. ") + echo $(gettext " Default is @sysconfdir@/pacman.conf") + echo $(gettext " --gpgdir - set an alternativ home directory for gnupg. ") + echo $(gettext " Default is @sysconfdir@/pacman.d/gnupg") + echo + echo $(gettext "The available commands are:") + echo $(gettext " pacman-key -a | --add <file> ... - add the key contained ") + echo $(gettext " in <file> ('-' for stdin)") + echo $(gettext " pacman-key -d | --del <keyid> ... - remove the key <keyid>") + echo $(gettext " pacman-key -e | --export <keyid> ... - output the key <keyid>") + echo $(gettext " pacman-key -x | --exportall - output all trusted keys") + echo $(gettext " pacman-key -r | --receive <keyserver> <keyid> ... - fetch the keyids from") + echo $(gettext " the specified keyserver URL") + echo $(gettext " pacman-key -t | --trust <keyid> ... - set the truslevel of the given key") + echo $(gettext " pacman-key -u | --updatedb - update the trustdb of pacman") + echo $(gettext " pacman-key --reload - reloads the keys from the keyring package") + echo $(gettext " pacman-key -l | --list - list keys") + echo $(gettext " pacman-key -f | --finger <keyid> ... - list fingerprints") + echo $(gettext " pacman-key --adv <params> - pass advanced options to gpg") + echo $(gettext " pacman-key -h | --help - displays this message") + echo $(gettext " pacman-key -v | --version - displays the current version") +} + +version() { + printf "pacman-key (pacman) %s\n" "${myver}" + printf "$(gettext "\ +Copyright (c) 2010 Pacman Development Team <pacman-dev@archlinux.org>.\n\ +This is free software; see the source for copying conditions.\n\ +There is NO WARRANTY, to the extent permitted by law.\n")" +} + +prepare_homedir() { + if [[ ! -d "${PACMAN_KEYRING_DIR}" ]] ; then + mkdir -p "${PACMAN_KEYRING_DIR}" + touch "${PACMAN_KEYRING_DIR}/secring.gpg" + touch "${PACMAN_KEYRING_DIR}/pubring.gpg" + chmod 700 "${PACMAN_KEYRING_DIR}" + chmod 600 "${PACMAN_KEYRING_DIR}"/{sec,pub}ring.gpg + fi +} + +update_trustdb() { + ${GPG_PACMAN} --batch --check-trustdb +} + +reload_keyring() { + # Verify the signature of removed keys file + if [[ -f "${REMOVED_KEYS}" ]] && ! ${GPG_PACMAN} --quiet --verify "${REMOVED_KEYS}${SIG_EXT}" ; then + echo >&2 $(gettext "The signature of file ${REMOVED_KEYS} is not valid.") + exit 1 + fi + + # Verify the signature of the added keys file + if [[ -f "${ADDED_KEYS}" ]] && ! ${GPG_PACMAN} --quiet --verify "${ADDED_KEYS}${SIG_EXT}" ; then + echo >&2 $(gettext "The signature of file ${ADDED_KEYS} is not valid.") + exit 1 + fi + + # Remove the keys from REMOVED_KEYS keyring + [[ -r "${REMOVED_KEYS}" ]] && cat "${REMOVED_KEYS}" | while read key ; do + ${GPG_PACMAN} --quiet --batch --yes --delete-keys "${key}" + done + + # 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 + add_keys=$(${GPG_NOKEYRING} --keyring "${ADDED_KEYS}" --with-colons --list-keys | grep ^pub | cut -d: -f5) + for key in ${add_keys}; do + ${GPG_NOKEYRING} --quiet --batch --keyring "${ADDED_KEYS}" --export "${key}" | ${GPG_PACMAN} --import + done + fi + + # Update trustdb, just to be sure + update_trustdb +} + +receive() { + keyserver="$1" + shift + ${GPG_PACMAN} --keyserver "${keyserver}" $* +} + +# PROGRAM START + +if ! type gettext &>/dev/null; then + gettext() { + echo "$@" + } +fi + +if [[ "$command" != "version" && "$command" != "help" ]] && ! which "${GPG}" >/dev/null 2>&1; then + echo >&2 $(gettext "Warning: gnupg does not seem to be installed.") + echo >&2 $(gettext "Warning: pacman-key requires gnupg for most operations.") + exit 1 +fi + +# Parse global options +CONFIG="@sysconfdir@/pacman.conf" +PACMAN_KEYRING_DIR="@sysconfdir@/pacman.d/gnupg" +while [[ $1 =~ ^--(config|gpgdir)$ ]] ; do + case "$1" in + --config) shift; CONFIG="$1" ;; + --gpgdir) shift; PACMAN_KEYRING_DIR="$1" ;; + esac + shift +done + +if [[ ! -r "${CONFIG}" ]] ; then + echo >&2 $(gettext "It is not possible to read ${CONFIG}.") + exit 1 +fi + +# Read GPGDIR from $CONFIG. +# The pattern is: any spaces or tabs, GPGDir, any spaces or tabs, equal sign +# and the rest of the line. The string is splitted after the first occurrence of = +GPGDIR=$(cat ${CONFIG} | awk '/^(\t| )*GPGDir(\t| )*=.*/ { print substr($0,index($0, "=")+1) }') +if [[ "${GPGDIR}" ]] ; then + PACMAN_KEYRING_DIR="${GPGDIR}" +fi +GPG_PACMAN="${GPG} --homedir ${PACMAN_KEYRING_DIR}" + +prepare_homedir + +# Parse and execute command +command="$1" +if [[ -z "${command}" ]]; then + usage + exit 1 +fi +shift + +case "${command}" in + -a|--add) + if (( $# == 0 )) ; then + echo >&2 $(gettext "You need to specify at least one key identifier") + usage + exit 1 + fi + while (( $# > 0 )) ; do + ${GPG_PACMAN} --quiet --batch --import "$1" + shift + done + ;; + -d|--del) + if (( $# == 0 )) ; then + echo >&2 $(gettext "You need to specify at least one key identifier") + usage + exit 1 + fi + while (( $# > 0 )) ; do + ${GPG_PACMAN} --quiet --batch --delete-key --yes "$1" + shift + done + ;; + -u|--updatedb) + update_trustdb + ;; + --reload) + reload_keyring + ;; + -l|-list) + ${GPG_PACMAN} --batch --list-sigs + ;; + -f|--finger) + if (( $# == 0 )) ; then + echo >&2 $(gettext "You need to specify at least one key identifier") + usage + exit 1 + fi + ${GPG_PACMAN} --batch --fingerprint $* + ;; + -e|--export) + if (( $# == 0 )) ; then + echo >&2 $(gettext "You need to specify at least one key identifier") + usage + exit 1 + fi + while (( $# > 0 )) ; do + ${GPG_PACMAN} --armor --export "$1" + shift + done + ;; + -x|--exportall) + ${GPG_PACMAN} --armor --export + ;; + -r|--receive) + if (( $# < 2 )) ; then + echo >&2 $(gettext "You need to specify the keyserver and at least one key identifier") + usage + exit 1 + fi + receive $* + ;; + -t|--trust) + if (( $# == 0 )) ; then + echo >&2 $(gettext "You need to specify at least one key identifier") + usage + exit 1 + fi + while (( $# > 0 )) ; do + # Verify if the key exists in pacman's keyring + if ${GPG_PACMAN} --list-key "$1" > /dev/null 2>&1 ; then + ${GPG_PACMAN} --edit-key "$1" + else + echo >&2 $(gettext "The key identified by $1 doesn't exist") + exit 1 + fi + shift + done + ;; + --adv) + echo $(gettext "Executing: ${GPG_PACMAN} $*") + ${GPG_PACMAN} $* || ret=$? + exit $ret + ;; + --help) + usage + ;; + --version) + version + exit 0 + ;; + *) + usage + exit 1 + ;; +esac -- 1.7.2
On 28/07/10 13:50, Denis A. Altoé Falqueto wrote:
The script pacman-key will manage pacman's keyring. It imports, exports, fetches from keyservers, helps in the process of trusting and updates the trust database.
Signed-off-by: Denis A. Altoé Falqueto<denisfalqueto@gmail.com>
Hi Denis, I think it would be good for us to focus on getting this onto the gpg branch and then move onto the other patches. I do not think this requires massive changes to be ready.
--- scripts/.gitignore | 1 + scripts/Makefile.am | 3 + scripts/pacman-key.sh.in | 280 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 284 insertions(+), 0 deletions(-) create mode 100644 scripts/pacman-key.sh.in
diff --git a/scripts/.gitignore b/scripts/.gitignore index eafc493..1c662de 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -4,3 +4,4 @@ rankmirrors repo-add repo-remove pkgdelta +pacman-key diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 31e8fb5..c81f703 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -7,6 +7,7 @@ bin_SCRIPTS = \
OURSCRIPTS = \ makepkg \ + pacman-key \ pacman-optimize \ pkgdelta \ rankmirrors \ @@ -14,6 +15,7 @@ OURSCRIPTS = \
EXTRA_DIST = \ makepkg.sh.in \ + pacman-key.sh.in \ pacman-optimize.sh.in \ pkgdelta.sh.in \ rankmirrors.sh.in \ @@ -60,6 +62,7 @@ $(OURSCRIPTS): Makefile @mv $@.tmp $@
makepkg: $(srcdir)/makepkg.sh.in +pacman-key: ${srcdir}/pacman-key.sh.in pacman-optimize: $(srcdir)/pacman-optimize.sh.in pkgdelta: $(srcdir)/pkgdelta.sh.in rankmirrors: $(srcdir)/rankmirrors.sh.in diff --git a/scripts/pacman-key.sh.in b/scripts/pacman-key.sh.in new file mode 100644 index 0000000..05745a9 --- /dev/null +++ b/scripts/pacman-key.sh.in @@ -0,0 +1,280 @@ +#!/bin/bash -e +# +# pacman-key - manages pacman's keyring +# @configure_input@ +# +# Copyright (c) 2010 - Pacman Development Team<pacman-dev@archlinux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see<http://www.gnu.org/licenses/>. +# + +# gettext initialization +export TEXTDOMAIN='pacman' +export TEXTDOMAINDIR='@localedir@' + +# Based on apt-key, from Debian
Move that comment up, either right below the copyright of below the @condigure_input@? It seems out of place there.
+myver="@PACKAGE_VERSION@" + +# According to apt-key, gpg doesn't like to be called without a secret keyring.
Has that been verified? If so, remove the "According to apt-key". But as far as I can tell, gpg just dislikes no keyrings being added. See the man page and the --no-default-keyring option.
+# We will not really need one, because pacman will not sign packages, just verify +# their integrities.
And why exactly is that whole comment here? I can not figure out how it is relevant to the following line.
+PACMAN_SHARE_DIR="@pkgdatadir@" + +# Default parameters for the command gpg. Some more will be added when needed +GPG="gpg"
Do we need this? The makepkg changes assume "gpg" is the program name for signing. If it is going to be set by a variable, I would prefer to autoconf it and have it default to gpg with a configure switch to change it. But I really do not think it is necessary at all.
+GPG_NOKEYRING="${GPG} --ignore-time-conflict --no-options --no-default-keyring"
This is used only twice in the same function. I would prefer it defined there - see comment in relevant place below.
+SIG_EXT=".sig"
Get rid of this variable. Its value can be considered standard.
+ +# Read-only keyring with keys to be added to the keyring +ADDED_KEYS="${PACMAN_SHARE_DIR}/addedkeys.gpg"
That seems a weird place to put these files to me. I think that @sysconfdir@/pacman.d/gnupg/ would be a better place. Maybe that directory could be configured...
+# Read-only keyring with keys removed from the keyring. They need to be removed before +# the keys from the added keyring be really imported
..from the added keyring are imported? Anyway, that amount of detail is unneeded. Maybe just have "Read-only keyrings provided by distribution" as the comment for both these files.
+REMOVED_KEYS="${PACMAN_SHARE_DIR}/removedkeys.gpg" + +usage() { + printf "pacman-key (pacman) %s\n" ${myver} + echo + printf $(gettext "Usage: %s [options] command [arguments]") $(basename $0) + echo + echo $(gettext "Manage pacman's list of trusted keys") + echo + echo $(gettext "Options must be placed before commands. The abailable options are:")
typo - available
+ echo $(gettext " --config - set an alternative configuraton file to use. ")
typo - configuration
+ echo $(gettext " Default is @sysconfdir@/pacman.conf") + echo $(gettext " --gpgdir - set an alternativ home directory for gnupg. ")
typo - alternative
+ echo $(gettext " Default is @sysconfdir@/pacman.d/gnupg")
Default is set in @sysconfdir@/pacman.conf.
+ echo + echo $(gettext "The available commands are:") + echo $(gettext " pacman-key -a | --add<file> ... - add the key contained ") + echo $(gettext " in<file> ('-' for stdin)")
I could not find reference the behaviour on passing "-" to --import in the gpg man page. Worth double checking.
+ echo $(gettext " pacman-key -d | --del<keyid> ... - remove the key<keyid>") + echo $(gettext " pacman-key -e | --export<keyid> ... - output the key<keyid>") + echo $(gettext " pacman-key -x | --exportall - output all trusted keys") + echo $(gettext " pacman-key -r | --receive<keyserver> <keyid> ... - fetch the keyids from") + echo $(gettext " the specified keyserver URL") + echo $(gettext " pacman-key -t | --trust<keyid> ... - set the truslevel of the given key")
typo - trust level
+ echo $(gettext " pacman-key -u | --updatedb - update the trustdb of pacman") + echo $(gettext " pacman-key --reload - reloads the keys from the keyring package") + echo $(gettext " pacman-key -l | --list - list keys") + echo $(gettext " pacman-key -f | --finger<keyid> ... - list fingerprints")
"fingerprints" is not clear. i.e. is it just the fingerprint of <keyid> of does omitting that print them all? Also, see below.
+ echo $(gettext " pacman-key --adv<params> - pass advanced options to gpg")
This is a bit vague. It sounds like I would be passing additional options to gpg than what the program already is passing rather than the manually telling gpg what to do to the pacman keyring. Maybe "manually manage the pacman keyring"
+ echo $(gettext " pacman-key -h | --help - displays this message") + echo $(gettext " pacman-key -v | --version - displays the current version") +} + +version() { + printf "pacman-key (pacman) %s\n" "${myver}" + printf "$(gettext "\ +Copyright (c) 2010 Pacman Development Team<pacman-dev@archlinux.org>.\n\ +This is free software; see the source for copying conditions.\n\ +There is NO WARRANTY, to the extent permitted by law.\n")" +} + +prepare_homedir() { + if [[ ! -d "${PACMAN_KEYRING_DIR}" ]] ; then + mkdir -p "${PACMAN_KEYRING_DIR}" + touch "${PACMAN_KEYRING_DIR}/secring.gpg" + touch "${PACMAN_KEYRING_DIR}/pubring.gpg" + chmod 700 "${PACMAN_KEYRING_DIR}" + chmod 600 "${PACMAN_KEYRING_DIR}"/{sec,pub}ring.gpg
We should just use: install -dm700 ${PACMAN_KEYRING_DIR} to create the directory with the right permissions. And should those files actually be part of the pacman package and so guaranteed to be present.
+ fi +} + +update_trustdb() { + ${GPG_PACMAN} --batch --check-trustdb
Should we be using --update-trustdb?
+} + +reload_keyring() { + # Verify the signature of removed keys file + if [[ -f "${REMOVED_KEYS}" ]]&& ! ${GPG_PACMAN} --quiet --verify "${REMOVED_KEYS}${SIG_EXT}" ; then + echo>&2 $(gettext "The signature of file ${REMOVED_KEYS} is not valid.")
$(gettext "The signature of file %s is not valid.") "${REMOVED_KEYS}"
+ exit 1 + fi + + # Verify the signature of the added keys file + if [[ -f "${ADDED_KEYS}" ]]&& ! ${GPG_PACMAN} --quiet --verify "${ADDED_KEYS}${SIG_EXT}" ; then + echo>&2 $(gettext "The signature of file ${ADDED_KEYS} is not valid.")
$(gettext "The signature of file %s is not valid.") "${ADDED_KEYS}"
+ exit 1 + fi + + # Remove the keys from REMOVED_KEYS keyring + [[ -r "${REMOVED_KEYS}" ]]&& cat "${REMOVED_KEYS}" | while read key ; do + ${GPG_PACMAN} --quiet --batch --yes --delete-keys "${key}"
--delete-key
+ done + + # 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
I'd define something like: local GPG_COMMAND="${GPG} --ignore-time-conflict --no-options --no-default-keyring --keyring "${ADDED_KEYS}" here rather than the GPG_NOKEYRING variable at the start as it is used no-where else.
+ add_keys=$(${GPG_NOKEYRING} --keyring "${ADDED_KEYS}" --with-colons --list-keys | grep ^pub | cut -d: -f5)
local add_keys
+ for key in ${add_keys}; do + ${GPG_NOKEYRING} --quiet --batch --keyring "${ADDED_KEYS}" --export "${key}" | ${GPG_PACMAN} --import + done + fi + + # Update trustdb, just to be sure + update_trustdb +} + +receive() { + keyserver="$1"
local keyserver
+ shift + ${GPG_PACMAN} --keyserver "${keyserver}" $*
--recv-keys is needed?
+} + +# PROGRAM START + +if ! type gettext&>/dev/null; then + gettext() { + echo "$@" + } +fi + +if [[ "$command" != "version"&& "$command" != "help" ]]&& ! which "${GPG}">/dev/null 2>&1; then
"! type -p gpg" instead of which. Also, quotes around $command are unnecessary and they should be compared to "--version" and "--help". Also... $command is not defined yet! You would be wanting $1.
+ echo>&2 $(gettext "Warning: gnupg does not seem to be installed.") + echo>&2 $(gettext "Warning: pacman-key requires gnupg for most operations.")
It is not a warning if you are exiting... so change that to "Error:". Also, the second output line does not need "Warning/Error:" at the start.
+ exit 1 +fi + +# Parse global options +CONFIG="@sysconfdir@/pacman.conf" +PACMAN_KEYRING_DIR="@sysconfdir@/pacman.d/gnupg" +while [[ $1 =~ ^--(config|gpgdir)$ ]] ; do + case "$1" in + --config) shift; CONFIG="$1" ;; + --gpgdir) shift; PACMAN_KEYRING_DIR="$1" ;; + esac + shift +done + +if [[ ! -r "${CONFIG}" ]] ; then + echo>&2 $(gettext "It is not possible to read ${CONFIG}.")
$(gettext "It is not possible to read %s.") "${CONFIG}"
+ exit 1 +fi + +# Read GPGDIR from $CONFIG. +# The pattern is: any spaces or tabs, GPGDir, any spaces or tabs, equal sign +# and the rest of the line. The string is splitted after the first occurrence of = +GPGDIR=$(cat ${CONFIG} | awk '/^(\t| )*GPGDir(\t| )*=.*/ { print substr($0,index($0, "=")+1) }')
cat a file to awk it... yuck. Also, we do not use awk anywhere else, so grep followed by a bash substitution to remove "*=" from the start may be better.
+if [[ "${GPGDIR}" ]] ; then
-z $GPGDIR?
+ PACMAN_KEYRING_DIR="${GPGDIR}" +fi +GPG_PACMAN="${GPG} --homedir ${PACMAN_KEYRING_DIR}" + +prepare_homedir + +# Parse and execute command +command="$1" +if [[ -z "${command}" ]]; then + usage + exit 1 +fi +shift + +case "${command}" in + -a|--add) + if (( $# == 0 )) ; then + echo>&2 $(gettext "You need to specify at least one key identifier")
Error: You need... (and throughout.)
+ usage + exit 1 + fi + while (( $#> 0 )) ; do + ${GPG_PACMAN} --quiet --batch --import "$1" + shift + done + ;; + -d|--del) + if (( $# == 0 )) ; then + echo>&2 $(gettext "You need to specify at least one key identifier") + usage + exit 1 + fi + while (( $#> 0 )) ; do + ${GPG_PACMAN} --quiet --batch --delete-key --yes "$1" + shift + done + ;; + -u|--updatedb) + update_trustdb
I am not sure this single line function needs to not be inline (even though it is called once elsewhere).
+ ;; + --reload) + reload_keyring + ;; + -l|-list) + ${GPG_PACMAN} --batch --list-sigs + ;; + -f|--finger) + if (( $# == 0 )) ; then + echo>&2 $(gettext "You need to specify at least one key identifier") + usage + exit 1 + fi
It appears --fingerprint works fine without an argument...
+ ${GPG_PACMAN} --batch --fingerprint $* + ;; + -e|--export) + if (( $# == 0 )) ; then + echo>&2 $(gettext "You need to specify at least one key identifier") + usage + exit 1 + fi + while (( $#> 0 )) ; do + ${GPG_PACMAN} --armor --export "$1" + shift + done + ;; + -x|--exportall) + ${GPG_PACMAN} --armor --export + ;;
This can be combined with --export. No need for a separate option. if (( $# == 0 )); ... --export else while (( $# > 0 )); do...
+ -r|--receive) + if (( $#< 2 )) ; then + echo>&2 $(gettext "You need to specify the keyserver and at least one key identifier") + usage + exit 1 + fi + receive $*
The receive function is only 3 lines so could be inlined.
+ ;; + -t|--trust) + if (( $# == 0 )) ; then + echo>&2 $(gettext "You need to specify at least one key identifier") + usage + exit 1 + fi + while (( $#> 0 )) ; do + # Verify if the key exists in pacman's keyring + if ${GPG_PACMAN} --list-key "$1"> /dev/null 2>&1 ; then
--list-keys
+ ${GPG_PACMAN} --edit-key "$1" + else + echo>&2 $(gettext "The key identified by $1 doesn't exist")
$(gettext "The key identified by %s does not exist") $1
+ exit 1 + fi + shift + done + ;; + --adv) + echo $(gettext "Executing: ${GPG_PACMAN} $*")
$(gettext "Executing: %s") "${GPG_PACMAN} $*" (I think... check formatting works)
+ ${GPG_PACMAN} $* || ret=$? + exit $ret + ;; + --help) + usage + ;; + --version) + version + exit 0 + ;; + *) + usage + exit 1 + ;; +esac Add blank line at the end.
General thoughts: 1) I see that I have made _A LOT_ of comments here. But as I said at the top, I feel this is basically ready to go so please do not get discouraged. Blame Dan. He has turned me into a pedantic bastard! :) 2) I think there should also be a check that this is being run as root when not using --version or --help. See what makepkg does to check for root. 3) We will really need a man-page for this. The usage() output is a bit brief to stand alone. 4) I still have not actually run this... this is entirely static code review. Allan
On Wed, Aug 4, 2010 at 10:17 AM, Allan McRae <allan@archlinux.org> wrote: > On 28/07/10 13:50, Denis A. Altoé Falqueto wrote: >> >> The script pacman-key will manage pacman's keyring. It imports, exports, >> fetches from keyservers, helps in the process of trusting and updates >> the trust database. >> >> Signed-off-by: Denis A. Altoé Falqueto<denisfalqueto@gmail.com> > > Hi Denis, > > I think it would be good for us to focus on getting this onto the gpg branch > and then move onto the other patches. I do not think this requires massive > changes to be ready. Hi. Sorry for the delay again. Time is so short lately... It took way longer than I would like. But here I am again. I'll answer only the things that I would like to discuss further. The other points were implemented as advised by you. >> +prepare_homedir() { >> + if [[ ! -d "${PACMAN_KEYRING_DIR}" ]] ; then >> + mkdir -p "${PACMAN_KEYRING_DIR}" >> + touch "${PACMAN_KEYRING_DIR}/secring.gpg" >> + touch "${PACMAN_KEYRING_DIR}/pubring.gpg" >> + chmod 700 "${PACMAN_KEYRING_DIR}" >> + chmod 600 "${PACMAN_KEYRING_DIR}"/{sec,pub}ring.gpg > > We should just use: > install -dm700 ${PACMAN_KEYRING_DIR} > to create the directory with the right permissions. > > And should those files actually be part of the pacman package and so > guaranteed to be present. Yes, I believe the best place is pacman package. I removed the function, so we need to make sure the PKGBUILD for pacman creates the proper files and directory. >> +update_trustdb() { >> + ${GPG_PACMAN} --batch --check-trustdb > > Should we be using --update-trustdb? >From gpg's man page: "The processing is identical to that of --update-trustdb but it skips keys with a not yet defined "ownertrust"." I'm not sure what is the best option. update-trustdb may ask the user what is the ownertrust value for the keys gpg can't compute with the web of trust. check-trustdb ignores those keys. According to the man page, none of them are necessary. The values are computed correctly when importing keys. Should we drop that option? It would be still accessible through --adv command, if someone really needs it. >> +# Read GPGDIR from $CONFIG. >> +# The pattern is: any spaces or tabs, GPGDir, any spaces or tabs, equal >> sign >> +# and the rest of the line. The string is splitted after the first >> occurrence of = >> +GPGDIR=$(cat ${CONFIG} | awk '/^(\t| )*GPGDir(\t| )*=.*/ { print >> substr($0,index($0, "=")+1) }') > > cat a file to awk it... yuck. Also, we do not use awk anywhere else, so > grep followed by a bash substitution to remove "*=" from the start may be > better. I see. I googled about grep and tabs and some links showed that grep couldn't accept correctly the \t character, but I found now that the class [:blank:] is what we really need: spaces and tabs. And grep accepts it. I'll change the script. >> +if [[ "${GPGDIR}" ]] ; then > > -z $GPGDIR? > >> + PACMAN_KEYRING_DIR="${GPGDIR}" >> +fi >> +GPG_PACMAN="${GPG} --homedir ${PACMAN_KEYRING_DIR}" >> + >> +prepare_homedir >> + >> +# Parse and execute command >> +command="$1" >> +if [[ -z "${command}" ]]; then >> + usage > (...) > General thoughts: > 1) I see that I have made _A LOT_ of comments here. But as I said at the > top, I feel this is basically ready to go so please do not get discouraged. > Blame Dan. He has turned me into a pedantic bastard! :) > 2) I think there should also be a check that this is being run as root when > not using --version or --help. See what makepkg does to check for root. > 3) We will really need a man-page for this. The usage() output is a bit > brief to stand alone. The man page will be done, no doubt about that. But not right now, as don't have all the time I would like to have. If any of the collaborators want to help, this is the time (hint for all those who mailed me telling they would be glad to help :D) > 4) I still have not actually run this... this is entirely static code > review. I've tried all options and they are working alright. I'll send the new version of script in the next email, as a reply to this message. Thanks for the patience. -- A: Because it obfuscates the reading. Q: Why is top posting so bad? ------------------------------------------- Denis A. Altoe Falqueto -------------------------------------------
The script pacman-key will manage pacman's keyring. It imports, exports, fetches from keyservers, helps in the process of trusting and updates the trust database. Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com> --- scripts/.gitignore | 1 + scripts/Makefile.am | 3 + scripts/pacman-key.sh.in | 275 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+), 0 deletions(-) create mode 100644 scripts/pacman-key.sh.in diff --git a/scripts/.gitignore b/scripts/.gitignore index eafc493..1c662de 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -4,3 +4,4 @@ rankmirrors repo-add repo-remove pkgdelta +pacman-key diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 31e8fb5..c81f703 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -7,6 +7,7 @@ bin_SCRIPTS = \ OURSCRIPTS = \ makepkg \ + pacman-key \ pacman-optimize \ pkgdelta \ rankmirrors \ @@ -14,6 +15,7 @@ OURSCRIPTS = \ EXTRA_DIST = \ makepkg.sh.in \ + pacman-key.sh.in \ pacman-optimize.sh.in \ pkgdelta.sh.in \ rankmirrors.sh.in \ @@ -60,6 +62,7 @@ $(OURSCRIPTS): Makefile @mv $@.tmp $@ makepkg: $(srcdir)/makepkg.sh.in +pacman-key: ${srcdir}/pacman-key.sh.in pacman-optimize: $(srcdir)/pacman-optimize.sh.in pkgdelta: $(srcdir)/pkgdelta.sh.in rankmirrors: $(srcdir)/rankmirrors.sh.in diff --git a/scripts/pacman-key.sh.in b/scripts/pacman-key.sh.in new file mode 100644 index 0000000..7b5fb29 --- /dev/null +++ b/scripts/pacman-key.sh.in @@ -0,0 +1,275 @@ +#!/bin/bash -e +# +# pacman-key - manages pacman's keyring +# Based on apt-key, from Debian +# @configure_input@ +# +# Copyright (c) 2010 - Pacman Development Team <pacman-dev@archlinux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# gettext initialization +export TEXTDOMAIN='pacman' +export TEXTDOMAINDIR='@localedir@' + +myver="@PACKAGE_VERSION@" + +msg() { + local mesg=$1; shift + printf "==> ${mesg}\n" "$@" >&1 +} + +msg2() { + (( QUIET )) && return + local mesg=$1; shift + printf " -> ${mesg}\n" "$@" >&1 +} + +warning() { + local mesg=$1; shift + printf "==> $(gettext "WARNING:") ${mesg}\n" "$@" >&2 +} + +error() { + local mesg=$1; shift + printf "==> $(gettext "ERROR:") ${mesg}\n" "$@" >&2 +} + +usage() { + printf "pacman-key (pacman) %s\n" ${myver} + echo + printf "$(gettext "Usage: %s [options] command [arguments]")\n" $(basename $0) + echo + echo "$(gettext "Manage pacman's list of trusted keys")" + echo + echo "$(gettext "Options must be placed before commands. The available options are:")" + echo "$(gettext " --config - set an alternative configuration file to use. ")" + printf "$(gettext " Default is %s")\n" "@sysconfdir@/pacman.conf" + echo "$(gettext " --gpgdir - set an alternative home directory for gnupg. ")" + printf "$(gettext " Default is set in %s")\n" "@sysconfdir@/pacman.conf" + echo + echo "$(gettext "The available commands are:")" + echo "$(gettext " pacman-key -a | --add [<file>] ... - add the key contained ")" + echo "$(gettext " in <file> (empty for stdin)")" + echo "$(gettext " pacman-key -d | --del <keyid> ... - remove the key <keyid>")" + echo "$(gettext " pacman-key -e | --export <keyid> ... - output the key <keyid>")" + echo "$(gettext " pacman-key -r | --receive <keyserver> <keyid> ... - fetch the keyids from")" + echo "$(gettext " the specified keyserver URL")" + echo "$(gettext " pacman-key -t | --trust <keyid> ... - set the trust level of the given key")" + echo "$(gettext " pacman-key -u | --updatedb - update the trustdb of pacman")" + echo "$(gettext " pacman-key --reload - reloads the keys from the keyring package")" + echo "$(gettext " pacman-key -l | --list - list keys")" + echo "$(gettext " pacman-key -f | --finger [<keyid]> ... - list fingerprint for specified keyids (or for all, if none is specified)")" + echo "$(gettext " pacman-key --adv <params> - use pacman's keyring as target for advanced commands")" + echo "$(gettext " pacman-key -h | --help - displays this message")" + echo "$(gettext " pacman-key -v | --version - displays the current version")" +} + +version() { + printf "pacman-key (pacman) %s\n" "${myver}" + printf "$(gettext "\ +Copyright (c) 2010 Pacman Development Team <pacman-dev@archlinux.org>.\n\ +This is free software; see the source for copying conditions.\n\ +There is NO WARRANTY, to the extent permitted by law.\n")" +} + +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}" + + # Read-only keyring with keys to be added to the keyring + local ADDED_KEYS="${PACMAN_SHARE_DIR}/addedkeys.gpg" + + # Read-only list of keys removed from the keyring. + local REMOVED_KEYS="${PACMAN_SHARE_DIR}/removedkeys" + + # 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 "Verifying official keys file signature...")" + if ! ${GPG_PACMAN} --quiet --verify "${ADDED_KEYS}.sig" 1>/dev/null; then + error "$(gettext "The signature of file %s is not valid.")" "${ADDED_KEYS}" + exit 1 + fi + + msg "$(gettext "Appending official keys...")" + local add_keys=$(${GPG_NOKEYRING} --keyring "${ADDED_KEYS}" --with-colons --list-keys | grep ^pub | cut -d: -f5) + for key in ${add_keys}; do + msg "$(gettext " key id: %s")" "$key" + ${GPG_NOKEYRING} --keyring "${ADDED_KEYS}" --export "${key}" | ${GPG_PACMAN} --import + done + fi + + # Remove the keys from REMOVED_KEYS keyring + if [[ -r "${REMOVED_KEYS}" ]]; then + msg "$(gettext "Verifying deleted keys file signature...")" + if ! ${GPG_PACMAN} --quiet --verify "${REMOVED_KEYS}.sig"; then + error "$(gettext "The signature of file %s is not valid.")" "${REMOVED_KEYS}" + exit 1 + fi + + msg "$(gettext "Removing deleted keys from keyring...")" + cat "${REMOVED_KEYS}" | while read key; do + msg "$(gettext " key id: %s")" "$key" + ${GPG_PACMAN} --quiet --batch --yes --delete-key "${key}" + done + fi + + # Update trustdb, just to be sure + msg "$(gettext "Updating trust database...")" + ${GPG_PACMAN} --batch --check-trustdb +} + +# PROGRAM START +if ! type gettext &>/dev/null; then + gettext() { + echo "$@" + } +fi + +if [[ $1 != "--version" && $1 != "-v" && $1 != "--help" && $1 != "-h" ]]; then + if type -p gpg >/dev/null 2>&1 = 1; then + error "$(gettext "gnupg does not seem to be installed.")" + msg2 "$(gettext "pacman-key requires gnupg for most operations.")" + exit 1 + elif (( EUID != 0 )); then + error "$(gettext "pacman-key needs to be run as root.")" + exit 1 + fi +fi + +# Parse global options +CONFIG="@sysconfdir@/pacman.conf" +PACMAN_KEYRING_DIR="@sysconfdir@/pacman.d/gnupg" +while [[ $1 =~ ^--(config|gpgdir)$ ]]; do + case "$1" in + --config) shift; CONFIG="$1" ;; + --gpgdir) shift; PACMAN_KEYRING_DIR="$1" ;; + esac + shift +done + +if [[ ! -r "${CONFIG}" ]]; then + error "$(gettext "It is not possible to read %s")" "${CONFIG}" + exit 1 +fi + +# Read GPGDIR from $CONFIG. +# The pattern is: any spaces or tabs, GPGDir, any spaces or tabs, equal sign +# and the rest of the line. The string is splitted after the first occurrence of = +if [[ GPGDIR=$(grep -e '^[[:blank:]]*GPGDir[[:blank:]]*=.*' "$CONFIG") == 0 ]]; then + GPGDIR=${GPGDIR#*=} + PACMAN_KEYRING_DIR="${GPGDIR}" +fi +GPG_PACMAN="gpg --homedir ${PACMAN_KEYRING_DIR}" + +# Parse and execute command +command="$1" +if [[ -z "${command}" ]]; then + usage + exit 1 +fi +shift + +case "${command}" in + -a|--add) + if (( $# == 0 )); then + error "$(gettext "You need to specify at least one key identifier")" + usage + exit 1 + fi + while (( $# > 0 )); do + ${GPG_PACMAN} --quiet --batch --import "$1" + shift + done + ;; + -d|--del) + if (( $# == 0 )); then + error "$(gettext "You need to specify at least one key identifier")" + usage + exit 1 + fi + while (( $# > 0 )); do + ${GPG_PACMAN} --quiet --batch --delete-key --yes "$1" + shift + done + ;; + -u|--updatedb) + ${GPG_PACMAN} --batch --check-trustdb + ;; + --reload) + reload_keyring + ;; + -l|--list) + ${GPG_PACMAN} --batch --list-sigs + ;; + -f|--finger) + ${GPG_PACMAN} --batch --fingerprint $* + ;; + -e|--export) + if (( $# == 0 )); then + ${GPG_PACMAN} --armor --export + else + while (( $# > 0 )); do + ${GPG_PACMAN} --armor --export "$1" + shift + done + fi + ;; + -r|--receive) + if (( $# < 2 )); then + error "$(gettext "You need to specify the keyserver and at least one key identifier")" + usage + exit 1 + fi + keyserver="$1" + shift + ${GPG_PACMAN} --keyserver "${keyserver}" --recv-keys $* + ;; + -t|--trust) + if (( $# == 0 )); then + error "$(gettext "You need to specify at least one key identifier")" + usage + exit 1 + fi + while (( $# > 0 )); do + # Verify if the key exists in pacman's keyring + if ${GPG_PACMAN} --list-keys "$1" > /dev/null 2>&1; then + ${GPG_PACMAN} --edit-key "$1" + else + error "$(gettext "The key identified by %s doesn't exist")" "$1" + exit 1 + fi + shift + done + ;; + --adv) + msg "$(gettext "Executing: %s ")$*" "${GPG_PACMAN}" + ${GPG_PACMAN} $* || ret=$? + exit $ret + ;; + --help) + usage + ;; + --version) + version + exit 0 + ;; + *) + usage + exit 1 + ;; +esac + -- 1.7.2.3
On 16 September 2010 05:40, Denis A. Altoé Falqueto <denisfalqueto@gmail.com> wrote:
The script pacman-key will manage pacman's keyring. It imports, exports, fetches from keyservers, helps in the process of trusting and updates the trust database.
Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com> --- scripts/.gitignore | 1 + scripts/Makefile.am | 3 + scripts/pacman-key.sh.in | 275 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+), 0 deletions(-) create mode 100644 scripts/pacman-key.sh.in
diff --git a/scripts/.gitignore b/scripts/.gitignore index eafc493..1c662de 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -4,3 +4,4 @@ rankmirrors repo-add repo-remove pkgdelta +pacman-key diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 31e8fb5..c81f703 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -7,6 +7,7 @@ bin_SCRIPTS = \
OURSCRIPTS = \ makepkg \ + pacman-key \ pacman-optimize \ pkgdelta \ rankmirrors \ @@ -14,6 +15,7 @@ OURSCRIPTS = \
EXTRA_DIST = \ makepkg.sh.in \ + pacman-key.sh.in \ pacman-optimize.sh.in \ pkgdelta.sh.in \ rankmirrors.sh.in \ @@ -60,6 +62,7 @@ $(OURSCRIPTS): Makefile @mv $@.tmp $@
makepkg: $(srcdir)/makepkg.sh.in +pacman-key: ${srcdir}/pacman-key.sh.in pacman-optimize: $(srcdir)/pacman-optimize.sh.in pkgdelta: $(srcdir)/pkgdelta.sh.in rankmirrors: $(srcdir)/rankmirrors.sh.in diff --git a/scripts/pacman-key.sh.in b/scripts/pacman-key.sh.in new file mode 100644 index 0000000..7b5fb29 --- /dev/null +++ b/scripts/pacman-key.sh.in @@ -0,0 +1,275 @@ +#!/bin/bash -e +# +# pacman-key - manages pacman's keyring +# Based on apt-key, from Debian +# @configure_input@ +# +# Copyright (c) 2010 - Pacman Development Team <pacman-dev@archlinux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# gettext initialization +export TEXTDOMAIN='pacman' +export TEXTDOMAINDIR='@localedir@' + +myver="@PACKAGE_VERSION@" + +msg() { + local mesg=$1; shift + printf "==> ${mesg}\n" "$@" >&1 +} + +msg2() { + (( QUIET )) && return + local mesg=$1; shift + printf " -> ${mesg}\n" "$@" >&1 +} + +warning() { + local mesg=$1; shift + printf "==> $(gettext "WARNING:") ${mesg}\n" "$@" >&2 +} + +error() { + local mesg=$1; shift + printf "==> $(gettext "ERROR:") ${mesg}\n" "$@" >&2 +} + +usage() { + printf "pacman-key (pacman) %s\n" ${myver} + echo + printf "$(gettext "Usage: %s [options] command [arguments]")\n" $(basename $0) + echo + echo "$(gettext "Manage pacman's list of trusted keys")" + echo + echo "$(gettext "Options must be placed before commands. The available options are:")" + echo "$(gettext " --config - set an alternative configuration file to use. ")" + printf "$(gettext " Default is %s")\n" "@sysconfdir@/pacman.conf" + echo "$(gettext " --gpgdir - set an alternative home directory for gnupg. ")" + printf "$(gettext " Default is set in %s")\n" "@sysconfdir@/pacman.conf" + echo + echo "$(gettext "The available commands are:")" + echo "$(gettext " pacman-key -a | --add [<file>] ... - add the key contained ")" + echo "$(gettext " in <file> (empty for stdin)")" + echo "$(gettext " pacman-key -d | --del <keyid> ... - remove the key <keyid>")" + echo "$(gettext " pacman-key -e | --export <keyid> ... - output the key <keyid>")" + echo "$(gettext " pacman-key -r | --receive <keyserver> <keyid> ... - fetch the keyids from")" + echo "$(gettext " the specified keyserver URL")" + echo "$(gettext " pacman-key -t | --trust <keyid> ... - set the trust level of the given key")" + echo "$(gettext " pacman-key -u | --updatedb - update the trustdb of pacman")" + echo "$(gettext " pacman-key --reload - reloads the keys from the keyring package")" + echo "$(gettext " pacman-key -l | --list - list keys")" + echo "$(gettext " pacman-key -f | --finger [<keyid]> ... - list fingerprint for specified keyids (or for all, if none is specified)")" + echo "$(gettext " pacman-key --adv <params> - use pacman's keyring as target for advanced commands")" + echo "$(gettext " pacman-key -h | --help - displays this message")" + echo "$(gettext " pacman-key -v | --version - displays the current version")" +} + +version() { + printf "pacman-key (pacman) %s\n" "${myver}" + printf "$(gettext "\ +Copyright (c) 2010 Pacman Development Team <pacman-dev@archlinux.org>.\n\ +This is free software; see the source for copying conditions.\n\ +There is NO WARRANTY, to the extent permitted by law.\n")" +} + +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}" + + # Read-only keyring with keys to be added to the keyring + local ADDED_KEYS="${PACMAN_SHARE_DIR}/addedkeys.gpg" + + # Read-only list of keys removed from the keyring. + local REMOVED_KEYS="${PACMAN_SHARE_DIR}/removedkeys" + + # 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 "Verifying official keys file signature...")" + if ! ${GPG_PACMAN} --quiet --verify "${ADDED_KEYS}.sig" 1>/dev/null; then + error "$(gettext "The signature of file %s is not valid.")" "${ADDED_KEYS}" + exit 1 + fi + + msg "$(gettext "Appending official keys...")" + local add_keys=$(${GPG_NOKEYRING} --keyring "${ADDED_KEYS}" --with-colons --list-keys | grep ^pub | cut -d: -f5) + for key in ${add_keys}; do + msg "$(gettext " key id: %s")" "$key" + ${GPG_NOKEYRING} --keyring "${ADDED_KEYS}" --export "${key}" | ${GPG_PACMAN} --import + done + fi + + # Remove the keys from REMOVED_KEYS keyring + if [[ -r "${REMOVED_KEYS}" ]]; then + msg "$(gettext "Verifying deleted keys file signature...")" + if ! ${GPG_PACMAN} --quiet --verify "${REMOVED_KEYS}.sig"; then + error "$(gettext "The signature of file %s is not valid.")" "${REMOVED_KEYS}" + exit 1 + fi + + msg "$(gettext "Removing deleted keys from keyring...")" + cat "${REMOVED_KEYS}" | while read key; do + msg "$(gettext " key id: %s")" "$key" + ${GPG_PACMAN} --quiet --batch --yes --delete-key "${key}" + done + fi + + # Update trustdb, just to be sure + msg "$(gettext "Updating trust database...")" + ${GPG_PACMAN} --batch --check-trustdb +} + +# PROGRAM START +if ! type gettext &>/dev/null; then + gettext() { + echo "$@" + } +fi + +if [[ $1 != "--version" && $1 != "-v" && $1 != "--help" && $1 != "-h" ]]; then + if type -p gpg >/dev/null 2>&1 = 1; then + error "$(gettext "gnupg does not seem to be installed.")" + msg2 "$(gettext "pacman-key requires gnupg for most operations.")" + exit 1 + elif (( EUID != 0 )); then + error "$(gettext "pacman-key needs to be run as root.")" + exit 1 + fi +fi + +# Parse global options +CONFIG="@sysconfdir@/pacman.conf" +PACMAN_KEYRING_DIR="@sysconfdir@/pacman.d/gnupg" +while [[ $1 =~ ^--(config|gpgdir)$ ]]; do + case "$1" in + --config) shift; CONFIG="$1" ;; + --gpgdir) shift; PACMAN_KEYRING_DIR="$1" ;; + esac + shift +done + +if [[ ! -r "${CONFIG}" ]]; then + error "$(gettext "It is not possible to read %s")" "${CONFIG}" + exit 1 +fi + +# Read GPGDIR from $CONFIG. +# The pattern is: any spaces or tabs, GPGDir, any spaces or tabs, equal sign +# and the rest of the line. The string is splitted after the first occurrence of = +if [[ GPGDIR=$(grep -e '^[[:blank:]]*GPGDir[[:blank:]]*=.*' "$CONFIG") == 0 ]]; then + GPGDIR=${GPGDIR#*=} + PACMAN_KEYRING_DIR="${GPGDIR}" +fi +GPG_PACMAN="gpg --homedir ${PACMAN_KEYRING_DIR}" + +# Parse and execute command +command="$1" +if [[ -z "${command}" ]]; then + usage + exit 1 +fi +shift + +case "${command}" in + -a|--add) + if (( $# == 0 )); then + error "$(gettext "You need to specify at least one key identifier")" + usage + exit 1 + fi + while (( $# > 0 )); do + ${GPG_PACMAN} --quiet --batch --import "$1" + shift + done + ;; + -d|--del) + if (( $# == 0 )); then + error "$(gettext "You need to specify at least one key identifier")" + usage + exit 1 + fi + while (( $# > 0 )); do + ${GPG_PACMAN} --quiet --batch --delete-key --yes "$1" + shift + done + ;; + -u|--updatedb) + ${GPG_PACMAN} --batch --check-trustdb + ;; + --reload) + reload_keyring + ;; + -l|--list) + ${GPG_PACMAN} --batch --list-sigs + ;; + -f|--finger) + ${GPG_PACMAN} --batch --fingerprint $* + ;; + -e|--export) + if (( $# == 0 )); then + ${GPG_PACMAN} --armor --export + else + while (( $# > 0 )); do + ${GPG_PACMAN} --armor --export "$1" + shift + done + fi + ;; + -r|--receive) + if (( $# < 2 )); then + error "$(gettext "You need to specify the keyserver and at least one key identifier")" + usage + exit 1 + fi + keyserver="$1" + shift + ${GPG_PACMAN} --keyserver "${keyserver}" --recv-keys $* + ;; + -t|--trust) + if (( $# == 0 )); then + error "$(gettext "You need to specify at least one key identifier")" + usage + exit 1 + fi + while (( $# > 0 )); do + # Verify if the key exists in pacman's keyring + if ${GPG_PACMAN} --list-keys "$1" > /dev/null 2>&1; then + ${GPG_PACMAN} --edit-key "$1" + else + error "$(gettext "The key identified by %s doesn't exist")" "$1" + exit 1 + fi + shift + done + ;; + --adv) + msg "$(gettext "Executing: %s ")$*" "${GPG_PACMAN}" + ${GPG_PACMAN} $* || ret=$? + exit $ret + ;; + --help) + usage + ;; + --version) + version + exit 0 + ;; + *) + usage + exit 1 + ;; +esac + -- 1.7.2.3
Hi Denis, Hi everyone, I'm really pleased to see you're back on track.
If any of the collaborators want to help, this is the time I would be glad to. I see you need help to write the man right? Is this the number one task? Because I could also help with some more technical things if needed.
Just let me know. -- Guillaume
On Thu, Sep 16, 2010 at 4:37 AM, Guillaume ALAUX <guillaume@archlinux.org> wrote:
Hi Denis, Hi everyone,
I'm really pleased to see you're back on track.
Hi and thanks for the support :)
If any of the collaborators want to help, this is the time I would be glad to. I see you need help to write the man right? Is this the number one task? Because I could also help with some more technical things if needed.
Just let me know.
Yeah, right now the man page for pacman-key is the work that needs to be done. The script is not very complex, but 1) English is not my mother language and 2) I don't have lots of time. My wife is doing an internship and my daughter is consuming almost all my free time. But if you want to start a draft, I can surely help to develop it. If you want to discuss the script itself, you are also welcome. When the developers start to comment on the other patches, you can also help. Or you can start commenting on them right now. Don't be shy :) -- A: Because it obfuscates the reading. Q: Why is top posting so bad? ------------------------------------------- Denis A. Altoe Falqueto -------------------------------------------
On 16 September 2010 17:58, Denis A. Altoé Falqueto <denisfalqueto@gmail.com> wrote:
On Thu, Sep 16, 2010 at 4:37 AM, Guillaume ALAUX <guillaume@archlinux.org> wrote:
Hi Denis, Hi everyone,
I'm really pleased to see you're back on track.
Hi and thanks for the support :)
If any of the collaborators want to help, this is the time I would be glad to. I see you need help to write the man right? Is this the number one task? Because I could also help with some more technical things if needed.
Just let me know.
Yeah, right now the man page for pacman-key is the work that needs to be done. The script is not very complex, but 1) English is not my mother language and 2) I don't have lots of time. My wife is doing an internship and my daughter is consuming almost all my free time. But if you want to start a draft, I can surely help to develop it.
If you want to discuss the script itself, you are also welcome. When the developers start to comment on the other patches, you can also help. Or you can start commenting on them right now. Don't be shy :)
-- A: Because it obfuscates the reading. Q: Why is top posting so bad?
------------------------------------------- Denis A. Altoe Falqueto -------------------------------------------
Denis, Silly question: what is the format for "source man pages" for pacman? Pacman man page has a comment saying it was generated by "DocBook XSL Stylesheets" but I can't find SGML (nor XML file). Are they plain text? AsciiDoc? -- Guillaume
On 18 September 2010 22:05, Guillaume ALAUX <guillaume@alaux.net> wrote:
On 16 September 2010 17:58, Denis A. Altoé Falqueto <denisfalqueto@gmail.com> wrote:
On Thu, Sep 16, 2010 at 4:37 AM, Guillaume ALAUX <guillaume@archlinux.org> wrote:
Hi Denis, Hi everyone,
I'm really pleased to see you're back on track.
Hi and thanks for the support :)
If any of the collaborators want to help, this is the time I would be glad to. I see you need help to write the man right? Is this the number one task? Because I could also help with some more technical things if needed.
Just let me know.
Yeah, right now the man page for pacman-key is the work that needs to be done. The script is not very complex, but 1) English is not my mother language and 2) I don't have lots of time. My wife is doing an internship and my daughter is consuming almost all my free time. But if you want to start a draft, I can surely help to develop it.
If you want to discuss the script itself, you are also welcome. When the developers start to comment on the other patches, you can also help. Or you can start commenting on them right now. Don't be shy :)
-- A: Because it obfuscates the reading. Q: Why is top posting so bad?
------------------------------------------- Denis A. Altoe Falqueto -------------------------------------------
Denis,
Silly question: what is the format for "source man pages" for pacman? Pacman man page has a comment saying it was generated by "DocBook XSL Stylesheets" but I can't find SGML (nor XML file). Are they plain text? AsciiDoc?
-- Guillaume
Here is a TXT version of the "usage" of pacman-key. Nothing more from now on but that is a starter. Anyone willing to check my English or add things is welcome. Footer.txt is the one from pacman/doc folder. I may dig a bit further into the script to elaborate the man. As I'm completely new to writing man pages, here is how I get HTML and man page from this: Generate HTML from TXT asciidoc pacman-key.8.txt Generate XML from TXT asciidoc -b docbook -d manpage pacman-key.8.txt Generate man from XML docbook2man pacman-key.8.xml Let me know if that looks useful. -- Guillaume
On 18 September 2010 23:40, Guillaume ALAUX <guillaume@alaux.net> wrote:
On 18 September 2010 22:05, Guillaume ALAUX <guillaume@alaux.net> wrote:
On 16 September 2010 17:58, Denis A. Altoé Falqueto <denisfalqueto@gmail.com> wrote:
On Thu, Sep 16, 2010 at 4:37 AM, Guillaume ALAUX <guillaume@archlinux.org> wrote:
Hi Denis, Hi everyone,
I'm really pleased to see you're back on track.
Hi and thanks for the support :)
If any of the collaborators want to help, this is the time I would be glad to. I see you need help to write the man right? Is this the number one task? Because I could also help with some more technical things if needed.
Just let me know.
Yeah, right now the man page for pacman-key is the work that needs to be done. The script is not very complex, but 1) English is not my mother language and 2) I don't have lots of time. My wife is doing an internship and my daughter is consuming almost all my free time. But if you want to start a draft, I can surely help to develop it.
If you want to discuss the script itself, you are also welcome. When the developers start to comment on the other patches, you can also help. Or you can start commenting on them right now. Don't be shy :)
-- A: Because it obfuscates the reading. Q: Why is top posting so bad?
------------------------------------------- Denis A. Altoe Falqueto -------------------------------------------
Denis,
Silly question: what is the format for "source man pages" for pacman? Pacman man page has a comment saying it was generated by "DocBook XSL Stylesheets" but I can't find SGML (nor XML file). Are they plain text? AsciiDoc?
-- Guillaume
Here is a TXT version of the "usage" of pacman-key. Nothing more from now on but that is a starter. Anyone willing to check my English or add things is welcome. Footer.txt is the one from pacman/doc folder.
I may dig a bit further into the script to elaborate the man.
As I'm completely new to writing man pages, here is how I get HTML and man page from this:
Generate HTML from TXT asciidoc pacman-key.8.txt
Generate XML from TXT asciidoc -b docbook -d manpage pacman-key.8.txt
Generate man from XML docbook2man pacman-key.8.xml
Let me know if that looks useful. -- Guillaume
Denis, Here is already an other version that is a bit more verbose. Please correct it if I misunderstood some options. BTW: "usage" says: pacman-key -a | --add [<file>] ... - add the key contained in <file> (empty for stdin)") But I guess it won't go without a file (I'm talking about the "stdin" feature). So I removed it from the man. You may want to fix the usage (or implement the feature :) ) I haven't actually tested it but command "--trust" sets the user on an interactive gpg dialogue right? Then we should document this a bit and maybe tell user to read "man gpg" if needed. -- Guillaume
Hi, FYI, it is much easier to comment (and separate comments from the patch) if things are posted inline. Here goes some brief comments in cut paste sort of format... hopefully you can follow them. Anyway, overall this looks quite good. Make these small adjustments it is basically good to go. Even better if you submit as a patch with the necessary autotools changes, but I can handle those if not.
Description ----------- The script *pacman-key* manage *pacman*'s keyring, ie the keyring of GnuPG keys used to sign packages. It enables to import, export and fetch keys from keyservers as well as update the key trust database.
Hmm... how about something like: The script *pacman-key* manage *pacman*'s keyring, which is the collection of GnuPG keys used to check signed packages. It provides the ability to import and export keys, fetch keys from keyservers and update the key trust database.
*\--config* 'file':: Set an alternative configuration file to use (default is /etc/pacman.conf)
you should use {sysconfdir}/pacman.conf instead so we can adjust this based on the configure output.
*-l*, *\--list*:: List keys and signatures in pacman's keyring. Same as option \--list-sigs of GnuPG. See GnuPG's man pages for flag significations
Equivalent to --list-sigs from GnuPG. (?)
*-u*, *\--updatedb*:: Update the trustdb of pacman. Same as option \--check-trustdb of GnuPG.
Equivalent to \--check-trustdb in GnuPG Allan
On 20 September 2010 13:58, Allan McRae <allan@archlinux.org> wrote:
Hi,
FYI, it is much easier to comment (and separate comments from the patch) if things are posted inline. Here goes some brief comments in cut paste sort of format... hopefully you can follow them.
Anyway, overall this looks quite good. Make these small adjustments it is basically good to go. Even better if you submit as a patch with the necessary autotools changes, but I can handle those if not.
Description ----------- The script *pacman-key* manage *pacman*'s keyring, ie the keyring of GnuPG keys used to sign packages. It enables to import, export and fetch keys from keyservers as well as update the key trust database.
Hmm... how about something like:
The script *pacman-key* manage *pacman*'s keyring, which is the collection of GnuPG keys used to check signed packages. It provides the ability to import and export keys, fetch keys from keyservers and update the key trust database.
*\--config* 'file':: Set an alternative configuration file to use (default is /etc/pacman.conf)
you should use {sysconfdir}/pacman.conf instead so we can adjust this based on the configure output.
*-l*, *\--list*:: List keys and signatures in pacman's keyring. Same as option \--list-sigs of GnuPG. See GnuPG's man pages for flag significations
Equivalent to --list-sigs from GnuPG. (?)
*-u*, *\--updatedb*:: Update the trustdb of pacman. Same as option \--check-trustdb of GnuPG.
Equivalent to \--check-trustdb in GnuPG
Allan
Hello,
it is much easier to comment if things are posted inline OK I hadn't think about it !
Make these small adjustments it is basically good to go They also look OK to me, I will change that.
Even better if you submit as a patch with the necessary autotools changes Will try to :)
-- Guillaume
On 20 September 2010 14:06, Guillaume ALAUX <guillaume@alaux.net> wrote:
On 20 September 2010 13:58, Allan McRae <allan@archlinux.org> wrote:
Hi,
FYI, it is much easier to comment (and separate comments from the patch) if things are posted inline. Here goes some brief comments in cut paste sort of format... hopefully you can follow them.
Anyway, overall this looks quite good. Make these small adjustments it is basically good to go. Even better if you submit as a patch with the necessary autotools changes, but I can handle those if not.
Description ----------- The script *pacman-key* manage *pacman*'s keyring, ie the keyring of GnuPG keys used to sign packages. It enables to import, export and fetch keys from keyservers as well as update the key trust database.
Hmm... how about something like:
The script *pacman-key* manage *pacman*'s keyring, which is the collection of GnuPG keys used to check signed packages. It provides the ability to import and export keys, fetch keys from keyservers and update the key trust database.
*\--config* 'file':: Set an alternative configuration file to use (default is /etc/pacman.conf)
you should use {sysconfdir}/pacman.conf instead so we can adjust this based on the configure output.
*-l*, *\--list*:: List keys and signatures in pacman's keyring. Same as option \--list-sigs of GnuPG. See GnuPG's man pages for flag significations
Equivalent to --list-sigs from GnuPG. (?)
*-u*, *\--updatedb*:: Update the trustdb of pacman. Same as option \--check-trustdb of GnuPG.
Equivalent to \--check-trustdb in GnuPG
Allan
Hello,
it is much easier to comment if things are posted inline OK I hadn't think about it !
Make these small adjustments it is basically good to go They also look OK to me, I will change that.
Even better if you submit as a patch with the necessary autotools changes Will try to :)
-- Guillaume
OK, how about that? === PATCH === diff --git a/doc/.gitignore b/doc/.gitignore index f047aaa..aebf7a0 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -3,6 +3,7 @@ libalpm.3 makepkg.8 makepkg.conf.5 pacman.8 +pacman-key.8 pacman.conf.5 repo-add.8 repo-remove.8 diff --git a/doc/Makefile.am b/doc/Makefile.am index 2e656f6..5c84234 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -7,6 +7,7 @@ ASCIIDOC_MANS = \ pacman.8 \ makepkg.8 \ repo-add.8 \ + pacman-key.8 \ PKGBUILD.5 \ makepkg.conf.5 \ pacman.conf.5 \ @@ -18,6 +19,7 @@ HTML_MANPAGES = \ pacman.8.html \ makepkg.8.html \ repo-add.8.html \ + pacman-key.8.html \ PKGBUILD.5.html \ makepkg.conf.5.html \ pacman.conf.5.html \ @@ -38,6 +40,7 @@ EXTRA_DIST = \ pacman.8.txt \ makepkg.8.txt \ repo-add.8.txt \ + pacman-key.8.txt \ PKGBUILD.5.txt \ PKGBUILD-example.txt \ makepkg.conf.5.txt \ @@ -128,6 +131,7 @@ $(HTML_OTHER): asciidoc.conf pacman.8 pacman.8.html: pacman.8.txt makepkg.8 makepkg.8.html: makepkg.8.txt repo-add.8 repo-add.8.html: repo-add.8.txt +pacman-key.8 pacman-key.8.html: pacman-key.8.txt PKGBUILD.5 PKGBUILD.5.html: PKGBUILD.5.txt PKGBUILD-example.txt makepkg.conf.5 makepkg.conf.5.html: makepkg.conf.5.txt pacman.conf.5 pacman.conf.5.html: pacman.conf.5.txt diff --git a/doc/pacman-key.8.txt b/doc/pacman-key.8.txt new file mode 100644 index 0000000..6b70f80 --- /dev/null +++ b/doc/pacman-key.8.txt @@ -0,0 +1,76 @@ +///// +vim:set ts=4 sw=4 syntax=asciidoc noet: +///// +pacman-key(8) +============= + + +Name +---- +pacman-key - manage pacman's list of trusted keys + + +Synopsis +-------- +*pacman-key* [options] *command* ['arguments'] + + +Description +----------- +The script *pacman-key* manage *pacman*'s keyring, which is the collection of GnuPG keys used to check signed packages. +It provides the ability to import and export keys, fetch keys from keyservers and update the key trust database. + + +Options +------- +*\--config* 'file':: + Set an alternative configuration file to use (default is {sysconfdir}/pacman.conf) + +*\--gpgdir* 'directory':: + Set an alternative home directory for GnuPG (default is set in {sysconfdir}/pacman.conf) + + +Commands +------- +*-a*, *\--add* 'file ...':: + Add the key(s) contained in 'file'(s) to pacman's keyring. If a key already exists, update it. + +*\--adv* 'param ...':: + Use this option to issue particular GnuPG actions to pacman's keyring. This option should be used with care as it can modify pacman's trust in packages' signatures. + +*-d*, *\--del* 'keyid ...':: + Remove the key(s) identified by 'keyid'(s) from pacman's keyring + +*-e*, *\--export* ['keyid ...']:: + Export key(s) identified by 'keyid'(s) to STDOUT or all keys if no 'keyid' is specified + +*-f*, *\--finger* ['keyid ...']:: + List fingerprint(s) for specified 'keyid'(s) or for all if none is specified + +*\--help*:: + Displays this message + +*-l*, *\--list*:: + Equivalent to --list-sigs from GnuPG + +*-r*, *\--receive* 'keyserver' 'keyid ...':: + Fetch the 'keyid'(s) from the specified 'keyserver' URL + +*\--reload*:: + Reloads the keys from the keyring package + +*-t*, *\--trust* 'keyid':: + Set the trust level of the given key + +*-u*, *\--updatedb*:: + Equivalent to \--check-trustdb in GnuPG + +*-v*, *\--version*:: + Displays the current version + + +See Also +-------- +linkman:pacman.conf[5] + +include::footer.txt[]
On Mon, Sep 20, 2010 at 1:00 PM, Guillaume ALAUX <guillaume@alaux.net> wrote:
OK, how about that?
Hi, Guillaume. First, thank you very much for your work. It is very good. But it seems that your mail server is breaking lines where it shouldn't. I got bitten by that too in my first patches, so you can do the following: 1. Install msmtp and configure it, according to our wiki: http://wiki.archlinux.org/index.php/Msmtp 2. add the following lines in your ~/.gitconfig: [sendemail] smtpserver = /usr/bin/msmtp So, you can use git --format-patch and git --send-email to create and send a patch to the list. I believe git --send-email also formats the patch if used with the right parameters, but I like to take a look on the patches before sending them. @devs: Do you think we should give a little introduction on the concept of GnuPG and web of trust into the man page? Or should we direct the users to some interesting urls? -- A: Because it obfuscates the reading. Q: Why is top posting so bad? ------------------------------------------- Denis A. Altoe Falqueto -------------------------------------------
On Thu, Sep 16, 2010 at 12:40 AM, Denis A. Altoé Falqueto <denisfalqueto@gmail.com> wrote:
The script pacman-key will manage pacman's keyring. It imports, exports, fetches from keyservers, helps in the process of trusting and updates the trust database.
Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com> ---
Allan, I'm thinking in change some parts of this patch. I've seen you already applied to your git repository, so should I send the patch all again or should I send a "patch to the patch"? I want to do two things: 1. the --add command should behave just like gpg, importing from stdin when there's no files specified. I'm struggling with parameter expansion, but I'll figure the correct way soon :) 2. the --reload option could be enhanced for the following scenario: Let's say an active developer leaves the project. So, his key must be put on the list of removed keys. But he has a personal repository that lots of people use and he signs his packages too. The current behavior would delete his key from the keyring and the user would need to manually add it every time the --reload option is run. We could have an option in pacman.conf that indicates which keys must be kept, even if in the list of removed keys. For example, an option called HoldKeys, in the same spirit of HoldPkg. Do you agree with the need and solution? -- A: Because it obfuscates the reading. Q: Why is top posting so bad? ------------------------------------------- Denis A. Altoe Falqueto -------------------------------------------
On 23/09/10 03:14, Denis A. Altoé Falqueto wrote:
On Thu, Sep 16, 2010 at 12:40 AM, Denis A. Altoé Falqueto <denisfalqueto@gmail.com> wrote:
The script pacman-key will manage pacman's keyring. It imports, exports, fetches from keyservers, helps in the process of trusting and updates the trust database.
Signed-off-by: Denis A. Altoé Falqueto<denisfalqueto@gmail.com> ---
Allan, I'm thinking in change some parts of this patch. I've seen you already applied to your git repository, so should I send the patch all again or should I send a "patch to the patch"?
Go with a patch to the patch. I will rebase them together before it becomes time to pull it into master.
I want to do two things:
1. the --add command should behave just like gpg, importing from stdin when there's no files specified. I'm struggling with parameter expansion, but I'll figure the correct way soon :)
Sounds fine.
2. the --reload option could be enhanced for the following scenario: Let's say an active developer leaves the project. So, his key must be put on the list of removed keys. But he has a personal repository that lots of people use and he signs his packages too. The current behavior would delete his key from the keyring and the user would need to manually add it every time the --reload option is run.
We could have an option in pacman.conf that indicates which keys must be kept, even if in the list of removed keys. For example, an option called HoldKeys, in the same spirit of HoldPkg.
Do you agree with the need and solution?
Just because a developer leaves, does not make his old packages unsafe. So we really do not want to be rebuilding everything just to resign them. I think that removing of a key needs to be handled more gracefully at a distribution level rather than immediately removing the key. Saying that, the case of a developer leaving but still hosting a third party repo is interesting and probably needs some work around such as you have pointed out. The pacman.conf options seems reasonable. But how about having a system like: addedkeys - key currently being used to sign packages depricatedkeys - keys previously used to sign packages but still safe removedkeys - keys that have been revoked. I guess these are the sort of things that we will discover with some real world usage. Also, I just noticed: local ADDED_KEYS="${PACMAN_SHARE_DIR}/addedkeys.gpg" local REMOVED_KEYS="${PACMAN_SHARE_DIR}/removedkeys" The file extension is not consistent. Allan
On Wed, Sep 22, 2010 at 6:20 PM, Allan McRae <allan@archlinux.org> wrote:
We could have an option in pacman.conf that indicates which keys must be kept, even if in the list of removed keys. For example, an option called HoldKeys, in the same spirit of HoldPkg.
Do you agree with the need and solution?
Just because a developer leaves, does not make his old packages unsafe. So we really do not want to be rebuilding everything just to resign them. I think that removing of a key needs to be handled more gracefully at a distribution level rather than immediately removing the key.
Saying that, the case of a developer leaving but still hosting a third party repo is interesting and probably needs some work around such as you have pointed out. The pacman.conf options seems reasonable. But how about having a system like:
addedkeys - key currently being used to sign packages depricatedkeys - keys previously used to sign packages but still safe removedkeys - keys that have been revoked.
I guess these are the sort of things that we will discover with some real world usage.
Yes, I agree. I'll try to implement the option and what you propose, because it will give a better organization. So, the operation of reloading could be summarized as: keyring = (added keys + deprecated keys) - (removed keys - kept keys)
Also, I just noticed: local ADDED_KEYS="${PACMAN_SHARE_DIR}/addedkeys.gpg" local REMOVED_KEYS="${PACMAN_SHARE_DIR}/removedkeys"
The file extension is not consistent.
Yes, it is kind of intentional :) it needs some explanation. The added keys must be a file with the complete public key that is being imported, of course. But the removed keys don't need to. In the current implementation, it is only a list of key identifiers and the added keys are a real keyring, not just an export of a set of keys. This seems contrived at first, as I thought when I saw it in apt-get, but later I saw the purpose of it. We can manipulate the added keys and select which ones will be imported. The above "equation" is an example of such manipulation. We can also change the extensions. I don't have a problem with that. But this explanation may help the decision. -- A: Because it obfuscates the reading. Q: Why is top posting so bad? ------------------------------------------- Denis A. Altoe Falqueto -------------------------------------------
On 23/09/10 11:38, Denis A. Altoé Falqueto wrote:
On Wed, Sep 22, 2010 at 6:20 PM, Allan McRae<allan@archlinux.org> wrote:
We could have an option in pacman.conf that indicates which keys must be kept, even if in the list of removed keys. For example, an option called HoldKeys, in the same spirit of HoldPkg.
Do you agree with the need and solution?
Just because a developer leaves, does not make his old packages unsafe. So we really do not want to be rebuilding everything just to resign them. I think that removing of a key needs to be handled more gracefully at a distribution level rather than immediately removing the key.
Saying that, the case of a developer leaving but still hosting a third party repo is interesting and probably needs some work around such as you have pointed out. The pacman.conf options seems reasonable. But how about having a system like:
addedkeys - key currently being used to sign packages depricatedkeys - keys previously used to sign packages but still safe removedkeys - keys that have been revoked.
I guess these are the sort of things that we will discover with some real world usage.
Yes, I agree. I'll try to implement the option and what you propose, because it will give a better organization. So, the operation of reloading could be summarized as:
keyring = (added keys + deprecated keys) - (removed keys - kept keys)
Also, I just noticed: local ADDED_KEYS="${PACMAN_SHARE_DIR}/addedkeys.gpg" local REMOVED_KEYS="${PACMAN_SHARE_DIR}/removedkeys"
The file extension is not consistent.
Yes, it is kind of intentional :) it needs some explanation.
The added keys must be a file with the complete public key that is being imported, of course. But the removed keys don't need to. In the current implementation, it is only a list of key identifiers and the added keys are a real keyring, not just an export of a set of keys.
This seems contrived at first, as I thought when I saw it in apt-get, but later I saw the purpose of it. We can manipulate the added keys and select which ones will be imported. The above "equation" is an example of such manipulation.
We can also change the extensions. I don't have a problem with that. But this explanation may help the decision.
I am not particularly worried here, and is a relatively minor thing that can be adjusted later anyway. Also, we will need to have those file names listed in the pacman-key man page... Allan
On 16/09/10 13:29, Denis A. Altoé Falqueto wrote:
On Wed, Aug 4, 2010 at 10:17 AM, Allan McRae<allan@archlinux.org> wrote:
On 28/07/10 13:50, Denis A. Altoé Falqueto wrote:
The script pacman-key will manage pacman's keyring. It imports, exports, fetches from keyservers, helps in the process of trusting and updates the trust database.
Signed-off-by: Denis A. Altoé Falqueto<denisfalqueto@gmail.com>
Hi Denis,
I think it would be good for us to focus on getting this onto the gpg branch and then move onto the other patches. I do not think this requires massive changes to be ready.
Hi.
Sorry for the delay again. Time is so short lately... It took way longer than I would like. But here I am again. I'll answer only the things that I would like to discuss further. The other points were implemented as advised by you.
+prepare_homedir() { + if [[ ! -d "${PACMAN_KEYRING_DIR}" ]] ; then + mkdir -p "${PACMAN_KEYRING_DIR}" + touch "${PACMAN_KEYRING_DIR}/secring.gpg" + touch "${PACMAN_KEYRING_DIR}/pubring.gpg" + chmod 700 "${PACMAN_KEYRING_DIR}" + chmod 600 "${PACMAN_KEYRING_DIR}"/{sec,pub}ring.gpg
We should just use: install -dm700 ${PACMAN_KEYRING_DIR} to create the directory with the right permissions.
And should those files actually be part of the pacman package and so guaranteed to be present.
Yes, I believe the best place is pacman package. I removed the function, so we need to make sure the PKGBUILD for pacman creates the proper files and directory.
I will adjust the Makefile to ensure these directories get made.
+update_trustdb() { + ${GPG_PACMAN} --batch --check-trustdb
Should we be using --update-trustdb?
From gpg's man page:
"The processing is identical to that of --update-trustdb but it skips keys with a not yet defined "ownertrust"."
I'm not sure what is the best option. update-trustdb may ask the user what is the ownertrust value for the keys gpg can't compute with the web of trust. check-trustdb ignores those keys. According to the man page, none of them are necessary. The values are computed correctly when importing keys. Should we drop that option? It would be still accessible through --adv command, if someone really needs it.
I think this is fine at the moment.
+# Read GPGDIR from $CONFIG. +# The pattern is: any spaces or tabs, GPGDir, any spaces or tabs, equal sign +# and the rest of the line. The string is splitted after the first occurrence of = +GPGDIR=$(cat ${CONFIG} | awk '/^(\t| )*GPGDir(\t| )*=.*/ { print substr($0,index($0, "=")+1) }')
cat a file to awk it... yuck. Also, we do not use awk anywhere else, so grep followed by a bash substitution to remove "*=" from the start may be better.
I see. I googled about grep and tabs and some links showed that grep couldn't accept correctly the \t character, but I found now that the class [:blank:] is what we really need: spaces and tabs. And grep accepts it. I'll change the script.
Looks better to me. Overall, I think this patch looks good now. I am pulling the new version of this patch onto my gpg branch. We can always make more changes before it gets merged to master. Allan
On 16/09/10 23:36, Allan McRae wrote:
On 16/09/10 13:29, Denis A. Altoé Falqueto wrote:
On Wed, Aug 4, 2010 at 10:17 AM, Allan McRae<allan@archlinux.org> wrote:
On 28/07/10 13:50, Denis A. Altoé Falqueto wrote:
The script pacman-key will manage pacman's keyring. It imports, exports, fetches from keyservers, helps in the process of trusting and updates the trust database.
Signed-off-by: Denis A. Altoé Falqueto<denisfalqueto@gmail.com>
Hi Denis,
I think it would be good for us to focus on getting this onto the gpg branch and then move onto the other patches. I do not think this requires massive changes to be ready.
Hi.
Sorry for the delay again. Time is so short lately... It took way longer than I would like. But here I am again. I'll answer only the things that I would like to discuss further. The other points were implemented as advised by you.
+prepare_homedir() { + if [[ ! -d "${PACMAN_KEYRING_DIR}" ]] ; then + mkdir -p "${PACMAN_KEYRING_DIR}" + touch "${PACMAN_KEYRING_DIR}/secring.gpg" + touch "${PACMAN_KEYRING_DIR}/pubring.gpg" + chmod 700 "${PACMAN_KEYRING_DIR}" + chmod 600 "${PACMAN_KEYRING_DIR}"/{sec,pub}ring.gpg
We should just use: install -dm700 ${PACMAN_KEYRING_DIR} to create the directory with the right permissions.
And should those files actually be part of the pacman package and so guaranteed to be present.
Yes, I believe the best place is pacman package. I removed the function, so we need to make sure the PKGBUILD for pacman creates the proper files and directory.
I will adjust the Makefile to ensure these directories get made.
It has become abundantly clear to me that my autotools knowledge is lacking and I have no idea how to actually do this... especially the restricted permissions part. Anyone else care to take a stab? Allan
_alpm_gpg_checksig was renamed to _alpm_gpg_checksig_memory. It checks signatures which are in memory. The packages' signatures are stored in the repository database and are kept in memory when they need to be verified. _alpm_gpg_checksig_file is very similar to _alpm_gpg_checksig_memory, but the signature is read from a file. The return values for both functions are: 0 = valid; 1 = invalid; -1 = error while verifying. Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com> --- lib/libalpm/signing.c | 159 ++++++++++++++++++++++++++++++++++++------------- lib/libalpm/signing.h | 3 +- 2 files changed, 120 insertions(+), 42 deletions(-) diff --git a/lib/libalpm/signing.c b/lib/libalpm/signing.c index 2b15528..781a1d1 100644 --- a/lib/libalpm/signing.c +++ b/lib/libalpm/signing.c @@ -91,21 +91,76 @@ error: RET_ERR(PM_ERR_GPGME, 1); } +static int gpgme_checksig(gpgme_ctx_t ctx, gpgme_data_t filedata, gpgme_data_t sigdata, + char *filepath) +{ + int ret = 0; + gpgme_error_t err; + gpgme_signature_t gpgsig; + gpgme_verify_result_t result; + + /* here's where the magic happens */ + err = gpgme_op_verify(ctx, sigdata, filedata, NULL); + CHECK_ERR(); + result = gpgme_op_verify_result(ctx); + gpgsig = result->signatures; + if (!gpgsig || gpgsig->next) { + _alpm_log(PM_LOG_ERROR, _("Unexpected number of signatures\n")); + ret = -1; + goto error; + } + fprintf(stdout, "\nsummary=%x\n", gpgsig->summary); + fprintf(stdout, "fpr=%s\n", gpgsig->fpr); + fprintf(stdout, "status=%d\n", gpgsig->status); + fprintf(stdout, "timestamp=%lu\n", gpgsig->timestamp); + fprintf(stdout, "wrong_key_usage=%u\n", gpgsig->wrong_key_usage); + fprintf(stdout, "pka_trust=%u\n", gpgsig->pka_trust); + fprintf(stdout, "chain_model=%u\n", gpgsig->chain_model); + fprintf(stdout, "validity=%d\n", gpgsig->validity); + fprintf(stdout, "validity_reason=%d\n", gpgsig->validity_reason); + fprintf(stdout, "key=%d\n", gpgsig->pubkey_algo); + fprintf(stdout, "hash=%d\n", gpgsig->hash_algo); + + if(gpgsig->summary & GPGME_SIGSUM_VALID) { + /* good signature, continue */ + _alpm_log(PM_LOG_DEBUG, _("Package %s has a valid signature.\n"), + filepath); + } else if(gpgsig->summary & GPGME_SIGSUM_GREEN) { + /* 'green' signature, not sure what to do here */ + _alpm_log(PM_LOG_WARNING, _("Package %s has a green signature.\n"), + filepath); + } else if(gpgsig->summary & GPGME_SIGSUM_KEY_MISSING) { + pm_errno = PM_ERR_SIG_UNKNOWN; + _alpm_log(PM_LOG_WARNING, _("Package %s has a signature from an unknown key.\n"), + filepath); + ret = -1; + } else { + /* we'll capture everything else here */ + pm_errno = PM_ERR_SIG_INVALID; + _alpm_log(PM_LOG_ERROR, _("Package %s has an invalid signature.\n"), + filepath); + ret = 1; + } + return ret; +error: + _alpm_log(PM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(err)); + RET_ERR(PM_ERR_GPGME, 1); +} + /** * Check the PGP package signature for the given package file. * @param pkgpath the full path to a package file * @param sig PGP signature data in raw form (already decoded) * @return a int value : 0 (valid), 1 (invalid), -1 (an error occured) */ -int _alpm_gpgme_checksig(const char *pkgpath, const pmpgpsig_t *sig) +int _alpm_gpgme_checksig_memory(const char *pkgpath, const pmpgpsig_t *sig) { int ret = 0; gpgme_error_t err; gpgme_ctx_t ctx; gpgme_data_t pkgdata, sigdata; gpgme_verify_result_t result; - gpgme_signature_t gpgsig; - FILE *pkgfile = NULL, *sigfile = NULL; + FILE *pkgfile = NULL; ALPM_LOG_FUNC; @@ -137,58 +192,80 @@ int _alpm_gpgme_checksig(const char *pkgpath, const pmpgpsig_t *sig) err = gpgme_data_new_from_mem(&sigdata, (char*)sig->rawdata, sig->rawlen, 0); CHECK_ERR(); - /* here's where the magic happens */ - err = gpgme_op_verify(ctx, sigdata, pkgdata, NULL); + ret = gpgme_checksig(ctx, pkgdata, sigdata, pkgpath); +error: + gpgme_data_release(sigdata); + gpgme_data_release(pkgdata); + gpgme_release(ctx); + if(pkgfile) { + fclose(pkgfile); + } + if(err != GPG_ERR_NO_ERROR) { + _alpm_log(PM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(err)); + RET_ERR(PM_ERR_GPGME, -1); + } + return(ret); +} + +/** + * Check the PGP signature for the some file + * @param filepath the full path to the signed file + * @param sigpath the full path to the signature file + * @return a int value : 0 (valid), 1 (invalid), -1 (an error occured) + */ +int _alpm_gpgme_checksig_file(const char *filepath, const char *sigpath) +{ + int ret = 0; + gpgme_error_t err; + gpgme_ctx_t ctx; + gpgme_data_t filedata, sigdata; + gpgme_verify_result_t result; + gpgme_signature_t gpgsig; + FILE *file = NULL, *sigfile = NULL; + + ALPM_LOG_FUNC; + + if(!filepath || access(filepath, R_OK) != 0) { + RET_ERR(PM_ERR_PKG_NOT_FOUND, -1); + } + if(gpgme_init()) { + /* pm_errno was set in gpgme_init() */ + return(-1); + } + err = gpgme_new(&ctx); CHECK_ERR(); - result = gpgme_op_verify_result(ctx); - gpgsig = result->signatures; - if (!gpgsig || gpgsig->next) { - _alpm_log(PM_LOG_ERROR, _("Unexpected number of signatures\n")); + + /* create our necessary data objects to verify the signature */ + /* first the package itself */ + file = fopen(filepath, "rb"); + if(file == NULL) { + pm_errno = PM_ERR_PKG_OPEN; ret = -1; goto error; } - fprintf(stdout, "\nsummary=%x\n", gpgsig->summary); - fprintf(stdout, "fpr=%s\n", gpgsig->fpr); - fprintf(stdout, "status=%d\n", gpgsig->status); - fprintf(stdout, "timestamp=%lu\n", gpgsig->timestamp); - fprintf(stdout, "wrong_key_usage=%u\n", gpgsig->wrong_key_usage); - fprintf(stdout, "pka_trust=%u\n", gpgsig->pka_trust); - fprintf(stdout, "chain_model=%u\n", gpgsig->chain_model); - fprintf(stdout, "validity=%d\n", gpgsig->validity); - fprintf(stdout, "validity_reason=%d\n", gpgsig->validity_reason); - fprintf(stdout, "key=%d\n", gpgsig->pubkey_algo); - fprintf(stdout, "hash=%d\n", gpgsig->hash_algo); + err = gpgme_data_new_from_stream(&filedata, file); + CHECK_ERR(); - if(gpgsig->summary & GPGME_SIGSUM_VALID) { - /* good signature, continue */ - _alpm_log(PM_LOG_DEBUG, _("Package %s has a valid signature.\n"), - pkgpath); - } else if(gpgsig->summary & GPGME_SIGSUM_GREEN) { - /* 'green' signature, not sure what to do here */ - _alpm_log(PM_LOG_WARNING, _("Package %s has a green signature.\n"), - pkgpath); - } else if(gpgsig->summary & GPGME_SIGSUM_KEY_MISSING) { + /* next create data object for the signature */ + sigfile = fopen(sigpath, "rb"); + if (sigfile == NULL) { pm_errno = PM_ERR_SIG_UNKNOWN; - _alpm_log(PM_LOG_WARNING, _("Package %s has a signature from an unknown key.\n"), - pkgpath); ret = -1; - } else { - /* we'll capture everything else here */ - pm_errno = PM_ERR_SIG_INVALID; - _alpm_log(PM_LOG_ERROR, _("Package %s has an invalid signature.\n"), - pkgpath); - ret = 1; + goto error; } + err = gpgme_data_new_from_stream(&sigdata, sigfile); + CHECK_ERR(); + ret = gpgme_checksig(ctx, filedata, sigdata, filepath); error: gpgme_data_release(sigdata); - gpgme_data_release(pkgdata); + gpgme_data_release(filedata); gpgme_release(ctx); if(sigfile) { fclose(sigfile); } - if(pkgfile) { - fclose(pkgfile); + if(file) { + fclose(file); } if(err != GPG_ERR_NO_ERROR) { _alpm_log(PM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(err)); @@ -207,7 +284,7 @@ int SYMEXPORT alpm_pkg_check_pgp_signature(pmpkg_t *pkg) ALPM_LOG_FUNC; ASSERT(pkg != NULL, return(0)); - return(_alpm_gpgme_checksig(alpm_pkg_get_filename(pkg), + return(_alpm_gpgme_checksig_memory(alpm_pkg_get_filename(pkg), alpm_pkg_get_pgpsig(pkg))); } diff --git a/lib/libalpm/signing.h b/lib/libalpm/signing.h index c004697..9ae3dc1 100644 --- a/lib/libalpm/signing.h +++ b/lib/libalpm/signing.h @@ -21,7 +21,8 @@ #include "alpm.h" -int _alpm_gpgme_checksig(const char *pkgpath, const pmpgpsig_t *sig); +int _alpm_gpgme_checksig_memory(const char *pkgpath, const pmpgpsig_t *sig); +int _alpm_gpgme_checksig_file(const char *filepath, const char *sigpath); #endif /* _ALPM_SIGNING_H */ -- 1.7.1.1
The signatures of databases will be verified when the database is being updated. The option VerifySig in the database section of pacman.conf will dictate how to deal with the signature. If there is a need to verify (VerifySig is Always or Optional), the signature will be downloaded and verifyed. When signing packages, the signature will come from the database repository where it belongs. So, if a package will be installed from core, the signature will be read from the desc file, field %PGPSIG%. Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com> --- lib/libalpm/be_files.c | 84 +++++++++++++++++++++++++++++++++++++++++++++-- lib/libalpm/sync.c | 2 +- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/lib/libalpm/be_files.c b/lib/libalpm/be_files.c index 97c5c4b..505560f 100644 --- a/lib/libalpm/be_files.c +++ b/lib/libalpm/be_files.c @@ -49,6 +49,9 @@ #include "delta.h" #include "deps.h" #include "dload.h" +#include "signing.h" + +#define DBSIGEXT ".sig" static int checkdbdir(pmdb_t *db) @@ -241,6 +244,55 @@ int SYMEXPORT alpm_db_update(int force, pmdb_t *db) sprintf(syncpath, "%s%s", dbpath, "sync/"); ret = _alpm_download_single_file(dbfile, db->servers, syncpath, force); + /* Check the signature of the database, if it is marked as signed */ + if(db->pgp_verify != PM_PGP_VERIFY_NEVER) { + char *dbsigfile = NULL; + + /* Assemble the signature file name */ + len = strlen(dbfile) + strlen(DBSIGEXT) + 1; + MALLOC(dbsigfile, len, RET_ERR(PM_ERR_MEMORY, -1)); + sprintf(dbsigfile, "%s" DBSIGEXT, dbfile); + + /* Try to download the signature file */ + ret = _alpm_download_single_file(dbsigfile, db->servers, syncpath, force); + if (ret == -1 && db->pgp_verify == PM_PGP_VERIFY_ALWAYS) { + _alpm_log(PM_LOG_DEBUG, "failed to download signature for db: %s\n", alpm_strerrorlast()); + free(dbfile); + free(dbsigfile); + RET_ERR(PM_ERR_SIG_MISSINGDIR, -1); + } + else if (ret != -1) { + char *fulldbfile = NULL, *fulldbsigfile = NULL; + + /* Assemble the full db and signature file names */ + MALLOC(fulldbfile, strlen(syncpath) + strlen(dbfile) + 2, RET_ERR(PM_ERR_MEMORY, -1)); + MALLOC(fulldbsigfile, strlen(syncpath) + strlen(dbsigfile) + 2, RET_ERR(PM_ERR_MEMORY, -1)); + sprintf(fulldbfile, "%s/%s", syncpath, dbfile); + sprintf(fulldbsigfile, "%s/%s", syncpath, dbsigfile); + + /* Check the signature */ + _alpm_log(PM_LOG_DEBUG, "fulldbfile = %s\n", fulldbfile); + _alpm_log(PM_LOG_DEBUG, "fulldbsigfile = %s\n", fulldbsigfile); + int ret = _alpm_gpgme_checksig_file(fulldbfile, fulldbsigfile); + _alpm_log(PM_LOG_DEBUG, "return from _alpm_gpgme_checksig_file = %d\n", ret); + + FREE(fulldbfile); + FREE(fulldbsigfile); + + /* VerifSig = Always -> we will only accept 0 as a correct value + * (missing or invalid signatures are errors) + * VerifSig = Optional -> we will accept 0 or -1 as correct values + * (missing signature is ok, but if it present, it must be valid) */ + if((db->pgp_verify == PM_PGP_VERIFY_ALWAYS && ret != 0) || + (db->pgp_verify == PM_PGP_VERIFY_OPTIONAL && ret == 1)) { + _alpm_log(PM_LOG_ERROR, "the signature doesn't match the repository database.\n"); + free(dbfile); + free(dbsigfile); + RET_ERR(PM_ERR_SIG_INVALID, -1); + } + } + FREE(dbsigfile); + } free(dbfile); free(syncpath); @@ -614,11 +666,35 @@ int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) STRDUP(info->md5sum, _alpm_strtrim(line), goto error); } else if(strcmp(line, "%PGPSIG%") == 0) { /* PGPSIG tag only appears in sync repositories, - * not the local one. */ - if(fgets(line, 512, fp) == NULL) { - goto error; + * not the local one. The size must not be fixed, + * because the key used will affect the final size */ + alpm_list_t *pgpsiglines = NULL; + int len = 0; + + /* Create a list of strings to store each line of the signature */ + while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) { + char *linedup; + STRDUP(linedup, _alpm_strtrim(line), goto error); + pgpsiglines = alpm_list_add(pgpsiglines, linedup); + len += strlen(linedup); + } + + /* Allocate a buffer to hold the full signature */ + MALLOC(info->pgpsig.encdata, (size_t)(len + 1), goto error); + info->pgpsig.encdata[0] = '\0'; + + /* Assemble the signature from the list of strings */ + while (pgpsiglines != NULL) { + alpm_list_t *pgpsig_line = pgpsiglines; + pgpsiglines = pgpsiglines->next; + + strcat(info->pgpsig.encdata, (char *)pgpsig_line->data); + + /* Free the current node, as it is not needed anymore */ + free(pgpsig_line->data); + free(pgpsig_line); } - STRDUP(info->pgpsig.encdata, _alpm_strtrim(line), goto error); + pgpsiglines = NULL; } else if(strcmp(line, "%REPLACES%") == 0) { while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) { char *linedup; diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c index 77afe24..38aa508 100644 --- a/lib/libalpm/sync.c +++ b/lib/libalpm/sync.c @@ -942,7 +942,7 @@ int _alpm_sync_commit(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t **data) pmdb_t *sdb = alpm_pkg_get_db(spkg); if(sdb->pgp_verify != PM_PGP_VERIFY_NEVER) { - int ret = _alpm_gpgme_checksig(filepath, pgpsig); + int ret = _alpm_gpgme_checksig_memory(filepath, pgpsig); if((sdb->pgp_verify == PM_PGP_VERIFY_ALWAYS && ret != 0) || (sdb->pgp_verify == PM_PGP_VERIFY_OPTIONAL && ret == 1)) { errors++; -- 1.7.1.1
There is a possibility of another key being used, instead of the user's default. For exemple, the pacman-keyring package will be signed by a master key, because it needs to be trusted explicitly by the user before the installation of that package. So, the parameter --signwithkey will be used to supply an id of a key that will be used to sign a database or package. Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com> --- scripts/makepkg.sh.in | 38 ++++++++++++++++++++-------- scripts/repo-add.sh.in | 63 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in index 080e530..f6f9dfe 100644 --- a/scripts/makepkg.sh.in +++ b/scripts/makepkg.sh.in @@ -28,7 +28,7 @@ # makepkg uses quite a few external programs during its execution. You # need to have at least the following installed for makepkg to function: # bsdtar (libarchive), bzip2, coreutils, fakeroot, find (findutils), -# gettext, grep, gzip, openssl, sed, tput (ncurses), xz +# gettext, grep, gzip, openssl, sed, tput (ncurses), xz, gpg # gettext initialization export TEXTDOMAIN='pacman' @@ -43,6 +43,8 @@ BUILDSCRIPT='@BUILDSCRIPT@' startdir="$PWD" srcdir="$startdir/src" pkgdir="$startdir/pkg" +GPG="gpg2" +SIG_EXT=".sig" packaging_options=('strip' 'docs' 'libtool' 'emptydirs' 'zipman' 'purge') other_options=('ccache' 'distcc' 'makeflags' 'force') @@ -74,6 +76,7 @@ BUILDFUNC=0 PKGFUNC=0 SPLITPKG=0 PKGLIST="" +SIGNKEY="" # Forces the pkgver of the current PKGBUILD. Used by the fakeroot call # when dealing with svn/cvs/etc PKGBUILDs. @@ -391,8 +394,8 @@ check_deps() { local ret=0 local pmout pmout=$(run_pacman -T "$@") || ret=$? - set -E - + set -E + if (( ret == 127 )); then #unresolved deps echo "$pmout" elif (( ret )); then @@ -1040,7 +1043,7 @@ create_package() { local ret=0 [[ -f $pkg_file ]] && rm -f "$pkg_file" - [[ -f $pkg_file.sig ]] && rm -f "$pkg_file.sig" + [[ -f ${pkg_file}${SIG_EXT} ]] && rm -f "${pkg_file}${SIG_EXT}" # when fileglobbing, we want * in an empty directory to expand to # the null string rather than itself @@ -1067,7 +1070,7 @@ create_package() { if (( ! ret )) && [[ "$PKGDEST" != "${startdir}" ]]; then ln -sf "${pkg_file}" "${pkg_file/$PKGDEST/$startdir}" ret=$? - [[ -f $pkg_file.sig ]] && ln -sf "$pkg_file.sig" "${pkg_file/$PKGDEST/$startdir}.sig" + [[ -f ${pkg_file}${SIG_EXT} ]] && ln -sf "${pkg_file}${SIG_EXT}" "${pkg_file/$PKGDEST/$startdir}${SIG_EXT}" fi if (( ret )); then @@ -1082,13 +1085,24 @@ create_signature() { local ret=0 local filename="$1" msg "$(gettext "Signing package...")" - if [ ! $(type -p "gpg") ]; then - error "$(gettext "Cannot find the gpg binary! Is gnupg installed?")" + if [[ ! $(type -p "${GPG}") ]]; then + error "$(gettext "Cannot find the ${GPG} binary! Is gnupg installed?")" exit 1 # $E_MISSING_PROGRAM fi - gpg --detach-sign --use-agent "$filename" || ret=$? + + # Check if SIGNKEY is valid. + local SIGNWITHKEY="" + if [[ "${SIGNKEY}" ]]; then + if ! ${GPG} --list-key "${SIGNKEY}" 1>/dev/null 2>&1; then + error "$(gettext "The key ${SIGNKEY} doesn\'t exist.")" + exit 1 + fi + SIGNWITHKEY="-u ${SIGNKEY}" + fi + # The signature will be generated directly in ascii-friendly format + ${GPG} --detach-sign ${SIGNWITHKEY} "$filename" || ret=$? if (( ! ret )); then - msg2 "$(gettext "Created signature file %s.")" "$filename.sig" + msg2 "$(gettext "Created signature file %s.")" "${filename}${SIG_EXT}" else warning "$(gettext "Failed to sign package file.")" fi @@ -1269,7 +1283,7 @@ check_sanity() { local i for i in 'changelog' 'install'; do - local filelist=$(sed -n "s/^[[:space:]]*$i=//p" "$BUILDFILE") + local filelist=$(sed -n "s/^[[:space:]]*$i=//p" "$BUILDFILE") local file for file in $filelist; do # evaluate any bash variables used @@ -1557,6 +1571,7 @@ usage() { echo "$(gettext " --pkg <list> Only build listed packages from a split package")" echo "$(gettext " --skipinteg Do not fail when integrity checks are missing")" echo "$(gettext " --source Generate a source-only tarball without downloaded sources")" + echo "$(gettext " --signwithkey Selects an specific key to use for signing")" echo echo "$(gettext "These options can be passed to pacman:")" echo @@ -1592,7 +1607,7 @@ OPT_SHORT="AcCdefFghiLmop:rRsV" OPT_LONG="allsource,asroot,ignorearch,clean,cleancache,nodeps" OPT_LONG+=",noextract,force,forcever:,geninteg,help,holdver" OPT_LONG+=",install,log,nocolor,nobuild,pkg:,rmdeps,repackage,skipinteg" -OPT_LONG+=",source,syncdeps,version,config:" +OPT_LONG+=",source,syncdeps,version,config:,signwithkey" # Pacman Options OPT_LONG+=",noconfirm,noprogressbar" OPT_TEMP="$(parse_options $OPT_SHORT $OPT_LONG "$@" || echo 'PARSE_OPTIONS FAILED')" @@ -1635,6 +1650,7 @@ while true; do --skipinteg) SKIPINTEG=1 ;; --source) SOURCEONLY=1 ;; -s|--syncdeps) DEP_BIN=1 ;; + --signwithkey) shift; SIGNKEY=$1 ;; -h|--help) usage; exit 0 ;; # E_OK -V|--version) version; exit 0 ;; # E_OK diff --git a/scripts/repo-add.sh.in b/scripts/repo-add.sh.in index 4ee63d8..ac734aa 100644 --- a/scripts/repo-add.sh.in +++ b/scripts/repo-add.sh.in @@ -26,6 +26,8 @@ export TEXTDOMAINDIR='@localedir@' myver='@PACKAGE_VERSION@' confdir='@sysconfdir@' +GPG="gpg2" +SIG_EXT=".sig" QUIET=0 SIGN=0 @@ -62,8 +64,8 @@ error() { # print usage instructions usage() { printf "repo-add, repo-remove (pacman) %s\n\n" "$myver" - printf "$(gettext "Usage: repo-add [-q] [-s] [-v] <path-to-db> <package|delta> ...\n")" - printf "$(gettext "Usage: repo-remove [-q] <path-to-db> <packagename|delta> ...\n\n")" + printf "$(gettext "Usage: repo-add [-q] [-s [-k|--signwithkey key]] [-v] <path-to-db> <package|delta> ...\n")" + printf "$(gettext "Usage: repo-remove [-q] [-s [-k|--signwithkey key]] <path-to-db> <packagename|delta> ...\n\n")" printf "$(gettext "\ repo-add will update a package database by reading a package file.\n\ Multiple packages to add can be specified on the command line.\n\n")" @@ -185,13 +187,24 @@ create_signature() { local dbfile="$1" local ret=0 msg "$(gettext "Signing database...")" - if [ ! $(type -p "gpg") ]; then - error "$(gettext "Cannot find the gpg binary! Is gnupg installed?")" + if [ ! $(type -p "${GPG}") ]; then + error "$(gettext "Cannot find the ${GPG} binary! Is gnupg installed?")" exit 1 # $E_MISSING_PROGRAM fi - gpg --detach-sign --use-agent "$dbfile" || ret=$? + + # Check if SIGNKEY is valid. + local SIGNWITHKEY="" + if [[ "${SIGNKEY}" ]]; then + if ! "${GPG}" --list-key "${SIGNKEY}" 1>/dev/null 2>&1; then + error "$(gettext "The key ${SIGNKEY} doesnn't exist.")" + exit 1 + fi + SIGNWITHKEY="-u ${SIGNKEY}" + fi + echo "${GPG} --detach-sign ${SIGNWITHKEY} $dbfile" + ${GPG} --detach-sign ${SIGNWITHKEY} "$dbfile" || ret=$? if (( ! ret )); then - msg2 "$(gettext "Created signature file %s.")" "$dbfile.sig" + msg2 "$(gettext "Created signature file %s.")" "${dbfile}${SIG_EXT}" else warning "$(gettext "Failed to sign package database.")" fi @@ -203,15 +216,15 @@ verify_signature() { local dbfile="$1" local ret=0 msg "$(gettext "Verifying database signature...")" - if [ ! $(type -p "gpg") ]; then - error "$(gettext "Cannot find the gpg binary! Is gnupg installed?")" + if [ ! $(type -p "${GPG}") ]; then + error "$(gettext "Cannot find the ${GPG} binary! Is gnupg installed?")" exit 1 # $E_MISSING_PROGRAM fi - if [[ ! -f $dbfile.sig ]]; then + if [[ ! -f ${dbfile}${SIG_EXT} ]]; then warning "$(gettext "No existing signature found, skipping verification.")" return fi - gpg --verify "$dbfile.sig" || ret=$? + ${GPG} --verify "${dbfile}${SIG_EXT}" || ret=$? if (( ! ret )); then msg2 "$(gettext "Database signature file verified.")" else @@ -298,9 +311,9 @@ db_write_entry() echo -e "%MD5SUM%\n$md5sum\n" >>desc # add base64'd PGP signature - if [[ -f $startdir/$pkgfile.sig ]]; then + if [[ -f $startdir/$pkgfile${SIG_EXT} ]]; then echo -e "%PGPSIG%" >>desc - echo -e "$(openssl base64 -in "$startdir/$pkgfile.sig" | tr -d '\n')\n" >>desc + echo -e "$(openssl enc -base64 -in "$startdir/$pkgfile${SIG_EXT}")\n" >>desc fi [[ -n $url ]] && echo -e "%URL%\n$url\n" >>desc @@ -492,10 +505,24 @@ trap 'trap_exit "$(gettext "An unknown error has occured. Exiting...")"' ERR success=0 # parse arguments -for arg in "$@"; do +while [[ $# > 0 ]] ; do + arg="$1" case "$arg" in -q|--quiet) QUIET=1;; - -s|--sign) SIGN=1;; + -s|--sign) + SIGN=1 + # The signature will be made, even if there are no operations + success=1 + ;; + -k|--signwithkey) + shift + SIGNKEY="$1" + # Check if key really exists + if ! ${GPG} --list-key ${SIGNKEY} 1> /dev/null 2>&1; then + error "$(gettext "Cannot find key $SIGNKEY.")" + exit 1 + fi + ;; -v|--verify) VERIFY=1;; *) if [[ -z $REPO_DB_FILE ]]; then @@ -510,6 +537,7 @@ for arg in "$@"; do fi ;; esac + shift done # if at least one operation was a success, re-zip database @@ -529,18 +557,19 @@ if (( success )); then cd "$tmpdir" if [[ -n $(ls) ]]; then bsdtar -c${TAR_OPT}f "$filename" * - create_signature "$filename" else # we have no packages remaining? zip up some emptyness warning "$(gettext "No packages remain, creating empty database.")" bsdtar -c${TAR_OPT}f "$filename" -T /dev/null fi + # The signature must be dealt with in both cases, empty repo or not. + create_signature "$filename" cd "$startdir" [[ -f $REPO_DB_FILE ]] && mv -f "$REPO_DB_FILE" "${REPO_DB_FILE}.old" - [[ -f $REPO_DB_FILE.sig ]] && rm -f "$REPO_DB_FILE.sig" + [[ -f $REPO_DB_FILE${SIG_EXT} ]] && rm -f "$REPO_DB_FILE${SIG_EXT}" [[ -f $tmpdir/$filename ]] && mv "$tmpdir/$filename" "$REPO_DB_FILE" - [[ -f $tmpdir/$filename.sig ]] && mv "$tmpdir/$filename.sig" "$REPO_DB_FILE.sig" + [[ -f $tmpdir/$filename${SIG_EXT} ]] && mv "$tmpdir/$filename${SIG_EXT}" "$REPO_DB_FILE${SIG_EXT}" dblink="${REPO_DB_FILE%.tar.*}" ln -sf "$REPO_DB_FILE" "$dblink" 2>/dev/null || \ ln -f "$REPO_DB_FILE" "$dblink" 2>/dev/null || \ -- 1.7.1.1
On 27/07/10 06:26, Denis A. Altoé Falqueto wrote:
There is a possibility of another key being used, instead of the user's default. For exemple, the pacman-keyring package will be signed by a master key, because it needs to be trusted explicitly by the user before the installation of that package. So, the parameter --signwithkey will be used to supply an id of a key that will be used to sign a database or package.
Signed-off-by: Denis A. Altoé Falqueto<denisfalqueto@gmail.com> --- scripts/makepkg.sh.in | 38 ++++++++++++++++++++-------- scripts/repo-add.sh.in | 63 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 73 insertions(+), 28 deletions(-)
I would prefer this patch to be split into makepkg and repo-add parts and have the documentation patch similarly split and then the changes and documentation merged together.
diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in index 080e530..f6f9dfe 100644 --- a/scripts/makepkg.sh.in +++ b/scripts/makepkg.sh.in @@ -28,7 +28,7 @@ # makepkg uses quite a few external programs during its execution. You # need to have at least the following installed for makepkg to function: # bsdtar (libarchive), bzip2, coreutils, fakeroot, find (findutils), -# gettext, grep, gzip, openssl, sed, tput (ncurses), xz +# gettext, grep, gzip, openssl, sed, tput (ncurses), xz, gpg
Try keeping this alphabetical.
# gettext initialization export TEXTDOMAIN='pacman' @@ -43,6 +43,8 @@ BUILDSCRIPT='@BUILDSCRIPT@' startdir="$PWD" srcdir="$startdir/src" pkgdir="$startdir/pkg" +GPG="gpg2"
Why change to gpg2? In fact, I would prefer a separate patch that changes the "gpg" references to $GPG (=gpg by default) but allows the value to be overridden by environmental variables. So if someone wants to use gpg2 they would do something like "GPG=gpg2 makepkg".
+SIG_EXT=".sig" We consider ".sig" hard-coded in pacman. We should do the same here.
<snip> otherwise makepkg changes are fine.
diff --git a/scripts/repo-add.sh.in b/scripts/repo-add.sh.in index 4ee63d8..ac734aa 100644 --- a/scripts/repo-add.sh.in +++ b/scripts/repo-add.sh.in @@ -26,6 +26,8 @@ export TEXTDOMAINDIR='@localedir@'
myver='@PACKAGE_VERSION@' confdir='@sysconfdir@' +GPG="gpg2" +SIG_EXT=".sig"
Same comments as applied to makepkg.
QUIET=0 SIGN=0 @@ -62,8 +64,8 @@ error() { # print usage instructions usage() { printf "repo-add, repo-remove (pacman) %s\n\n" "$myver" - printf "$(gettext "Usage: repo-add [-q] [-s] [-v]<path-to-db> <package|delta> ...\n")" - printf "$(gettext "Usage: repo-remove [-q]<path-to-db> <packagename|delta> ...\n\n")" + printf "$(gettext "Usage: repo-add [-q] [-s [-k|--signwithkey key]] [-v]<path-to-db> <package|delta> ...\n")" + printf "$(gettext "Usage: repo-remove [-q] [-s [-k|--signwithkey key]]<path-to-db> <packagename|delta> ...\n\n")"
Just list the short option here as is done with all other options. <snip>
@@ -492,10 +505,24 @@ trap 'trap_exit "$(gettext "An unknown error has occured. Exiting...")"' ERR
success=0 # parse arguments -for arg in "$@"; do +while [[ $#> 0 ]] ; do + arg="$1" case "$arg" in -q|--quiet) QUIET=1;; - -s|--sign) SIGN=1;; + -s|--sign) + SIGN=1 + # The signature will be made, even if there are no operations + success=1 + ;; + -k|--signwithkey) + shift + SIGNKEY="$1" + # Check if key really exists
We have not done any checks for the gpg binary at this stage...
+ if ! ${GPG} --list-key ${SIGNKEY} 1> /dev/null 2>&1; then + error "$(gettext "Cannot find key $SIGNKEY.")" + exit 1 + fi + ;; -v|--verify) VERIFY=1;; *) if [[ -z $REPO_DB_FILE ]]; then @@ -510,6 +537,7 @@ for arg in "$@"; do fi ;; esac + shift done
# if at least one operation was a success, re-zip database @@ -529,18 +557,19 @@ if (( success )); then cd "$tmpdir" if [[ -n $(ls) ]]; then bsdtar -c${TAR_OPT}f "$filename" * - create_signature "$filename" else # we have no packages remaining? zip up some emptyness warning "$(gettext "No packages remain, creating empty database.")" bsdtar -c${TAR_OPT}f "$filename" -T /dev/null fi + # The signature must be dealt with in both cases, empty repo or not. + create_signature "$filename"
Good catch
makepkg and repo-add got a new option, so it is possible to select the key used for signing. makepkg.conf got a new option in BUILDENV, so the new packages built with makepkg will be signed in the process of building. pacman.conf got a new option for repositories. VerifySig will enable verification of signatures in repositories that support them. Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com> --- doc/makepkg.8.txt | 4 ++++ doc/makepkg.conf.5.txt | 6 +++--- doc/pacman.conf.5.txt | 20 ++++++++++++++++++++ doc/repo-add.8.txt | 7 +++++-- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/doc/makepkg.8.txt b/doc/makepkg.8.txt index a2fdb3f..4d8f26b 100644 --- a/doc/makepkg.8.txt +++ b/doc/makepkg.8.txt @@ -161,6 +161,10 @@ Options (Passed to pacman) Prevent pacman from displaying a progress bar; useful if you are redirecting makepkg output to file. +*\--signwithkey*:: + Select a specific key to be used to sign the package. If absent, + the default from the keyring key will be used. + Additional Features ------------------- diff --git a/doc/makepkg.conf.5.txt b/doc/makepkg.conf.5.txt index a565bd6..f82bc19 100644 --- a/doc/makepkg.conf.5.txt +++ b/doc/makepkg.conf.5.txt @@ -94,9 +94,9 @@ Options PKGBUILD options array. *sign*;; - Generate a PGP signature file using GnuPG. This will execute `gpg - --detach-sign --use-agent` on the built package to generate a detached - signature file, using the GPG agent if it is available. The signature + Generate a PGP signature file using GnuPG. This will execute `gpg2 + --detach-sign` on the built package to generate a detached signature + file, using the GPG agent if it is available. The signature file will be the entire filename of the package with a ``.sig'' extension. diff --git a/doc/pacman.conf.5.txt b/doc/pacman.conf.5.txt index 8c83232..16d1c89 100644 --- a/doc/pacman.conf.5.txt +++ b/doc/pacman.conf.5.txt @@ -204,6 +204,26 @@ listed first will take precedence over those listed later in the file when packages in two repositories have identical names, regardless of version number. +There is an option to allow the verification of digital signatures for +repositories that support them. The option is 'VerifySig' and the possible +values are: + +*Always*:: + Will enforce the verification of signatures as a requirement to + update the database. If there is no signature in the source location + or if the signature is not valid, the updating of this repository + is aborted. + +*Optional*:: + The verification of signatures will be made, but if there is no + signature in the source location, it will proceed with the updating. + The only situation of error will be when the database doesn't match + with the signature (download problem or real mismatch of signature). + +*Never*:: + There will be no verification of signatures for this repository. This + is the default. + Using Your Own Repository ------------------------- If you have numerous custom packages of your own, it is often easier to generate diff --git a/doc/repo-add.8.txt b/doc/repo-add.8.txt index e6cc940..0a5d980 100644 --- a/doc/repo-add.8.txt +++ b/doc/repo-add.8.txt @@ -10,9 +10,9 @@ repo-add - package database maintenance utility Synopsis -------- -repo-add [-q] <path-to-db> <package1> [<package2> ...] +repo-add [-q] [-s [-k|\--signwithkey key]] <path-to-db> <package1> [<package2> ...] -repo-remove [-q] <path-to-db> <packagename> [<packagename2> ...] +repo-remove [-q] [-s [-k|\--signwithkey key]] <path-to-db> <packagename> [<packagename2> ...] Description @@ -40,6 +40,9 @@ Options signature file, using the GPG agent if it is available. The signature file will be the entire filename of the database with a ``.sig'' extension. +*-k, \--sighwithkey key*:: + Select a specific key to be used for the signing of the database file. + If absent, the default key from the default keyring will be used. See Also -------- -- 1.7.1.1
On Mon, 26 Jul 2010 17:26:00 -0300 Denis A. Altoé Falqueto <denisfalqueto@gmail.com> wrote:
So, I changed the code to use gpgme. Here are the patches for your evaluation.
By the way, I'm not {angry,upset,crying} :) I really want to see package signing in pacman, but I know that this is a complex issue that will need lots of discussion.
Below, follows a little explanation of the general idea of each patch.
[PATCH 1/5] pacman-key: keyring management tool
The script that helps with management for pacman keyring. It uses gpg, instead of gpg2 and is heavly inpired on apt-key, from debian. It is very straightforward.
[PATCH 2/5] Signature verification functions
Two functions: one for signatures in memory and another for signatures in files. Signatures of packages are stored in the repository and are copied to memory before verification. The signatures of database files are stored on files, hence the new function.
[PATCH 3/5] Verify the signatures of databases and packages
The calls for the signature functions. Verification of database updates and package instalations from the repositories.
I didn't worry about local instalations, but it doesn't mean they are not there. If it were verified before (as Dan suggests), they are there. :)
A point raised by Dan was that the reading of the signature from the repository was too complex. The reaasoning behind it is that signatures grow according to the size of the key used to sign. So, we can't be never sure if some buffer size is really enough. Maybe it is enough now, but in the future it may be not and we'll have a new bug in the bugtracker. My implementation is simple and robust, so it will work with any signature size.
[PATCH 4/5] Parameter to select key to sign
Just a new parameter to allow the packager to select the key he wants to use. if the key is not specified, his default key will be used.
[PATCH 5/5] Document new options related to package signing
Just documentation. No imporant comment.
As always, comments and suggestions are welcome.
-- Denis A. Altoé Falqueto
This is really encouraging Denis, could you possibly update your Wiki article with a status report? http://wiki.archlinux.org/index.php/Package_Signing_Proposal_for_Pacman Or maybe someone could summarise what the situation is now so us impatient folk can surmise how close we are to seeing gpg signing in Pacman. Denis have you also considering the hash function that is used when signing? It seems that sha256 is considered the best to use at the moment. That is until sha-3 is finalised in 2012. thanks, Ananda
On Tue, Jul 27, 2010 at 11:11 PM, Ananda Samaddar <ananda@samaddar.co.uk> wrote:
This is really encouraging Denis, could you possibly update your Wiki article with a status report?
http://wiki.archlinux.org/index.php/Package_Signing_Proposal_for_Pacman
Or maybe someone could summarise what the situation is now so us impatient folk can surmise how close we are to seeing gpg signing in Pacman. Denis have you also considering the hash function that is used when signing? It seems that sha256 is considered the best to use at the moment. That is until sha-3 is finalised in 2012.
Well, the current status is very the following: the gpg branch from Allan's repository is quite advanced and only some finishing touches are needed. My patches are supposed to be those touches, I hope. But there will be lots of discussions before they can be merged. For example, now we are discussing the pacman-key management tool. We all want a high level of quality, so every possible detail will be raised and the best solution will come out. I just wouldn't hold my breath for anything yet. About the hash functions, it depends on the type of key used for the signature, mainly. There are usually DSA or RSA keys, which don't use SHA-1 anymore, according to Wikipedia. In fact, what gpg uses, we'll use too. Still according to wikipedia [1], it is very hard to break a OpenPGP encryption (it doesn't talk about signatures though, but I presume it is similar). It shouldn't be a concern. [1] http://en.wikipedia.org/wiki/Pretty_Good_Privacy#Security_quality -- A: Because it obfuscates the reading. Q: Why is top posting so bad? ------------------------------------------- Denis A. Altoe Falqueto -------------------------------------------
participants (7)
-
Allan McRae
-
Ananda Samaddar
-
Daenyth Blank
-
Dan McGee
-
Denis A. Altoé Falqueto
-
Guillaume ALAUX
-
Guillaume ALAUX