#autoload # This makes for interesting reading: # # http://blog.plover.com/Unix/ssh-agent-revisted.html # # Looks like he and others went through very similar thought processes # I did, and settled on the solution of caching the pid and socket # values in a predictably named file. However my solution goes # several steps further, by trying to rescue the information if the # cache file is missing. To this end it tries to guess the parent pid # of the ssh-agent if necessary, and even uses a heuristic approach in # the case that one likely looking socket is found but it cannot be # matched with a given process. # # Generally my code works pretty reliably these days. One known issue # is that if ssh-agent disallows ptrace attaching, lsof won't yield # any output without resorting to use of sudo. # # Perhaps in my CFT, I should also check out # # http://www.gentoo.org/doc/en/keychain-guide.xml # # and maybe even do the world a favour and port this to a lower common # denominator shell so more people can use it ... # Returns 0 on success _detect_ssh_agent_pid () { if [[ -n "$1" ]]; then SSH_AGENT_PID="$1" else local pids pids=( `pgrep -u $USER ssh-agent` ) if (( $#pids > 1 )); then echo "$#pids pids found ($pids); aborting." echo "Please ensure only one agent is running." return 1 fi SSH_AGENT_PID=$pids[1] fi } # Parses output of `lsof $socket` contained in $_lsof and set # _socket_status accordingly. Also outputs an indented version # of the output. _parse_lsof_output () { if [[ "$_lsof" == *sshd* ]]; then # Can this ever happen, now that we changed the pid detection # from using ps | grep technique to using pidof? _socket_status=fwd elif [[ "$_lsof" == *ssh/master-* ]]; then _socket_status=fwd # shared connection elif [[ "$_lsof" == *ssh-agent* ]]; then _socket_status=matched #if ! [[ -g "$agentbin" ]]; then if [[ "$USERNAME" != root ]]; then cat <<'EOF' >&2 *** WARNING! *** It looks like ssh-agent is neither setgid nor explicitly does prctl(PR_SET_DUMPABLE, 0) to disallow ptrace attaching. lsof of $socket returned information, which suggests there is an insecure ssh-agent running. Please fix. For more info, see: https://www.redhat.com/archives/fedora-devel-list/2004-December/msg00380.html EOF # return 1 fi elif [[ "$_lsof" == *gnome-key* ]]; then # ssh-agent running but secure : else echo "BUG? weird lsof output:\n$_lsof" >&2 return 1 fi # The following code indents "$_lsof", i.e. does the same as: # echo "$_lsof" | sed -e 's/^/ /' # by splitting at \n into an array (f), substituting start # of each element (#s) for ' ' and rejoining with \n (F). echo "${(F@)${(f)_lsof}//(#s)/ }" } export SSH_AGENT_PID= export SSH_AUTH_SOCK= echo "Recalculating ssh-agent info ..." _detect_ssh_agent_pid "$1" || return 1 if [[ "$SSH_AGENT_PID" != *[0-9]* ]]; then # If interactive, prompt whether to start a new one. # # UPDATE: don't prompt; I always answer yes. # if false && [[ -t 1 ]]; then if [[ -t 1 ]]; then echo -n "ssh-agent process not found; start a new one [y/N] ? " if read -q; then eval `ssh-agent` write_ssh_agent_cache return 0 else echo "Unable to determine ssh-agent info; aborting." return 1 fi else eval `ssh-agent` write_ssh_agent_cache return 0 fi fi echo "Found a single owned ssh-agent, pid $SSH_AGENT_PID" _sockets=( /tmp/ssh-*/*(=UN) ) if (( $#_sockets == 0 )); then echo "No sockets found; aborting." return 1 fi local -a _dead_sockets _matched_sockets _orphan_sockets guessed_parent_pid socket echo "$#_sockets socket(s) found: $_sockets[*]" if ! agentbin="`which ssh-agent`"; then echo "Error: couldn't find ssh-agent! Aborting." return 1 else : echo "Found agent $agentbin" fi # If ssh-agent pid Y is started by gnome-session (say) with pid X then # Y's parent is X, SSH_AGENT_PID refers to pid Y, and the suffix of # the /tmp/ssh-XXXXXX/agent.XXXX socket is pid X (the parent). agent_parent_pid=$( awk '{print $4}' /proc/$SSH_AGENT_PID/stat ) if [[ "$agent_parent_pid" == 1 ]]; then guessed_agent_parent_pid=$(( SSH_AGENT_PID - 1 )) echo "Agent with pid $SSH_AGENT_PID has been adopted by init; guessing parent pid $agent_parent_pid" else echo "Agent with pid $SSH_AGENT_PID has parent pid $agent_parent_pid" fi # Now we look for a socket corresponding to the ssh-agent we found running. for socket in "${_sockets[@]}"; do echo "Examining $socket ..." unset _socket_status if _lsof="`lsof $socket`" && [[ -n "$_lsof" ]]; then _parse_lsof_output else echo "WARNING: lsof $socket failed" fi if [[ -z "$_socket_status" ]]; then # ssh-agent is secure, so without useful info from lsof or fuser, # we have to do it the hard way :-( socket_parent_pid="${socket##*agent.}" _socket_status= if [[ "$agent_parent_pid" == "$socket_parent_pid" ]]; then # We just matched the only running ssh-agent's pid with its socket, # since the parent pids match. (OK, so the same parent could # have started two agents with two sockets having the same # prefix, and then one of them died leaving a socket, and the # other is still running without a socket, but that's very unlikely!) _socket_status=matched echo " \$SSH_AGENT_PID's parent is $agent_parent_pid, matches socket filename $socket" elif [[ "$guessed_agent_parent_pid" == "$socket_parent_pid" ]]; then _socket_status=matched echo " ppid from socket $socket matches guessed agent parent pid $guessed_agent_parent_pid" else _socket_status=unknown echo " \$SSH_AGENT_PID's parent is $agent_parent_pid, doesn't match socket filename $socket" fi fi case "$_socket_status" in fwd) echo " $socket used for agent forwarding" ;; unknown) echo " $socket of unknown origin" _unknown_sockets=( "${_unknown_sockets[@]}" "$socket" ) ;; matched) echo " $socket matched agent pid $SSH_AGENT_PID" _matched_sockets=( "${_matched_sockets[@]}" "$socket" ) ;; dead) echo " $socket appears dead" _dead_sockets=( "${_dead_sockets[@]}" "$socket" ) ;; *) echo BUG >&2 ; return 1 ;; esac done if (( $#_dead_sockets > 0 )); then echo "Could remove dead sockets:\n rm $_dead_sockets" #rm "$_dead_sockets[@]" fi if (( $#_matched_sockets != 1 )); then if (( $#_unknown_sockets == 1 && $#_dead_sockets == 0 )); then echo "Only one unknown socket $_unknown_sockets[1] and none dead; assuming it's matched." : ${SSH_AUTH_SOCK:=$_unknown_sockets[1]} else echo "BUG? Didn't find a unique matched socket" >&2 return 1 fi else echo "Choosing first of matched sockets: $_matched_sockets" : ${SSH_AUTH_SOCK:=$_matched_sockets[1]} fi write_ssh_agent_cache return 0