ssh-keyman 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. #!/bin/sh
  2. ##
  3. ## ssh-keyman -- authentication key agent management
  4. ## Copyright (c) 2002 Ralf S. Engelschall <rse@engelschall.com>
  5. ##
  6. ## Permission to use, copy, modify, and distribute this software for
  7. ## any purpose with or without fee is hereby granted, provided that
  8. ## the above copyright notice and this permission notice appear in all
  9. ## copies.
  10. ##
  11. ## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  12. ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  13. ## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  14. ## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
  15. ## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  16. ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  17. ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  18. ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  19. ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  20. ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  21. ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  22. ## SUCH DAMAGE.
  23. ##
  24. ## ssh-keyman: program implementation (language: Bourne-Shell)
  25. ##
  26. # program information
  27. prog_name="ssh-keyman"
  28. prog_vers="1.0.1"
  29. prog_date="31-May-2002"
  30. # OpenSSH programs
  31. ssh_agent="@l_prefix@/bin/ssh-agent"
  32. ssh_add="@l_prefix@/bin/ssh-add"
  33. # parse command line options
  34. opt_q=no; alias_quiet=q
  35. opt_c=no; alias_cluster=c
  36. opt_e=no; alias_env=e
  37. opt_s=no; alias_start=s
  38. opt_k=no; alias_kill=k
  39. opt_a=no; alias_add=a
  40. opt_d=no; alias_delete=d
  41. opt_l=no; alias_list=l
  42. opt_h=no; alias_help=h
  43. opt_v=no; alias_version=v
  44. if [ $# -eq 0 ]; then
  45. opt_h=yes
  46. fi
  47. while [ $# -gt 0 ]; do
  48. if [ ".$1" = ".--" ]; then
  49. shift
  50. break
  51. fi
  52. case $1 in
  53. --quiet|--cluster|--env|--start|--kill|--add|--delete|--list|--help|--version )
  54. name=`echo x$1 | sed -e 's;^x--;;'`
  55. eval "name=\$alias_${name}"
  56. eval "opt_${name}=yes"
  57. ;;
  58. -[qceskadlhv] )
  59. name=`echo x$1 | sed -e 's;^x-;;'`
  60. eval "opt_${name}=yes"
  61. ;;
  62. -* )
  63. echo "$prog_name:ERROR: unknown option \"$opt\"" 1>&2
  64. exit 1
  65. ;;
  66. * )
  67. break
  68. ;;
  69. esac
  70. shift
  71. done
  72. # stand-alone operation: display help information
  73. if [ ".$opt_h" = .yes ]; then
  74. echo "Usage: $prog_name [-h] [-v] [-q] [-c] [-e] [-s] [-k] [-a] [-d] [-l] [keyfile ...]"
  75. exit 0
  76. fi
  77. # stand-alone operation: display version information
  78. if [ ".$opt_v" = .yes ]; then
  79. echo "$prog_name $prog_vers ($prog_date)"
  80. exit 0
  81. fi
  82. # determine agent information filename
  83. hostname=`hostname`
  84. agentfile="$HOME/.ssh/agent-$hostname"
  85. if [ ".$opt_c" = .no ]; then
  86. if [ ! -f $agentfile ]; then
  87. agentfile="$HOME/.ssh/agent"
  88. fi
  89. fi
  90. # export agent configuration
  91. export SSH_AUTH_SOCK
  92. export SSH_AGENT_PID
  93. # perform agent information sanity check
  94. check_agent_info () {
  95. context="$1"
  96. invalid1=""
  97. invalid2=""
  98. if [ ".$SSH_AUTH_SOCK" != . ]; then
  99. # make sure the agent socket is (still) working
  100. if [ ! -r $SSH_AUTH_SOCK ]; then
  101. invalid1="agent socket $SSH_AUTH_SOCK no longer exists"
  102. else
  103. ssh-add -l >/dev/null 2>&1
  104. if [ $? -eq 2 ]; then
  105. invalid1="agent socket $SSH_AUTH_SOCK no longer valid"
  106. fi
  107. fi
  108. fi
  109. if [ ".$SSH_AGENT_PID" != . ]; then
  110. # make sure the agent process is (still) running
  111. kill -0 $SSH_AGENT_PID >/dev/null 2>&1
  112. if [ $? -ne 0 ]; then
  113. invalid2="agent process $SSH_AGENT_PID no longer exists"
  114. fi
  115. fi
  116. if [ ".$invalid1" != . -o ".$invalid2" != . ]; then
  117. if [ ".$opt_e" = .no ]; then
  118. echo "$prog_name:WARNING: invalid agent setup found in $context." 1>&2
  119. if [ ".$invalid1" != . ]; then
  120. echo "$prog_name:WARNING: reason: $invalid1." 1>&2
  121. fi
  122. if [ ".$invalid2" != . ]; then
  123. echo "$prog_name:WARNING: reason: $invalid2." 1>&2
  124. fi
  125. if [ ".$context" = ".your shell environment" ]; then
  126. echo "$prog_name:HINT: run \"eval \`$prog_name -q -s -e\`\" to fix." 1>&2
  127. fi
  128. fi
  129. unset SSH_AUTH_SOCK
  130. unset SSH_AGENT_PID
  131. fi
  132. }
  133. check_agent_info "your shell environment"
  134. if [ -f $agentfile ]; then
  135. . $agentfile
  136. check_agent_info "in saved agent state"
  137. if [ ".$SSH_AUTH_SOCK" = . -o ".$SSH_AGENT_PID" = . ]; then
  138. rm -f $agentfile
  139. fi
  140. fi
  141. # if (now guarrantied to be correct) agent state is in
  142. # environment, but it is (no longer?) saved, save it now to fix situation.
  143. if [ ! -f $agentfile ]; then
  144. if [ ".$SSH_AUTH_SOCK" != . -a ".$SSH_AGENT_PID" != . ]; then
  145. ( echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK"
  146. echo "SSH_AGENT_PID=$SSH_AGENT_PID"
  147. ) >$agentfile && chmod 600 $agentfile
  148. echo "$prog_name:WARNING: valid agent information in your environment" 1>&2
  149. echo "$prog_name:WARNING: but no saved agent state file -- fixed" 1>&2
  150. fi
  151. fi
  152. # stop the agent
  153. if [ ".$opt_k" = .yes ]; then
  154. # stop the agent
  155. kill=yes
  156. if [ ".$SSH_AUTH_SOCK" != . -a ".$SSH_AGENT_PID" != . ]; then
  157. kill $SSH_AGENT_PID >/dev/null 2>&1 || true
  158. if [ ".$opt_q" = .no ]; then
  159. echo "$prog_name: stopped agent (pid $SSH_AGENT_PID)" 1>&2
  160. fi
  161. rm -f $agentfile
  162. unset SSH_AUTH_SOCK
  163. unset SSH_AGENT_PID
  164. elif [ ".$SSH_AUTH_SOCK" != . -a ".$SSH_AGENT_PID" = . ]; then
  165. if [ ".$opt_q" = .no ]; then
  166. echo "$prog_name: agent running remotely - cannot kill locally" 1>&2
  167. fi
  168. else
  169. if [ ".$opt_q" = .no ]; then
  170. echo "$prog_name: agent not running" 1>&2
  171. fi
  172. fi
  173. fi
  174. # start the agent
  175. if [ ".$opt_s" = .yes ]; then
  176. if [ ".$SSH_AUTH_SOCK" = . -a ".$SSH_AGENT_PID" = . ]; then
  177. eval `nohup $ssh_agent -s </dev/null 2>/dev/null | grep -v 'Agent pid'`
  178. if [ ".$opt_q" != .yes ]; then
  179. echo "$prog_name: spawned agent (pid $SSH_AGENT_PID)" 1>&2
  180. fi
  181. ( echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK"
  182. echo "SSH_AGENT_PID=$SSH_AGENT_PID"
  183. ) >$agentfile && chmod 600 $agentfile
  184. elif [ ".$SSH_AUTH_SOCK" != . -a ".$SSH_AGENT_PID" = . ]; then
  185. if [ ".$opt_q" = .no ]; then
  186. echo "$prog_name: agent already running remotely - no need to start locally" 1>&2
  187. fi
  188. else
  189. if [ ".$opt_q" = .no ]; then
  190. echo "$prog_name: agent already running" 1>&2
  191. fi
  192. fi
  193. fi
  194. # setup environment
  195. if [ ".$opt_e" = .yes ]; then
  196. if [ -r $agentfile ]; then
  197. sed -e 's/$/;/g' <$agentfile
  198. echo "export SSH_AUTH_SOCK;"
  199. echo "export SSH_AGENT_PID;"
  200. else
  201. echo "$prog_name:WARNING: agent not (or no longer) available" 1>&2
  202. echo "unset SSH_AUTH_SOCK;"
  203. echo "unset SSH_AGENT_PID;"
  204. fi
  205. fi
  206. # delete key(s) from agent
  207. if [ ".$opt_d" = .yes ]; then
  208. if [ ".$SSH_AUTH_SOCK" = . ]; then
  209. echo "$prog_name:WARNING: agent not available" 1>&2
  210. else
  211. if [ $# -eq 0 ]; then
  212. if [ ".$opt_q" = .no ]; then
  213. echo "$prog_name: deleting all keys" 1>&2
  214. fi
  215. $ssh_add -D
  216. else
  217. if [ ".$opt_q" = .no ]; then
  218. for key in "$@"; do
  219. echo "$prog_name: deleting key $key" 1>&2
  220. done
  221. fi
  222. $ssh_add -d "$@"
  223. fi
  224. fi
  225. fi
  226. # add key(s) into agent
  227. if [ ".$opt_a" = .yes ]; then
  228. if [ ".$SSH_AUTH_SOCK" = . ]; then
  229. echo "$prog_name:WARNING: agent not available" 1>&2
  230. else
  231. if [ $# -eq 0 ]; then
  232. echo "$prog_name:ERROR: no keys specified on command line" 1>&2
  233. exit 1
  234. fi
  235. key_loaded=`$ssh_add -l | awk '{ print $2; }'`
  236. key_missing=""
  237. for key_file in "$@"; do
  238. if [ -f "${key_file}.pub" ]; then
  239. key_this=`ssh-keygen -l -f ${key_file}.pub 2>&1 | awk '{ print $2; }'`
  240. else
  241. key_this=`ssh-keygen -l -f ${key_file} 2>&1 | awk '{ print $2; }'`
  242. fi
  243. load=yes
  244. for key in $key_loaded; do
  245. if [ ".$key" = ".$key_this" ]; then
  246. load=no
  247. break
  248. fi
  249. done
  250. if [ ".$load" = .yes ]; then
  251. if [ ".$opt_q" = .no ]; then
  252. echo "$prog_name: loading key $key_file" 1>&2
  253. fi
  254. key_missing="$key_missing $key_file"
  255. else
  256. if [ ".$opt_q" = .no ]; then
  257. echo "$prog_name: skipping key $key_file (already loaded)" 1>&2
  258. fi
  259. fi
  260. done
  261. if [ ".$key_missing" != . ]; then
  262. $ssh_add $key_missing
  263. fi
  264. fi
  265. fi
  266. # list key(s) available in agent
  267. if [ ".$opt_l" = .yes ]; then
  268. if [ ".$SSH_AUTH_SOCK" = . ]; then
  269. echo "$prog_name:WARNING: agent not available" 1>&2
  270. else
  271. $ssh_add -l
  272. fi
  273. fi