Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/bin/sh
- # Copyright 1999-2005 Gentoo Foundation
- # Copyright 2007 Aron Griffis <agriffis@n01se.net>
- # Copyright 2009-2015 Funtoo Technologies, LLC
- # lockfile() Copyright 2009 Parallels, Inc.
- # Distributed under the terms of the GNU General Public License v2
- # Originally authored by Daniel Robbins <drobbins@gentoo.org>
- # Maintained August 2002 - April 2003 by Seth Chandler <sethbc@gentoo.org>
- # Maintained and rewritten April 2004 - July 2007 by Aron Griffis <agriffis@n01se.net>
- # Maintained July 2009 - present by Daniel Robbins <drobbins@funtoo.org>
- version=2.8.1
- PATH="${PATH:-/usr/bin:/bin:/sbin:/usr/sbin:/usr/ucb}"
- maintainer="drobbins@funtoo.org"
- zero=`basename "$0"`
- unset mesglog
- unset myaction
- unset agentsopt
- havelock=false
- unset hostopt
- ignoreopt=false
- noaskopt=false
- noguiopt=false
- nolockopt=false
- lockwait=5
- openssh=unknown
- sunssh=unknown
- confhost=unknown
- sshconfig=false
- quickopt=false
- quietopt=false
- clearopt=false
- color=true
- inheritwhich=local-once
- unset stopwhich
- unset timeout
- unset ssh_timeout
- attempts=1
- unset sshavail
- unset sshkeys
- unset gpgkeys
- unset mykeys
- keydir="${HOME}/.keychain"
- unset envf
- evalopt=false
- queryopt=false
- confirmopt=false
- absoluteopt=false
- systemdopt=false
- unset ssh_confirm
- unset GREP_OPTIONS
- realpath_bin="`command -v realpath`"
- BLUE="[34;01m"
- CYAN="[36;01m"
- CYANN="[36m"
- GREEN="[32;01m"
- RED="[31;01m"
- PURP="[35;01m"
- OFF="[0m"
- # GNU awk and sed have regex issues in a multibyte environment. If any locale
- # variables are set, then override by setting LC_ALL
- unset pinentry_locale
- lvars=`locale 2>/dev/null | egrep -v '="?(|POSIX|C)"?$' 2>/dev/null`
- if [ -n "$lvars$LANG$LC_ALL" ]; then
- # save LC_ALL so that pinentry-curses works right. This has always worked
- # correctly for me but peper and kloeri had problems with it.
- pinentry_lc_all="$LC_ALL"
- LC_ALL=C
- export LC_ALL
- fi
- # synopsis: qprint "message"
- qprint() {
- $quietopt || echo "$*" >&2
- }
- # synopsis: mesg "message"
- # Prettily print something to stderr, honors quietopt
- mesg() {
- qprint " ${GREEN}*${OFF} $*"
- }
- # synopsis: warn "message"
- # Prettily print a warning to stderr
- warn() {
- echo " ${RED}* Warning${OFF}: $*" >&2
- }
- # synopsis: error "message"
- # Prettily print an error
- error() {
- echo " ${RED}* Error${OFF}: $*" >&2
- }
- # synopsis: die "message"
- # Prettily print an error, then abort
- die() {
- [ -n "$1" ] && error "$*"
- qprint
- $evalopt && { echo; echo "false;"; }
- exit 1
- }
- # synopsis: versinfo
- # Display the version information
- versinfo() {
- qprint
- qprint " Copyright ${CYANN}2002-2006${OFF} Gentoo Foundation;"
- qprint " Copyright ${CYANN}2007${OFF} Aron Griffis;"
- qprint " Copyright ${CYANN}2009-2015${OFF} Funtoo Technologies, LLC;"
- qprint " lockfile() Copyright ${CYANN}2009${OFF} Parallels, Inc."
- qprint
- qprint " Keychain is free software: you can redistribute it and/or modify"
- qprint " it under the terms of the ${CYANN}GNU General Public License version 2${OFF} as"
- qprint " published by the Free Software Foundation."
- qprint
- }
- # synopsis: helpinfo
- # Display the help information. There's no really good way to use qprint for
- # this...
- helpinfo() {
- cat >&1 <<EOHELP
- SYNOPSIS
- keychain [ ${GREEN}-hklQqV${OFF} ] [ ${GREEN}--clear${OFF} ${GREEN}--confhost${OFF} ${GREEN}--help${OFF} ${GREEN}--ignore-missing${OFF} ${GREEN}--list${OFF}
- ${GREEN}--noask${OFF} ${GREEN}--nocolor${OFF} ${GREEN}--nogui${OFF} ${GREEN}--nolock${OFF} ${GREEN}--quick${OFF} ${GREEN}--quiet${OFF} ${GREEN}--version${OFF} ]
- [ ${GREEN}--agents${OFF} ${CYAN}list${OFF} ] [ ${GREEN}--attempts${OFF} ${CYAN}num${OFF} ] [ ${GREEN}--dir${OFF} ${CYAN}dirname${OFF} ]
- [ ${GREEN}--host${OFF} ${CYAN}name${OFF} ] [ ${GREEN}--lockwait${OFF} ${CYAN}seconds${OFF} ]
- [ ${GREEN}--stop${OFF} ${CYAN}which${OFF} ] [ ${GREEN}--timeout${OFF} ${CYAN}minutes${OFF} ] [ keys... ]
- OPTIONS
- ${GREEN}--agents${OFF} ${CYAN}list${OFF}
- Start the agents listed. By default keychain will start ssh-agent if
- it is found in your path. The list should be comma-separated, for
- example "gpg,ssh"
- ${GREEN}--attempts${OFF} ${CYAN}num${OFF}
- Try num times to add keys before giving up. The default is 1.
- ${GREEN}--clear${OFF}
- Delete all of ssh-agent's keys. Typically this is used in
- .bash_profile. The theory behind this is that keychain should assume
- that you are an intruder until proven otherwise. However, while this
- option increases security, it still allows your cron jobs to use
- your ssh keys when you're logged out.
- ${GREEN}--confhost${OFF}
- By default, keychain will look for key pairs in the ~/.ssh/
- directory. The ${GREEN}--confhost${OFF} option will inform keychain to look in
- ~/.ssh/config for IdentityFile settings defined for particular
- hosts, and use these paths to locate keys.
- ${GREEN}--confirm${OFF}
- Keys are subject to interactive confirmation by the SSH_ASKPASS
- program before being used for authentication. See the ${GREEN}-c${OFF} option for
- ssh-add(1).
- ${GREEN}--absolute${OFF}
- Any arguments to "--dir" are interpreted to be absolute. The default
- behavior is to append "/.keychain" to the argument for backwards
- compatibility.
- ${GREEN}--dir${OFF} ${CYAN}dirname${OFF}
- Keychain will use dirname rather than \$HOME/.keychain
- ${GREEN}--query${OFF}
- Keychain will print lines in KEY=value format representing the
- values which are set by the agents.
- ${GREEN}--eval${OFF}
- Keychain will print lines to be evaluated in the shell on stdout. It
- respects the SHELL environment variable to determine if Bourne shell
- or C shell output is expected.
- ${GREEN}--env${OFF} ${CYAN}filename${OFF}
- After parsing options, keychain will load additional environment
- settings from "filename". By default, if "--env" is not given, then
- keychain will attempt to load from ~/.keychain/[hostname]-env or
- alternatively ~/.keychain/env. The purpose of this file is to
- override settings such as PATH, in case ssh is stored in a
- non-standard place.
- ${GREEN}-h${OFF} ${GREEN}--help${OFF}
- Show help that looks remarkably like this man-page. As of 2.6.10,
- help is sent to stdout so it can be easily piped to a pager.
- ${GREEN}--host${OFF} ${CYAN}name${OFF}
- Set alternate hostname for creation of pidfiles
- ${GREEN}--ignore-missing${OFF}
- Don't warn if some keys on the command-line can't be found. This is
- useful for situations where you have a shared .bash_profile, but
- your keys might not be available on every machine where keychain is
- run.
- ${GREEN}--inherit${OFF} ${CYAN}which${OFF}
- Attempt to inherit agent variables from the environment. This can be
- useful in a variety of circumstances, for example when ssh-agent is
- started by gdm. The following values are valid for "which":
- local Inherit when a pid (e.g. SSH_AGENT_PID) is set in the
- environment. This disallows inheriting a forwarded
- agent.
- any Inherit when a sock (e.g. SSH_AUTH_SOCK) is set in the
- environment. This allows inheriting a forwarded agent.
- local-once Same as "local", but only inherit if keychain isn't
- already providing an agent.
- any-once Same as "any", but only inherit if keychain isn't
- already providing an agent.
- By default, keychain-2.5.0 and later will behave as if "--inherit
- local-once" is specified. You should specify "--noinherit" if you
- want the older behavior.
- ${GREEN}-l${OFF} ${GREEN}--list${OFF}
- List signatures of all active SSH keys, and exit, similar to
- "ssh-add ${GREEN}-l${OFF}".
- ${GREEN}--lockwait${OFF} ${CYAN}seconds${OFF}
- How long to wait for the lock to become available. Defaults to 5
- seconds. Specify a value of zero or more. If the lock cannot be
- acquired within the specified number of seconds, then this keychain
- process will forcefully acquire the lock.
- ${GREEN}--noask${OFF}
- This option tells keychain do everything it normally does (ensure
- ssh-agent is running, set up the ~/.keychain/[hostname]-{c}sh files)
- except that it will not prompt you to add any of the keys you
- specified if they haven't yet been added to ssh-agent.
- ${GREEN}--nocolor${OFF}
- Disable color hilighting for non ANSI-compatible terms.
- ${GREEN}--nogui${OFF}
- Don't honor SSH_ASKPASS, if it is set. This will cause ssh-add to
- prompt on the terminal instead of using a graphical program.
- ${GREEN}--noinherit${OFF}
- Don't inherit any agent processes, overriding the default "--inherit
- local-once"
- ${GREEN}--nolock${OFF}
- Don't attempt to use a lockfile while manipulating files, pids and
- keys.
- ${GREEN}-k${OFF} ${GREEN}--stop${OFF} ${CYAN}which${OFF}
- Kill currently running agent processes. The following values are
- valid for "which":
- ${GREEN}--systemd${OFF}
- Inject environment variables into the systemd ${GREEN}--user${OFF} session.
- all Kill all agent processes and quit keychain immediately.
- Prior to keychain-2.5.0, this was the behavior of the bare
- "--stop" option.
- others Kill agent processes other than the one keychain is
- providing. Prior to keychain-2.5.0, keychain would do this
- automatically. The new behavior requires that you specify
- it explicitly if you want it.
- mine Kill keychain's agent processes, leaving other agents
- alone.
- ${GREEN}-Q${OFF} ${GREEN}--quick${OFF}
- If an ssh-agent process is running then use it. Don't verify the
- list of keys, other than making sure it's non-empty. This option
- avoids locking when possible so that multiple terminals can be
- opened simultaneously without waiting on each other.
- ${GREEN}-q${OFF} ${GREEN}--quiet${OFF}
- Only print messages in case of warning, error or required
- interactivity. As of version 2.6.10, this also suppresses
- "Identities added" messages for ssh-agent.
- ${GREEN}--timeout${OFF} ${CYAN}minutes${OFF}
- Set a timeout in minutes on your keys. This is conveyed to ssh-agent
- which does the actual timing out of keys since keychain doesn't run
- continuously.
- ${GREEN}-V${OFF} ${GREEN}--version${OFF}
- Show version information.
- EOHELP
- }
- # synopsis: testssh
- # Figure out which ssh is in use, set the global boolean $openssh and $sunssh
- testssh() {
- # Query local host for SSH application, presently supporting
- # OpenSSH, Sun SSH, and ssh.com
- openssh=false
- sunssh=false
- case "`ssh -V 2>&1`" in
- *OpenSSH*) openssh=true ;;
- *Sun?SSH*) sunssh=true ;;
- esac
- }
- # synopsis: getuser
- # Set the global string $me
- getuser() {
- # whoami gives euid, which might be different from USER or LOGNAME
- me=`whoami` || die "Who are you? whoami doesn't know..."
- }
- # synopsis: getos
- # Set the global string $OSTYPE
- getos() {
- OSTYPE=`uname` || die 'uname failed'
- }
- # synopsis: verifykeydir
- # Make sure the key dir is set up correctly. Exits on error.
- verifykeydir() {
- # Create keydir if it doesn't exist already
- if [ -f "${keydir}" ]; then
- die "${keydir} is a file (it should be a directory)"
- # Solaris 9 doesn't have -e; using -d....
- elif [ ! -d "${keydir}" ]; then
- ( umask 0077 && mkdir "${keydir}"; ) || die "can't create ${keydir}"
- fi
- }
- lockfile() {
- # This function originates from Parallels Inc.'s OpenVZ vpsreboot script
- # Description: This function attempts to acquire the lock. If it succeeds,
- # it returns 0. If it fails, it returns 1. This function retuns immediately
- # and only tries to acquire the lock once.
- local tmpfile="$lockf.$$"
- echo $$ >"$tmpfile" 2>/dev/null || exit
- if ln "$tmpfile" "$lockf" 2>/dev/null; then
- rm -f "$tmpfile"
- havelock=true && return 0
- fi
- if kill -0 `cat $lockf 2>/dev/null` 2>/dev/null; then
- rm -f "$tmpfile"
- return 1
- fi
- if ln "$tmpfile" "$lockf" 2>/dev/null; then
- rm -f "$tmpfile"
- havelock=true && return 0
- fi
- rm -f "$tmpfile" "$lockf" && return 1
- }
- takelock() {
- # Description: This function calls lockfile() multiple times if necessary
- # to try to acquire the lock. It returns 0 on success and 1 on failure.
- # Change in behavior: if timeout expires, we will forcefully acquire lock.
- [ "$havelock" = "true" ] && return 0
- [ "$nolockopt" = "true" ] && return 0
- # First attempt:
- lockfile && return 0
- local counter=0
- mesg "Waiting $lockwait seconds for lock..."
- while [ "$counter" -lt "$(( $lockwait * 2 ))" ]
- do
- lockfile && return 0
- sleep 0.5; counter=$(( $counter + 1 ))
- done
- rm -f "$lockf" && lockfile && return 0
- return 1
- }
- # synopsis: droplock
- # Drops the lock if we're holding it.
- droplock() {
- $havelock && [ -n "$lockf" ] && rm -f "$lockf"
- }
- # synopsis: findpids [prog]
- # Returns a space-separated list of agent pids.
- # prog can be ssh or gpg, defaults to ssh. Note that if another prog is ever
- # added, need to pay attention to the length for Solaris compatibility.
- findpids() {
- fp_prog=${1-ssh}
- unset fp_psout
- # Different systems require different invocations of ps. Try to generalize
- # the best we can. The only requirement is that the agent command name
- # appears in the line, and the PID is the first item on the line.
- [ -n "$OSTYPE" ] || getos
- # Try systems where we know what to do first
- case "$OSTYPE" in
- AIX|*bsd*|*BSD*|CYGWIN|darwin*|Linux|linux-gnu|OSF1)
- fp_psout=`ps x 2>/dev/null` ;; # BSD syntax
- HP-UX)
- fp_psout=`ps -u $me 2>/dev/null` ;; # SysV syntax
- SunOS)
- case `uname -r` in
- [56]*)
- fp_psout=`ps -u $me 2>/dev/null` ;; # SysV syntax
- *)
- fp_psout=`ps x 2>/dev/null` ;; # BSD syntax
- esac ;;
- GNU|gnu)
- fp_psout=`ps -g 2>/dev/null` ;; # GNU Hurd syntax
- esac
- # If we didn't get a match above, try a list of possibilities...
- # The first one will probably fail on systems supporting only BSD syntax.
- if [ -z "$fp_psout" ]; then
- fp_psout=`UNIX95=1 ps -u $me -o pid,comm 2>/dev/null | grep '^ *[0-9]'`
- [ -z "$fp_psout" ] && fp_psout=`ps x 2>/dev/null`
- fi
- # Return the list of pids; ignore case for Cygwin.
- # Check only 8 characters since Solaris truncates at that length.
- # Ignore defunct ssh-agents (bug 28599)
- if [ -n "$fp_psout" ]; then
- echo "$fp_psout" | \
- awk "BEGIN{IGNORECASE=1} /defunct/{next}
- /$fp_prog-[a]gen/{print \$1}" | xargs
- return 0
- fi
- # If none worked, we're stuck
- error "Unable to use \"ps\" to scan for $fp_prog-agent processes"
- error "Please report to $maintainer via http://bugs.gentoo.org"
- return 1
- }
- # synopsis: stopagent [prog]
- # --stop tells keychain to kill the existing agent(s)
- # prog can be ssh or gpg, defaults to ssh.
- stopagent() {
- stop_prog=${1-ssh}
- eval stop_except=\$\{${stop_prog}_agent_pid\}
- stop_mypids=`findpids "$stop_prog"`
- [ $? = 0 ] || die
- if [ -z "$stop_mypids" ]; then
- mesg "No $stop_prog-agent(s) found running"
- return 0
- fi
- case "$stopwhich" in
- all)
- kill $stop_mypids >/dev/null 2>&1
- mesg "All ${CYANN}$me${OFF}'s $stop_prog-agents stopped: ${CYANN}$stop_mypids${OFF}"
- ;;
- others)
- # Try to handle the case where we *will* inherit a pid
- kill -0 $stop_except >/dev/null 2>&1
- if [ -z "$stop_except" -o $? != 0 -o \
- "$inheritwhich" = local -o "$inheritwhich" = any ]; then
- if [ "$inheritwhich" != none ]; then
- eval stop_except=\$\{inherit_${stop_prog}_agent_pid\}
- kill -0 $stop_except >/dev/null 2>&1
- if [ -z "$stop_except" -o $? != 0 ]; then
- # Handle ssh2
- eval stop_except=\$\{inherit_${stop_prog}2_agent_pid\}
- fi
- fi
- fi
- # Filter out the running agent pid
- unset stop_mynewpids
- for stop_x in $stop_mypids; do
- [ $stop_x -eq $stop_except ] 2>/dev/null && continue
- stop_mynewpids="${stop_mynewpids+$stop_mynewpids }$stop_x"
- done
- if [ -n "$stop_mynewpids" ]; then
- kill $stop_mynewpids >/dev/null 2>&1
- mesg "Other ${CYANN}$me${OFF}'s $stop_prog-agents stopped: ${CYANN}$stop_mynewpids${OFF}"
- else
- mesg "No other $stop_prog-agent(s) than keychain's $stop_except found running"
- fi
- ;;
- mine)
- if [ $stop_except -gt 0 ] 2>/dev/null; then
- kill $stop_except >/dev/null 2>&1
- mesg "Keychain $stop_prog-agents stopped: ${CYANN}$stop_except${OFF}"
- else
- mesg "No keychain $stop_prog-agent found running"
- fi
- ;;
- esac
- # remove pid files if keychain-controlled
- if [ "$stopwhich" != others ]; then
- if [ "$stop_prog" != ssh ]; then
- rm -f "${pidf}-$stop_prog" "${cshpidf}-$stop_prog" "${fishpidf}-$stop_prog" 2>/dev/null
- else
- rm -f "${pidf}" "${cshpidf}" "${fishpidf}" 2>/dev/null
- fi
- eval unset ${stop_prog}_agent_pid
- fi
- }
- # synopsis: inheritagents
- # Save agent variables from the environment before they get wiped out
- inheritagents() {
- # Verify these global vars are null
- unset inherit_ssh_auth_sock inherit_ssh_agent_pid
- unset inherit_ssh2_auth_sock inherit_ssh2_agent_sock
- unset inherit_gpg_agent_info inherit_gpg_agent_pid
- # Save variables so we can inherit a running agent
- if [ "$inheritwhich" != none ]; then
- if wantagent ssh; then
- if [ -n "$SSH_AUTH_SOCK" ]; then
- inherit_ssh_auth_sock="$SSH_AUTH_SOCK"
- inherit_ssh_agent_pid="$SSH_AGENT_PID"
- fi
- if [ -n "$SSH2_AUTH_SOCK" ]; then
- inherit_ssh2_auth_sock="$SSH2_AUTH_SOCK"
- inherit_ssh2_agent_pid="$SSH2_AGENT_PID"
- fi
- fi
- if wantagent gpg; then
- if [ -n "$GPG_AGENT_INFO" ]; then
- inherit_gpg_agent_info="$GPG_AGENT_INFO"
- inherit_gpg_agent_pid=`echo "$GPG_AGENT_INFO" | cut -f2 -d:`
- # GnuPG v.2.1+ removes $GPG_AGENT_INFO
- elif [ -S "${GNUPGHOME:=$HOME/.gnupg}/S.gpg-agent" ]; then
- inherit_gpg_agent_pid=$(findpids gpg)
- inherit_gpg_agent_info="$GNUPGHOME/S.gpg-agent:${inherit_gpg_agent_pid}:1"
- fi
- fi
- fi
- }
- # synopsis: validinherit
- # Test inherit_* variables for validity
- validinherit() {
- vi_agent="$1"
- vi_status=0
- if [ "$vi_agent" = ssh ]; then
- if [ -n "$inherit_ssh_auth_sock" ]; then
- ls "$inherit_ssh_auth_sock" >/dev/null 2>&1
- if [ $? != 0 ]; then
- warn "SSH_AUTH_SOCK in environment is invalid; ignoring it"
- unset inherit_ssh_auth_sock inherit_ssh_agent_pid
- vi_status=1
- fi
- fi
- if [ -n "$inherit_ssh2_auth_sock" ]; then
- ls "$inherit_ssh2_auth_sock" >/dev/null 2>&1
- if [ $? != 0 ]; then
- warn "SSH2_AUTH_SOCK in environment is invalid; ignoring it"
- unset inherit_ssh2_auth_sock inherit_ssh2_agent_pid
- vi_status=1
- fi
- fi
- elif [ "$vi_agent" = gpg ]; then
- if [ -n "$inherit_gpg_agent_pid" ]; then
- kill -0 "$inherit_gpg_agent_pid" >/dev/null 2>&1
- if [ $? != 0 ]; then
- unset inherit_gpg_agent_pid inherit_gpg_agent_info
- warn "GPG_AGENT_INFO in environment is invalid; ignoring it"
- vi_status=1
- fi
- fi
- fi
- return $vi_status
- }
- # synopsis: catpidf_shell shell agents...
- # cat the pid files for the given agents. This is used by loadagents and also
- # for keychain output when --eval is given.
- catpidf_shell() {
- case "$1" in
- */fish|fish) cp_pidf="$fishpidf" ;;
- *csh) cp_pidf="$cshpidf" ;;
- *) cp_pidf="$pidf" ;;
- esac
- shift
- for cp_a in "$@"; do
- case "${cp_a}" in
- ssh) [ -f "$cp_pidf" ] && cat "$cp_pidf" ;;
- *) [ -f "${cp_pidf}-$cp_a" ] && cat "${cp_pidf}-$cp_a" ;;
- esac
- echo
- done
- return 0
- }
- # synopsis: catpidf agents...
- # cat the pid files for the given agents, appropriate for the current value of
- # $SHELL. This is used for keychain output when --eval is given.
- catpidf() {
- catpidf_shell "$SHELL" "$@"
- }
- # synopsis: loadagents agents...
- # Load agent variables from $pidf and copy implementation-specific environment
- # variables into generic global strings
- loadagents() {
- for la_a in "$@"; do
- case "$la_a" in
- ssh)
- unset SSH_AUTH_SOCK SSH_AGENT_PID SSH2_AUTH_SOCK SSH2_AGENT_PID
- eval "`catpidf_shell sh $la_a`"
- if [ -n "$SSH_AUTH_SOCK" ]; then
- ssh_auth_sock=$SSH_AUTH_SOCK
- ssh_agent_pid=$SSH_AGENT_PID
- elif [ -n "$SSH2_AUTH_SOCK" ]; then
- ssh_auth_sock=$SSH2_AUTH_SOCK
- ssh_agent_pid=$SSH2_AGENT_PID
- else
- unset ssh_auth_sock ssh_agent_pid
- fi
- ;;
- gpg)
- unset GPG_AGENT_INFO
- eval "`catpidf_shell sh $la_a`"
- if [ -n "$GPG_AGENT_INFO" ]; then
- la_IFS="$IFS" # save current IFS
- IFS=':' # set IFS to colon to separate PATH
- set -- $GPG_AGENT_INFO
- IFS="$la_IFS" # restore IFS
- gpg_agent_pid=$2
- fi
- ;;
- *)
- eval "`catpidf_shell sh $la_a`"
- ;;
- esac
- done
- return 0
- }
- # synopsis: startagent [prog]
- # Starts an agent if it isn't already running.
- # Requires $ssh_agent_pid
- startagent() {
- start_prog=${1-ssh}
- start_proto=${2-${start_prog}}
- unset start_pid
- start_inherit_pid=none
- start_mypids=`findpids "$start_prog"`
- [ $? = 0 ] || die
- # Unfortunately there isn't much way to genericize this without introducing
- # a lot more supporting code/structures.
- if [ "$start_prog" = ssh ]; then
- start_pidf="$pidf"
- start_cshpidf="$cshpidf"
- start_fishpidf="$fishpidf"
- start_pid="$ssh_agent_pid"
- if [ -n "$inherit_ssh_auth_sock" -o -n "$inherit_ssh2_auth_sock" ]; then
- if [ -n "$inherit_ssh_agent_pid" ]; then
- start_inherit_pid="$inherit_ssh_agent_pid"
- elif [ -n "$inherit_ssh2_agent_pid" ]; then
- start_inherit_pid="$inherit_ssh2_agent_pid"
- else
- start_inherit_pid="forwarded"
- fi
- fi
- else
- start_pidf="${pidf}-$start_prog"
- start_cshpidf="${cshpidf}-$start_prog"
- start_fishpidf="${fishpidf}-$start_prog"
- if [ "$start_prog" = gpg ]; then
- start_pid="$gpg_agent_pid"
- if [ -n "$inherit_gpg_agent_pid" ]; then
- start_inherit_pid="$inherit_gpg_agent_pid"
- fi
- else
- error "I don't know how to start $start_prog-agent (1)"
- return 1
- fi
- fi
- [ "$start_pid" -gt 0 ] 2>/dev/null || start_pid=none
- # This hack makes the case statement easier
- if [ "$inheritwhich" = any -o "$inheritwhich" = any-once ]; then
- start_fwdflg=forwarded
- else
- unset start_fwdflg
- fi
- # Check for an existing agent
- start_tester="$inheritwhich: $start_mypids $start_fwdflg "
- case "$start_tester" in
- none:*" $start_pid "*|*-once:*" $start_pid "*)
- mesg "Found existing ${start_prog}-agent: ${CYANN}$start_pid${OFF}"
- return 0
- ;;
- *:*" $start_inherit_pid "*)
- # This test was postponed until now to prevent generating warnings
- validinherit "$start_prog"
- if [ $? != 0 ]; then
- # inherit_* vars have been removed from the environment. Try
- # again now
- startagent "$start_prog"
- return $?
- fi
- mesg "Inheriting ${start_prog}-agent ($start_inherit_pid)"
- ;;
- *)
- # start_inherit_pid might be "forwarded" which we don't allow with,
- # for example, local-once (the default setting)
- start_inherit_pid=none
- ;;
- esac
- # Init the bourne-formatted pidfile
- ( umask 0177 && :> "$start_pidf"; )
- if [ $? != 0 ]; then
- rm -f "$start_pidf" "$start_cshpidf" "$start_fishpidf" 2>/dev/null
- error "can't create $start_pidf"
- return 1
- fi
- # Init the csh-formatted pidfile
- ( umask 0177 && :> "$start_cshpidf"; )
- if [ $? != 0 ]; then
- rm -f "$start_pidf" "$start_cshpidf" "$start_fishpidf" 2>/dev/null
- error "can't create $start_cshpidf"
- return 1
- fi
- # Init the fish-formatted pidfile
- ( umask 0177 && :> "$start_fishpidf"; )
- if [ $? != 0 ]; then
- rm -f "$start_pidf" "$start_cshpidf" "$start_fishpidf" 2>/dev/null
- error "can't create $start_fishpidf"
- return 1
- fi
- # Determine content for files
- unset start_out
- if [ "$start_inherit_pid" = none ]; then
- # Start the agent.
- # Branch again since the agents start differently
- mesg "Starting ${start_prog}-agent..."
- if [ "$start_prog" = ssh ]; then
- start_out=`ssh-agent`
- elif [ "$start_prog" = gpg ]; then
- if [ -n "${timeout}" ]; then
- start_gpg_timeout="--default-cache-ttl `expr $timeout \* 60`"
- else
- unset start_gpg_timeout
- fi
- # the 1.9.x series of gpg spews debug on stderr
- start_out=`gpg-agent --daemon --write-env-file $start_gpg_timeout 2>/dev/null`
- else
- error "I don't know how to start $start_prog-agent (2)"
- return 1
- fi
- if [ $? != 0 -a $? != 2 ]; then
- rm -f "$start_pidf" "$start_cshpidf" "$start_fishpidf" 2>/dev/null
- error "Failed to start ${start_prog}-agent"
- return 1
- fi
- elif [ "$start_prog" = ssh -a -n "$inherit_ssh_auth_sock" ]; then
- start_out="SSH_AUTH_SOCK=$inherit_ssh_auth_sock; export SSH_AUTH_SOCK;"
- if [ "$inherit_ssh_agent_pid" -gt 0 ] 2>/dev/null; then
- start_out="$start_out
- SSH_AGENT_PID=$inherit_ssh_agent_pid; export SSH_AGENT_PID;"
- fi
- elif [ "$start_prog" = ssh -a -n "$inherit_ssh2_auth_sock" ]; then
- start_out="SSH2_AUTH_SOCK=$inherit_ssh2_auth_sock; export SSH2_AUTH_SOCK;
- SSH2_AGENT_PID=$inherit_ssh2_agent_pid; export SSH2_AGENT_PID;"
- if [ "$inherit_ssh2_agent_pid" -gt 0 ] 2>/dev/null; then
- start_out="$start_out
- SSH2_AGENT_PID=$inherit_ssh2_agent_pid; export SSH2_AGENT_PID;"
- fi
- elif [ "$start_prog" = gpg -a -n "$inherit_gpg_agent_info" ]; then
- start_out="GPG_AGENT_INFO=$inherit_gpg_agent_info; export GPG_AGENT_INFO;"
- else
- die "something bad happened" # should never be here
- fi
- # Add content to pidfiles.
- # Some versions of ssh-agent don't understand -s, which means to
- # generate Bourne shell syntax. It appears they also ignore SHELL,
- # according to http://bugs.gentoo.org/show_bug.cgi?id=52874
- # So make no assumptions.
- start_out=`echo "$start_out" | grep -v 'Agent pid'`
- case "$start_out" in
- setenv*)
- echo "$start_out" >"$start_cshpidf"
- echo "$start_out" | awk '{print $2"="$3" export "$2";"}' >"$start_pidf"
- ;;
- *)
- echo "$start_out" >"$start_pidf"
- echo "$start_out" | sed 's/;.*/;/' | sed 's/=/ /' | sed 's/^/setenv /' >"$start_cshpidf"
- echo "$start_out" | sed 's/;.*/;/' | sed 's/^\(.*\)=\(.*\);/set -e \1; set -x -U \1 \2;/' >"$start_fishpidf"
- ;;
- esac
- # Hey the agent should be started now... load it up!
- loadagents "$start_prog"
- }
- # synopsis: extract_fingerprints
- # Extract the fingerprints from standard input, returns space-separated list.
- # Utility routine for ssh_l and ssh_f
- extract_fingerprints() {
- while read ef_line; do
- case "$ef_line" in
- *\ *\ [0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:*)
- # Sun SSH spits out different things depending on the type of
- # key. For example:
- # md5 1024 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 /home/barney/.ssh/id_dsa(DSA)
- # 2048 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 /home/barney/.ssh/id_rsa.pub
- echo "$ef_line" | cut -f3 -d' '
- ;;
- *\ [0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:*)
- # The more consistent OpenSSH format, we hope
- # 1024 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 /home/barney/.ssh/id_dsa (DSA)
- echo "$ef_line" | cut -f2 -d' '
- ;;
- *\ SHA256:[0-9a-zA-Z\+\/=]*|*\ MD5:[0-9a-zA-Z\+\/=]*)
- # The new OpenSSH 6.8+ format,
- # 1024 SHA256:mVPwvezndPv/ARoIadVY98vAC0g+P/5633yTC4d/wXE /home/barney/.ssh/id_dsa (DSA)
- echo "$ef_line" | cut -f2 -d' '
- ;;
- *)
- # Fall back to filename. Note that commercial ssh is handled
- # explicitly in ssh_l and ssh_f, so hopefully this rule will
- # never fire.
- warn "Can't determine fingerprint from the following line, falling back to filename"
- mesg "$ef_line"
- basename "$ef_line" | sed 's/[ (].*//'
- ;;
- esac
- done | xargs
- }
- # synopsis: ssh_l
- # Return space-separated list of known fingerprints
- ssh_l() {
- sl_mylist=`ssh-add -l 2>/dev/null`
- sl_retval=$?
- if $openssh; then
- # Error codes:
- # 0 success
- # 1 OpenSSH_3.8.1p1 on Linux: no identities (not an error)
- # OpenSSH_3.0.2p1 on HP-UX: can't connect to auth agent
- # 2 can't connect to auth agent
- case $sl_retval in
- 0)
- echo "$sl_mylist" | extract_fingerprints
- ;;
- 1)
- case "$sl_mylist" in
- *"open a connection"*) sl_retval=2 ;;
- esac
- ;;
- esac
- return $sl_retval
- elif $sunssh; then
- # Error codes (from http://docs.sun.com/db/doc/817-3936/6mjgdbvio?a=view)
- # 0 success (even when there are no keys)
- # 1 error
- case $sl_retval in
- 0)
- echo "$sl_mylist" | extract_fingerprints
- ;;
- 1)
- case "$sl_mylist" in
- *"open a connection"*) sl_retval=2 ;;
- esac
- ;;
- esac
- return $sl_retval
- else
- # Error codes:
- # 0 success - however might say "The authorization agent has no keys."
- # 1 can't connect to auth agent
- # 2 bad passphrase
- # 3 bad identity file
- # 4 the agent does not have the requested identity
- # 5 unspecified error
- if [ $sl_retval = 0 ]; then
- # Output of ssh-add -l:
- # The authorization agent has one key:
- # id_dsa_2048_a: 2048-bit dsa, agriffis@alpha.zk3.dec.com, Fri Jul 25 2003 10:53:49 -0400
- # Since we don't have a fingerprint, just get the filenames *shrug*
- echo "$sl_mylist" | sed '2,$s/:.*//' | xargs
- fi
- return $sl_retval
- fi
- }
- # synopsis: ssh_f filename
- # Return fingerprint for a keyfile
- # Requires $openssh and $sunssh
- ssh_f() {
- sf_filename="$1"
- if $openssh || $sunssh; then
- # if private key is symlink and symlink to *.pub is missing:
- if [ -L "$sf_filename" ] && [ ! -z "$realpath_bin" ]; then
- sf_filename="`$realpath_bin $sf_filename`"
- fi
- lsf_filename="$sf_filename.pub"
- if [ ! -f "$lsf_filename" ]; then
- # try to remove extension from private key, *then* add .pub, and see if we now find it:
- if [ -L "$sf_filename" ] && [ ! -z "$realpath_bin" ]; then
- sf_filename="`$realpath_bin $sf_filename`"
- fi
- lsf_filename=`echo "$sf_filename" | sed 's/\.[^\.]*$//'`.pub
- if [ ! -f "$lsf_filename" ]; then
- warn "Cannot find public key for $1."
- return 1
- fi
- fi
- sf_fing=`ssh-keygen -l -f "$lsf_filename"` || return 1
- echo "$sf_fing" | extract_fingerprints
- else
- # can't get fingerprint for ssh2 so use filename *shrug*
- basename "$sf_filename"
- fi
- return 0
- }
- # synopsis: gpg_listmissing
- # Uses $gpgkeys
- # Returns a newline-separated list of keys found to be missing.
- gpg_listmissing() {
- unset glm_missing
- GPG_TTY=`tty`
- # Parse $gpgkeys into positional params to preserve spaces in filenames
- set -f # disable globbing
- glm_IFS="$IFS" # save current IFS
- IFS="
- " # set IFS to newline
- set -- $gpgkeys
- IFS="$glm_IFS" # restore IFS
- set +f # re-enable globbing
- for glm_k in "$@"; do
- # Check if this key is known to the agent. Don't know another way...
- if echo | env -i GPG_TTY="$GPG_TTY" PATH="$PATH" GPG_AGENT_INFO="$GPG_AGENT_INFO" \
- gpg --no-options --use-agent --no-tty --sign --local-user "$glm_k" -o- >/dev/null 2>&1; then
- # already know about this key
- mesg "Known gpg key: ${CYANN}${glm_k}${OFF}"
- continue
- else
- # need to add this key
- if [ -z "$glm_missing" ]; then
- glm_missing="$glm_k"
- else
- glm_missing="$glm_missing
- $glm_k"
- fi
- fi
- done
- echo "$glm_missing"
- }
- # synopsis: ssh_listmissing
- # Uses $sshkeys and $sshavail
- # Returns a newline-separated list of keys found to be missing.
- ssh_listmissing() {
- unset slm_missing
- # Parse $sshkeys into positional params to preserve spaces in filenames
- set -f # disable globbing
- slm_IFS="$IFS" # save current IFS
- IFS="
- " # set IFS to newline
- set -- $sshkeys
- IFS="$slm_IFS" # restore IFS
- set +f # re-enable globbing
- for slm_k in "$@"; do
- # Fingerprint current user-specified key
- slm_finger=`ssh_f "$slm_k"` || continue
- # Check if it needs to be added
- case " $sshavail " in
- *" $slm_finger "*)
- # already know about this key
- mesg "Known ssh key: ${CYANN}${slm_k}${OFF}"
- ;;
- *)
- # need to add this key
- if [ -z "$slm_missing" ]; then
- slm_missing="$slm_k"
- else
- slm_missing="$slm_missing
- $slm_k"
- fi
- ;;
- esac
- done
- echo "$slm_missing"
- }
- # synopsis: add_gpgkey
- # Adds a key to $gpgkeys
- add_gpgkey() {
- gpgkeys=${gpgkeys+"$gpgkeys
- "}"$1"
- }
- # synopsis: add_sshkey
- # Adds a key to $sshkeys
- add_sshkey() {
- sshkeys=${sshkeys+"$sshkeys
- "}"$1"
- }
- # synopsis: parse_mykeys
- # Sets $sshkeys and $gpgkeys based on $mykeys
- parse_mykeys() {
- # Possible path to the private key: if --confhost variable used.
- pkeypath="$1"
- # Parse $mykeys into positional params to preserve spaces in filenames
- set -f # disable globbing
- pm_IFS="$IFS" # save current IFS
- IFS="
- " # set IFS to newline
- set -- $mykeys
- IFS="$pm_IFS" # restore IFS
- set +f # re-enable globbing
- for pm_k in "$@"; do
- # Check for ssh
- if wantagent ssh; then
- if [ -f "$pm_k" ]; then
- add_sshkey "$pm_k" ; continue
- elif [ -f "$HOME/.ssh/$pm_k" ]; then
- add_sshkey "$HOME/.ssh/$pm_k" ; continue
- elif [ -f "$HOME/.ssh2/$pm_k" ]; then
- add_sshkey "$HOME/.ssh2/$pm_k" ; continue
- elif [ -f "$pkeypath" ]; then
- add_sshkey "$pkeypath"; continue
- fi
- fi
- # Check for gpg
- if wantagent gpg; then
- if [ -z "$pm_gpgsecrets" ]; then
- pm_gpgsecrets="`gpg --list-secret-keys 2>/dev/null | cut -d/ -f2 | cut -d' ' -f1 | xargs`"
- [ -z "$pm_gpgsecrets" ] && pm_gpgsecrets='/' # arbitrary
- fi
- case " $pm_gpgsecrets " in *" $pm_k "*)
- add_gpgkey "$pm_k" ; continue ;;
- esac
- fi
- $ignoreopt || warn "can't find $pm_k; skipping"
- continue
- done
- return 0
- }
- # synopsis: setaction
- # Sets $myaction or dies if $myaction is already set
- setaction() {
- if [ -n "$myaction" ]; then
- die "you can't specify --$myaction and $1 at the same time"
- else
- myaction="$1"
- fi
- }
- # synopsis: setagents
- # Check validity of agentsopt
- setagents() {
- if [ -n "$agentsopt" ]; then
- agentsopt=`echo "$agentsopt" | sed 's/,/ /g'`
- unset new_agentsopt
- for a in $agentsopt; do
- if command -v ${a}-agent >/dev/null; then
- new_agentsopt="${new_agentsopt+$new_agentsopt }${a}"
- else
- warn "can't find ${a}-agent, removing from list"
- fi
- done
- agentsopt="${new_agentsopt}"
- else
- for a in ssh; do
- command -v ${a}-agent >/dev/null || continue
- agentsopt="${agentsopt+$agentsopt }${a}"
- done
- fi
- if [ -z "$agentsopt" ]; then
- die "no agents available to start"
- fi
- }
- # synopsis: confpath
- # Return private key path if found in ~/.ssh/config SSH configuration file.
- # Input: the name of the host we would like to connect to.
- confpath() {
- h=""
- while IFS= read -r line; do
- # get the Host directives
- if [[ $line == *"Host "* ]]; then
- h=$(echo $line | awk '{print $2}')
- fi
- if [[ $line == *IdentityFile* ]] && [[ $h == "$1" ]]; then
- echo $line | awk '{print $2}'
- break
- fi
- done < ~/.ssh/config
- }
- # synopsis: wantagent prog
- # Return 0 (true) or 1 (false) depending on whether prog is one of the agents in
- # agentsopt
- wantagent() {
- case "$agentsopt" in
- "$1"|"$1 "*|*" $1 "*|*" $1")
- return 0 ;;
- *)
- return 1 ;;
- esac
- }
- #
- # MAIN PROGRAM
- #
- # parse the command-line
- while [ -n "$1" ]; do
- case "$1" in
- --help|-h)
- setaction help
- ;;
- --stop|-k)
- # As of version 2.5, --stop takes an argument. For the sake of
- # backward compatibility, only eat the arg if it's one we recognize.
- if [ "$2" = mine ]; then
- stopwhich=mine; shift
- elif [ "$2" = others ]; then
- stopwhich=others; shift
- elif [ "$2" = all ]; then
- stopwhich=all; shift
- else
- # backward compat
- stopwhich=all-warn
- fi
- ;;
- --version|-V)
- setaction version
- ;;
- --agents)
- shift
- agentsopt="$1"
- ;;
- --attempts)
- shift
- if [ "$1" -gt 0 ] 2>/dev/null; then
- attempts=$1
- else
- die "--attempts requires a numeric argument greater than zero"
- fi
- ;;
- --clear)
- clearopt=true
- $quickopt && die "--quick and --clear are not compatible"
- ;;
- --confirm)
- confirmopt=true
- ;;
- --absolute)
- absoluteopt=true
- ;;
- --dir)
- shift
- case "$1" in
- */.*) keydir="$1" ;;
- '') die "--dir requires an argument" ;;
- *)
- if $absoluteopt; then
- keydir="$1"
- else
- keydir="$1/.keychain" # be backward-compatible
- fi
- ;;
- esac
- ;;
- --env)
- shift
- if [ -z "$1" ]; then
- die "--env requires an argument"
- else
- envf="$1"
- fi
- ;;
- --eval)
- evalopt=true
- ;;
- --list|-l)
- setaction list
- quietopt=true
- ;;
- --query)
- queryopt=true
- ;;
- --host)
- shift
- hostopt="$1"
- ;;
- --ignore-missing)
- ignoreopt=true
- ;;
- --inherit)
- shift
- case "$1" in
- local|any|local-once|any-once)
- inheritwhich="$1"
- ;;
- *)
- die "--inherit requires an argument (local, any, local-once or any-once)"
- ;;
- esac
- ;;
- --noinherit)
- inheritwhich=none
- ;;
- --noask)
- noaskopt=true
- ;;
- --nogui)
- noguiopt=true
- ;;
- --nolock)
- nolockopt=true
- ;;
- --lockwait)
- shift
- if [ "$1" -ge 0 ] 2>/dev/null; then
- lockwait="$1"
- else
- die "--lockwait requires an argument zero or greater."
- fi
- ;;
- --quick|-Q)
- quickopt=true
- $clearopt && die "--quick and --clear are not compatible"
- ;;
- --quiet|-q)
- quietopt=true
- ;;
- --confhost|-c)
- if [ -e ~/.ssh/config ]; then
- sshconfig=true
- confhost="$2"
- else
- warn "~/.ssh/config not found; --confhost/-c option ignored."
- fi
- ;;
- --nocolor)
- color=false
- ;;
- --timeout)
- shift
- if [ "$1" -gt 0 ] 2>/dev/null; then
- timeout=$1
- else
- die "--timeout requires a numeric argument greater than zero"
- fi
- ;;
- --systemd)
- systemdopt=true
- ;;
- --)
- shift
- IFS="
- "
- mykeys=${mykeys+"$mykeys
- "}"$*"
- unset IFS
- break
- ;;
- -*)
- echo "$zero: unknown option $1" >&2
- $evalopt && { echo; echo "false;"; }
- exit 1
- ;;
- *)
- mykeys=${mykeys+"$mykeys
- "}"$1"
- ;;
- esac
- shift
- done
- # Set filenames *after* parsing command-line options to allow
- # modification of $keydir and/or $hostopt
- #
- # pidf holds the specific name of the keychain .ssh-agent-myhostname file.
- # We use the new hostname extension for NFS compatibility. cshpidf is the
- # .ssh-agent file with csh-compatible syntax. fishpidf is the .ssh-agent
- # file with fish-compatible syntax. lockf is the lockfile, used
- # to serialize the execution of multiple ssh-agent processes started
- # simultaneously
- [ -z "$hostopt" ] && hostopt="${HOSTNAME}"
- [ -z "$hostopt" ] && hostopt=`uname -n 2>/dev/null || echo unknown`
- pidf="${keydir}/${hostopt}-sh"
- cshpidf="${keydir}/${hostopt}-csh"
- fishpidf="${keydir}/${hostopt}-fish"
- olockf="${keydir}/${hostopt}-lock"
- lockf="${keydir}/${hostopt}-lockf"
- # Read the env snippet (especially for things like PATH, but could modify
- # basically anything)
- if [ -z "$envf" ]; then
- envf="${keydir}/${hostopt}-env"
- [ -f "$envf" ] || envf="${keydir}/env"
- [ -f "$envf" ] || unset envf
- fi
- if [ -n "$envf" ]; then
- . "$envf"
- fi
- # Don't use color if there's no terminal on stderr
- if [ -n "$OFF" ]; then
- tty <&2 >/dev/null 2>&1 || color=false
- fi
- #disable color if necessary, right before our initial newline
- $color || unset BLUE CYAN CYANN GREEN PURP OFF RED
- qprint #initial newline
- mesg "${PURP}keychain ${OFF}${CYANN}${version}${OFF} ~ ${GREEN}http://www.funtoo.org${OFF}"
- [ "$myaction" = version ] && { versinfo; exit 0; }
- [ "$myaction" = help ] && { versinfo; helpinfo; exit 0; }
- # Set up traps
- # Don't use signal names because they don't work on Cygwin.
- if $clearopt; then
- trap '' 2 # disallow ^C until we've had a chance to --clear
- trap 'droplock; exit 1' 1 15 # drop the lock on signal
- trap 'droplock; exit 0' 0 # drop the lock on exit
- else
- # Don't use signal names because they don't work on Cygwin.
- trap 'droplock; exit 1' 1 2 15 # drop the lock on signal
- trap 'droplock; exit 0' 0 # drop the lock on exit
- fi
- setagents # verify/set $agentsopt
- verifykeydir # sets up $keydir
- wantagent ssh && testssh # sets $openssh and $sunssh
- getuser # sets $me
- # Inherit agent info from the environment before loadagents wipes it out.
- # Always call this since it checks $inheritopt and sets variables accordingly.
- inheritagents
- # --stop: kill the existing ssh-agent(s) and quit
- if [ -n "$stopwhich" ]; then
- if [ "$stopwhich" = all-warn ]; then
- warn "--stop without an argument is deprecated; see --help"
- stopwhich=all
- fi
- takelock || die
- if [ "$stopwhich" = mine -o "$stopwhich" = others ]; then
- loadagents $agentsopt
- fi
- for a in $agentsopt; do
- stopagent $a
- done
- if [ "$stopwhich" != others ]; then
- qprint
- exit 0 # stopagent is always successful
- fi
- fi
- # Note regarding locking: if we're trying to be quick, then don't take the lock.
- # It will be taken later if we discover we can't be quick.
- if $quickopt; then
- loadagents $agentsopt # sets ssh_auth_sock, ssh_agent_pid, etc
- unset nagentsopt
- for a in $agentsopt; do
- needstart=true
- # Trying to be quick has a price... If we discover the agent isn't running,
- # then we'll have to check things again (in startagent) after taking the
- # lock. So don't do the initial check unless --quick was specified.
- if [ $a = ssh ]; then
- sshavail=`ssh_l` # try to use existing agent
- # 0 = found keys, 1 = no keys, 2 = no agent
- if [ $? = 0 -o \( $? = 1 -a -z "$mykeys" \) ]; then
- mesg "Found existing ssh-agent: ${CYANN}$ssh_agent_pid${OFF}"
- needstart=false
- fi
- elif [ $a = gpg ]; then
- # not much way to be quick on this
- if [ -n "$gpg_agent_pid" ]; then
- case " `findpids gpg` " in
- *" $gpg_agent_pid "*)
- mesg "Found existing gpg-agent: ${CYANN}$gpg_agent_pid${OFF}"
- needstart=false ;;
- esac
- fi
- fi
- if $needstart; then
- nagentsopt="$nagentsopt $a"
- elif $evalopt; then
- catpidf $a
- fi
- done
- agentsopt="$nagentsopt"
- fi
- # If there are no agents remaining, then bow out now...
- [ -n "$agentsopt" ] || { qprint; exit 0; }
- # There are agents remaining to start, and we now know we can't be quick. Take
- # the lock before continuing
- takelock || die
- loadagents $agentsopt
- unset nagentsopt
- for a in $agentsopt; do
- if $queryopt; then
- catpidf_shell sh $a | cut -d\; -f1
- elif startagent $a; then
- nagentsopt="${nagentsopt+$nagentsopt }$a"
- $evalopt && catpidf $a
- fi
- done
- agentsopt="$nagentsopt"
- # If we are just querying the services, exit.
- $queryopt && exit 0
- # If there are no agents remaining, then duck out now...
- [ -n "$agentsopt" ] || { qprint; exit 0; }
- # --timeout translates almost directly to ssh-add -t, but ssh.com uses
- # minutes and OpenSSH uses seconds
- if [ -n "$timeout" ] && wantagent ssh; then
- ssh_timeout=$timeout
- if $openssh || $sunssh; then
- ssh_timeout=`expr $ssh_timeout \* 60`
- fi
- ssh_timeout="-t $ssh_timeout"
- fi
- # --confirm translates to ssh-add -c
- if $confirmopt && wantagent ssh; then
- if $openssh || $sunssh; then
- ssh_confirm=-c
- else
- warn "--confirm only works with OpenSSH"
- fi
- fi
- # --clear: remove all keys from the agent(s)
- if $clearopt; then
- for a in ${agentsopt}; do
- if [ $a = ssh ]; then
- sshout=`ssh-add -D 2>&1`
- if [ $? = 0 ]; then
- mesg "ssh-agent: $sshout"
- else
- warn "ssh-agent: $sshout"
- fi
- elif [ $a = gpg ]; then
- kill -1 $gpg_agent_pid 2>/dev/null
- mesg "gpg-agent: All identities removed."
- else
- warn "--clear not supported for ${a}-agent"
- fi
- done
- trap 'droplock' 2 # done clearing, safe to ctrl-c
- fi
- if $systemdopt; then
- for a in $agentsopt; do
- systemctl --user set-environment $( catpidf_shell sh $a | cut -d\; -f1 )
- done
- fi
- # --noask: "don't ask for keys", so we're all done
- $noaskopt && { qprint; exit 0; }
- # If the --confhost option used, determine the path to the private key as
- # written in the ~/.ssh/config and add it to ssh-add.
- if $sshconfig; then
- pkeypath=$(confpath "$confhost")
- eval pkeypath=$pkeypath
- fi
- # Parse $mykeys into ssh vs. gpg keys; it may be necessary in the future to
- # differentiate on the cmdline
- parse_mykeys "$pkeypath" || die
- # Load ssh keys
- if wantagent ssh; then
- sshavail=`ssh_l` # update sshavail now that we're locked
- if [ "$myaction" = "list" ]; then
- for key in $sshavail end; do
- [ "$key" == "end" ] && continue
- echo "$key"
- done
- else
- sshkeys="`ssh_listmissing`" # cache list of missing keys, newline-separated
- sshattempts=$attempts
- savedisplay="$DISPLAY"
- # Attempt to add the keys
- while [ -n "$sshkeys" ]; do
- mesg "Adding ${CYANN}"`echo "$sshkeys" | wc -l`"${OFF} ssh key(s): `echo $sshkeys`"
- # Parse $sshkeys into positional params to preserve spaces in filenames.
- # This *must* happen after any calls to subroutines because pure Bourne
- # shell doesn't restore "$@" following a call. Eeeeek!
- set -f # disable globbing
- old_IFS="$IFS" # save current IFS
- IFS="
- " # set IFS to newline
- set -- $sshkeys
- IFS="$old_IFS" # restore IFS
- set +f # re-enable globbing
- if $noguiopt || [ -z "$SSH_ASKPASS" -o -z "$DISPLAY" ]; then
- unset DISPLAY # DISPLAY="" can cause problems
- unset SSH_ASKPASS # make sure ssh-add doesn't try SSH_ASKPASS
- sshout=`ssh-add ${ssh_timeout} ${ssh_confirm} "$@" 2>&1`
- else
- sshout=`ssh-add ${ssh_timeout} ${ssh_confirm} "$@" 2>&1 </dev/null`
- fi
- if [ $? = 0 ]
- then
- blurb=""
- [ -n "$timeout" ] && blurb="life=${timeout}m"
- [ -n "$timeout" ] && $confirmopt && blurb="${blurb},"
- $confirmopt && blurb="${blurb}confirm"
- [ -n "$blurb" ] && blurb=" (${blurb})"
- mesg "ssh-add: Identities added: `echo $sshkeys`${blurb}"
- break
- fi
- if [ $sshattempts = 1 ]; then
- die "Problem adding; giving up"
- else
- warn "Problem adding; trying again"
- fi
- # Update the list of missing keys
- sshavail=`ssh_l`
- [ $? = 0 ] || die "problem running ssh-add -l"
- sshkeys="`ssh_listmissing`" # remember, newline-separated
- # Decrement the countdown
- sshattempts=`expr $sshattempts - 1`
- done
- [ -n "$savedisplay" ] && DISPLAY="$savedisplay"
- fi
- fi
- # Load gpg keys
- if wantagent gpg; then
- gpgkeys="`gpg_listmissing`" # cache list of missing keys, newline-separated
- gpgattempts=$attempts
- $noguiopt && unset DISPLAY
- [ -n "$DISPLAY" ] || unset DISPLAY # DISPLAY="" can cause problems
- GPG_TTY=`tty` ; export GPG_TTY # fall back to ncurses pinentry
- # Attempt to add the keys
- while [ -n "$gpgkeys" ]; do
- tryagain=false
- mesg "Adding ${BLUE}"`echo "$gpgkeys" | wc -l`"${OFF} gpg key(s): `echo $gpgkeys`"
- # Parse $gpgkeys into positional params to preserve spaces in filenames.
- # This *must* happen after any calls to subroutines because pure Bourne
- # shell doesn't restore "$@" following a call. Eeeeek!
- set -f # disable globbing
- old_IFS="$IFS" # save current IFS
- IFS="
- " # set IFS to newline
- set -- $gpgkeys
- IFS="$old_IFS" # restore IFS
- set +f # re-enable globbing
- for k in "$@"; do
- echo | env LC_ALL="$pinentry_lc_all" \
- gpg --no-options --use-agent --no-tty --sign --local-user "$k" -o- >/dev/null 2>&1
- [ $? != 0 ] && tryagain=true
- done
- $tryagain || break
- if [ $gpgattempts = 1 ]; then
- die "Problem adding (is pinentry installed?); giving up"
- else
- warn "Problem adding; trying again"
- fi
- # Update the list of missing keys
- gpgkeys="`gpg_listmissing`" # remember, newline-separated
- # Decrement the countdown
- gpgattempts=`expr $gpgattempts - 1`
- done
- fi
- qprint # trailing newline
- # vim:sw=4 noexpandtab tw=120
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement