diff options
| author | Dan McGee <dan@archlinux.org> | 2011-07-18 21:10:56 -0500 | 
|---|---|---|
| committer | Dan McGee <dan@archlinux.org> | 2011-07-18 21:10:56 -0500 | 
| commit | 61410814c2cb33055e0eb0276444b9073c3a1e7a (patch) | |
| tree | 242c60dd71b701222f6018f15fe2a8b77f197bb6 /scripts | |
| parent | 05f7c0280e31d25e0b9b5c5cca349eb93e24f04f (diff) | |
| parent | 333269482a08e02d4e730dd739fc9e8625efde32 (diff) | |
Merge remote-tracking branch 'allan/pacman-key'
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/Makefile.am | 3 | ||||
| -rw-r--r-- | scripts/pacman-key.sh.in | 347 | 
2 files changed, 209 insertions, 141 deletions
diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 8491b743..adb259a7 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -80,7 +80,8 @@ pacman-db-upgrade: \  pacman-key: \  	$(srcdir)/pacman-key.sh.in \ -	$(srcdir)/library/output_format.sh +	$(srcdir)/library/output_format.sh \ +	$(srcdir)/library/parse_options.sh  pacman-optimize: \  	$(srcdir)/pacman-optimize.sh.in \ diff --git a/scripts/pacman-key.sh.in b/scripts/pacman-key.sh.in index 833943cb..cb108acc 100644 --- a/scripts/pacman-key.sh.in +++ b/scripts/pacman-key.sh.in @@ -26,33 +26,48 @@ export TEXTDOMAINDIR='@localedir@'  myver="@PACKAGE_VERSION@" +# Options +ADD=0 +DELETE=0 +EDITKEY=0 +EXPORT=0 +FINGER=0 +INIT=0 +LIST=0 +RECEIVE=0 +RELOAD=0 +UPDATEDB=0 +VERIFY=0 +  m4_include(library/output_format.sh) +m4_include(library/parse_options.sh) +  usage() {  	printf "pacman-key (pacman) %s\n" ${myver}  	echo -	printf "$(gettext "Usage: %s [options] <command> [arguments]")\n" $(basename $0) +	printf "$(gettext "Usage: %s [options]")\n" $(basename $0)  	echo  	printf "$(gettext "Manage pacman\'s list of trusted keys")\n"  	echo -	echo "$(gettext "Options must be placed before commands. The available options are:")" -	printf "$(gettext "  --config <file>  Use an alternate config file (instead of '%s')")\n" "$CONFIG" -	printf "$(gettext "  --gpgdir         Set an alternate directory for gnupg (instead of '%s')")\n" "$PACMAN_KEYRING_DIR" -	echo -	echo "$(gettext "The available commands are:")" +	echo "$(gettext "Options:")"  	echo "$(gettext "  -a, --add [<file(s)>]     Add the specified keys (empty for stdin)")" -	echo "$(gettext "  -d, --del <keyid(s)>      Remove the specified keyids")" -	echo "$(gettext "  -e, --export <keyid(s)>   Export the specified keyids")" +	echo "$(gettext "  -d, --delete <keyid(s)>   Remove the specified keyids")" +	echo "$(gettext "  -e, --export [<keyid(s)>] Export the specified or all keyids")"  	echo "$(gettext "  -f, --finger [<keyid(s)>] List fingerprint for specified or all keyids")"  	echo "$(gettext "  -h, --help                Show this help message and exit")"  	echo "$(gettext "  -l, --list                List keys")"  	echo "$(gettext "  -r, --receive <keyserver> <keyid(s)> Fetch the specified keyids")" -	echo "$(gettext "  -t, --trust <keyid(s)>    Set the trust level of the given keyids")"  	echo "$(gettext "  -u, --updatedb            Update the trustdb of pacman")" +	echo "$(gettext "  -v, --verify <signature>  Verify the file specified by the signature")"  	echo "$(gettext "  -V, --version             Show program version")" -	echo "$(gettext "  --adv <params>            Use pacman's keyring with advanced gpg commands")" -	printf "$(gettext "  --reload                  Reload the default keys")" -	echo +	echo "$(gettext "  --config <file>           Use an alternate config file")" +	printf "$(gettext "                                    (instead of '%s')")\n" "@sysconfdir@/pacman.conf" +	echo "$(gettext "  --edit-key <keyid(s)>     Present a menu for key management task on keyids")" +	echo "$(gettext "  --gpgdir <dir>            Set an alternate directory for gnupg")" +	printf "$(gettext "                                    (instead of '%s')")\n" "@sysconfdir@/pacman.d/gnupg" +	echo "$(gettext "  --init                    Ensure the keyring is properly initialized")" +	echo "$(gettext "  --reload                  Reload the default keys")"  }  version() { @@ -63,62 +78,110 @@ This is free software; see the source for copying conditions.\n\  There is NO WARRANTY, to the extent permitted by law.\n")"  } -# Read provided file and search for values matching the given key -# The contents of the file are expected to be in this format: key = value -# 'key', 'equal sign' and 'value' can be surrounded by random whitespace -# Usage: get_from "$file" "$key" # returns the value for the first matching key in the file +# read the config file "$1" which has key=value pairs, and return the key which +# matches "$2". the equals sign between pairs may be surrounded by any amount +# of whitespace.  get_from() { -	while read key _ value; do -		if [[ $key = $2 ]]; then -			echo "$value" -			break +	while IFS='=' read -r key value; do +		[[ -z $key || ${key:0:1} = '#' ]] && continue + +		if [[ ${key%% *} = "$2" && -n ${value##* } ]]; then +			echo "${value##* }" +			return 0  		fi  	done < "$1" +	return 1  } -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}" +initialize() { +	# Check for simple existence rather than for a directory as someone +	# may want to use a symlink here +	[[ -e ${PACMAN_KEYRING_DIR} ]] || mkdir -p -m 755 "${PACMAN_KEYRING_DIR}" -	# Variable used for iterating on keyrings -	local key -	local key_id +	# keyring files +	[[ -f ${PACMAN_KEYRING_DIR}/pubring.gpg ]] || touch ${PACMAN_KEYRING_DIR}/pubring.gpg +	[[ -f ${PACMAN_KEYRING_DIR}/secring.gpg ]] || touch ${PACMAN_KEYRING_DIR}/secring.gpg +	[[ -f ${PACMAN_KEYRING_DIR}/trustdb.gpg ]] || ${GPG_PACMAN} --update-trustdb +	chmod 644 ${PACMAN_KEYRING_DIR}/{{pub,sec}ring,trustdb}.gpg -	# Keyring with keys to be added to the keyring -	local ADDED_KEYS="${PACMAN_SHARE_DIR}/addedkeys.gpg" +	# gpg.conf +	[[ -f ${PACMAN_KEYRING_DIR}/gpg.conf ]] || touch  ${PACMAN_KEYRING_DIR}/gpg.conf +	chmod 644 ${PACMAN_KEYRING_DIR}/gpg.conf +	if ! grep -w -q "lock-never" ${PACMAN_KEYRING_DIR}/gpg.conf &>/dev/null; then +		echo "lock-never" >> ${PACMAN_KEYRING_DIR}/gpg.conf +	fi +} -	# Keyring with keys that were deprecated and will eventually be deleted -	local DEPRECATED_KEYS="${PACMAN_SHARE_DIR}/deprecatedkeys.gpg" +check_keyring() { +	if [[ ! -r ${PACMAN_KEYRING_DIR}/pubring.gpg || \ +			! -r ${PACMAN_KEYRING_DIR}/secring.gpg || \ +			! -r ${PACMAN_KEYRING_DIR}/trustdb.gpg ]]; then +		error "$(gettext "You do not have sufficient permissions to read the %s keyring...")" "pacman" +		msg "$(gettext "Use '%s' to correct the keyring permissions.")" "pacman-key --init" +		exit 1 +	fi -	# List of keys removed from the keyring. This file is not a keyring, unlike the others. -	# It is a textual list of values that gpg recogniezes as identifiers for keys. -	local REMOVED_KEYS="${PACMAN_SHARE_DIR}/removedkeys" +	if (( (EXPORT || FINGER || LIST || VERIFY) && EUID != 0 )); then +		if ! grep -w -q "lock-never" ${PACMAN_KEYRING_DIR}/gpg.conf &>/dev/null; then +			error "$(gettext "You do not have sufficient permissions to run this command...")" +			msg "$(gettext "Use '%s' to correct the keyring permissions.")" "pacman-key --init" +			exit 1 +		fi +	fi + +} + +verify_keyring_input() { +	local ret=0;  	# Verify signatures of related files, if they exist  	if [[ -r "${ADDED_KEYS}" ]]; then  		msg "$(gettext "Verifying official keys file signature...")" -		if ! ${GPG_PACMAN} --quiet --batch --verify "${ADDED_KEYS}.sig" 1>/dev/null; then +		if ! ${GPG_PACMAN} --verify "${ADDED_KEYS}.sig" &>/dev/null; then  			error "$(gettext "The signature of file %s is not valid.")" "${ADDED_KEYS}" -			exit 1 +			ret=1  		fi  	fi  	if [[ -r "${DEPRECATED_KEYS}" ]]; then  		msg "$(gettext "Verifying deprecated keys file signature...")" -		if ! ${GPG_PACMAN} --quiet --batch --verify "${DEPRECATED_KEYS}.sig" 1>/dev/null; then +		if ! ${GPG_PACMAN} --verify "${DEPRECATED_KEYS}.sig" &>/dev/null; then  			error "$(gettext "The signature of file %s is not valid.")" "${DEPRECATED_KEYS}" -			exit 1 +			ret=1  		fi  	fi  	if [[ -r "${REMOVED_KEYS}" ]]; then  		msg "$(gettext "Verifying deleted keys file signature...")" -		if ! ${GPG_PACMAN} --quiet --batch --verify "${REMOVED_KEYS}.sig"; then +		if ! ${GPG_PACMAN} --verify "${REMOVED_KEYS}.sig" &>/dev/null; then  			error "$(gettext "The signature of file %s is not valid.")" "${REMOVED_KEYS}" -			exit 1 +			ret=1  		fi  	fi +	return errors +} + +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}" + +	# Variable used for iterating on keyrings +	local key +	local key_id + +	# Keyring with keys to be added to the keyring +	local ADDED_KEYS="${PACMAN_SHARE_DIR}/addedkeys.gpg" + +	# Keyring with keys that were deprecated and will eventually be deleted +	local DEPRECATED_KEYS="${PACMAN_SHARE_DIR}/deprecatedkeys.gpg" + +	# List of keys removed from the keyring. This file is not a keyring, unlike the others. +	# It is a textual list of values that gpg recogniezes as identifiers for keys. +	local REMOVED_KEYS="${PACMAN_SHARE_DIR}/removedkeys" + +	verify_keyring_input || exit 1 +  	# Read the key ids to an array. The conversion from whatever is inside the file  	# to key ids is important, because key ids are the only guarantee of identification  	# for the keys. @@ -126,12 +189,12 @@ reload_keyring() {  	if [[ -r "${REMOVED_KEYS}" ]]; then  		while read key; do  			local key_values name -			key_values=$(${GPG_PACMAN} --quiet --with-colons --list-key "${key}" | grep ^pub | cut -d: -f5,10 --output-delimiter=' ') +			key_values="$(${GPG_PACMAN} --quiet --with-colons --list-key "${key}" | grep ^pub | cut -d: -f5,10 --output-delimiter=' ')"  			if [[ -n $key_values ]]; then  				# The first word is the key_id -				key_id=${key_values%% *} +				key_id="${key_values%% *}"  				# the rest if the name of the owner -				name=${key_values#* } +				name="${key_values#* }"  				if [[ -n ${key_id} ]]; then  					# Mark this key to be deleted  					removed_ids[$key_id]="$name" @@ -141,12 +204,12 @@ reload_keyring() {  	fi  	# List of keys that must be kept installed, even if in the list of keys to be removed -	local HOLD_KEYS=$(get_from "$CONFIG" "HoldKeys") +	local HOLD_KEYS="$(get_from "$CONFIG" "HoldKeys")"  	# Remove the keys that must be kept from the set of keys that should be removed  	if [[ -n ${HOLD_KEYS} ]]; then  		for key in ${HOLD_KEYS}; do -			key_id=$(${GPG_PACMAN} --quiet --with-colons --list-key "${key}" | grep ^pub | cut -d: -f5) +			key_id="$(${GPG_PACMAN} --quiet --with-colons --list-key "${key}" | grep ^pub | cut -d: -f5)"  			if [[ -n "${removed_ids[$key_id]}" ]]; then  				unset removed_ids[$key_id]  			fi @@ -157,7 +220,7 @@ reload_keyring() {  	# be updated automatically.  	if [[ -r "${ADDED_KEYS}" ]]; then  		msg "$(gettext "Appending official keys...")" -		local add_keys=$(${GPG_NOKEYRING} --keyring "${ADDED_KEYS}" --with-colons --list-keys | grep ^pub | cut -d: -f5) +		local add_keys="$(${GPG_NOKEYRING} --keyring "${ADDED_KEYS}" --with-colons --list-keys | grep ^pub | cut -d: -f5)"  		for key_id in ${add_keys}; do  			# There is no point in adding a key that will be deleted right after  			if [[ -z "${removed_ids[$key_id]}" ]]; then @@ -168,7 +231,7 @@ reload_keyring() {  	if [[ -r "${DEPRECATED_KEYS}" ]]; then  		msg "$(gettext "Appending deprecated keys...")" -		local add_keys=$(${GPG_NOKEYRING} --keyring "${DEPRECATED_KEYS}" --with-colons --list-keys | grep ^pub | cut -d: -f5) +		local add_keys="$(${GPG_NOKEYRING} --keyring "${DEPRECATED_KEYS}" --with-colons --list-keys | grep ^pub | cut -d: -f5)"  		for key_id in ${add_keys}; do  			# There is no point in adding a key that will be deleted right after  			if [[ -z "${removed_ids[$key_id]}" ]]; then @@ -191,6 +254,30 @@ reload_keyring() {  	${GPG_PACMAN} --batch --check-trustdb  } +receive_keys() { +	if [[ -z ${KEYIDS[@]} ]]; then +		error "$(gettext "You need to specify the keyserver and at least one key identifier")" +		exit 1 +	fi +	${GPG_PACMAN} --keyserver "$KEYSERVER" --recv-keys "${KEYIDS[@]}" +} + +edit_keys() { +	local errors=0; +	for key in ${KEYIDS[@]}; do +		# Verify if the key exists in pacman's keyring +		if ! ${GPG_PACMAN} --list-keys "$key" &>/dev/null; then +			error "$(gettext "The key identified by %s does not exist")" "$key" +			errors=1; +		fi +	done +	(( errors )) && exit 1; + +	for key in ${KEYIDS[@]}; do +		${GPG_PACMAN} --edit-key "$key" +	done +} +  # PROGRAM START  if ! type gettext &>/dev/null; then  	gettext() { @@ -198,116 +285,96 @@ if ! type gettext &>/dev/null; then  	}  fi -if [[ $1 != "--version" && $1 != "-V" && $1 != "--help" && $1 != "-h" && $1 != "" ]]; 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 +OPT_SHORT="a::d:e:f::hlr:uv:V" +OPT_LONG="add::,config:,delete:,edit-key:,export::,finger::,gpgdir:" +OPT_LONG+=",help,init,list,receive:,reload,updatedb,verify:,version" +if ! OPT_TEMP="$(parse_options $OPT_SHORT $OPT_LONG "$@")"; then +	echo; usage; exit 1 # E_INVALID_OPTION;  fi +eval set -- "$OPT_TEMP" +unset OPT_SHORT OPT_LONG OPT_TEMP -# Parse global options -CONFIG="@sysconfdir@/pacman.conf" -PACMAN_KEYRING_DIR="@sysconfdir@/pacman.d/gnupg" -while [[ $1 =~ ^--(config|gpgdir)$ ]]; do +if [[ $1 == "--" ]]; then +	usage; +	exit 0; +fi + +while true; do  	case "$1" in -		--config) shift; CONFIG="$1" ;; -		--gpgdir) shift; PACMAN_KEYRING_DIR="$1" ;; +		-a|--add)         ADD=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYFILES=($1) ;; +		--config)         shift; CONFIG=$1 ;; +		-d|--delete)      DELETE=1; shift; KEYIDS=($1) ;; +		--edit-key)       EDITKEY=1; shift; KEYIDS=($1) ;; +		-e|--export)      EXPORT=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; +		-f|--finger)      FINGER=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; +		--gpgdir)         shift; PACMAN_KEYRING_DIR=$1 ;; +		--init)           INIT=1 ;; +		-l|--list)        LIST=1 ;; +		-r|--receive)     RECEIVE=1; shift; KEYSERVER="${1[0]}"; KEYIDS=("${1[@]:1}") ;; +		--reload)         RELOAD=1 ;; +		-u|--updatedb)    UPDATEDB=1 ;; +		-v|--verify)      VERIFY=1; shift; SIGNATURE=$1 ;; + +		-h|--help)        usage; exit 0 ;; +		-V|--version)     version; exit 0 ;; + +		--)               OPT_IND=0; shift; break;; +		*)                usage; exit 1 ;;  	esac  	shift  done -if [[ ! -r "${CONFIG}" ]]; then -	error "$(gettext "%s not found.")" "$CONFIG" + +if ! type -p gpg >/dev/null; then +    error "$(gettext "Cannot find the %s binary required for all %s operations.")" "gpg" "pacman-key" +	exit 1 +fi + +if (( (ADD || DELETE || EDITKEY || INIT || RECEIVE || RELOAD || UPDATEDB) && EUID != 0 )); then +	error "$(gettext "%s needs to be run as root for this operation.")" "pacman-key"  	exit 1  fi -# Read GPGDIR from $CONFIG. -if [[ GPGDIR=$(get_from "$CONFIG" "GPGDir") == 0 ]]; then -	PACMAN_KEYRING_DIR="${GPGDIR}" +CONFIG=${CONFIG:-@sysconfdir@/pacman.conf} +if [[ ! -r "${CONFIG}" ]]; then +	error "$(gettext "%s configuation file '%s' not found.")" "pacman" "$CONFIG" +	exit 1  fi + +# if PACMAN_KEYRING_DIR isn't assigned, try to get it from the config +# file, falling back on a hard default +PACMAN_KEYRING_DIR=${PACMAN_KEYRING_DIR:-$(get_from "$CONFIG" "GPGDir" || echo "@sysconfdir@/pacman.d/gnupg")} +  GPG_PACMAN="gpg --homedir ${PACMAN_KEYRING_DIR} --no-permission-warning" -# Try to create $PACMAN_KEYRING_DIR if non-existent -# Check for simple existence rather than for a directory as someone may want -# to use a symlink here -[[ -e ${PACMAN_KEYRING_DIR} ]] || mkdir -p -m 755 "${PACMAN_KEYRING_DIR}" +# check only a single operation has been given +numopt=$(( ADD + DELETE + EDITKEY + EXPORT + FINGER + INIT + LIST + RECEIVE + RELOAD + UPDATEBD + VERIFY )) -# Parse and execute command -command="$1" -if [[ -z "${command}" ]]; then +if (( ! numopt )); then +	error "$(gettext "No operations specified")" +	echo  	usage  	exit 1  fi -shift - -case "${command}" in -	-a|--add) -		# If there is no extra parameter, gpg will read stdin -		${GPG_PACMAN} --quiet --batch --import "$@" -		;; -	-d|--del) -		if (( $# == 0 )); then -			error "$(gettext "You need to specify at least one key identifier")" -			exit 1 -		fi -		${GPG_PACMAN} --quiet --batch --delete-key --yes "$@" -		;; -	-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) -		${GPG_PACMAN} --armor --export "$@" -		;; -	-r|--receive) -		if (( $# < 2 )); then -			error "$(gettext "You need to specify the keyserver and at least one key identifier")" -			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")" -			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 -		;; -	-h|--help) -		usage; exit 0 ;; -	-V|--version) -		version; exit 0 ;; -	*) -		error "$(gettext "Unknown command:") $command" -		usage; exit 1 ;; -esac + +if (( numopt != 1 )); then +	error "$(gettext "Multiple operations specified")" +	printf "$(gettext "Please run %s with each operation separately\n")" "pacman-key" +	exit 1 +fi + +(( ! INIT )) && check_keyring + +(( ADD )) && ${GPG_PACMAN} --quiet --batch --import "${KEYFILES[@]}" +(( DELETE )) && ${GPG_PACMAN} --quiet --batch --delete-key --yes "${KEYIDS[@]}" +(( EDITKEY )) && edit_keys +(( EXPORT )) && ${GPG_PACMAN} --armor --export "${KEYIDS[@]}" +(( FINGER )) && ${GPG_PACMAN} --batch --fingerprint "${KEYIDS[@]}" +(( INIT )) && initialize +(( LIST )) && ${GPG_PACMAN} --batch --list-sigs "${KEYIDS[@]}" +(( RECEIVE )) && receive_keys +(( RELOAD )) && reload_keyring +(( UPDATEDB )) && ${GPG_PACMAN} --batch --check-trustdb +(( VERIFY )) && ${GPG_PACMAN} --verify $SIGNATURE  # vim: set ts=2 sw=2 noet:  | 
