|
|
|
|
#!/bin/sh
|
|
|
|
|
##
|
|
|
|
|
## ssh-keyman -- authentication key agent management
|
|
|
|
|
## Copyright (c) 2002 Ralf S. Engelschall <rse@engelschall.com>
|
|
|
|
|
##
|
|
|
|
|
## Permission to use, copy, modify, and distribute this software for
|
|
|
|
|
## any purpose with or without fee is hereby granted, provided that
|
|
|
|
|
## the above copyright notice and this permission notice appear in all
|
|
|
|
|
## copies.
|
|
|
|
|
##
|
|
|
|
|
## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
|
|
|
|
## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
|
|
## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
|
## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
|
|
|
|
|
## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
|
## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
|
## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
|
|
|
|
## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
|
## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
|
|
## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
|
|
|
## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
|
## SUCH DAMAGE.
|
|
|
|
|
##
|
|
|
|
|
## ssh-keyman: program implementation (language: Bourne-Shell)
|
|
|
|
|
##
|
|
|
|
|
|
|
|
|
|
# program information
|
|
|
|
|
prog_name="ssh-keyman"
|
|
|
|
|
prog_vers="1.0.1"
|
|
|
|
|
prog_date="31-May-2002"
|
|
|
|
|
|
|
|
|
|
# OpenSSH programs
|
|
|
|
|
ssh_agent="@l_prefix@/bin/ssh-agent"
|
|
|
|
|
ssh_add="@l_prefix@/bin/ssh-add"
|
|
|
|
|
|
|
|
|
|
# parse command line options
|
|
|
|
|
opt_q=no; alias_quiet=q
|
|
|
|
|
opt_c=no; alias_cluster=c
|
|
|
|
|
opt_e=no; alias_env=e
|
|
|
|
|
opt_s=no; alias_start=s
|
|
|
|
|
opt_k=no; alias_kill=k
|
|
|
|
|
opt_a=no; alias_add=a
|
|
|
|
|
opt_d=no; alias_delete=d
|
|
|
|
|
opt_l=no; alias_list=l
|
|
|
|
|
opt_h=no; alias_help=h
|
|
|
|
|
opt_v=no; alias_version=v
|
|
|
|
|
if [ $# -eq 0 ]; then
|
|
|
|
|
opt_h=yes
|
|
|
|
|
fi
|
|
|
|
|
while [ $# -gt 0 ]; do
|
|
|
|
|
if [ ".$1" = ".--" ]; then
|
|
|
|
|
shift
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
case $1 in
|
|
|
|
|
--quiet|--cluster|--env|--start|--kill|--add|--delete|--list|--help|--version )
|
|
|
|
|
name=`echo x$1 | sed -e 's;^x--;;'`
|
|
|
|
|
eval "name=\$alias_${name}"
|
|
|
|
|
eval "opt_${name}=yes"
|
|
|
|
|
;;
|
|
|
|
|
-[qceskadlhv] )
|
|
|
|
|
name=`echo x$1 | sed -e 's;^x-;;'`
|
|
|
|
|
eval "opt_${name}=yes"
|
|
|
|
|
;;
|
|
|
|
|
-* )
|
|
|
|
|
echo "$prog_name:ERROR: unknown option \"$opt\"" 1>&2
|
|
|
|
|
exit 1
|
|
|
|
|
;;
|
|
|
|
|
* )
|
|
|
|
|
break
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
shift
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# stand-alone operation: display help information
|
|
|
|
|
if [ ".$opt_h" = .yes ]; then
|
|
|
|
|
echo "Usage: $prog_name [-h] [-v] [-q] [-c] [-e] [-s] [-k] [-a] [-d] [-l] [keyfile ...]"
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# stand-alone operation: display version information
|
|
|
|
|
if [ ".$opt_v" = .yes ]; then
|
|
|
|
|
echo "$prog_name $prog_vers ($prog_date)"
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# determine agent information filename
|
|
|
|
|
hostname=`hostname`
|
|
|
|
|
agentfile="$HOME/.ssh/agent-$hostname"
|
|
|
|
|
if [ ".$opt_c" = .no ]; then
|
|
|
|
|
if [ ! -f $agentfile ]; then
|
|
|
|
|
agentfile="$HOME/.ssh/agent"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# export agent configuration
|
|
|
|
|
export SSH_AUTH_SOCK
|
|
|
|
|
export SSH_AGENT_PID
|
|
|
|
|
|
|
|
|
|
# perform agent information sanity check
|
|
|
|
|
check_agent_info () {
|
|
|
|
|
context="$1"
|
|
|
|
|
invalid1=""
|
|
|
|
|
invalid2=""
|
|
|
|
|
if [ ".$SSH_AUTH_SOCK" != . ]; then
|
|
|
|
|
# make sure the agent socket is (still) working
|
|
|
|
|
if [ ! -r $SSH_AUTH_SOCK ]; then
|
|
|
|
|
invalid1="agent socket $SSH_AUTH_SOCK no longer exists"
|
|
|
|
|
else
|
|
|
|
|
ssh-add -l >/dev/null 2>&1
|
|
|
|
|
if [ $? -eq 2 ]; then
|
|
|
|
|
invalid1="agent socket $SSH_AUTH_SOCK no longer valid"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
if [ ".$SSH_AGENT_PID" != . ]; then
|
|
|
|
|
# make sure the agent process is (still) running
|
|
|
|
|
kill -0 $SSH_AGENT_PID >/dev/null 2>&1
|
|
|
|
|
if [ $? -ne 0 ]; then
|
|
|
|
|
invalid2="agent process $SSH_AGENT_PID no longer exists"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
if [ ".$invalid1" != . -o ".$invalid2" != . ]; then
|
|
|
|
|
if [ ".$opt_e" = .no ]; then
|
|
|
|
|
echo "$prog_name:WARNING: invalid agent setup found in $context." 1>&2
|
|
|
|
|
if [ ".$invalid1" != . ]; then
|
|
|
|
|
echo "$prog_name:WARNING: reason: $invalid1." 1>&2
|
|
|
|
|
fi
|
|
|
|
|
if [ ".$invalid2" != . ]; then
|
|
|
|
|
echo "$prog_name:WARNING: reason: $invalid2." 1>&2
|
|
|
|
|
fi
|
|
|
|
|
if [ ".$context" = ".your shell environment" ]; then
|
|
|
|
|
echo "$prog_name:HINT: run \"eval \`$prog_name -q -s -e\`\" to fix." 1>&2
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
unset SSH_AUTH_SOCK
|
|
|
|
|
unset SSH_AGENT_PID
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
check_agent_info "your shell environment"
|
|
|
|
|
if [ -f $agentfile ]; then
|
|
|
|
|
. $agentfile
|
|
|
|
|
check_agent_info "in saved agent state"
|
|
|
|
|
if [ ".$SSH_AUTH_SOCK" = . -o ".$SSH_AGENT_PID" = . ]; then
|
|
|
|
|
rm -f $agentfile
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# if (now guarrantied to be correct) agent state is in
|
|
|
|
|
# environment, but it is (no longer?) saved, save it now to fix situation.
|
|
|
|
|
if [ ! -f $agentfile ]; then
|
|
|
|
|
if [ ".$SSH_AUTH_SOCK" != . -a ".$SSH_AGENT_PID" != . ]; then
|
|
|
|
|
( echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK"
|
|
|
|
|
echo "SSH_AGENT_PID=$SSH_AGENT_PID"
|
|
|
|
|
) >$agentfile && chmod 600 $agentfile
|
|
|
|
|
echo "$prog_name:WARNING: valid agent information in your environment" 1>&2
|
|
|
|
|
echo "$prog_name:WARNING: but no saved agent state file -- fixed" 1>&2
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# stop the agent
|
|
|
|
|
if [ ".$opt_k" = .yes ]; then
|
|
|
|
|
# stop the agent
|
|
|
|
|
kill=yes
|
|
|
|
|
|
|
|
|
|
if [ ".$SSH_AUTH_SOCK" != . -a ".$SSH_AGENT_PID" != . ]; then
|
|
|
|
|
kill $SSH_AGENT_PID >/dev/null 2>&1 || true
|
|
|
|
|
if [ ".$opt_q" = .no ]; then
|
|
|
|
|
echo "$prog_name: stopped agent (pid $SSH_AGENT_PID)" 1>&2
|
|
|
|
|
fi
|
|
|
|
|
rm -f $agentfile
|
|
|
|
|
unset SSH_AUTH_SOCK
|
|
|
|
|
unset SSH_AGENT_PID
|
|
|
|
|
elif [ ".$SSH_AUTH_SOCK" != . -a ".$SSH_AGENT_PID" = . ]; then
|
|
|
|
|
if [ ".$opt_q" = .no ]; then
|
|
|
|
|
echo "$prog_name: agent running remotely - cannot kill locally" 1>&2
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
if [ ".$opt_q" = .no ]; then
|
|
|
|
|
echo "$prog_name: agent not running" 1>&2
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# start the agent
|
|
|
|
|
if [ ".$opt_s" = .yes ]; then
|
|
|
|
|
if [ ".$SSH_AUTH_SOCK" = . -a ".$SSH_AGENT_PID" = . ]; then
|
|
|
|
|
eval `nohup $ssh_agent -s </dev/null 2>/dev/null | grep -v 'Agent pid'`
|
|
|
|
|
if [ ".$opt_q" != .yes ]; then
|
|
|
|
|
echo "$prog_name: spawned agent (pid $SSH_AGENT_PID)" 1>&2
|
|
|
|
|
fi
|
|
|
|
|
( echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK"
|
|
|
|
|
echo "SSH_AGENT_PID=$SSH_AGENT_PID"
|
|
|
|
|
) >$agentfile && chmod 600 $agentfile
|
|
|
|
|
elif [ ".$SSH_AUTH_SOCK" != . -a ".$SSH_AGENT_PID" = . ]; then
|
|
|
|
|
if [ ".$opt_q" = .no ]; then
|
|
|
|
|
echo "$prog_name: agent already running remotely - no need to start locally" 1>&2
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
if [ ".$opt_q" = .no ]; then
|
|
|
|
|
echo "$prog_name: agent already running" 1>&2
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# setup environment
|
|
|
|
|
if [ ".$opt_e" = .yes ]; then
|
|
|
|
|
if [ -r $agentfile ]; then
|
|
|
|
|
sed -e 's/$/;/g' <$agentfile
|
|
|
|
|
echo "export SSH_AUTH_SOCK;"
|
|
|
|
|
echo "export SSH_AGENT_PID;"
|
|
|
|
|
else
|
|
|
|
|
echo "$prog_name:WARNING: agent not (or no longer) available" 1>&2
|
|
|
|
|
echo "unset SSH_AUTH_SOCK;"
|
|
|
|
|
echo "unset SSH_AGENT_PID;"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# delete key(s) from agent
|
|
|
|
|
if [ ".$opt_d" = .yes ]; then
|
|
|
|
|
if [ ".$SSH_AUTH_SOCK" = . ]; then
|
|
|
|
|
echo "$prog_name:WARNING: agent not available" 1>&2
|
|
|
|
|
else
|
|
|
|
|
if [ $# -eq 0 ]; then
|
|
|
|
|
if [ ".$opt_q" = .no ]; then
|
|
|
|
|
echo "$prog_name: deleting all keys" 1>&2
|
|
|
|
|
fi
|
|
|
|
|
$ssh_add -D
|
|
|
|
|
else
|
|
|
|
|
if [ ".$opt_q" = .no ]; then
|
|
|
|
|
for key in "$@"; do
|
|
|
|
|
echo "$prog_name: deleting key $key" 1>&2
|
|
|
|
|
done
|
|
|
|
|
fi
|
|
|
|
|
$ssh_add -d "$@"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# add key(s) into agent
|
|
|
|
|
if [ ".$opt_a" = .yes ]; then
|
|
|
|
|
if [ ".$SSH_AUTH_SOCK" = . ]; then
|
|
|
|
|
echo "$prog_name:WARNING: agent not available" 1>&2
|
|
|
|
|
else
|
|
|
|
|
if [ $# -eq 0 ]; then
|
|
|
|
|
echo "$prog_name:ERROR: no keys specified on command line" 1>&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
key_loaded=`$ssh_add -l | awk '{ print $2; }'`
|
|
|
|
|
key_missing=""
|
|
|
|
|
for key_file in "$@"; do
|
|
|
|
|
if [ -f "${key_file}.pub" ]; then
|
|
|
|
|
key_this=`ssh-keygen -l -f ${key_file}.pub 2>&1 | awk '{ print $2; }'`
|
|
|
|
|
else
|
|
|
|
|
key_this=`ssh-keygen -l -f ${key_file} 2>&1 | awk '{ print $2; }'`
|
|
|
|
|
fi
|
|
|
|
|
load=yes
|
|
|
|
|
for key in $key_loaded; do
|
|
|
|
|
if [ ".$key" = ".$key_this" ]; then
|
|
|
|
|
load=no
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
if [ ".$load" = .yes ]; then
|
|
|
|
|
if [ ".$opt_q" = .no ]; then
|
|
|
|
|
echo "$prog_name: loading key $key_file" 1>&2
|
|
|
|
|
fi
|
|
|
|
|
key_missing="$key_missing $key_file"
|
|
|
|
|
else
|
|
|
|
|
if [ ".$opt_q" = .no ]; then
|
|
|
|
|
echo "$prog_name: skipping key $key_file (already loaded)" 1>&2
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
if [ ".$key_missing" != . ]; then
|
|
|
|
|
$ssh_add $key_missing
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# list key(s) available in agent
|
|
|
|
|
if [ ".$opt_l" = .yes ]; then
|
|
|
|
|
if [ ".$SSH_AUTH_SOCK" = . ]; then
|
|
|
|
|
echo "$prog_name:WARNING: agent not available" 1>&2
|
|
|
|
|
else
|
|
|
|
|
$ssh_add -l
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|