#!/bin/sh ## ## ssh-keyman -- authentication key agent management ## Copyright (c) 2002-2003 Ralf S. Engelschall ## ## 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" ssh="@l_prefix@/bin/ssh" # 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_i=no; alias_install=i 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" ;; -[qceskadlihv] ) 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] [-i] [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 | 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 # install key(s) into remote account if [ ".$opt_i" = .yes ]; then if [ ".$SSH_AUTH_SOCK" = . ]; then echo "$prog_name:WARNING: agent not available" 1>&2 else for remote in "$@"; do echo "$prog_name: installing public keys into $remote" $ssh_add -L |\ $ssh $remote "umask 077; test -d ~/.ssh || mkdir ~/.ssh; cat >>~/.ssh/authorized_keys" done fi fi