3 # pacman-key - manages pacman's keyring
4 # Based on apt-key, from Debian
7 # Copyright (c) 2010-2011 Pacman Development Team <pacman-dev@archlinux.org>
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 # gettext initialization
24 export TEXTDOMAIN
='pacman-scripts'
25 export TEXTDOMAINDIR
='@localedir@'
27 myver
="@PACKAGE_VERSION@"
45 m4_include
(library
/output_format.sh
)
47 m4_include
(library
/parse_options.sh
)
50 printf "pacman-key (pacman) %s\n" ${myver}
52 printf "$(gettext "Usage
: %s
[options
]")\n" $
(basename $0)
54 printf "$(gettext "Manage pacman
\'s list of trusted keys
")\n"
56 echo "$(gettext "Options
:")"
57 echo "$(gettext " -a, --add [<file(s
)>] Add the specified keys
(empty
for stdin
)")"
58 echo "$(gettext " -d, --delete <keyid
(s
)> Remove the specified keyids
")"
59 echo "$(gettext " -e, --export [<keyid
(s
)>] Export the specified or all keyids
")"
60 echo "$(gettext " -f, --finger [<keyid
(s
)>] List fingerprint
for specified or all keyids
")"
61 echo "$(gettext " -h, --help Show this
help message and
exit")"
62 echo "$(gettext " -l, --list-keys [<keyid
(s
)>] List the specified or all keys
")"
63 echo "$(gettext " -r, --receive <keyserver
> <keyid
(s
)> Fetch the specified keyids
")"
64 echo "$(gettext " -u, --updatedb Update the trustdb of pacman
")"
65 echo "$(gettext " -v, --verify <signature
> Verify the
file specified by the signature
")"
66 echo "$(gettext " -V, --version Show program version
")"
67 printf "$(gettext " --config <file> Use an alternate config
file (instead of
\n\
68 '%s')")\n" "@sysconfdir@/pacman.conf"
69 echo "$(gettext " --edit-key <keyid
(s
)> Present a menu
for key management task on keyids
")"
70 printf "$(gettext " --gpgdir <dir
> Set an alternate directory
for GnuPG
(instead
\n\
71 of
'%s')")\n" "@sysconfdir@/pacman.d/gnupg"
72 echo "$(gettext " --import <dir
(s
)> Imports pubring.gpg and trustdb.gpg from dir
(s
)")"
73 echo "$(gettext " --import-trustdb <dir
(s
)> Imports ownertrust values from trustdb.gpg
in dir
(s
)")"
74 echo "$(gettext " --init Ensure the keyring is properly initialized
")"
75 echo "$(gettext " --list-sigs [<keyid
(s
)>] List keys and their signatures
")"
76 echo "$(gettext " --reload Reload the default keys
")"
80 printf "pacman-key (pacman) %s\n" "${myver}"
82 Copyright
(c
) 2010-2011 Pacman Development Team
<pacman-dev@archlinux.org
>.
\n\
83 This is free software
; see the
source for copying conditions.
\n\
84 There is NO WARRANTY
, to the extent permitted by law.
\n")"
87 # read the config file "$1" which has key=value pairs, and return the key which
88 # matches "$2". the equals sign between pairs may be surrounded by any amount
91 while IFS
='=' read -r key value
; do
92 [[ -z $key ||
${key:0:1} = '#' ]] && continue
94 if [[ ${key%% *} = "$2" && -n ${value##* } ]]; then
103 # Check for simple existence rather than for a directory as someone
104 # may want to use a symlink here
105 [[ -e ${PACMAN_KEYRING_DIR} ]] || mkdir
-p -m 755 "${PACMAN_KEYRING_DIR}"
108 [[ -f ${PACMAN_KEYRING_DIR}/pubring.gpg
]] ||
touch ${PACMAN_KEYRING_DIR}/pubring.gpg
109 [[ -f ${PACMAN_KEYRING_DIR}/secring.gpg
]] ||
touch ${PACMAN_KEYRING_DIR}/secring.gpg
110 [[ -f ${PACMAN_KEYRING_DIR}/trustdb.gpg
]] ||
"${GPG_PACMAN[@]}" --update-trustdb
111 chmod 644 ${PACMAN_KEYRING_DIR}/{{pub
,sec
}ring
,trustdb
}.gpg
114 [[ -f ${PACMAN_KEYRING_DIR}/gpg.conf
]] ||
touch ${PACMAN_KEYRING_DIR}/gpg.conf
115 chmod 644 ${PACMAN_KEYRING_DIR}/gpg.conf
116 if ! grep -w -q "lock-never" ${PACMAN_KEYRING_DIR}/gpg.conf
&>/dev
/null
; then
117 echo "lock-never" >> ${PACMAN_KEYRING_DIR}/gpg.conf
122 if [[ ! -r ${PACMAN_KEYRING_DIR}/pubring.gpg || \
123 ! -r ${PACMAN_KEYRING_DIR}/secring.gpg || \
124 ! -r ${PACMAN_KEYRING_DIR}/trustdb.gpg
]]; then
125 error
"$(gettext "You
do not have sufficient permissions to
read the
%s keyring...
")" "pacman"
126 msg
"$(gettext "Use
'%s' to correct the keyring permissions.
")" "pacman-key --init"
130 if (( (EXPORT || FINGER || LIST || VERIFY
) && EUID
!= 0 )); then
131 if ! grep -w -q "lock-never" ${PACMAN_KEYRING_DIR}/gpg.conf
&>/dev
/null
; then
132 error
"$(gettext "You
do not have sufficient permissions to run this
command...
")"
133 msg
"$(gettext "Use
'%s' to correct the keyring permissions.
")" "pacman-key --init"
140 verify_keyring_input
() {
143 # Verify signatures of related files, if they exist
144 if [[ -r "${ADDED_KEYS}" ]]; then
145 msg
"$(gettext "Verifying official keys
file signature...
")"
146 if ! "${GPG_PACMAN[@]}" --verify "${ADDED_KEYS}.sig" &>/dev
/null
; then
147 error
"$(gettext "The signature of
file %s is not valid.
")" "${ADDED_KEYS}"
152 if [[ -r "${DEPRECATED_KEYS}" ]]; then
153 msg
"$(gettext "Verifying deprecated keys
file signature...
")"
154 if ! "${GPG_PACMAN[@]}" --verify "${DEPRECATED_KEYS}.sig" &>/dev
/null
; then
155 error
"$(gettext "The signature of
file %s is not valid.
")" "${DEPRECATED_KEYS}"
160 if [[ -r "${REMOVED_KEYS}" ]]; then
161 msg
"$(gettext "Verifying deleted keys
file signature...
")"
162 if ! "${GPG_PACMAN[@]}" --verify "${REMOVED_KEYS}.sig" &>/dev
/null
; then
163 error
"$(gettext "The signature of
file %s is not valid.
")" "${REMOVED_KEYS}"
172 local PACMAN_SHARE_DIR
='@prefix@/share/pacman'
173 local GPG_NOKEYRING
=(gpg
--batch --quiet --ignore-time-conflict --no-options --no-default-keyring --homedir ${PACMAN_KEYRING_DIR})
175 # Variable used for iterating on keyrings
179 # Keyring with keys to be added to the keyring
180 local ADDED_KEYS
="${PACMAN_SHARE_DIR}/addedkeys.gpg"
182 # Keyring with keys that were deprecated and will eventually be deleted
183 local DEPRECATED_KEYS
="${PACMAN_SHARE_DIR}/deprecatedkeys.gpg"
185 # List of keys removed from the keyring. This file is not a keyring, unlike the others.
186 # It is a textual list of values that gpg recogniezes as identifiers for keys.
187 local REMOVED_KEYS
="${PACMAN_SHARE_DIR}/removedkeys"
189 verify_keyring_input ||
exit 1
191 # Read the key ids to an array. The conversion from whatever is inside the file
192 # to key ids is important, because key ids are the only guarantee of identification
195 if [[ -r "${REMOVED_KEYS}" ]]; then
197 local key_values name
198 key_values
="$("${GPG_PACMAN[@]}" --quiet --with-colons --list-key "${key}" | grep ^pub | cut -d: -f5,10 --output-delimiter=' ')"
199 if [[ -n $key_values ]]; then
200 # The first word is the key_id
201 key_id
="${key_values%% *}"
202 # the rest if the name of the owner
203 name
="${key_values#* }"
204 if [[ -n ${key_id} ]]; then
205 # Mark this key to be deleted
206 removed_ids
[$key_id]="$name"
209 done < "${REMOVED_KEYS}"
212 # List of keys that must be kept installed, even if in the list of keys to be removed
213 local HOLD_KEYS
="$(get_from "$CONFIG" "HoldKeys
")"
215 # Remove the keys that must be kept from the set of keys that should be removed
216 if [[ -n ${HOLD_KEYS} ]]; then
217 for key
in ${HOLD_KEYS}; do
218 key_id
="$("${GPG_PACMAN[@]}" --quiet --with-colons --list-key "${key}" | grep ^pub | cut -d: -f5)"
219 if [[ -n "${removed_ids[$key_id]}" ]]; then
220 unset removed_ids
[$key_id]
225 # Add keys from the current set of keys from pacman-keyring package. The web of trust will
226 # be updated automatically.
227 if [[ -r "${ADDED_KEYS}" ]]; then
228 msg
"$(gettext "Appending official keys...
")"
229 local add_keys
="$("${GPG_NOKEYRING[@]}" --keyring "${ADDED_KEYS}" --with-colons --list-keys | grep ^pub | cut -d: -f5)"
230 for key_id
in ${add_keys}; do
231 # There is no point in adding a key that will be deleted right after
232 if [[ -z "${removed_ids[$key_id]}" ]]; then
233 "${GPG_NOKEYRING[@]}" --keyring "${ADDED_KEYS}" --export "${key_id}" | "${GPG_PACMAN[@]}" --import
238 if [[ -r "${DEPRECATED_KEYS}" ]]; then
239 msg
"$(gettext "Appending deprecated keys...
")"
240 local add_keys
="$("${GPG_NOKEYRING[@]}" --keyring "${DEPRECATED_KEYS}" --with-colons --list-keys | grep ^pub | cut -d: -f5)"
241 for key_id
in ${add_keys}; do
242 # There is no point in adding a key that will be deleted right after
243 if [[ -z "${removed_ids[$key_id]}" ]]; then
244 "${GPG_NOKEYRING[@]}" --keyring "${DEPRECATED_KEYS}" --export "${key_id}" | "${GPG_PACMAN[@]}" --import
249 # Remove the keys not marked to keep
250 if (( ${#removed_ids[@]} > 0 )); then
251 msg
"$(gettext "Removing deleted keys from keyring...
")"
252 for key_id
in "${!removed_ids[@]}"; do
253 echo " removing key $key_id - ${removed_ids[$key_id]}"
254 "${GPG_PACMAN[@]}" --quiet --batch --yes --delete-key "${key_id}"
258 # Update trustdb, just to be sure
259 msg
"$(gettext "Updating trust database...
")"
260 "${GPG_PACMAN[@]}" --batch --check-trustdb
264 if [[ -z ${KEYIDS[@]} ]]; then
265 error
"$(gettext "You need to specify the keyserver and
at least one key identifier
")"
268 "${GPG_PACMAN[@]}" --keyserver "$KEYSERVER" --recv-keys "${KEYIDS[@]}"
273 for key
in ${KEYIDS[@]}; do
274 # Verify if the key exists in pacman's keyring
275 if ! "${GPG_PACMAN[@]}" --list-keys "$key" &>/dev
/null
; then
276 error
"$(gettext "The key identified by
%s does not exist
")" "$key"
280 (( errors
)) && exit 1;
282 for key
in ${KEYIDS[@]}; do
283 "${GPG_PACMAN[@]}" --edit-key "$key"
289 local trustdb
=$
(mktemp
)
290 "${GPG_PACMAN[@]}" --export-ownertrust > ${trustdb}
292 for importdir
in "${IMPORT_DIRS[@]}"; do
293 if [[ -f "${importdir}/trustdb.gpg" ]]; then
294 gpg
--homedir "${importdir}" --export-ownertrust >> ${trustdb}
298 "${GPG_PACMAN[@]}" --import-ownertrust ${trustdb}
305 # Imports public keys, then import trustdbs
306 for importdir
in "${IMPORT_DIRS[@]}"; do
307 if [[ -f "${importdir}/pubring.gpg" ]]; then
308 "${GPG_PACMAN[@]}" --quiet --batch --import "${importdir}/pubring.gpg"
316 if ! type gettext &>/dev
/null
; then
322 OPT_SHORT
="a::d:e:f::hl::r:uv:V"
323 OPT_LONG
="add::,config:,delete:,edit-key:,export::,finger::,gpgdir:"
324 OPT_LONG
+=",help,import:,import-trustdb:,init,list-keys::,list-sigs::,,receive:"
325 OPT_LONG
+=",reload,updatedb,verify:,version"
326 if ! OPT_TEMP
="$(parse_options $OPT_SHORT $OPT_LONG "$@
")"; then
327 echo; usage
; exit 1 # E_INVALID_OPTION;
329 eval set -- "$OPT_TEMP"
330 unset OPT_SHORT OPT_LONG OPT_TEMP
332 if [[ $1 == "--" ]]; then
339 -a|
--add) ADD
=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYFILES
=($1) ;;
340 --config) shift; CONFIG
=$1 ;;
341 -d|
--delete) DELETE
=1; shift; KEYIDS
=($1) ;;
342 --edit-key) EDITKEY
=1; shift; KEYIDS
=($1) ;;
343 -e|
--export) EXPORT
=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS
=($1) ;;
344 -f|
--finger) FINGER
=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS
=($1) ;;
345 --gpgdir) shift; PACMAN_KEYRING_DIR
=$1 ;;
346 --import) IMPORT
=1; shift; IMPORT_DIRS
=($1) ;;
347 --import-trustdb) IMPORT_TRUSTDB
=1; shift; IMPORT_DIRS
=($1) ;;
349 -l|
--list-keys) LISTKEYS
=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS
=($1) ;;
350 --list-sigs) LISTSIGS
=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS
=($1) ;;
351 -r|
--receive) RECEIVE
=1; shift; TMP
=($1); KEYSERVER
=${TMP[0]}; KEYIDS
=(${TMP[@]:1}); unset TMP
;;
352 --reload) RELOAD
=1 ;;
353 -u|
--updatedb) UPDATEDB
=1 ;;
354 -v|
--verify) VERIFY
=1; shift; SIGNATURE
=$1 ;;
356 -h|
--help) usage
; exit 0 ;;
357 -V|
--version) version
; exit 0 ;;
359 --) OPT_IND
=0; shift; break;;
366 if ! type -p gpg
>/dev
/null
; then
367 error
"$(gettext "Cannot
find the
%s binary required
for all
%s operations.
")" "gpg" "pacman-key"
371 if (( (ADD || DELETE || EDITKEY || IMPORT || IMPORT_TRUSTDB || INIT || RECEIVE || RELOAD || UPDATEDB
) && EUID
!= 0 )); then
372 error
"$(gettext "%s needs to be run as root
for this operation.
")" "pacman-key"
376 CONFIG
=${CONFIG:-@sysconfdir@/pacman.conf}
377 if [[ ! -r "${CONFIG}" ]]; then
378 error
"$(gettext "%s configuration
file '%s' not found.
")" "pacman" "$CONFIG"
382 # if PACMAN_KEYRING_DIR isn't assigned, try to get it from the config
383 # file, falling back on a hard default
384 PACMAN_KEYRING_DIR
=${PACMAN_KEYRING_DIR:-$(get_from "$CONFIG" "GPGDir" || echo "@sysconfdir@/pacman.d/gnupg")}
386 GPG_PACMAN
=(gpg
--homedir ${PACMAN_KEYRING_DIR} --no-permission-warning)
388 # check only a single operation has been given
389 numopt
=$
(( ADD
+ DELETE
+ EDITKEY
+ EXPORT
+ FINGER
+ IMPORT
+ IMPORT_TRUSTDB
+
390 INIT
+ LISTKEYS
+ LISTSIGS
+ RECEIVE
+ RELOAD
+ UPDATEDB
+ VERIFY
))
394 error
"$(gettext "no operation specified
(use
-h for help)")"
398 error
"$(gettext "Multiple operations specified
")"
399 printf "$(gettext "Please run
%s with each operation separately
\n")" "pacman-key"
404 (( ! INIT
)) && check_keyring
406 (( ADD
)) && "${GPG_PACMAN[@]}" --quiet --batch --import "${KEYFILES[@]}"
407 (( DELETE
)) && "${GPG_PACMAN[@]}" --quiet --batch --delete-key --yes "${KEYIDS[@]}"
408 (( EDITKEY
)) && edit_keys
409 (( EXPORT
)) && "${GPG_PACMAN[@]}" --armor --export "${KEYIDS[@]}"
410 (( FINGER
)) && "${GPG_PACMAN[@]}" --batch --fingerprint "${KEYIDS[@]}"
411 (( IMPORT
)) && import
412 (( IMPORT_TRUSTDB
)) && import_trustdb
413 (( INIT
)) && initialize
414 (( LISTKEYS
)) && "${GPG_PACMAN[@]}" --batch --list-keys "${KEYIDS[@]}"
415 (( LISTSIGS
)) && "${GPG_PACMAN[@]}" --batch --list-sigs "${KEYIDS[@]}"
416 (( RECEIVE
)) && receive_keys
417 (( RELOAD
)) && reload_keyring
418 (( UPDATEDB
)) && "${GPG_PACMAN[@]}" --batch --check-trustdb
419 (( VERIFY
)) && "${GPG_PACMAN[@]}" --verify $SIGNATURE
421 # vim: set ts=2 sw=2 noet: