diff options
-rw-r--r-- | contrib/ssh-copy-id | 53 | ||||
-rw-r--r-- | contrib/ssh-copy-id.1 | 5 |
2 files changed, 40 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 |
57 | fi | 57 | fi |
58 | 58 | ||
59 | DEFAULT_PUB_ID_FILE=$(ls -t ${HOME}/.ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1) | 59 | DEFAULT_PUB_ID_FILE="$HOME/$(cd "$HOME" ; ls -t .ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1)" |
60 | 60 | ||
61 | usage () { | 61 | usage () { |
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 | |||
194 | populate_new_ids() { | 203 | populate_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 | ||
246 | REMOTE_VERSION=$(ssh -v -o PreferredAuthentications=',' "$@" 2>&1 | | 264 | REMOTE_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 | ||
249 | case "$REMOTE_VERSION" in | 267 | case "$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 | ;; |
diff --git a/contrib/ssh-copy-id.1 b/contrib/ssh-copy-id.1 index 67a59e492..8850cceda 100644 --- a/contrib/ssh-copy-id.1 +++ b/contrib/ssh-copy-id.1 | |||
@@ -29,6 +29,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
29 | .Nd use locally available keys to authorise logins on a remote machine | 29 | .Nd use locally available keys to authorise logins on a remote machine |
30 | .Sh SYNOPSIS | 30 | .Sh SYNOPSIS |
31 | .Nm | 31 | .Nm |
32 | .Op Fl f | ||
32 | .Op Fl n | 33 | .Op Fl n |
33 | .Op Fl i Op Ar identity_file | 34 | .Op Fl i Op Ar identity_file |
34 | .Op Fl p Ar port | 35 | .Op Fl p Ar port |
@@ -76,6 +77,10 @@ is used. | |||
76 | Note that this can be used to ensure that the keys copied have the | 77 | Note that this can be used to ensure that the keys copied have the |
77 | comment one prefers and/or extra options applied, by ensuring that the | 78 | comment one prefers and/or extra options applied, by ensuring that the |
78 | key file has these set as preferred before the copy is attempted. | 79 | key file has these set as preferred before the copy is attempted. |
80 | .It Fl f | ||
81 | Forced mode: doesn't check if the keys are present on the remote server. | ||
82 | This means that it does not need the private key. Of course, this can result | ||
83 | in more than one copy of the key being installed on the remote system. | ||
79 | .It Fl n | 84 | .It Fl n |
80 | do a dry-run. Instead of installing keys on the remote system simply | 85 | do a dry-run. Instead of installing keys on the remote system simply |
81 | prints the key(s) that would have been installed. | 86 | prints the key(s) that would have been installed. |