xref: /dflybsd-src/crypto/openssh/contrib/ssh-copy-id (revision ba1276acd1c8c22d225b1bcf370a14c878644f44)
153254411SSascha Wildner#!/bin/sh
253254411SSascha Wildner
3*ba1276acSMatthew Dillon# Copyright (c) 1999-2023 Philip Hands <phil@hands.com>
4*ba1276acSMatthew Dillon#               2021 Carlos Rodríguez Gili <carlos.rodriguez-gili@upc.edu>
550a69bb5SSascha Wildner#               2020 Matthias Blümel <blaimi@blaimi.de>
650a69bb5SSascha Wildner#               2017 Sebastien Boyron <seb@boyron.eu>
753254411SSascha Wildner#               2013 Martin Kletzander <mkletzan@redhat.com>
853254411SSascha Wildner#               2010 Adeodato =?iso-8859-1?Q?Sim=F3?= <asp16@alu.ua.es>
953254411SSascha Wildner#               2010 Eric Moret <eric.moret@gmail.com>
1053254411SSascha Wildner#               2009 Xr <xr@i-jeuxvideo.com>
1153254411SSascha Wildner#               2007 Justin Pryzby <justinpryzby@users.sourceforge.net>
1253254411SSascha Wildner#               2004 Reini Urban <rurban@x-ray.at>
1353254411SSascha Wildner#               2003 Colin Watson <cjwatson@debian.org>
1453254411SSascha Wildner# All rights reserved.
1553254411SSascha Wildner#
1653254411SSascha Wildner# Redistribution and use in source and binary forms, with or without
1753254411SSascha Wildner# modification, are permitted provided that the following conditions
1853254411SSascha Wildner# are met:
1953254411SSascha Wildner# 1. Redistributions of source code must retain the above copyright
2053254411SSascha Wildner#    notice, this list of conditions and the following disclaimer.
2153254411SSascha Wildner# 2. Redistributions in binary form must reproduce the above copyright
2253254411SSascha Wildner#    notice, this list of conditions and the following disclaimer in the
2353254411SSascha Wildner#    documentation and/or other materials provided with the distribution.
2453254411SSascha Wildner#
2553254411SSascha Wildner# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2653254411SSascha Wildner# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2753254411SSascha Wildner# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2853254411SSascha Wildner# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2953254411SSascha Wildner# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
3053254411SSascha Wildner# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3153254411SSascha Wildner# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3253254411SSascha Wildner# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3353254411SSascha Wildner# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3453254411SSascha Wildner# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3553254411SSascha Wildner
3653254411SSascha Wildner# Shell script to install your public key(s) on a remote machine
3753254411SSascha Wildner# See the ssh-copy-id(1) man page for details
3853254411SSascha Wildner
3950a69bb5SSascha Wildner# shellcheck shell=dash
4050a69bb5SSascha Wildner
4153254411SSascha Wildner# check that we have something mildly sane as our shell, or try to find something better
4253254411SSascha Wildnerif false ^ printf "%s: WARNING: ancient shell, hunting for a more modern one... " "$0"
4353254411SSascha Wildnerthen
4453254411SSascha Wildner  SANE_SH=${SANE_SH:-/usr/bin/ksh}
4553254411SSascha Wildner  if printf 'true ^ false\n' | "$SANE_SH"
4653254411SSascha Wildner  then
4750a69bb5SSascha Wildner    printf "'%s' seems viable.\\n" "$SANE_SH"
4853254411SSascha Wildner    exec "$SANE_SH" "$0" "$@"
4953254411SSascha Wildner  else
5053254411SSascha Wildner    cat <<-EOF
5153254411SSascha Wildner	oh dear.
5253254411SSascha Wildner
5353254411SSascha Wildner	  If you have a more recent shell available, that supports \$(...) etc.
5453254411SSascha Wildner	  please try setting the environment variable SANE_SH to the path of that
5553254411SSascha Wildner	  shell, and then retry running this script. If that works, please report
5653254411SSascha Wildner	  a bug describing your setup, and the shell you used to make it work.
5753254411SSascha Wildner
5853254411SSascha Wildner	EOF
5950a69bb5SSascha Wildner    printf '%s: ERROR: Less dimwitted shell required.\n' "$0"
6053254411SSascha Wildner    exit 1
6153254411SSascha Wildner  fi
6253254411SSascha Wildnerfi
6353254411SSascha Wildner
6450a69bb5SSascha Wildner# shellcheck disable=SC2010
6550a69bb5SSascha WildnerDEFAULT_PUB_ID_FILE=$(ls -t "${HOME}"/.ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1)
6650a69bb5SSascha WildnerSSH="ssh -a -x"
67*ba1276acSMatthew DillonTARGET_PATH=".ssh/authorized_keys"
6850a69bb5SSascha Wildnerumask 0177
6953254411SSascha Wildner
7053254411SSascha Wildnerusage () {
71*ba1276acSMatthew Dillon  printf 'Usage: %s [-h|-?|-f|-n|-s|-x] [-i [identity_file]] [-p port] [-F alternative ssh_config file] [-t target_path] [[-o <ssh -o options>] ...] [user@]hostname\n' "$0" >&2
7253254411SSascha Wildner  printf '\t-f: force mode -- copy keys without trying to check if they are already installed\n' >&2
7353254411SSascha Wildner  printf '\t-n: dry run    -- no keys are actually copied\n' >&2
7450a69bb5SSascha Wildner  printf '\t-s: use sftp   -- use sftp instead of executing remote-commands. Can be useful if the remote only allows sftp\n' >&2
75*ba1276acSMatthew Dillon  printf '\t-x: debug      -- enables -x in this shell, for debugging\n' >&2
7653254411SSascha Wildner  printf '\t-h|-?: print this help\n' >&2
7753254411SSascha Wildner  exit 1
7853254411SSascha Wildner}
7953254411SSascha Wildner
8053254411SSascha Wildner# escape any single quotes in an argument
8153254411SSascha Wildnerquote() {
8250a69bb5SSascha Wildner  printf '%s\n' "$1" | sed -e "s/'/'\\\\''/g"
8353254411SSascha Wildner}
8453254411SSascha Wildner
8553254411SSascha Wildneruse_id_file() {
8650a69bb5SSascha Wildner  L_ID_FILE="$1"
8753254411SSascha Wildner
8853254411SSascha Wildner  if [ -z "$L_ID_FILE" ] ; then
8950a69bb5SSascha Wildner    printf '%s: ERROR: no ID file found\n' "$0"
9053254411SSascha Wildner    exit 1
9153254411SSascha Wildner  fi
9253254411SSascha Wildner
9350a69bb5SSascha Wildner  if expr "$L_ID_FILE" : '.*\.pub$' >/dev/null ; then
9453254411SSascha Wildner    PUB_ID_FILE="$L_ID_FILE"
9553254411SSascha Wildner  else
9653254411SSascha Wildner    PUB_ID_FILE="$L_ID_FILE.pub"
9753254411SSascha Wildner  fi
9853254411SSascha Wildner
9953254411SSascha Wildner  [ "$FORCED" ] || PRIV_ID_FILE=$(dirname "$PUB_ID_FILE")/$(basename "$PUB_ID_FILE" .pub)
10053254411SSascha Wildner
10153254411SSascha Wildner  # check that the files are readable
10253254411SSascha Wildner  for f in "$PUB_ID_FILE" ${PRIV_ID_FILE:+"$PRIV_ID_FILE"} ; do
10353254411SSascha Wildner    ErrMSG=$( { : < "$f" ; } 2>&1 ) || {
10450a69bb5SSascha Wildner      L_PRIVMSG=""
10553254411SSascha Wildner      [ "$f" = "$PRIV_ID_FILE" ] && L_PRIVMSG="	(to install the contents of '$PUB_ID_FILE' anyway, look at the -f option)"
10650a69bb5SSascha Wildner      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/.*: *//')"
10753254411SSascha Wildner      exit 1
10853254411SSascha Wildner    }
10953254411SSascha Wildner  done
11053254411SSascha Wildner  printf '%s: INFO: Source of key(s) to be installed: "%s"\n' "$0" "$PUB_ID_FILE" >&2
11153254411SSascha Wildner  GET_ID="cat \"$PUB_ID_FILE\""
11253254411SSascha Wildner}
11353254411SSascha Wildner
11453254411SSascha Wildnerif [ -n "$SSH_AUTH_SOCK" ] && ssh-add -L >/dev/null 2>&1 ; then
11553254411SSascha Wildner  GET_ID="ssh-add -L"
11653254411SSascha Wildnerfi
11753254411SSascha Wildner
118*ba1276acSMatthew Dillonwhile getopts "i:o:p:F:t:fnsxh?" OPT
11953254411SSascha Wildnerdo
12053254411SSascha Wildner  case "$OPT" in
12150a69bb5SSascha Wildner    i)
12250a69bb5SSascha Wildner      [ "${SEEN_OPT_I}" ] && {
12350a69bb5SSascha Wildner        printf '\n%s: ERROR: -i option must not be specified more than once\n\n' "$0"
12450a69bb5SSascha Wildner        usage
12550a69bb5SSascha Wildner      }
12653254411SSascha Wildner      SEEN_OPT_I="yes"
12753254411SSascha Wildner      use_id_file "${OPTARG:-$DEFAULT_PUB_ID_FILE}"
12853254411SSascha Wildner      ;;
129*ba1276acSMatthew Dillon    o|F)
130*ba1276acSMatthew Dillon      OPTS_oF="${OPTS_oF:+$OPTS_oF }-$OPT '$(quote "${OPTARG}")'"
13153254411SSascha Wildner      ;;
13250a69bb5SSascha Wildner    f)
13353254411SSascha Wildner      FORCED=1
13453254411SSascha Wildner      ;;
13550a69bb5SSascha Wildner    n)
13653254411SSascha Wildner      DRY_RUN=1
13753254411SSascha Wildner      ;;
138*ba1276acSMatthew Dillon    p)
139*ba1276acSMatthew Dillon      SSH_PORT=${OPTARG}
140*ba1276acSMatthew Dillon      ;;
14150a69bb5SSascha Wildner    s)
14250a69bb5SSascha Wildner      SFTP=sftp
14350a69bb5SSascha Wildner      ;;
144*ba1276acSMatthew Dillon    t)
145*ba1276acSMatthew Dillon      TARGET_PATH="${OPTARG}"
146*ba1276acSMatthew Dillon      ;;
147*ba1276acSMatthew Dillon    x)
148*ba1276acSMatthew Dillon      SET_X="set -x;"
149*ba1276acSMatthew Dillon      set -x
150*ba1276acSMatthew Dillon      ;;
15150a69bb5SSascha Wildner    h|\?)
15253254411SSascha Wildner      usage
15353254411SSascha Wildner      ;;
15453254411SSascha Wildner  esac
15553254411SSascha Wildnerdone
15650a69bb5SSascha Wildner#shift all args to keep only USER_HOST
15750a69bb5SSascha Wildnershift $((OPTIND-1))
15853254411SSascha Wildner
15953254411SSascha Wildnerif [ $# = 0 ] ; then
16053254411SSascha Wildner  usage
16153254411SSascha Wildnerfi
16253254411SSascha Wildnerif [ $# != 1 ] ; then
16353254411SSascha Wildner  printf '%s: ERROR: Too many arguments.  Expecting a target hostname, got: %s\n\n' "$0" "$SAVEARGS" >&2
16453254411SSascha Wildner  usage
16553254411SSascha Wildnerfi
16653254411SSascha Wildner
16750a69bb5SSascha WildnerUSER_HOST="$*"
16853254411SSascha Wildner# tack the hostname onto SSH_OPTS
169*ba1276acSMatthew DillonOPTS_USER_HOST="${OPTS_oF:+$OPTS_oF }'$(quote "$USER_HOST")'"
170*ba1276acSMatthew DillonSSH_OPTS="${SSH_PORT:+-p $SSH_PORT }$OPTS_USER_HOST"
17153254411SSascha Wildner# and populate "$@" for later use (only way to get proper quoting of options)
17253254411SSascha Wildnereval set -- "$SSH_OPTS"
17353254411SSascha Wildner
17450a69bb5SSascha Wildner# shellcheck disable=SC2086
17553254411SSascha Wildnerif [ -z "$(eval $GET_ID)" ] && [ -r "${PUB_ID_FILE:=$DEFAULT_PUB_ID_FILE}" ] ; then
17653254411SSascha Wildner  use_id_file "$PUB_ID_FILE"
17753254411SSascha Wildnerfi
17853254411SSascha Wildner
17950a69bb5SSascha Wildner# shellcheck disable=SC2086
18053254411SSascha Wildnerif [ -z "$(eval $GET_ID)" ] ; then
18153254411SSascha Wildner  printf '%s: ERROR: No identities found\n' "$0" >&2
18253254411SSascha Wildner  exit 1
18353254411SSascha Wildnerfi
18453254411SSascha Wildner
18550a69bb5SSascha Wildner# filter_ids()
18650a69bb5SSascha Wildner# tries to log in using the keys piped to it, and filters out any that work
18750a69bb5SSascha Wildnerfilter_ids() {
18850a69bb5SSascha Wildner  L_SUCCESS="$1"
18950a69bb5SSascha Wildner  L_TMP_ID_FILE="$SCRATCH_DIR"/popids_tmp_id
19050a69bb5SSascha Wildner  L_OUTPUT_FILE="$SCRATCH_DIR"/popids_output
19153254411SSascha Wildner
19253254411SSascha Wildner  # repopulate "$@" inside this function
19353254411SSascha Wildner  eval set -- "$SSH_OPTS"
19453254411SSascha Wildner
19550a69bb5SSascha Wildner  while read -r ID || [ "$ID" ] ; do
19653254411SSascha Wildner    printf '%s\n' "$ID" > "$L_TMP_ID_FILE"
19753254411SSascha Wildner
19853254411SSascha Wildner    # the next line assumes $PRIV_ID_FILE only set if using a single id file - this
19953254411SSascha Wildner    # assumption will break if we implement the possibility of multiple -i options.
20053254411SSascha Wildner    # The point being that if file based, ssh needs the private key, which it cannot
20153254411SSascha Wildner    # find if only given the contents of the .pub file in an unrelated tmpfile
20250a69bb5SSascha Wildner    $SSH -i "${PRIV_ID_FILE:-$L_TMP_ID_FILE}" \
20353254411SSascha Wildner      -o ControlPath=none \
20453254411SSascha Wildner      -o LogLevel=INFO \
20553254411SSascha Wildner      -o PreferredAuthentications=publickey \
20650a69bb5SSascha Wildner      -o IdentitiesOnly=yes "$@" exit >"$L_OUTPUT_FILE" 2>&1 </dev/null
20750a69bb5SSascha Wildner    if [ "$?" = "$L_SUCCESS" ] || {
20850a69bb5SSascha Wildner         [ "$SFTP" ] && grep 'allows sftp connections only' "$L_OUTPUT_FILE" >/dev/null
20950a69bb5SSascha Wildner         # this error counts as a success if we're setting up an sftp connection
21050a69bb5SSascha Wildner       }
21150a69bb5SSascha Wildner    then
21253254411SSascha Wildner      : > "$L_TMP_ID_FILE"
21353254411SSascha Wildner    else
21450a69bb5SSascha Wildner      grep 'Permission denied' "$L_OUTPUT_FILE" >/dev/null || {
21550a69bb5SSascha Wildner        sed -e 's/^/ERROR: /' <"$L_OUTPUT_FILE" >"$L_TMP_ID_FILE"
21653254411SSascha Wildner        cat >/dev/null #consume the other keys, causing loop to end
21753254411SSascha Wildner      }
21853254411SSascha Wildner    fi
21953254411SSascha Wildner
22053254411SSascha Wildner    cat "$L_TMP_ID_FILE"
22153254411SSascha Wildner  done
22253254411SSascha Wildner}
22350a69bb5SSascha Wildner
22450a69bb5SSascha Wildner# populate_new_ids() uses several global variables ($USER_HOST, $SSH_OPTS ...)
22550a69bb5SSascha Wildner# and has the side effect of setting $NEW_IDS
22650a69bb5SSascha Wildnerpopulate_new_ids() {
22750a69bb5SSascha Wildner  if [ "$FORCED" ] ; then
22850a69bb5SSascha Wildner    # shellcheck disable=SC2086
22950a69bb5SSascha Wildner    NEW_IDS=$(eval $GET_ID)
23050a69bb5SSascha Wildner    return
23150a69bb5SSascha Wildner  fi
23250a69bb5SSascha Wildner
23350a69bb5SSascha Wildner  printf '%s: INFO: attempting to log in with the new key(s), to filter out any that are already installed\n' "$0" >&2
23450a69bb5SSascha Wildner  # shellcheck disable=SC2086
23550a69bb5SSascha Wildner  NEW_IDS=$(eval $GET_ID | filter_ids $1)
23653254411SSascha Wildner
23753254411SSascha Wildner  if expr "$NEW_IDS" : "^ERROR: " >/dev/null ; then
23853254411SSascha Wildner    printf '\n%s: %s\n\n' "$0" "$NEW_IDS" >&2
23953254411SSascha Wildner    exit 1
24053254411SSascha Wildner  fi
24153254411SSascha Wildner  if [ -z "$NEW_IDS" ] ; then
24253254411SSascha Wildner    printf '\n%s: WARNING: All keys were skipped because they already exist on the remote system.\n' "$0" >&2
24350a69bb5SSascha Wildner    printf '\t\t(if you think this is a mistake, you may want to use -f option)\n\n' >&2
24453254411SSascha Wildner    exit 0
24553254411SSascha Wildner  fi
24653254411SSascha Wildner  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
24753254411SSascha Wildner}
24853254411SSascha Wildner
24950a69bb5SSascha Wildner# installkey_sh [target_path]
250*ba1276acSMatthew Dillon#    produce a one-liner to add the keys to remote $TARGET_PATH
25150a69bb5SSascha Wildnerinstallkeys_sh() {
25250a69bb5SSascha Wildner  # In setting INSTALLKEYS_SH:
25350a69bb5SSascha Wildner  #    the tr puts it all on one line (to placate tcsh)
25450a69bb5SSascha Wildner  #      (hence the excessive use of semi-colons (;) )
25550a69bb5SSascha Wildner  # then in the command:
25650a69bb5SSascha Wildner  #    cd to be at $HOME, just in case;
25750a69bb5SSascha Wildner  #    the -z `tail ...` checks for a trailing newline. The echo adds one if was missing
25850a69bb5SSascha Wildner  #    the cat adds the keys we're getting via STDIN
25950a69bb5SSascha Wildner  #    and if available restorecon is used to restore the SELinux context
260*ba1276acSMatthew Dillon  # OpenWrt has a special case for root only.
26150a69bb5SSascha Wildner  INSTALLKEYS_SH=$(tr '\t\n' ' ' <<-EOF
262*ba1276acSMatthew Dillon	$SET_X
26350a69bb5SSascha Wildner	cd;
26450a69bb5SSascha Wildner	umask 077;
265*ba1276acSMatthew Dillon	AUTH_KEY_FILE="${TARGET_PATH}";
266*ba1276acSMatthew Dillon	[ -f /etc/openwrt_release ] && [ "\$LOGNAME" = "root" ] &&
267*ba1276acSMatthew Dillon		AUTH_KEY_FILE=/etc/dropbear/authorized_keys;
268*ba1276acSMatthew Dillon	AUTH_KEY_DIR=\`dirname "\${AUTH_KEY_FILE}"\`;
269*ba1276acSMatthew Dillon	mkdir -p "\${AUTH_KEY_DIR}" &&
270*ba1276acSMatthew Dillon		{ [ -z "\`tail -1c "\${AUTH_KEY_FILE}" 2>/dev/null\`" ] ||
271*ba1276acSMatthew Dillon			echo >> "\${AUTH_KEY_FILE}" || exit 1; } &&
272*ba1276acSMatthew Dillon		cat >> "\${AUTH_KEY_FILE}" || exit 1;
27350a69bb5SSascha Wildner	if type restorecon >/dev/null 2>&1; then
274*ba1276acSMatthew Dillon		restorecon -F "\${AUTH_KEY_DIR}" "\${AUTH_KEY_FILE}";
27550a69bb5SSascha Wildner	fi
27650a69bb5SSascha Wildner	EOF
27750a69bb5SSascha Wildner  )
27850a69bb5SSascha Wildner
27950a69bb5SSascha Wildner  # to defend against quirky remote shells: use 'exec sh -c' to get POSIX;
28050a69bb5SSascha Wildner  printf "exec sh -c '%s'" "${INSTALLKEYS_SH}"
28150a69bb5SSascha Wildner}
28250a69bb5SSascha Wildner
28350a69bb5SSascha Wildner#shellcheck disable=SC2120 # the 'eval set' confuses this
28450a69bb5SSascha Wildnerinstallkeys_via_sftp() {
285*ba1276acSMatthew Dillon  AUTH_KEY_FILE=${TARGET_PATH}
286*ba1276acSMatthew Dillon  AUTH_KEY_DIR=$(dirname "${AUTH_KEY_FILE}")
28750a69bb5SSascha Wildner
28850a69bb5SSascha Wildner  # repopulate "$@" inside this function
28950a69bb5SSascha Wildner  eval set -- "$SSH_OPTS"
29050a69bb5SSascha Wildner
29150a69bb5SSascha Wildner  L_KEYS=$SCRATCH_DIR/authorized_keys
29250a69bb5SSascha Wildner  L_SHARED_CON=$SCRATCH_DIR/master-conn
29350a69bb5SSascha Wildner  $SSH -f -N -M -S "$L_SHARED_CON" "$@"
29450a69bb5SSascha Wildner  L_CLEANUP="$SSH -S $L_SHARED_CON -O exit 'ignored' >/dev/null 2>&1 ; $SCRATCH_CLEANUP"
29550a69bb5SSascha Wildner  #shellcheck disable=SC2064
29650a69bb5SSascha Wildner  trap "$L_CLEANUP" EXIT TERM INT QUIT
29750a69bb5SSascha Wildner  sftp -b - -o "ControlPath=$L_SHARED_CON" "ignored" <<-EOF || return 1
298*ba1276acSMatthew Dillon	-get "$AUTH_KEY_FILE" "$L_KEYS"
29950a69bb5SSascha Wildner	EOF
30050a69bb5SSascha Wildner  # add a newline or create file if it's missing, same like above
30150a69bb5SSascha Wildner  [ -z "$(tail -1c "$L_KEYS" 2>/dev/null)" ] || echo >> "$L_KEYS"
30250a69bb5SSascha Wildner  # append the keys being piped in here
30350a69bb5SSascha Wildner  cat >> "$L_KEYS"
30450a69bb5SSascha Wildner  sftp -b - -o "ControlPath=$L_SHARED_CON" "ignored" <<-EOF || return 1
305*ba1276acSMatthew Dillon	-mkdir "$AUTH_KEY_DIR"
306*ba1276acSMatthew Dillon	chmod 700 "$AUTH_KEY_DIR"
307*ba1276acSMatthew Dillon	put $L_KEYS "$AUTH_KEY_FILE"
308*ba1276acSMatthew Dillon	chmod 600 "$AUTH_KEY_FILE"
30950a69bb5SSascha Wildner	EOF
31050a69bb5SSascha Wildner  #shellcheck disable=SC2064
31150a69bb5SSascha Wildner  eval "$L_CLEANUP" && trap "$SCRATCH_CLEANUP" EXIT TERM INT QUIT
31250a69bb5SSascha Wildner}
31350a69bb5SSascha Wildner
31450a69bb5SSascha Wildner
31550a69bb5SSascha Wildner# create a scratch dir for any temporary files needed
31650a69bb5SSascha Wildnerif SCRATCH_DIR=$(mktemp -d ~/.ssh/ssh-copy-id.XXXXXXXXXX) &&
31750a69bb5SSascha Wildner    [ "$SCRATCH_DIR" ] && [ -d "$SCRATCH_DIR" ]
31850a69bb5SSascha Wildnerthen
31950a69bb5SSascha Wildner  chmod 0700 "$SCRATCH_DIR"
32050a69bb5SSascha Wildner  SCRATCH_CLEANUP="rm -rf \"$SCRATCH_DIR\""
32150a69bb5SSascha Wildner  #shellcheck disable=SC2064
32250a69bb5SSascha Wildner  trap "$SCRATCH_CLEANUP" EXIT TERM INT QUIT
32350a69bb5SSascha Wildnerelse
32450a69bb5SSascha Wildner  printf '%s: ERROR: failed to create required temporary directory under ~/.ssh\n' "$0" >&2
32550a69bb5SSascha Wildner  exit 1
32650a69bb5SSascha Wildnerfi
32750a69bb5SSascha Wildner
32850a69bb5SSascha WildnerREMOTE_VERSION=$($SSH -v -o PreferredAuthentications=',' -o ControlPath=none "$@" 2>&1 |
32953254411SSascha Wildner                 sed -ne 's/.*remote software version //p')
33053254411SSascha Wildner
33150a69bb5SSascha Wildner# shellcheck disable=SC2029
33253254411SSascha Wildnercase "$REMOTE_VERSION" in
33353254411SSascha Wildner  NetScreen*)
33453254411SSascha Wildner    populate_new_ids 1
33553254411SSascha Wildner    for KEY in $(printf "%s" "$NEW_IDS" | cut -d' ' -f2) ; do
33650a69bb5SSascha Wildner      KEY_NO=$((KEY_NO + 1))
33750a69bb5SSascha Wildner      printf '%s\n' "$KEY" | grep ssh-dss >/dev/null || {
33853254411SSascha Wildner         printf '%s: WARNING: Non-dsa key (#%d) skipped (NetScreen only supports DSA keys)\n' "$0" "$KEY_NO" >&2
33953254411SSascha Wildner         continue
34053254411SSascha Wildner      }
34150a69bb5SSascha Wildner      [ "$DRY_RUN" ] || printf 'set ssh pka-dsa key %s\nsave\nexit\n' "$KEY" | $SSH -T "$@" >/dev/null 2>&1
34253254411SSascha Wildner      if [ $? = 255 ] ; then
34353254411SSascha Wildner        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
34453254411SSascha Wildner      else
34550a69bb5SSascha Wildner        ADDED=$((ADDED + 1))
34653254411SSascha Wildner      fi
34753254411SSascha Wildner    done
34853254411SSascha Wildner    if [ -z "$ADDED" ] ; then
34953254411SSascha Wildner      exit 1
35053254411SSascha Wildner    fi
35153254411SSascha Wildner    ;;
35253254411SSascha Wildner  *)
353*ba1276acSMatthew Dillon    # Assuming that the remote host treats $TARGET_PATH as one might expect
35453254411SSascha Wildner    populate_new_ids 0
35550a69bb5SSascha Wildner    if ! [ "$DRY_RUN" ] ; then
35650a69bb5SSascha Wildner      printf '%s\n' "$NEW_IDS" | \
35750a69bb5SSascha Wildner        if [ "$SFTP" ] ; then
35850a69bb5SSascha Wildner          #shellcheck disable=SC2119
35950a69bb5SSascha Wildner          installkeys_via_sftp
36050a69bb5SSascha Wildner        else
36150a69bb5SSascha Wildner          $SSH "$@" "$(installkeys_sh)"
36250a69bb5SSascha Wildner        fi || exit 1
36350a69bb5SSascha Wildner    fi
36453254411SSascha Wildner    ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l)
36553254411SSascha Wildner    ;;
36653254411SSascha Wildneresac
36753254411SSascha Wildner
36853254411SSascha Wildnerif [ "$DRY_RUN" ] ; then
36953254411SSascha Wildner  cat <<-EOF
37053254411SSascha Wildner	=-=-=-=-=-=-=-=
37153254411SSascha Wildner	Would have added the following key(s):
37253254411SSascha Wildner
37353254411SSascha Wildner	$NEW_IDS
37453254411SSascha Wildner	=-=-=-=-=-=-=-=
37553254411SSascha Wildner	EOF
37653254411SSascha Wildnerelse
377*ba1276acSMatthew Dillon  [ -z "$SFTP" ] || PORT_OPT=P
37853254411SSascha Wildner  cat <<-EOF
37953254411SSascha Wildner
38053254411SSascha Wildner	Number of key(s) added: $ADDED
38153254411SSascha Wildner
382*ba1276acSMatthew Dillon	Now try logging into the machine, with:   "${SFTP:-ssh}${SSH_PORT:+ -${PORT_OPT:-p} $SSH_PORT} ${OPTS_USER_HOST}"
38353254411SSascha Wildner	and check to make sure that only the key(s) you wanted were added.
38453254411SSascha Wildner
38553254411SSascha Wildner	EOF
38653254411SSascha Wildnerfi
38753254411SSascha Wildner
38853254411SSascha Wildner# =-=-=-=
389