ssh-keyman 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. #!/bin/sh
  2. ##
  3. ## ssh-keyman -- authentication key agent management
  4. ## Copyright (c) 2002-2003 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. ssh="@l_prefix@/bin/ssh"
  34. # parse command line options
  35. opt_q=no; alias_quiet=q
  36. opt_c=no; alias_cluster=c
  37. opt_e=no; alias_env=e
  38. opt_s=no; alias_start=s
  39. opt_k=no; alias_kill=k
  40. opt_a=no; alias_add=a
  41. opt_d=no; alias_delete=d
  42. opt_l=no; alias_list=l
  43. opt_i=no; alias_install=i
  44. opt_h=no; alias_help=h
  45. opt_v=no; alias_version=v
  46. if [ $# -eq 0 ]; then
  47. opt_h=yes
  48. fi
  49. while [ $# -gt 0 ]; do
  50. if [ ".$1" = ".--" ]; then
  51. shift
  52. break
  53. fi
  54. case $1 in
  55. --quiet|--cluster|--env|--start|--kill|--add|--delete|--list|--help|--version )
  56. name=`echo x$1 | sed -e 's;^x--;;'`
  57. eval "name=\$alias_${name}"
  58. eval "opt_${name}=yes"
  59. ;;
  60. -[qceskadlihv] )
  61. name=`echo x$1 | sed -e 's;^x-;;'`
  62. eval "opt_${name}=yes"
  63. ;;
  64. -* )
  65. echo "$prog_name:ERROR: unknown option \"$opt\"" 1>&2
  66. exit 1
  67. ;;
  68. * )
  69. break
  70. ;;
  71. esac
  72. shift
  73. done
  74. # stand-alone operation: display help information
  75. if [ ".$opt_h" = .yes ]; then
  76. echo "Usage: $prog_name [-h] [-v] [-q] [-c] [-e] [-s] [-k] [-a] [-d] [-l] [-i] [keyfile ...]"
  77. exit 0
  78. fi
  79. # stand-alone operation: display version information
  80. if [ ".$opt_v" = .yes ]; then
  81. echo "$prog_name $prog_vers ($prog_date)"
  82. exit 0
  83. fi
  84. # determine agent information filename
  85. hostname=`hostname`
  86. agentfile="$HOME/.ssh/agent-$hostname"
  87. if [ ".$opt_c" = .no ]; then
  88. if [ ! -f $agentfile ]; then
  89. agentfile="$HOME/.ssh/agent"
  90. fi
  91. fi
  92. # export agent configuration
  93. export SSH_AUTH_SOCK
  94. export SSH_AGENT_PID
  95. # perform agent information sanity check
  96. check_agent_info () {
  97. context="$1"
  98. invalid1=""
  99. invalid2=""
  100. if [ ".$SSH_AUTH_SOCK" != . ]; then
  101. # make sure the agent socket is (still) working
  102. if [ ! -r $SSH_AUTH_SOCK ]; then
  103. invalid1="agent socket $SSH_AUTH_SOCK no longer exists"
  104. else
  105. $ssh_add -l >/dev/null 2>&1
  106. if [ $? -eq 2 ]; then
  107. invalid1="agent socket $SSH_AUTH_SOCK no longer valid"
  108. fi
  109. fi
  110. fi
  111. if [ ".$SSH_AGENT_PID" != . ]; then
  112. # make sure the agent process is (still) running
  113. kill -0 $SSH_AGENT_PID >/dev/null 2>&1
  114. if [ $? -ne 0 ]; then
  115. invalid2="agent process $SSH_AGENT_PID no longer exists"
  116. fi
  117. fi
  118. if [ ".$invalid1" != . -o ".$invalid2" != . ]; then
  119. if [ ".$opt_e" = .no ]; then
  120. echo "$prog_name:WARNING: invalid agent setup found in $context." 1>&2
  121. if [ ".$invalid1" != . ]; then
  122. echo "$prog_name:WARNING: reason: $invalid1." 1>&2
  123. fi
  124. if [ ".$invalid2" != . ]; then
  125. echo "$prog_name:WARNING: reason: $invalid2." 1>&2
  126. fi
  127. if [ ".$context" = ".your shell environment" ]; then
  128. echo "$prog_name:HINT: run \"eval \`$prog_name -q -s -e\`\" to fix." 1>&2
  129. fi
  130. fi
  131. unset SSH_AUTH_SOCK
  132. unset SSH_AGENT_PID
  133. fi
  134. }
  135. check_agent_info "your shell environment"
  136. if [ -f $agentfile ]; then
  137. . $agentfile
  138. check_agent_info "in saved agent state"
  139. if [ ".$SSH_AUTH_SOCK" = . -o ".$SSH_AGENT_PID" = . ]; then
  140. rm -f $agentfile
  141. fi
  142. fi
  143. # if (now guarrantied to be correct) agent state is in
  144. # environment, but it is (no longer?) saved, save it now to fix situation.
  145. if [ ! -f $agentfile ]; then
  146. if [ ".$SSH_AUTH_SOCK" != . -a ".$SSH_AGENT_PID" != . ]; then
  147. ( echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK"
  148. echo "SSH_AGENT_PID=$SSH_AGENT_PID"
  149. ) >$agentfile && chmod 600 $agentfile
  150. echo "$prog_name:WARNING: valid agent information in your environment" 1>&2
  151. echo "$prog_name:WARNING: but no saved agent state file -- fixed" 1>&2
  152. fi
  153. fi
  154. # stop the agent
  155. if [ ".$opt_k" = .yes ]; then
  156. # stop the agent
  157. kill=yes
  158. if [ ".$SSH_AUTH_SOCK" != . -a ".$SSH_AGENT_PID" != . ]; then
  159. kill $SSH_AGENT_PID >/dev/null 2>&1 || true
  160. if [ ".$opt_q" = .no ]; then
  161. echo "$prog_name: stopped agent (pid $SSH_AGENT_PID)" 1>&2
  162. fi
  163. rm -f $agentfile
  164. unset SSH_AUTH_SOCK
  165. unset SSH_AGENT_PID
  166. elif [ ".$SSH_AUTH_SOCK" != . -a ".$SSH_AGENT_PID" = . ]; then
  167. if [ ".$opt_q" = .no ]; then
  168. echo "$prog_name: agent running remotely - cannot kill locally" 1>&2
  169. fi
  170. else
  171. if [ ".$opt_q" = .no ]; then
  172. echo "$prog_name: agent not running" 1>&2
  173. fi
  174. fi
  175. fi
  176. # start the agent
  177. if [ ".$opt_s" = .yes ]; then
  178. if [ ".$SSH_AUTH_SOCK" = . -a ".$SSH_AGENT_PID" = . ]; then
  179. eval `nohup $ssh_agent -s </dev/null 2>/dev/null | grep -v 'Agent pid'`
  180. if [ ".$opt_q" != .yes ]; then
  181. echo "$prog_name: spawned agent (pid $SSH_AGENT_PID)" 1>&2
  182. fi
  183. ( echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK"
  184. echo "SSH_AGENT_PID=$SSH_AGENT_PID"
  185. ) >$agentfile && chmod 600 $agentfile
  186. elif [ ".$SSH_AUTH_SOCK" != . -a ".$SSH_AGENT_PID" = . ]; then
  187. if [ ".$opt_q" = .no ]; then
  188. echo "$prog_name: agent already running remotely - no need to start locally" 1>&2
  189. fi
  190. else
  191. if [ ".$opt_q" = .no ]; then
  192. echo "$prog_name: agent already running" 1>&2
  193. fi
  194. fi
  195. fi
  196. # setup environment
  197. if [ ".$opt_e" = .yes ]; then
  198. if [ -r $agentfile ]; then
  199. sed -e 's/$/;/g' <$agentfile
  200. echo "export SSH_AUTH_SOCK;"
  201. echo "export SSH_AGENT_PID;"
  202. else
  203. echo "$prog_name:WARNING: agent not (or no longer) available" 1>&2
  204. echo "unset SSH_AUTH_SOCK;"
  205. echo "unset SSH_AGENT_PID;"
  206. fi
  207. fi
  208. # delete key(s) from agent
  209. if [ ".$opt_d" = .yes ]; then
  210. if [ ".$SSH_AUTH_SOCK" = . ]; then
  211. echo "$prog_name:WARNING: agent not available" 1>&2
  212. else
  213. if [ $# -eq 0 ]; then
  214. if [ ".$opt_q" = .no ]; then
  215. echo "$prog_name: deleting all keys" 1>&2
  216. fi
  217. $ssh_add -D
  218. else
  219. if [ ".$opt_q" = .no ]; then
  220. for key in "$@"; do
  221. echo "$prog_name: deleting key $key" 1>&2
  222. done
  223. fi
  224. $ssh_add -d "$@"
  225. fi
  226. fi
  227. fi
  228. # add key(s) into agent
  229. if [ ".$opt_a" = .yes ]; then
  230. if [ ".$SSH_AUTH_SOCK" = . ]; then
  231. echo "$prog_name:WARNING: agent not available" 1>&2
  232. else
  233. if [ $# -eq 0 ]; then
  234. echo "$prog_name:ERROR: no keys specified on command line" 1>&2
  235. exit 1
  236. fi
  237. key_loaded=`$ssh_add -l | awk '{ print $2; }'`
  238. key_missing=""
  239. for key_file in "$@"; do
  240. if [ -f "${key_file}.pub" ]; then
  241. key_this=`ssh-keygen -l -f ${key_file}.pub 2>&1 | awk '{ print $2; }'`
  242. else
  243. key_this=`ssh-keygen -l -f ${key_file} 2>&1 | awk '{ print $2; }'`
  244. fi
  245. load=yes
  246. for key in $key_loaded; do
  247. if [ ".$key" = ".$key_this" ]; then
  248. load=no
  249. break
  250. fi
  251. done
  252. if [ ".$load" = .yes ]; then
  253. if [ ".$opt_q" = .no ]; then
  254. echo "$prog_name: loading key $key_file" 1>&2
  255. fi
  256. key_missing="$key_missing $key_file"
  257. else
  258. if [ ".$opt_q" = .no ]; then
  259. echo "$prog_name: skipping key $key_file (already loaded)" 1>&2
  260. fi
  261. fi
  262. done
  263. if [ ".$key_missing" != . ]; then
  264. $ssh_add $key_missing
  265. fi
  266. fi
  267. fi
  268. # list key(s) available in agent
  269. if [ ".$opt_l" = .yes ]; then
  270. if [ ".$SSH_AUTH_SOCK" = . ]; then
  271. echo "$prog_name:WARNING: agent not available" 1>&2
  272. else
  273. $ssh_add -l
  274. fi
  275. fi
  276. # install key(s) into remote account
  277. if [ ".$opt_i" = .yes ]; then
  278. if [ ".$SSH_AUTH_SOCK" = . ]; then
  279. echo "$prog_name:WARNING: agent not available" 1>&2
  280. else
  281. for remote in "$@"; do
  282. echo "$prog_name: installing public keys into $remote"
  283. $ssh_add -L |\
  284. $ssh $remote "umask 077; test -d ~/.ssh || mkdir ~/.ssh; cat >>~/.ssh/authorized_keys"
  285. done
  286. fi
  287. fi