summaryrefslogtreecommitdiff
path: root/contrib/ssh-copy-id
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ssh-copy-id')
-rw-r--r--contrib/ssh-copy-id53
1 files changed, 35 insertions, 18 deletions
diff --git a/contrib/ssh-copy-id b/contrib/ssh-copy-id
index ae88e9958..afde8b170 100644
--- a/contrib/ssh-copy-id
+++ b/contrib/ssh-copy-id
@@ -56,10 +56,13 @@ then
56 fi 56 fi
57fi 57fi
58 58
59DEFAULT_PUB_ID_FILE=$(ls -t ${HOME}/.ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1) 59DEFAULT_PUB_ID_FILE="$HOME/$(cd "$HOME" ; ls -t .ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1)"
60 60
61usage () { 61usage () {
62 printf 'Usage: %s [-h|-?|-n] [-i [identity_file]] [-p port] [[-o <ssh -o options>] ...] [user@]hostname\n' "$0" >&2 62 printf 'Usage: %s [-h|-?|-f|-n] [-i [identity_file]] [-p port] [[-o <ssh -o options>] ...] [user@]hostname\n' "$0" >&2
63 printf '\t-f: force mode -- copy keys without trying to check if they are already installed\n' >&2
64 printf '\t-n: dry run -- no keys are actually copied\n' >&2
65 printf '\t-h|-?: print this help\n' >&2
63 exit 1 66 exit 1
64} 67}
65 68
@@ -77,15 +80,18 @@ use_id_file() {
77 PUB_ID_FILE="$L_ID_FILE.pub" 80 PUB_ID_FILE="$L_ID_FILE.pub"
78 fi 81 fi
79 82
80 PRIV_ID_FILE=$(dirname "$PUB_ID_FILE")/$(basename "$PUB_ID_FILE" .pub) 83 [ "$FORCED" ] || PRIV_ID_FILE=$(dirname "$PUB_ID_FILE")/$(basename "$PUB_ID_FILE" .pub)
81 84
82 # check that the files are readable 85 # check that the files are readable
83 for f in $PUB_ID_FILE $PRIV_ID_FILE ; do 86 for f in "$PUB_ID_FILE" ${PRIV_ID_FILE:+"$PRIV_ID_FILE"} ; do
84 ErrMSG=$( { : < $f ; } 2>&1 ) || { 87 ErrMSG=$( { : < "$f" ; } 2>&1 ) || {
85 printf "\n%s: ERROR: failed to open ID file '%s': %s\n\n" "$0" "$f" "$(printf "%s\n" "$ErrMSG" | sed -e 's/.*: *//')" 88 local L_PRIVMSG=""
89 [ "$f" = "$PRIV_ID_FILE" ] && L_PRIVMSG=" (to install the contents of '$PUB_ID_FILE' anyway, look at the -f option)"
90 printf "\n%s: ERROR: failed to open ID file '%s': %s\n" "$0" "$f" "$(printf "%s\n%s\n" "$ErrMSG" "$L_PRIVMSG" | sed -e 's/.*: *//')"
86 exit 1 91 exit 1
87 } 92 }
88 done 93 done
94 printf '%s: INFO: Source of key(s) to be installed: "%s"\n' "$0" "$PUB_ID_FILE" >&2
89 GET_ID="cat \"$PUB_ID_FILE\"" 95 GET_ID="cat \"$PUB_ID_FILE\""
90} 96}
91 97
@@ -121,7 +127,7 @@ do
121 } 127 }
122 shift 128 shift
123 ;; 129 ;;
124 -n|-h|-\?) 130 -f|-n|-h|-\?)
125 OPT="$1" 131 OPT="$1"
126 OPTARG= 132 OPTARG=
127 shift 133 shift
@@ -154,6 +160,9 @@ do
154 -o|-p) 160 -o|-p)
155 SSH_OPTS="${SSH_OPTS:+$SSH_OPTS }$OPT '$(quote "$OPTARG")'" 161 SSH_OPTS="${SSH_OPTS:+$SSH_OPTS }$OPT '$(quote "$OPTARG")'"
156 ;; 162 ;;
163 -f)
164 FORCED=1
165 ;;
157 -n) 166 -n)
158 DRY_RUN=1 167 DRY_RUN=1
159 ;; 168 ;;
@@ -194,27 +203,35 @@ fi
194populate_new_ids() { 203populate_new_ids() {
195 local L_SUCCESS="$1" 204 local L_SUCCESS="$1"
196 205
206 if [ "$FORCED" ] ; then
207 NEW_IDS=$(eval $GET_ID)
208 return
209 fi
210
197 # repopulate "$@" inside this function 211 # repopulate "$@" inside this function
198 eval set -- "$SSH_OPTS" 212 eval set -- "$SSH_OPTS"
199 213
200 umask 0177 214 umask 0177
201 local L_TMP_ID_FILE=$(mktemp ~/.ssh/ssh-copy-id_id.XXXXXXXXXX) 215 local L_TMP_ID_FILE=$(mktemp ~/.ssh/ssh-copy-id_id.XXXXXXXXXX)
202 if test $? -ne 0 || test "x$L_TMP_ID_FILE" = "x" ; then 216 if test $? -ne 0 || test "x$L_TMP_ID_FILE" = "x" ; then
203 echo "mktemp failed" 1>&2 217 printf '%s: ERROR: mktemp failed\n' "$0" >&2
204 exit 1 218 exit 1
205 fi 219 fi
206 trap "rm -f $L_TMP_ID_FILE ${L_TMP_ID_FILE}.pub" EXIT TERM INT QUIT 220 local L_CLEANUP="rm -f \"$L_TMP_ID_FILE\" \"${L_TMP_ID_FILE}.stderr\""
221 trap "$L_CLEANUP" EXIT TERM INT QUIT
207 printf '%s: INFO: attempting to log in with the new key(s), to filter out any that are already installed\n' "$0" >&2 222 printf '%s: INFO: attempting to log in with the new key(s), to filter out any that are already installed\n' "$0" >&2
208 NEW_IDS=$( 223 NEW_IDS=$(
209 eval $GET_ID | { 224 eval $GET_ID | {
210 while read ID ; do 225 while read ID || [ "$ID" ] ; do
211 printf '%s\n' "$ID" > $L_TMP_ID_FILE 226 printf '%s\n' "$ID" > "$L_TMP_ID_FILE"
212 227
213 # the next line assumes $PRIV_ID_FILE only set if using a single id file - this 228 # the next line assumes $PRIV_ID_FILE only set if using a single id file - this
214 # assumption will break if we implement the possibility of multiple -i options. 229 # assumption will break if we implement the possibility of multiple -i options.
215 # The point being that if file based, ssh needs the private key, which it cannot 230 # The point being that if file based, ssh needs the private key, which it cannot
216 # find if only given the contents of the .pub file in an unrelated tmpfile 231 # find if only given the contents of the .pub file in an unrelated tmpfile
217 ssh -i "${PRIV_ID_FILE:-$L_TMP_ID_FILE}" \ 232 ssh -i "${PRIV_ID_FILE:-$L_TMP_ID_FILE}" \
233 -o ControlPath=none \
234 -o LogLevel=INFO \
218 -o PreferredAuthentications=publickey \ 235 -o PreferredAuthentications=publickey \
219 -o IdentitiesOnly=yes "$@" exit 2>$L_TMP_ID_FILE.stderr </dev/null 236 -o IdentitiesOnly=yes "$@" exit 2>$L_TMP_ID_FILE.stderr </dev/null
220 if [ "$?" = "$L_SUCCESS" ] ; then 237 if [ "$?" = "$L_SUCCESS" ] ; then
@@ -230,20 +247,21 @@ populate_new_ids() {
230 done 247 done
231 } 248 }
232 ) 249 )
233 rm -f $L_TMP_ID_FILE* && trap - EXIT TERM INT QUIT 250 eval "$L_CLEANUP" && trap - EXIT TERM INT QUIT
234 251
235 if expr "$NEW_IDS" : "^ERROR: " >/dev/null ; then 252 if expr "$NEW_IDS" : "^ERROR: " >/dev/null ; then
236 printf '\n%s: %s\n\n' "$0" "$NEW_IDS" >&2 253 printf '\n%s: %s\n\n' "$0" "$NEW_IDS" >&2
237 exit 1 254 exit 1
238 fi 255 fi
239 if [ -z "$NEW_IDS" ] ; then 256 if [ -z "$NEW_IDS" ] ; then
240 printf '\n%s: WARNING: All keys were skipped because they already exist on the remote system.\n\n' "$0" >&2 257 printf '\n%s: WARNING: All keys were skipped because they already exist on the remote system.\n' "$0" >&2
258 printf '\t\t(if you think this is a mistake, you may want to use -f option)\n\n' "$0" >&2
241 exit 0 259 exit 0
242 fi 260 fi
243 printf '%s: INFO: %d key(s) remain to be installed -- if you are prompted now it is to install the new keys\n' "$0" "$(printf '%s\n' "$NEW_IDS" | wc -l)" >&2 261 printf '%s: INFO: %d key(s) remain to be installed -- if you are prompted now it is to install the new keys\n' "$0" "$(printf '%s\n' "$NEW_IDS" | wc -l)" >&2
244} 262}
245 263
246REMOTE_VERSION=$(ssh -v -o PreferredAuthentications=',' "$@" 2>&1 | 264REMOTE_VERSION=$(ssh -v -o PreferredAuthentications=',' -o ControlPath=none "$@" 2>&1 |
247 sed -ne 's/.*remote software version //p') 265 sed -ne 's/.*remote software version //p')
248 266
249case "$REMOTE_VERSION" in 267case "$REMOTE_VERSION" in
@@ -269,10 +287,9 @@ case "$REMOTE_VERSION" in
269 *) 287 *)
270 # Assuming that the remote host treats ~/.ssh/authorized_keys as one might expect 288 # Assuming that the remote host treats ~/.ssh/authorized_keys as one might expect
271 populate_new_ids 0 289 populate_new_ids 0
272 [ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | ssh "$@" " 290 # in ssh below - to defend against quirky remote shells: use 'exec sh -c' to get POSIX; 'cd' to be at $HOME; and all on one line, because tcsh.
273 umask 077 ; 291 [ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | \
274 mkdir -p .ssh && cat >> .ssh/authorized_keys || exit 1 ; 292 ssh "$@" "exec sh -c 'cd ; umask 077 ; mkdir -p .ssh && cat >> .ssh/authorized_keys || exit 1 ; if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi'" \
275 if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi" \
276 || exit 1 293 || exit 1
277 ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l) 294 ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l)
278 ;; 295 ;;