summaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorPhilip Hands <phil@hands.com>2020-09-16 16:13:30 +0200
committerDarren Tucker <dtucker@dtucker.net>2020-10-13 11:52:47 +1100
commita9c9e91a82bc1a2cf801b4e3ef27a941dbd27717 (patch)
tree1870f40c60f27b3a4e75fdbf4a89d9d14852e4c6 /contrib
parentf92424970c02b78852ff149378c7f2616ada4ccf (diff)
downloadopenssh-git-a9c9e91a82bc1a2cf801b4e3ef27a941dbd27717.tar.gz
add -s flag: to install keys via SFTP
This is prompted by: https://bugzilla.mindrot.org/show_bug.cgi?id=3201 Thanks go to Matthias Blümel for the idea, and the helpful patch, from which this patch grew. SSH-Copy-ID-Upstream: f7c76dc64427cd20287a6868f672423b62057614
Diffstat (limited to 'contrib')
-rw-r--r--contrib/ssh-copy-id99
-rw-r--r--contrib/ssh-copy-id.19
2 files changed, 80 insertions, 28 deletions
diff --git a/contrib/ssh-copy-id b/contrib/ssh-copy-id
index ee3f6374..6b8aca7d 100644
--- a/contrib/ssh-copy-id
+++ b/contrib/ssh-copy-id
@@ -1,6 +1,7 @@
#!/bin/sh
# Copyright (c) 1999-2020 Philip Hands <phil@hands.com>
+# 2020 Matthias Blümel <blaimi@blaimi.de>
# 2017 Sebastien Boyron <seb@boyron.eu>
# 2013 Martin Kletzander <mkletzan@redhat.com>
# 2010 Adeodato =?iso-8859-1?Q?Sim=F3?= <asp16@alu.ua.es>
@@ -61,11 +62,14 @@ fi
# shellcheck disable=SC2010
DEFAULT_PUB_ID_FILE=$(ls -t "${HOME}"/.ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1)
+SSH="ssh -a -x"
+umask 0177
usage () {
- printf 'Usage: %s [-h|-?|-f|-n] [-i [identity_file]] [-p port] [-F alternative ssh_config file] [[-o <ssh -o options>] ...] [user@]hostname\n' "$0" >&2
+ printf 'Usage: %s [-h|-?|-f|-n|-s] [-i [identity_file]] [-p port] [-F alternative ssh_config file] [[-o <ssh -o options>] ...] [user@]hostname\n' "$0" >&2
printf '\t-f: force mode -- copy keys without trying to check if they are already installed\n' >&2
printf '\t-n: dry run -- no keys are actually copied\n' >&2
+ printf '\t-s: use sftp -- use sftp instead of executing remote-commands. Can be useful if the remote only allows sftp\n' >&2
printf '\t-h|-?: print this help\n' >&2
exit 1
}
@@ -108,9 +112,8 @@ if [ -n "$SSH_AUTH_SOCK" ] && ssh-add -L >/dev/null 2>&1 ; then
GET_ID="ssh-add -L"
fi
-while getopts "i:o:p:F:fnh?" OPT
+while getopts "i:o:p:F:fnsh?" OPT
do
-
case "$OPT" in
i)
[ "${SEEN_OPT_I}" ] && {
@@ -129,6 +132,9 @@ do
n)
DRY_RUN=1
;;
+ s)
+ SFTP=sftp
+ ;;
h|\?)
usage
;;
@@ -137,9 +143,6 @@ done
#shift all args to keep only USER_HOST
shift $((OPTIND-1))
-
-
-
if [ $# = 0 ] ; then
usage
fi
@@ -170,6 +173,8 @@ fi
# and has the side effect of setting $NEW_IDS
populate_new_ids() {
L_SUCCESS="$1"
+ L_TMP_ID_FILE="$SCRATCH_DIR"/popids_tmp_id
+ L_OUTPUT_FILE="$SCRATCH_DIR"/popids_output
# shellcheck disable=SC2086
if [ "$FORCED" ] ; then
@@ -180,15 +185,6 @@ populate_new_ids() {
# repopulate "$@" inside this function
eval set -- "$SSH_OPTS"
- umask 0177
- L_TMP_ID_FILE=$(mktemp ~/.ssh/ssh-copy-id_id.XXXXXXXXXX)
- if test $? -ne 0 || test "x$L_TMP_ID_FILE" = "x" ; then
- printf '%s: ERROR: mktemp failed\n' "$0" >&2
- exit 1
- fi
- L_CLEANUP="rm -f \"$L_TMP_ID_FILE\" \"${L_TMP_ID_FILE}.stderr\""
- # shellcheck disable=SC2064
- trap "$L_CLEANUP" EXIT TERM INT QUIT
printf '%s: INFO: attempting to log in with the new key(s), to filter out any that are already installed\n' "$0" >&2
# shellcheck disable=SC2086
NEW_IDS=$(
@@ -200,16 +196,19 @@ populate_new_ids() {
# assumption will break if we implement the possibility of multiple -i options.
# The point being that if file based, ssh needs the private key, which it cannot
# find if only given the contents of the .pub file in an unrelated tmpfile
- ssh -i "${PRIV_ID_FILE:-$L_TMP_ID_FILE}" \
+ $SSH -i "${PRIV_ID_FILE:-$L_TMP_ID_FILE}" \
-o ControlPath=none \
-o LogLevel=INFO \
-o PreferredAuthentications=publickey \
- -o IdentitiesOnly=yes "$@" exit 2>"$L_TMP_ID_FILE.stderr" </dev/null
+ -o IdentitiesOnly=yes "$@" exit >"$L_OUTPUT_FILE" 2>&1 </dev/null
if [ "$?" = "$L_SUCCESS" ] ; then
: > "$L_TMP_ID_FILE"
+ elif [ "$SFTP" ] && grep 'allows sftp connections only' "$L_OUTPUT_FILE" >/dev/null ; then
+ # this error counts as a success when we're setting up an sftp connection
+ : > "$L_TMP_ID_FILE"
else
- grep 'Permission denied' "$L_TMP_ID_FILE.stderr" >/dev/null || {
- sed -e 's/^/ERROR: /' <"$L_TMP_ID_FILE.stderr" >"$L_TMP_ID_FILE"
+ grep 'Permission denied' "$L_OUTPUT_FILE" >/dev/null || {
+ sed -e 's/^/ERROR: /' <"$L_OUTPUT_FILE" >"$L_TMP_ID_FILE"
cat >/dev/null #consume the other keys, causing loop to end
}
fi
@@ -218,7 +217,6 @@ populate_new_ids() {
done
}
)
- eval "$L_CLEANUP" && trap - EXIT TERM INT QUIT
if expr "$NEW_IDS" : "^ERROR: " >/dev/null ; then
printf '\n%s: %s\n\n' "$0" "$NEW_IDS" >&2
@@ -264,7 +262,49 @@ EOF
printf "exec sh -c '%s'" "${INSTALLKEYS_SH}"
}
-REMOTE_VERSION=$(ssh -v -o PreferredAuthentications=',' -o ControlPath=none "$@" 2>&1 |
+installkeys_via_sftp() {
+
+ # repopulate "$@" inside this function
+ eval set -- "$SSH_OPTS"
+
+ L_KEYS=$SCRATCH_DIR/authorized_keys
+ L_SHARED_CON=$SCRATCH_DIR/master-conn
+ $SSH -f -N -M -S $L_SHARED_CON "$@"
+ L_CLEANUP="$SSH -S $L_SHARED_CON -O exit 'ignored' >/dev/null 2>&1 ; $SCRATCH_CLEANUP"
+ trap "$L_CLEANUP" EXIT TERM INT QUIT
+ sftp -b - -o "ControlPath=$L_SHARED_CON" "ignored" <<-EOF || return 1
+ -get .ssh/authorized_keys $L_KEYS
+ EOF
+ # add a newline or create file if it's missing, same like above
+ [ -z "$(tail -1c $L_KEYS 2>/dev/null)" ] || echo >> $L_KEYS
+ # append the keys being piped in here
+ cat >> $L_KEYS
+ sftp -b - -o "ControlPath=$L_SHARED_CON" "ignored" <<-EOF || return 1
+ -mkdir .ssh
+ chmod 700 .ssh
+ put $L_KEYS .ssh/authorized_keys
+ chmod 600 .ssh/authorized_keys
+ EOF
+ eval "$L_CLEANUP" && trap "$SCRATCH_CLEANUP" EXIT TERM INT QUIT
+}
+
+
+# create a scratch dir for any temporary files needed
+SCRATCH_DIR=$(mktemp -d ~/.ssh/ssh-copy-id.XXXXXXXXXX)
+if test $? -ne 0 || test "x$SCRATCH_DIR" = "x" ; then
+ printf '%s: ERROR: mktemp failed\n' "$0" >&2
+ exit 1
+fi
+chmod 0700 $SCRATCH_DIR
+if [ -d "$SCRATCH_DIR" ] ; then
+ SCRATCH_CLEANUP="rm -rf \"$SCRATCH_DIR\""
+ trap "$SCRATCH_CLEANUP" EXIT TERM INT QUIT
+else
+ printf '%s: ERROR: Required scratch directory (%s) was not created\n' "$0" "$SCRATCH_DIR" >&2
+ exit 1
+fi
+
+REMOTE_VERSION=$($SSH -v -o PreferredAuthentications=',' -o ControlPath=none "$@" 2>&1 |
sed -ne 's/.*remote software version //p')
# shellcheck disable=SC2029
@@ -277,7 +317,7 @@ case "$REMOTE_VERSION" in
printf '%s: WARNING: Non-dsa key (#%d) skipped (NetScreen only supports DSA keys)\n' "$0" "$KEY_NO" >&2
continue
}
- [ "$DRY_RUN" ] || printf 'set ssh pka-dsa key %s\nsave\nexit\n' "$KEY" | ssh -T "$@" >/dev/null 2>&1
+ [ "$DRY_RUN" ] || printf 'set ssh pka-dsa key %s\nsave\nexit\n' "$KEY" | $SSH -T "$@" >/dev/null 2>&1
if [ $? = 255 ] ; then
printf '%s: ERROR: installation of key #%d failed (please report a bug describing what caused this, so that we can make this message useful)\n' "$0" "$KEY_NO" >&2
else
@@ -291,16 +331,21 @@ case "$REMOTE_VERSION" in
dropbear*)
populate_new_ids 0
[ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | \
- ssh "$@" "$(installkeys_sh /etc/dropbear/authorized_keys)" \
+ $SSH "$@" "$(installkeys_sh /etc/dropbear/authorized_keys)" \
|| exit 1
ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l)
;;
*)
# Assuming that the remote host treats ~/.ssh/authorized_keys as one might expect
populate_new_ids 0
- [ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | \
- ssh "$@" "$(installkeys_sh)" \
- || exit 1
+ if ! [ "$DRY_RUN" ] ; then
+ printf '%s\n' "$NEW_IDS" | \
+ if [ "$SFTP" ] ; then
+ installkeys_via_sftp
+ else
+ $SSH "$@" "$(installkeys_sh)"
+ fi || exit 1
+ fi
ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l)
;;
esac
@@ -318,7 +363,7 @@ else
Number of key(s) added: $ADDED
- Now try logging into the machine, with: "ssh $SSH_OPTS"
+ Now try logging into the machine, with: "${SFTP:-ssh} $SSH_OPTS"
and check to make sure that only the key(s) you wanted were added.
EOF
diff --git a/contrib/ssh-copy-id.1 b/contrib/ssh-copy-id.1
index b75a8836..c141a296 100644
--- a/contrib/ssh-copy-id.1
+++ b/contrib/ssh-copy-id.1
@@ -1,5 +1,5 @@
.ig \" -*- nroff -*-
-Copyright (c) 1999-2016 hands.com Ltd. <http://hands.com/>
+Copyright (c) 1999-2020 hands.com Ltd. <http://hands.com/>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -31,6 +31,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.Nm
.Op Fl f
.Op Fl n
+.Op Fl s
.Op Fl i Op Ar identity_file
.Op Fl p Ar port
.Op Fl o Ar ssh_option
@@ -84,6 +85,12 @@ in more than one copy of the key being installed on the remote system.
.It Fl n
do a dry-run. Instead of installing keys on the remote system simply
prints the key(s) that would have been installed.
+.It Fl s
+SFTP mode: usually the public keys are installed by executing commands on the remote side.
+With this option the user's
+.Pa ~/.ssh/authorized_keys
+file will be downloaded, modified locally and uploaded with sftp.
+This option is useful if the server has restrictions on commands which can be used on the remote side.
.It Fl h , Fl ?
Print Usage summary
.It Fl p Ar port , Fl o Ar ssh_option